mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 10: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>>,
|
||||
on_close: OnceCell<OnCloseCallback>,
|
||||
configured: Cell<bool>,
|
||||
repositioning: Cell<bool>,
|
||||
needs_relayout: Cell<bool>,
|
||||
component_instance: RefCell<Option<ComponentInstance>>,
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +40,8 @@ impl PopupWindow {
|
|||
popup_handle: Cell::new(None),
|
||||
on_close: OnceCell::new(),
|
||||
configured: Cell::new(false),
|
||||
repositioning: Cell::new(false),
|
||||
needs_relayout: Cell::new(false),
|
||||
component_instance: RefCell::new(None),
|
||||
}
|
||||
})
|
||||
|
|
@ -113,6 +117,15 @@ impl PopupWindow {
|
|||
self.set_size(WindowSize::Logical(slint::LogicalSize::new(width, height)));
|
||||
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 {
|
||||
|
|
@ -122,6 +135,11 @@ impl RenderableWindow for PopupWindow {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if self.repositioning.get() {
|
||||
info!("Popup repositioning in progress, skipping render");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if matches!(
|
||||
self.render_state.replace(RenderState::Clean),
|
||||
RenderState::Dirty
|
||||
|
|
@ -137,6 +155,12 @@ impl RenderableWindow for PopupWindow {
|
|||
message: format!("Error rendering popup frame: {e}"),
|
||||
})?;
|
||||
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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,6 +336,17 @@ impl Dispatch<XdgPopup, ()> for AppState {
|
|||
}
|
||||
xdg_popup::Event::Repositioned { 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) {
|
||||
let id = PopupId(key);
|
||||
if let Some(popup) = self.state.borrow().popups.get(&id) {
|
||||
popup.window.begin_repositioning();
|
||||
popup
|
||||
.surface
|
||||
.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)> {
|
||||
let id = PopupId(key);
|
||||
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 log::info;
|
||||
use slint::PhysicalSize;
|
||||
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 wayland_client::{
|
||||
protocol::{wl_compositor::WlCompositor, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
|
|
@ -41,6 +44,10 @@ pub struct PopupSurface {
|
|||
pub xdg_popup: Rc<XdgPopup>,
|
||||
pub fractional_scale: Option<Rc<WpFractionalScaleV1>>,
|
||||
pub viewport: Option<Rc<WpViewport>>,
|
||||
popup_config: PopupConfig,
|
||||
xdg_wm_base: Rc<XdgWmBase>,
|
||||
queue_handle: QueueHandle<AppState>,
|
||||
scale_factor: Cell<f32>,
|
||||
}
|
||||
|
||||
impl PopupSurface {
|
||||
|
|
@ -95,6 +102,10 @@ impl PopupSurface {
|
|||
xdg_popup,
|
||||
fractional_scale,
|
||||
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
|
||||
);
|
||||
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) {
|
||||
info!("Destroying popup surface");
|
||||
self.xdg_popup.destroy();
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ impl<'a> PopupBuilder<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn bind_anchored(self, trigger_callback: &str, strategy: AnchorStrategy) -> Result<()> {
|
||||
let component_name = self.component.clone();
|
||||
let grab = self.grab;
|
||||
|
|
@ -245,28 +246,48 @@ impl<'a> PopupBuilder<'a> {
|
|||
anchor_h
|
||||
);
|
||||
|
||||
let mut builder = PopupRequest::builder(component_clone.clone())
|
||||
.at(PopupAt::AnchorRect {
|
||||
x: anchor_x,
|
||||
y: anchor_y,
|
||||
w: anchor_w,
|
||||
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
|
||||
let (reference_x, reference_y, mode) = match strategy {
|
||||
AnchorStrategy::CenterBottom => {
|
||||
let center_x = anchor_x + anchor_w / 2.0;
|
||||
let bottom_y = anchor_y + anchor_h;
|
||||
(center_x, bottom_y, PopupPositioningMode::TopCenter)
|
||||
}
|
||||
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 {
|
||||
builder = builder.close_on(close_cb.clone());
|
||||
|
|
|
|||
|
|
@ -356,6 +356,10 @@ impl ShellContext<'_> {
|
|||
popup_key_cell.set(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);
|
||||
} else {
|
||||
return Err(Error::Domain(DomainError::Configuration {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue