feat: make popup requests explicit and typed

This commit is contained in:
drendog 2025-11-03 17:30:52 +01:00
parent f604a49268
commit a32140d41d
Signed by: dwenya
GPG key ID: 8DD77074645332D0
5 changed files with 252 additions and 101 deletions

View file

@ -180,11 +180,25 @@ impl WaylandWindowingSystem {
let serial = serial_holder.get(); let serial = serial_holder.get();
let params = popup_manager_clone.take_pending_popup_config(); let params = if let Some((request, width, height)) = popup_manager_clone.take_pending_popup_request() {
log::info!(
"Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}",
request.component,
request.at.position().0,
request.at.position().1,
width,
height,
request.mode
);
let params = if let Some(mut p) = params { CreatePopupParams {
p.last_pointer_serial = serial; last_pointer_serial: serial,
p reference_x: request.at.position().0,
reference_y: request.at.position().1,
width,
height,
positioning_mode: request.mode,
}
} else { } else {
let output_size = popup_manager_clone.output_size(); let output_size = popup_manager_clone.output_size();
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
@ -192,7 +206,7 @@ impl WaylandWindowingSystem {
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
let default_height = output_size.height as f32; let default_height = output_size.height as f32;
log::warn!("No popup config provided, using output size ({default_width}x{default_height}) as defaults"); log::warn!("No popup request provided, using output size ({default_width}x{default_height}) as defaults");
CreatePopupParams { CreatePopupParams {
last_pointer_serial: serial, last_pointer_serial: serial,
reference_x: 0.0, reference_x: 0.0,

View file

@ -3,6 +3,7 @@ use crate::rendering::egl::context::EGLContext;
use crate::rendering::femtovg::popup_window::PopupWindow; use crate::rendering::femtovg::popup_window::PopupWindow;
use layer_shika_domain::value_objects::popup_config::PopupConfig; use layer_shika_domain::value_objects::popup_config::PopupConfig;
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
use layer_shika_domain::value_objects::popup_request::PopupRequest;
use log::info; use log::info;
use slab::Slab; use slab::Slab;
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize}; use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize};
@ -72,7 +73,7 @@ pub struct PopupManager {
popups: RefCell<Slab<ActivePopup>>, popups: RefCell<Slab<ActivePopup>>,
current_scale_factor: RefCell<f32>, current_scale_factor: RefCell<f32>,
current_output_size: RefCell<PhysicalSize>, current_output_size: RefCell<PhysicalSize>,
pending_popup_config: RefCell<Option<CreatePopupParams>>, pending_popup_request: RefCell<Option<(PopupRequest, f32, f32)>>,
last_popup_key: RefCell<Option<usize>>, last_popup_key: RefCell<Option<usize>>,
} }
@ -84,33 +85,18 @@ impl PopupManager {
popups: RefCell::new(Slab::new()), popups: RefCell::new(Slab::new()),
current_scale_factor: RefCell::new(initial_scale_factor), current_scale_factor: RefCell::new(initial_scale_factor),
current_output_size: RefCell::new(PhysicalSize::new(0, 0)), current_output_size: RefCell::new(PhysicalSize::new(0, 0)),
pending_popup_config: RefCell::new(None), pending_popup_request: RefCell::new(None),
last_popup_key: RefCell::new(None), last_popup_key: RefCell::new(None),
} }
} }
pub fn set_pending_popup_config( pub fn set_pending_popup_request(&self, request: PopupRequest, width: f32, height: f32) {
&self, *self.pending_popup_request.borrow_mut() = Some((request, width, height));
reference_x: f32,
reference_y: f32,
width: f32,
height: f32,
positioning_mode: PopupPositioningMode,
) {
let last_pointer_serial = 0;
*self.pending_popup_config.borrow_mut() = Some(CreatePopupParams {
last_pointer_serial,
reference_x,
reference_y,
width,
height,
positioning_mode,
});
} }
#[must_use] #[must_use]
pub fn take_pending_popup_config(&self) -> Option<CreatePopupParams> { pub fn take_pending_popup_request(&self) -> Option<(PopupRequest, f32, f32)> {
self.pending_popup_config.borrow_mut().take() self.pending_popup_request.borrow_mut().take()
} }
pub fn close_current_popup(&self) { pub fn close_current_popup(&self) {

View file

@ -16,6 +16,9 @@ use layer_shika_adapters::wayland::{
use layer_shika_domain::config::WindowConfig; use layer_shika_domain::config::WindowConfig;
use layer_shika_domain::errors::DomainError; use layer_shika_domain::errors::DomainError;
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
use layer_shika_domain::value_objects::popup_request::{
PopupAt, PopupHandle, PopupRequest, PopupSize,
};
use std::cell::{Ref, RefCell}; use std::cell::{Ref, RefCell};
use std::os::unix::io::AsFd; use std::os::unix::io::AsFd;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
@ -129,6 +132,69 @@ impl RuntimeState<'_> {
self.window_state.compilation_result() self.window_state.compilation_result()
} }
pub fn show_popup(&mut self, req: PopupRequest) -> Result<()> {
let compilation_result = self.compilation_result().ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: "No compilation result available for popup creation".to_string(),
})
})?;
let definition = compilation_result
.component(&req.component)
.ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: format!(
"{} component not found in compilation result",
req.component
),
})
})?;
self.close_current_popup()?;
let (width, height) = match req.size {
PopupSize::Fixed { w, h } => {
log::debug!("Using fixed popup size: {}x{}", w, h);
(w, h)
}
PopupSize::Content => self.measure_popup_dimensions(&definition)?,
};
let popup_manager = self
.window_state
.popup_manager()
.as_ref()
.ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: "No popup manager available".to_string(),
})
})
.map(Rc::clone)?;
log::debug!(
"Setting pending popup request for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
req.component,
width,
height,
req.at.position().0,
req.at.position().1,
req.mode
);
popup_manager.set_pending_popup_request(req, width, height);
Self::create_popup_instance(&definition, &popup_manager)?;
Ok(())
}
pub fn close_popup(&mut self, handle: PopupHandle) -> Result<()> {
if let Some(popup_manager) = self.window_state.popup_manager() {
popup_manager.destroy_popup(handle.key());
}
Ok(())
}
pub fn close_current_popup(&mut self) -> Result<()> { pub fn close_current_popup(&mut self) -> Result<()> {
if let Some(popup_manager) = self.window_state.popup_manager() { if let Some(popup_manager) = self.window_state.popup_manager() {
popup_manager.close_current_popup(); popup_manager.close_current_popup();
@ -208,78 +274,6 @@ impl RuntimeState<'_> {
Ok(instance) Ok(instance)
} }
pub fn show_popup_component(
&mut self,
component_name: &str,
position: Option<(f32, f32)>,
size: Option<(f32, f32)>,
positioning_mode: PopupPositioningMode,
) -> Result<()> {
let compilation_result = self.compilation_result().ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: "No compilation result available for popup creation".to_string(),
})
})?;
let definition = compilation_result
.component(component_name)
.ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: format!(
"{} component not found in compilation result",
component_name
),
})
})?;
self.close_current_popup()?;
let (width, height) = if let Some(explicit_size) = size {
log::debug!(
"Using explicit popup size: {}x{}",
explicit_size.0,
explicit_size.1
);
explicit_size
} else {
self.measure_popup_dimensions(&definition)?
};
let popup_manager = self
.window_state
.popup_manager()
.as_ref()
.ok_or_else(|| {
Error::Domain(DomainError::Configuration {
message: "No popup manager available".to_string(),
})
})
.map(Rc::clone)?;
let (reference_x, reference_y) = position.unwrap_or((0.0, 0.0));
popup_manager.set_pending_popup_config(
reference_x,
reference_y,
width,
height,
positioning_mode,
);
log::debug!(
"Creating final popup instance with dimensions {}x{} at position ({}, {}), mode: {:?}",
width,
height,
reference_x,
reference_y,
positioning_mode
);
Self::create_popup_instance(&definition, &popup_manager)?;
Ok(())
}
} }
pub struct WindowingSystem { pub struct WindowingSystem {
@ -344,9 +338,14 @@ impl WindowingSystem {
let (_token, sender) = event_loop_handle.add_channel( let (_token, sender) = event_loop_handle.add_channel(
move |(component_name, x, y): (String, f32, f32), mut state| { move |(component_name, x, y): (String, f32, f32), mut state| {
let mode = *popup_mode_for_channel.borrow(); let mode = *popup_mode_for_channel.borrow();
if let Err(e) =
state.show_popup_component(&component_name, Some((x, y)), None, mode) let request = PopupRequest::builder(component_name)
{ .at(PopupAt::absolute(x, y))
.size(PopupSize::content())
.mode(mode)
.build();
if let Err(e) = state.show_popup(request) {
log::error!("Failed to show popup: {}", e); log::error!("Failed to show popup: {}", e);
} }
}, },

View file

@ -5,3 +5,4 @@ pub mod layer;
pub mod margins; pub mod margins;
pub mod popup_config; pub mod popup_config;
pub mod popup_positioning_mode; pub mod popup_positioning_mode;
pub mod popup_request;

View file

@ -0,0 +1,151 @@
use super::popup_positioning_mode::PopupPositioningMode;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PopupHandle(usize);
impl PopupHandle {
#[must_use]
pub const fn new(key: usize) -> Self {
Self(key)
}
#[must_use]
pub const fn key(self) -> usize {
self.0
}
}
#[derive(Debug, Clone)]
pub struct PopupRequest {
pub component: String,
pub at: PopupAt,
pub size: PopupSize,
pub mode: PopupPositioningMode,
}
impl PopupRequest {
#[must_use]
pub fn new(
component: String,
at: PopupAt,
size: PopupSize,
mode: PopupPositioningMode,
) -> Self {
Self {
component,
at,
size,
mode,
}
}
#[must_use]
pub fn builder(component: String) -> PopupRequestBuilder {
PopupRequestBuilder::new(component)
}
}
#[derive(Debug, Clone)]
pub enum PopupAt {
Absolute { x: f32, y: f32 },
Cursor,
AnchorRect { x: f32, y: f32, w: f32, h: f32 },
}
impl PopupAt {
#[must_use]
pub const fn absolute(x: f32, y: f32) -> Self {
Self::Absolute { x, y }
}
#[must_use]
pub const fn cursor() -> Self {
Self::Cursor
}
#[must_use]
pub const fn anchor_rect(x: f32, y: f32, w: f32, h: f32) -> Self {
Self::AnchorRect { x, y, w, h }
}
#[must_use]
pub const fn position(&self) -> (f32, f32) {
match *self {
Self::Absolute { x, y } | Self::AnchorRect { x, y, .. } => (x, y),
Self::Cursor => (0.0, 0.0),
}
}
}
#[derive(Debug, Clone)]
pub enum PopupSize {
Fixed { w: f32, h: f32 },
Content,
}
impl PopupSize {
#[must_use]
pub const fn fixed(w: f32, h: f32) -> Self {
Self::Fixed { w, h }
}
#[must_use]
pub const fn content() -> Self {
Self::Content
}
#[must_use]
pub const fn dimensions(&self) -> Option<(f32, f32)> {
match *self {
Self::Fixed { w, h } => Some((w, h)),
Self::Content => None,
}
}
}
pub struct PopupRequestBuilder {
component: String,
at: PopupAt,
size: PopupSize,
mode: PopupPositioningMode,
}
impl PopupRequestBuilder {
#[must_use]
pub fn new(component: String) -> Self {
Self {
component,
at: PopupAt::Cursor,
size: PopupSize::Content,
mode: PopupPositioningMode::default(),
}
}
#[must_use]
pub const fn at(mut self, at: PopupAt) -> Self {
self.at = at;
self
}
#[must_use]
pub const fn size(mut self, size: PopupSize) -> Self {
self.size = size;
self
}
#[must_use]
pub const fn mode(mut self, mode: PopupPositioningMode) -> Self {
self.mode = mode;
self
}
#[must_use]
pub fn build(self) -> PopupRequest {
PopupRequest {
component: self.component,
at: self.at,
size: self.size,
mode: self.mode,
}
}
}