diff --git a/adapters/src/lib.rs b/adapters/src/lib.rs index 62b4675..0944cca 100644 --- a/adapters/src/lib.rs +++ b/adapters/src/lib.rs @@ -5,9 +5,6 @@ pub mod rendering; pub mod wayland; pub use rendering::femtovg::popup_window::PopupWindow; -pub use rendering::slint_integration::platform::{ - clear_popup_config, close_current_popup, get_popup_config, set_popup_config, -}; pub mod platform { pub use slint; diff --git a/adapters/src/rendering/slint_integration/platform.rs b/adapters/src/rendering/slint_integration/platform.rs index 97d5b7a..623f3f8 100644 --- a/adapters/src/rendering/slint_integration/platform.rs +++ b/adapters/src/rendering/slint_integration/platform.rs @@ -1,4 +1,3 @@ -use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; use slint::{ PlatformError, platform::{Platform, WindowAdapter}, @@ -7,134 +6,31 @@ use std::cell::{Cell, RefCell}; use std::rc::{Rc, Weak}; use crate::rendering::femtovg::main_window::FemtoVGWindow; -use crate::rendering::femtovg::popup_window::PopupWindow; type PopupCreator = dyn Fn() -> Result, PlatformError>; -type PopupConfigData = (f32, f32, f32, f32, PopupPositioningMode); - -thread_local! { - static CURRENT_PLATFORM: RefCell>> = const { RefCell::new(None) }; -} - -pub fn close_current_popup() { - CURRENT_PLATFORM.with(|platform| { - if let Some(weak_platform) = platform.borrow().as_ref() { - if let Some(strong_platform) = weak_platform.upgrade() { - strong_platform.close_current_popup(); - } - } - }); -} - -pub fn set_popup_config( - reference_x: f32, - reference_y: f32, - width: f32, - height: f32, - positioning_mode: PopupPositioningMode, -) { - CURRENT_PLATFORM.with(|platform| { - if let Some(weak_platform) = platform.borrow().as_ref() { - if let Some(strong_platform) = weak_platform.upgrade() { - strong_platform.set_popup_config( - reference_x, - reference_y, - width, - height, - positioning_mode, - ); - } - } - }); -} - -pub fn get_popup_config() -> Option { - CURRENT_PLATFORM.with(|platform| { - platform - .borrow() - .as_ref() - .and_then(Weak::upgrade) - .and_then(|strong| strong.get_popup_config()) - }) -} - -pub fn clear_popup_config() { - CURRENT_PLATFORM.with(|platform| { - if let Some(weak_platform) = platform.borrow().as_ref() { - if let Some(strong_platform) = weak_platform.upgrade() { - strong_platform.clear_popup_config(); - } - } - }); -} pub struct CustomSlintPlatform { main_window: Weak, popup_creator: RefCell>>, first_call: Cell, - last_popup: RefCell>>, - popup_config: RefCell>, } impl CustomSlintPlatform { #[must_use] pub fn new(window: &Rc) -> Rc { - let platform = Rc::new(Self { + Rc::new(Self { main_window: Rc::downgrade(window), popup_creator: RefCell::new(None), first_call: Cell::new(true), - last_popup: RefCell::new(None), - popup_config: RefCell::new(None), - }); - - CURRENT_PLATFORM.with(|current| { - *current.borrow_mut() = Some(Rc::downgrade(&platform)); - }); - - platform + }) } - #[allow(dead_code)] pub fn set_popup_creator(&self, creator: F) where F: Fn() -> Result, PlatformError> + 'static, { *self.popup_creator.borrow_mut() = Some(Rc::new(creator)); } - - pub fn set_last_popup(&self, popup: &Rc) { - *self.last_popup.borrow_mut() = Some(Rc::downgrade(popup)); - } - - pub fn close_current_popup(&self) { - if let Some(weak_popup) = self.last_popup.borrow().as_ref() { - if let Some(popup) = weak_popup.upgrade() { - popup.close_popup(); - } - } - *self.last_popup.borrow_mut() = None; - } - - pub fn set_popup_config( - &self, - reference_x: f32, - reference_y: f32, - width: f32, - height: f32, - positioning_mode: PopupPositioningMode, - ) { - *self.popup_config.borrow_mut() = - Some((reference_x, reference_y, width, height, positioning_mode)); - } - - #[must_use] - pub fn get_popup_config(&self) -> Option { - *self.popup_config.borrow() - } - - pub fn clear_popup_config(&self) { - *self.popup_config.borrow_mut() = None; - } } impl Platform for CustomSlintPlatform { diff --git a/adapters/src/wayland/shell_adapter.rs b/adapters/src/wayland/shell_adapter.rs index 4f0199c..c412f99 100644 --- a/adapters/src/wayland/shell_adapter.rs +++ b/adapters/src/wayland/shell_adapter.rs @@ -11,9 +11,8 @@ use crate::wayland::{ use crate::{ errors::{EventLoopError, LayerShikaError, RenderingError, Result}, rendering::{ - egl::context::EGLContext, - femtovg::main_window::FemtoVGWindow, - slint_integration::platform::{CustomSlintPlatform, clear_popup_config, get_popup_config}, + egl::context::EGLContext, femtovg::main_window::FemtoVGWindow, + slint_integration::platform::CustomSlintPlatform, }, }; use core::result::Result as CoreResult; @@ -172,7 +171,6 @@ impl WaylandWindowingSystem { info!("Setting up popup creator with xdg-shell support"); let popup_manager_clone = Rc::clone(popup_manager); - let platform_weak = Rc::downgrade(platform); let layer_surface = state.layer_surface(); let queue_handle = event_queue.handle(); let serial_holder = Rc::clone(shared_serial); @@ -182,45 +180,37 @@ impl WaylandWindowingSystem { let serial = serial_holder.get(); - let output_size = popup_manager_clone.output_size(); - #[allow(clippy::cast_precision_loss)] - let default_width = output_size.width as f32; - #[allow(clippy::cast_precision_loss)] - let default_height = output_size.height as f32; + let params = popup_manager_clone.take_pending_popup_config(); - let (reference_x, reference_y, width, height, positioning_mode) = get_popup_config() - .unwrap_or_else(|| { - log::warn!("No popup config provided, using output size ({default_width}x{default_height}) as defaults"); - ( - 0.0, - 0.0, - default_width, - default_height, - PopupPositioningMode::TopLeft, - ) - }); + let params = if let Some(mut p) = params { + p.last_pointer_serial = serial; + p + } else { + let output_size = popup_manager_clone.output_size(); + #[allow(clippy::cast_precision_loss)] + let default_width = output_size.width as f32; + #[allow(clippy::cast_precision_loss)] + let default_height = output_size.height as f32; - clear_popup_config(); + log::warn!("No popup config provided, using output size ({default_width}x{default_height}) as defaults"); + CreatePopupParams { + last_pointer_serial: serial, + reference_x: 0.0, + reference_y: 0.0, + width: default_width, + height: default_height, + positioning_mode: PopupPositioningMode::TopLeft, + } + }; let popup_window = popup_manager_clone .create_popup( &queue_handle, &layer_surface, - CreatePopupParams { - last_pointer_serial: serial, - reference_x, - reference_y, - width, - height, - positioning_mode, - }, + params, ) .map_err(|e| PlatformError::Other(format!("Failed to create popup: {e}")))?; - if let Some(platform) = platform_weak.upgrade() { - platform.set_last_popup(&popup_window); - } - let result = Ok(popup_window as Rc); match &result { diff --git a/adapters/src/wayland/surfaces/popup_manager.rs b/adapters/src/wayland/surfaces/popup_manager.rs index dbf76b1..4b4918d 100644 --- a/adapters/src/wayland/surfaces/popup_manager.rs +++ b/adapters/src/wayland/surfaces/popup_manager.rs @@ -72,6 +72,8 @@ pub struct PopupManager { popups: RefCell>, current_scale_factor: RefCell, current_output_size: RefCell, + pending_popup_config: RefCell>, + last_popup_key: RefCell>, } impl PopupManager { @@ -82,6 +84,39 @@ impl PopupManager { popups: RefCell::new(Slab::new()), current_scale_factor: RefCell::new(initial_scale_factor), current_output_size: RefCell::new(PhysicalSize::new(0, 0)), + pending_popup_config: RefCell::new(None), + last_popup_key: RefCell::new(None), + } + } + + pub fn set_pending_popup_config( + &self, + 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] + pub fn take_pending_popup_config(&self) -> Option { + self.pending_popup_config.borrow_mut().take() + } + + pub fn close_current_popup(&self) { + let key = self.last_popup_key.borrow_mut().take(); + if let Some(key) = key { + self.destroy_popup(key); } } @@ -171,6 +206,7 @@ impl PopupManager { window: Rc::clone(&popup_window), }); popup_window.set_popup_manager(Rc::downgrade(self), key); + *self.last_popup_key.borrow_mut() = Some(key); info!("Popup window created successfully with key {key}"); diff --git a/composition/src/lib.rs b/composition/src/lib.rs index 393b4e8..f795863 100644 --- a/composition/src/lib.rs +++ b/composition/src/lib.rs @@ -9,9 +9,7 @@ use std::result::Result as StdResult; pub use builder::LayerShika; pub use layer_shika_adapters::PopupWindow; -pub use layer_shika_adapters::close_current_popup; pub use layer_shika_adapters::platform::{slint, slint_interpreter}; -pub use layer_shika_adapters::{clear_popup_config, get_popup_config, set_popup_config}; pub use layer_shika_domain::value_objects::anchor::AnchorEdges; pub use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity; pub use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; diff --git a/composition/src/system.rs b/composition/src/system.rs index 238aa89..75c0268 100644 --- a/composition/src/system.rs +++ b/composition/src/system.rs @@ -12,7 +12,6 @@ use layer_shika_adapters::wayland::{ config::WaylandWindowConfig, shell_adapter::WaylandWindowingSystem, surfaces::surface_state::WindowState, }; -use layer_shika_adapters::{clear_popup_config, close_current_popup, set_popup_config}; use layer_shika_domain::config::WindowConfig; use layer_shika_domain::errors::DomainError; use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode; @@ -129,8 +128,15 @@ impl RuntimeState<'_> { self.window_state.compilation_result() } + pub fn close_current_popup(&mut self) -> Result<()> { + if let Some(popup_manager) = self.window_state.popup_manager() { + popup_manager.close_current_popup(); + } + Ok(()) + } + pub fn show_popup_component( - &self, + &mut self, component_name: &str, position: Option<(f32, f32)>, size: Option<(f32, f32)>, @@ -153,41 +159,59 @@ impl RuntimeState<'_> { }) })?; - close_current_popup(); + self.close_current_popup()?; - let temp_instance = definition.create().map_err(|e| { - Error::Domain(DomainError::Configuration { - message: format!("Failed to create temporary popup instance: {}", e), - }) - })?; - temp_instance.hide().map_err(|e| { - Error::Domain(DomainError::Configuration { - message: format!("Failed to hide temporary popup instance: {}", e), - }) - })?; - - let width: f32 = temp_instance - .get_property("popup-width") - .ok() - .and_then(|v| v.try_into().ok()) - .or(size.map(|(w, _)| w)) - .unwrap_or(300.0); - - let height: f32 = temp_instance - .get_property("popup-height") - .ok() - .and_then(|v| v.try_into().ok()) - .or(size.map(|(_, h)| h)) - .unwrap_or(400.0); - - drop(temp_instance); - close_current_popup(); - - if let Some((reference_x, reference_y)) = position { - set_popup_config(reference_x, reference_y, width, height, positioning_mode); + let (width, height) = if let Some(explicit_size) = size { + explicit_size } else { - clear_popup_config(); - } + let temp_instance = definition.create().map_err(|e| { + Error::Domain(DomainError::Configuration { + message: format!("Failed to create temporary popup instance: {}", e), + }) + })?; + + temp_instance.show().map_err(|e| { + Error::Domain(DomainError::Configuration { + message: format!("Failed to show temporary popup instance: {}", e), + }) + })?; + + let width: f32 = temp_instance + .get_property("popup-width") + .ok() + .and_then(|v| v.try_into().ok()) + .unwrap_or(120.0); + + let height: f32 = temp_instance + .get_property("popup-height") + .ok() + .and_then(|v| v.try_into().ok()) + .unwrap_or(120.0); + + drop(temp_instance); + self.close_current_popup()?; + + (width, height) + }; + + 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, + ); let instance = definition.create().map_err(|e| { Error::Domain(DomainError::Configuration { @@ -195,9 +219,10 @@ impl RuntimeState<'_> { }) })?; + let popup_manager_for_callback = Rc::clone(&popup_manager); instance .set_callback("closed", move |_| { - close_current_popup(); + popup_manager_for_callback.close_current_popup(); Value::Void }) .map_err(|e| { @@ -276,7 +301,7 @@ impl WindowingSystem { let popup_mode_for_channel = Rc::clone(&self.popup_positioning_mode); let (_token, sender) = event_loop_handle.add_channel( - move |(component_name, x, y): (String, f32, f32), state| { + move |(component_name, x, y): (String, f32, f32), mut state| { let mode = *popup_mode_for_channel.borrow(); if let Err(e) = state.show_popup_component(&component_name, Some((x, y)), None, mode)