mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 11:25:54 +00:00
fix: anchoring and repositioning popup
This commit is contained in:
parent
64a18153b6
commit
73f1b52890
6 changed files with 143 additions and 20 deletions
|
|
@ -21,6 +21,8 @@ pub struct PopupWindow {
|
||||||
popup_handle: Cell<Option<PopupHandle>>,
|
popup_handle: Cell<Option<PopupHandle>>,
|
||||||
on_close: OnceCell<OnCloseCallback>,
|
on_close: OnceCell<OnCloseCallback>,
|
||||||
configured: Cell<bool>,
|
configured: Cell<bool>,
|
||||||
|
repositioning: Cell<bool>,
|
||||||
|
needs_relayout: Cell<bool>,
|
||||||
component_instance: RefCell<Option<ComponentInstance>>,
|
component_instance: RefCell<Option<ComponentInstance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,6 +40,8 @@ impl PopupWindow {
|
||||||
popup_handle: Cell::new(None),
|
popup_handle: Cell::new(None),
|
||||||
on_close: OnceCell::new(),
|
on_close: OnceCell::new(),
|
||||||
configured: Cell::new(false),
|
configured: Cell::new(false),
|
||||||
|
repositioning: Cell::new(false),
|
||||||
|
needs_relayout: Cell::new(false),
|
||||||
component_instance: RefCell::new(None),
|
component_instance: RefCell::new(None),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -113,6 +117,15 @@ impl PopupWindow {
|
||||||
self.set_size(WindowSize::Logical(slint::LogicalSize::new(width, height)));
|
self.set_size(WindowSize::Logical(slint::LogicalSize::new(width, height)));
|
||||||
RenderableWindow::request_redraw(self);
|
RenderableWindow::request_redraw(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn begin_repositioning(&self) {
|
||||||
|
self.repositioning.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_repositioning(&self) {
|
||||||
|
self.repositioning.set(false);
|
||||||
|
self.needs_relayout.set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderableWindow for PopupWindow {
|
impl RenderableWindow for PopupWindow {
|
||||||
|
|
@ -122,6 +135,11 @@ impl RenderableWindow for PopupWindow {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.repositioning.get() {
|
||||||
|
info!("Popup repositioning in progress, skipping render");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
self.render_state.replace(RenderState::Clean),
|
self.render_state.replace(RenderState::Clean),
|
||||||
RenderState::Dirty
|
RenderState::Dirty
|
||||||
|
|
@ -137,6 +155,12 @@ impl RenderableWindow for PopupWindow {
|
||||||
message: format!("Error rendering popup frame: {e}"),
|
message: format!("Error rendering popup frame: {e}"),
|
||||||
})?;
|
})?;
|
||||||
info!("Popup frame rendered successfully");
|
info!("Popup frame rendered successfully");
|
||||||
|
|
||||||
|
if self.needs_relayout.get() {
|
||||||
|
info!("Popup needs relayout, requesting additional render");
|
||||||
|
self.needs_relayout.set(false);
|
||||||
|
RenderableWindow::request_redraw(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,17 @@ impl Dispatch<XdgPopup, ()> for AppState {
|
||||||
}
|
}
|
||||||
xdg_popup::Event::Repositioned { token } => {
|
xdg_popup::Event::Repositioned { token } => {
|
||||||
info!("XdgPopup repositioned with token {token}");
|
info!("XdgPopup repositioned with token {token}");
|
||||||
|
|
||||||
|
let popup_id = xdg_popup.id();
|
||||||
|
for window in state.all_outputs_mut() {
|
||||||
|
if let Some(popup_manager) = window.popup_manager() {
|
||||||
|
if let Some(handle) = popup_manager.find_by_xdg_popup(&popup_id) {
|
||||||
|
info!("Committing popup surface after reposition");
|
||||||
|
popup_manager.commit_popup_surface(handle.key());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -445,12 +445,22 @@ impl PopupManager {
|
||||||
pub fn update_popup_viewport(&self, key: usize, logical_width: i32, logical_height: i32) {
|
pub fn update_popup_viewport(&self, key: usize, logical_width: i32, logical_height: i32) {
|
||||||
let id = PopupId(key);
|
let id = PopupId(key);
|
||||||
if let Some(popup) = self.state.borrow().popups.get(&id) {
|
if let Some(popup) = self.state.borrow().popups.get(&id) {
|
||||||
|
popup.window.begin_repositioning();
|
||||||
popup
|
popup
|
||||||
.surface
|
.surface
|
||||||
.update_viewport_size(logical_width, logical_height);
|
.update_viewport_size(logical_width, logical_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn commit_popup_surface(&self, key: usize) {
|
||||||
|
let id = PopupId(key);
|
||||||
|
if let Some(popup) = self.state.borrow().popups.get(&id) {
|
||||||
|
popup.surface.surface.commit();
|
||||||
|
popup.window.end_repositioning();
|
||||||
|
popup.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_popup_info(&self, key: usize) -> Option<(PopupRequest, u32)> {
|
pub fn get_popup_info(&self, key: usize) -> Option<(PopupRequest, u32)> {
|
||||||
let id = PopupId(key);
|
let id = PopupId(key);
|
||||||
self.state
|
self.state
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
use layer_shika_domain::dimensions::{LogicalSize as DomainLogicalSize, ScaleFactor};
|
||||||
|
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
||||||
use layer_shika_domain::value_objects::popup_config::PopupConfig;
|
use layer_shika_domain::value_objects::popup_config::PopupConfig;
|
||||||
use log::info;
|
use log::info;
|
||||||
use slint::PhysicalSize;
|
use slint::PhysicalSize;
|
||||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
protocol::{wl_compositor::WlCompositor, wl_seat::WlSeat, wl_surface::WlSurface},
|
protocol::{wl_compositor::WlCompositor, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||||
|
|
@ -41,6 +44,10 @@ pub struct PopupSurface {
|
||||||
pub xdg_popup: Rc<XdgPopup>,
|
pub xdg_popup: Rc<XdgPopup>,
|
||||||
pub fractional_scale: Option<Rc<WpFractionalScaleV1>>,
|
pub fractional_scale: Option<Rc<WpFractionalScaleV1>>,
|
||||||
pub viewport: Option<Rc<WpViewport>>,
|
pub viewport: Option<Rc<WpViewport>>,
|
||||||
|
popup_config: PopupConfig,
|
||||||
|
xdg_wm_base: Rc<XdgWmBase>,
|
||||||
|
queue_handle: QueueHandle<AppState>,
|
||||||
|
scale_factor: Cell<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PopupSurface {
|
impl PopupSurface {
|
||||||
|
|
@ -95,6 +102,10 @@ impl PopupSurface {
|
||||||
xdg_popup,
|
xdg_popup,
|
||||||
fractional_scale,
|
fractional_scale,
|
||||||
viewport,
|
viewport,
|
||||||
|
popup_config: params.popup_config,
|
||||||
|
xdg_wm_base: Rc::new(params.xdg_wm_base.clone()),
|
||||||
|
queue_handle: params.queue_handle.clone(),
|
||||||
|
scale_factor: Cell::new(params.scale_factor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,10 +158,52 @@ impl PopupSurface {
|
||||||
logical_height
|
logical_height
|
||||||
);
|
);
|
||||||
vp.set_destination(logical_width, logical_height);
|
vp.set_destination(logical_width, logical_height);
|
||||||
self.surface.commit();
|
|
||||||
|
self.reposition_popup(logical_width, logical_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
fn reposition_popup(&self, logical_width: i32, logical_height: i32) {
|
||||||
|
let scale_factor = self.scale_factor.get();
|
||||||
|
|
||||||
|
let updated_config = PopupConfig::new(
|
||||||
|
self.popup_config.reference_x(),
|
||||||
|
self.popup_config.reference_y(),
|
||||||
|
SurfaceDimensions::from_logical(
|
||||||
|
DomainLogicalSize::from_raw(logical_width as f32, logical_height as f32),
|
||||||
|
ScaleFactor::from_raw(scale_factor),
|
||||||
|
),
|
||||||
|
self.popup_config.positioning_mode(),
|
||||||
|
self.popup_config.output_bounds(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let calculated_x = updated_config.calculated_top_left_x() as i32;
|
||||||
|
let calculated_y = updated_config.calculated_top_left_y() as i32;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Repositioning popup: reference=({}, {}), new_size=({}x{}), new_top_left=({}, {})",
|
||||||
|
self.popup_config.reference_x(),
|
||||||
|
self.popup_config.reference_y(),
|
||||||
|
logical_width,
|
||||||
|
logical_height,
|
||||||
|
calculated_x,
|
||||||
|
calculated_y
|
||||||
|
);
|
||||||
|
|
||||||
|
let positioner = self.xdg_wm_base.create_positioner(&self.queue_handle, ());
|
||||||
|
positioner.set_anchor_rect(calculated_x, calculated_y, 1, 1);
|
||||||
|
positioner.set_size(logical_width, logical_height);
|
||||||
|
positioner.set_anchor(Anchor::TopLeft);
|
||||||
|
positioner.set_gravity(Gravity::BottomRight);
|
||||||
|
positioner.set_constraint_adjustment(ConstraintAdjustment::None);
|
||||||
|
|
||||||
|
self.xdg_popup.reposition(&positioner, 0);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn destroy(&self) {
|
pub fn destroy(&self) {
|
||||||
info!("Destroying popup surface");
|
info!("Destroying popup surface");
|
||||||
self.xdg_popup.destroy();
|
self.xdg_popup.destroy();
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,7 @@ impl<'a> PopupBuilder<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn bind_anchored(self, trigger_callback: &str, strategy: AnchorStrategy) -> Result<()> {
|
pub fn bind_anchored(self, trigger_callback: &str, strategy: AnchorStrategy) -> Result<()> {
|
||||||
let component_name = self.component.clone();
|
let component_name = self.component.clone();
|
||||||
let grab = self.grab;
|
let grab = self.grab;
|
||||||
|
|
@ -245,28 +246,48 @@ impl<'a> PopupBuilder<'a> {
|
||||||
anchor_h
|
anchor_h
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut builder = PopupRequest::builder(component_clone.clone())
|
let (reference_x, reference_y, mode) = match strategy {
|
||||||
.at(PopupAt::AnchorRect {
|
AnchorStrategy::CenterBottom => {
|
||||||
x: anchor_x,
|
let center_x = anchor_x + anchor_w / 2.0;
|
||||||
y: anchor_y,
|
let bottom_y = anchor_y + anchor_h;
|
||||||
w: anchor_w,
|
(center_x, bottom_y, PopupPositioningMode::TopCenter)
|
||||||
h: anchor_h,
|
|
||||||
})
|
|
||||||
.size(PopupSize::Content)
|
|
||||||
.grab(grab);
|
|
||||||
|
|
||||||
let mode = match strategy {
|
|
||||||
AnchorStrategy::CenterBottom => PopupPositioningMode::TopCenter,
|
|
||||||
AnchorStrategy::CenterTop => PopupPositioningMode::BottomCenter,
|
|
||||||
AnchorStrategy::RightBottom => PopupPositioningMode::TopRight,
|
|
||||||
AnchorStrategy::LeftTop => PopupPositioningMode::BottomLeft,
|
|
||||||
AnchorStrategy::RightTop => PopupPositioningMode::BottomRight,
|
|
||||||
AnchorStrategy::LeftBottom | AnchorStrategy::Cursor => {
|
|
||||||
PopupPositioningMode::TopLeft
|
|
||||||
}
|
}
|
||||||
|
AnchorStrategy::CenterTop => {
|
||||||
|
let center_x = anchor_x + anchor_w / 2.0;
|
||||||
|
(center_x, anchor_y, PopupPositioningMode::BottomCenter)
|
||||||
|
}
|
||||||
|
AnchorStrategy::RightBottom => {
|
||||||
|
let right_x = anchor_x + anchor_w;
|
||||||
|
let bottom_y = anchor_y + anchor_h;
|
||||||
|
(right_x, bottom_y, PopupPositioningMode::TopRight)
|
||||||
|
}
|
||||||
|
AnchorStrategy::LeftTop => {
|
||||||
|
(anchor_x, anchor_y, PopupPositioningMode::BottomLeft)
|
||||||
|
}
|
||||||
|
AnchorStrategy::RightTop => {
|
||||||
|
let right_x = anchor_x + anchor_w;
|
||||||
|
(right_x, anchor_y, PopupPositioningMode::BottomRight)
|
||||||
|
}
|
||||||
|
AnchorStrategy::LeftBottom => {
|
||||||
|
let bottom_y = anchor_y + anchor_h;
|
||||||
|
(anchor_x, bottom_y, PopupPositioningMode::TopLeft)
|
||||||
|
}
|
||||||
|
AnchorStrategy::Cursor => (anchor_x, anchor_y, PopupPositioningMode::TopLeft),
|
||||||
};
|
};
|
||||||
|
|
||||||
builder = builder.mode(mode);
|
log::debug!(
|
||||||
|
"Resolved anchored popup reference for '{}' -> ({}, {}), mode: {:?}",
|
||||||
|
component_clone,
|
||||||
|
reference_x,
|
||||||
|
reference_y,
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut builder = PopupRequest::builder(component_clone.clone())
|
||||||
|
.at(PopupAt::absolute(reference_x, reference_y))
|
||||||
|
.size(PopupSize::Content)
|
||||||
|
.grab(grab)
|
||||||
|
.mode(mode);
|
||||||
|
|
||||||
if let Some(ref close_cb) = close_cb {
|
if let Some(ref close_cb) = close_cb {
|
||||||
builder = builder.close_on(close_cb.clone());
|
builder = builder.close_on(close_cb.clone());
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,10 @@ impl ShellContext<'_> {
|
||||||
popup_key_cell.set(popup_handle.key());
|
popup_key_cell.set(popup_handle.key());
|
||||||
|
|
||||||
if let Some(popup_window) = popup_manager.get_popup_window(popup_handle.key()) {
|
if let Some(popup_window) = popup_manager.get_popup_window(popup_handle.key()) {
|
||||||
|
if matches!(req.size, PopupSize::Content) {
|
||||||
|
log::debug!("Marking content-sized popup as repositioning from creation");
|
||||||
|
popup_window.begin_repositioning();
|
||||||
|
}
|
||||||
popup_window.set_component_instance(instance);
|
popup_window.set_component_instance(instance);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Domain(DomainError::Configuration {
|
return Err(Error::Domain(DomainError::Configuration {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue