diff --git a/adapters/src/rendering/femtovg/popup_window.rs b/adapters/src/rendering/femtovg/popup_window.rs index 1ae8161..7fa0d2d 100644 --- a/adapters/src/rendering/femtovg/popup_window.rs +++ b/adapters/src/rendering/femtovg/popup_window.rs @@ -7,7 +7,7 @@ use slint::{ platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer}, }; use slint_interpreter::ComponentInstance; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, OnceCell, RefCell}; use std::rc::{Rc, Weak}; use super::main_window::RenderState; @@ -22,7 +22,7 @@ pub struct PopupWindow { popup_manager: RefCell>, popup_key: Cell>, configured: Cell, - component_instance: RefCell>, + component_instance: OnceCell, } #[allow(dead_code)] @@ -40,7 +40,7 @@ impl PopupWindow { popup_manager: RefCell::new(Weak::new()), popup_key: Cell::new(None), configured: Cell::new(false), - component_instance: RefCell::new(None), + component_instance: OnceCell::new(), } }) } @@ -121,7 +121,9 @@ impl PopupWindow { pub fn set_component_instance(&self, instance: ComponentInstance) { info!("Setting component instance for popup window"); - *self.component_instance.borrow_mut() = Some(instance); + if self.component_instance.set(instance).is_err() { + info!("Component instance already set for popup window"); + } } pub fn request_resize(&self, width: f32, height: f32) { diff --git a/adapters/src/rendering/slint_integration/platform.rs b/adapters/src/rendering/slint_integration/platform.rs index 623f3f8..50267b3 100644 --- a/adapters/src/rendering/slint_integration/platform.rs +++ b/adapters/src/rendering/slint_integration/platform.rs @@ -2,7 +2,7 @@ use slint::{ PlatformError, platform::{Platform, WindowAdapter}, }; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, OnceCell}; use std::rc::{Rc, Weak}; use crate::rendering::femtovg::main_window::FemtoVGWindow; @@ -11,7 +11,7 @@ type PopupCreator = dyn Fn() -> Result, PlatformError>; pub struct CustomSlintPlatform { main_window: Weak, - popup_creator: RefCell>>, + popup_creator: OnceCell>, first_call: Cell, } @@ -20,7 +20,7 @@ impl CustomSlintPlatform { pub fn new(window: &Rc) -> Rc { Rc::new(Self { main_window: Rc::downgrade(window), - popup_creator: RefCell::new(None), + popup_creator: OnceCell::new(), first_call: Cell::new(true), }) } @@ -29,7 +29,9 @@ impl CustomSlintPlatform { where F: Fn() -> Result, PlatformError> + 'static, { - *self.popup_creator.borrow_mut() = Some(Rc::new(creator)); + if self.popup_creator.set(Rc::new(creator)).is_err() { + log::warn!("Popup creator already set, ignoring new creator"); + } } } @@ -41,7 +43,7 @@ impl Platform for CustomSlintPlatform { .upgrade() .ok_or(PlatformError::NoPlatform) .map(|w| w as Rc) - } else if let Some(creator) = self.popup_creator.borrow().as_ref() { + } else if let Some(creator) = self.popup_creator.get() { creator() } else { Err(PlatformError::NoPlatform) diff --git a/adapters/src/wayland/event_handling/event_dispatcher.rs b/adapters/src/wayland/event_handling/event_dispatcher.rs index 7f3e140..5e9c7af 100644 --- a/adapters/src/wayland/event_handling/event_dispatcher.rs +++ b/adapters/src/wayland/event_handling/event_dispatcher.rs @@ -170,7 +170,7 @@ impl Dispatch for WindowState { state.set_last_pointer_serial(serial); state.set_current_pointer_position(surface_x, surface_y); - state.find_window_for_surface(&surface); + state.set_entered_surface(&surface); let position = state.current_pointer_position(); state.dispatch_to_active_window(WindowEvent::PointerMoved { position }); @@ -189,7 +189,7 @@ impl Dispatch for WindowState { wl_pointer::Event::Leave { .. } => { state.dispatch_to_active_window(WindowEvent::PointerExited); - state.clear_active_window(); + state.clear_entered_surface(); } wl_pointer::Event::Button { @@ -290,7 +290,6 @@ impl Dispatch for WindowState { if let Some(handle) = popup_handle { info!("Destroying popup with handle {handle:?}"); - state.clear_active_window_if_popup(handle.key()); if let Some(popup_service) = state.popup_service() { let _result = popup_service.close(handle); } diff --git a/adapters/src/wayland/services/popup_service.rs b/adapters/src/wayland/services/popup_service.rs index 9da9589..829cbb0 100644 --- a/adapters/src/wayland/services/popup_service.rs +++ b/adapters/src/wayland/services/popup_service.rs @@ -3,9 +3,9 @@ use crate::rendering::femtovg::popup_window::PopupWindow; use layer_shika_domain::value_objects::popup_request::{PopupHandle, PopupRequest}; use log::info; use slint::PhysicalSize; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::rc::Rc; -use wayland_client::{backend::ObjectId, protocol::wl_surface::WlSurface, Proxy}; +use wayland_client::{Proxy, backend::ObjectId, protocol::wl_surface::WlSurface}; use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1; use super::super::surfaces::popup_manager::PopupManager; @@ -14,11 +14,11 @@ use super::super::surfaces::popup_manager::PopupManager; pub enum ActiveWindow { Main, Popup(usize), + None, } pub struct PopupService { manager: Rc, - active_window: RefCell>, scale_factor: Cell, } @@ -28,7 +28,6 @@ impl PopupService { let scale_factor = manager.scale_factor(); Self { manager, - active_window: RefCell::new(None), scale_factor: Cell::new(scale_factor), } } @@ -38,16 +37,11 @@ impl PopupService { } pub fn close(&self, handle: PopupHandle) -> Result<()> { - let key = handle.key(); - self.clear_active_window_if_popup(key); - self.manager.destroy_popup(key); + self.manager.destroy_popup(handle.key()); Ok(()) } pub fn close_current(&self) { - if let Some(key) = self.manager.current_popup_key() { - self.clear_active_window_if_popup(key); - } self.manager.close_current_popup(); } @@ -99,35 +93,23 @@ impl PopupService { self.scale_factor.get() } - pub fn find_window_for_surface(&self, surface: &WlSurface, main_surface_id: &ObjectId) { + #[must_use] + pub fn get_active_window( + &self, + surface: &WlSurface, + main_surface_id: &ObjectId, + ) -> ActiveWindow { let surface_id = surface.id(); if *main_surface_id == surface_id { - *self.active_window.borrow_mut() = Some(ActiveWindow::Main); - return; + return ActiveWindow::Main; } if let Some(popup_key) = self.manager.find_popup_key_by_surface_id(&surface_id) { - *self.active_window.borrow_mut() = Some(ActiveWindow::Popup(popup_key)); - return; + return ActiveWindow::Popup(popup_key); } - *self.active_window.borrow_mut() = None; - } - - #[must_use] - pub fn active_window(&self) -> Option { - *self.active_window.borrow() - } - - pub fn clear_active_window(&self) { - *self.active_window.borrow_mut() = None; - } - - pub fn clear_active_window_if_popup(&self, popup_key: usize) { - if *self.active_window.borrow() == Some(ActiveWindow::Popup(popup_key)) { - *self.active_window.borrow_mut() = None; - } + ActiveWindow::None } #[allow(clippy::cast_precision_loss)] @@ -138,9 +120,9 @@ impl PopupService { ) { let fractional_scale_id = fractional_scale_proxy.id(); - if let Some(popup_key) = - self.manager - .find_popup_key_by_fractional_scale_id(&fractional_scale_id) + if let Some(popup_key) = self + .manager + .find_popup_key_by_fractional_scale_id(&fractional_scale_id) { if let Some(popup_window) = self.manager.get_popup_window(popup_key) { let new_scale_factor = scale_120ths as f32 / 120.0; diff --git a/adapters/src/wayland/surfaces/event_router.rs b/adapters/src/wayland/surfaces/event_router.rs index a6b324b..b67b7c2 100644 --- a/adapters/src/wayland/surfaces/event_router.rs +++ b/adapters/src/wayland/surfaces/event_router.rs @@ -25,26 +25,20 @@ impl EventRouter { self.popup_service = Some(popup_service); } - pub fn find_window_for_surface(&mut self, surface: &WlSurface) { + pub fn dispatch_to_active_window(&self, event: WindowEvent, surface: &WlSurface) { if let Some(popup_service) = &self.popup_service { - popup_service.find_window_for_surface(surface, &self.main_surface_id); - } - } - - pub fn dispatch_to_active_window(&self, event: WindowEvent) { - if let Some(popup_service) = &self.popup_service { - match popup_service.active_window() { - Some(ActiveWindow::Main) => { + match popup_service.get_active_window(surface, &self.main_surface_id) { + ActiveWindow::Main => { self.main_window.window().dispatch_event(event); } - Some(ActiveWindow::Popup(index)) => { + ActiveWindow::Popup(index) => { if let Some(popup_window) = popup_service.get_popup_window(PopupHandle::new(index)) { popup_window.dispatch_event(event); } } - None => {} + ActiveWindow::None => {} } } } diff --git a/adapters/src/wayland/surfaces/interaction_state.rs b/adapters/src/wayland/surfaces/interaction_state.rs index e1279be..19828b0 100644 --- a/adapters/src/wayland/surfaces/interaction_state.rs +++ b/adapters/src/wayland/surfaces/interaction_state.rs @@ -55,12 +55,8 @@ impl InteractionState { self.event_router.set_popup_service(popup_service); } - pub fn find_window_for_surface(&mut self, surface: &WlSurface) { - self.event_router.find_window_for_surface(surface); - } - - pub fn dispatch_to_active_window(&self, event: WindowEvent) { - self.event_router.dispatch_to_active_window(event); + pub fn dispatch_to_active_window(&self, event: WindowEvent, surface: &WlSurface) { + self.event_router.dispatch_to_active_window(event, surface); } pub fn scale_factor(&self) -> f32 { diff --git a/adapters/src/wayland/surfaces/popup_manager.rs b/adapters/src/wayland/surfaces/popup_manager.rs index 8983923..dfdfe61 100644 --- a/adapters/src/wayland/surfaces/popup_manager.rs +++ b/adapters/src/wayland/surfaces/popup_manager.rs @@ -76,23 +76,35 @@ impl Drop for ActivePopup { } } -struct PopupState { - scale_factor: f32, - output_size: PhysicalSize, -} - struct PendingPopup { request: PopupRequest, width: f32, height: f32, } +struct PopupManagerState { + popups: Slab, + scale_factor: f32, + output_size: PhysicalSize, + current_popup_key: Option, + pending_popup: Option, +} + +impl PopupManagerState { + fn new(initial_scale_factor: f32) -> Self { + Self { + popups: Slab::new(), + scale_factor: initial_scale_factor, + output_size: PhysicalSize::new(0, 0), + current_popup_key: None, + pending_popup: None, + } + } +} + pub struct PopupManager { context: PopupContext, - popups: RefCell>, - state: RefCell, - current_popup_key: RefCell>, - pending_popup: RefCell>, + state: RefCell, } impl PopupManager { @@ -100,18 +112,12 @@ impl PopupManager { pub fn new(context: PopupContext, initial_scale_factor: f32) -> Self { Self { context, - popups: RefCell::new(Slab::new()), - state: RefCell::new(PopupState { - scale_factor: initial_scale_factor, - output_size: PhysicalSize::new(0, 0), - }), - current_popup_key: RefCell::new(None), - pending_popup: RefCell::new(None), + state: RefCell::new(PopupManagerState::new(initial_scale_factor)), } } pub fn set_pending_popup(&self, request: PopupRequest, width: f32, height: f32) { - *self.pending_popup.borrow_mut() = Some(PendingPopup { + self.state.borrow_mut().pending_popup = Some(PendingPopup { request, width, height, @@ -120,8 +126,9 @@ impl PopupManager { #[must_use] pub fn take_pending_popup(&self) -> Option<(PopupRequest, f32, f32)> { - self.pending_popup + self.state .borrow_mut() + .pending_popup .take() .map(|p| (p.request, p.width, p.height)) } @@ -145,7 +152,7 @@ impl PopupManager { } pub fn close_current_popup(&self) { - let key = self.current_popup_key.borrow_mut().take(); + let key = self.state.borrow_mut().current_popup_key.take(); if let Some(key) = key { self.destroy_popup(key); } @@ -153,7 +160,7 @@ impl PopupManager { #[must_use] pub fn current_popup_key(&self) -> Option { - *self.current_popup_key.borrow() + self.state.borrow().current_popup_key } pub fn create_popup( @@ -235,14 +242,15 @@ impl PopupManager { params.height, ))); - let key = self.popups.borrow_mut().insert(ActivePopup { + let mut state = self.state.borrow_mut(); + let key = state.popups.insert(ActivePopup { surface: popup_surface, window: Rc::clone(&popup_window), request, last_serial: params.last_pointer_serial, }); popup_window.set_popup_manager(Rc::downgrade(self), key); - *self.current_popup_key.borrow_mut() = Some(key); + state.current_popup_key = Some(key); info!("Popup window created successfully with key {key}"); @@ -250,7 +258,8 @@ impl PopupManager { } pub fn render_popups(&self) -> Result<()> { - for (_key, popup) in self.popups.borrow().iter() { + let state = self.state.borrow(); + for (_key, popup) in &state.popups { popup.window.render_frame_if_dirty()?; } Ok(()) @@ -261,14 +270,16 @@ impl PopupManager { } pub fn mark_all_popups_dirty(&self) { - for (_key, popup) in self.popups.borrow().iter() { + let state = self.state.borrow(); + for (_key, popup) in &state.popups { popup.window.request_redraw(); } } pub fn find_popup_key_by_surface_id(&self, surface_id: &ObjectId) -> Option { - self.popups + self.state .borrow() + .popups .iter() .find_map(|(key, popup)| (popup.surface.surface.id() == *surface_id).then_some(key)) } @@ -277,7 +288,7 @@ impl PopupManager { &self, fractional_scale_id: &ObjectId, ) -> Option { - self.popups.borrow().iter().find_map(|(key, popup)| { + self.state.borrow().popups.iter().find_map(|(key, popup)| { popup .surface .fractional_scale @@ -288,14 +299,15 @@ impl PopupManager { } pub fn get_popup_window(&self, key: usize) -> Option> { - self.popups + self.state .borrow() + .popups .get(key) .map(|popup| Rc::clone(&popup.window)) } pub fn destroy_popup(&self, key: usize) { - if let Some(popup) = self.popups.borrow_mut().try_remove(key) { + if let Some(popup) = self.state.borrow_mut().popups.try_remove(key) { info!("Destroying popup with key {key}"); popup.surface.destroy(); @@ -303,20 +315,21 @@ impl PopupManager { } pub fn find_popup_key_by_xdg_popup_id(&self, xdg_popup_id: &ObjectId) -> Option { - self.popups + self.state .borrow() + .popups .iter() .find_map(|(key, popup)| (popup.surface.xdg_popup.id() == *xdg_popup_id).then_some(key)) } pub fn find_popup_key_by_xdg_surface_id(&self, xdg_surface_id: &ObjectId) -> Option { - self.popups.borrow().iter().find_map(|(key, popup)| { + self.state.borrow().popups.iter().find_map(|(key, popup)| { (popup.surface.xdg_surface.id() == *xdg_surface_id).then_some(key) }) } pub fn update_popup_viewport(&self, key: usize, logical_width: i32, logical_height: i32) { - if let Some(popup) = self.popups.borrow().get(key) { + if let Some(popup) = self.state.borrow().popups.get(key) { popup .surface .update_viewport_size(logical_width, logical_height); @@ -324,14 +337,15 @@ impl PopupManager { } pub fn get_popup_info(&self, key: usize) -> Option<(PopupRequest, u32)> { - self.popups + self.state .borrow() + .popups .get(key) .map(|popup| (popup.request.clone(), popup.last_serial)) } pub fn mark_popup_configured(&self, key: usize) { - if let Some(popup) = self.popups.borrow().get(key) { + if let Some(popup) = self.state.borrow().popups.get(key) { popup.window.mark_configured(); } } diff --git a/adapters/src/wayland/surfaces/popup_state.rs b/adapters/src/wayland/surfaces/popup_state.rs index c0e484c..84b3f5c 100644 --- a/adapters/src/wayland/surfaces/popup_state.rs +++ b/adapters/src/wayland/surfaces/popup_state.rs @@ -42,18 +42,6 @@ impl PopupState { } } - pub fn clear_active_window(&mut self) { - if let Some(popup_service) = &self.popup_service { - popup_service.clear_active_window(); - } - } - - pub fn clear_active_window_if_popup(&mut self, popup_key: usize) { - if let Some(popup_service) = &self.popup_service { - popup_service.clear_active_window_if_popup(popup_key); - } - } - pub const fn popup_service(&self) -> &Option> { &self.popup_service } diff --git a/adapters/src/wayland/surfaces/scale_coordinator.rs b/adapters/src/wayland/surfaces/scale_coordinator.rs index b4b4a1c..f1dfa4e 100644 --- a/adapters/src/wayland/surfaces/scale_coordinator.rs +++ b/adapters/src/wayland/surfaces/scale_coordinator.rs @@ -1,10 +1,10 @@ use log::info; use slint::LogicalPosition; -use std::cell::RefCell; +use std::cell::Cell; use std::rc::Rc; pub struct SharedPointerSerial { - serial: RefCell, + serial: Cell, } impl Default for SharedPointerSerial { @@ -16,16 +16,16 @@ impl Default for SharedPointerSerial { impl SharedPointerSerial { pub const fn new() -> Self { Self { - serial: RefCell::new(0), + serial: Cell::new(0), } } pub fn update(&self, serial: u32) { - *self.serial.borrow_mut() = serial; + self.serial.set(serial); } pub fn get(&self) -> u32 { - *self.serial.borrow() + self.serial.get() } } diff --git a/adapters/src/wayland/surfaces/surface_state.rs b/adapters/src/wayland/surfaces/surface_state.rs index bacf26c..c783dec 100644 --- a/adapters/src/wayland/surfaces/surface_state.rs +++ b/adapters/src/wayland/surfaces/surface_state.rs @@ -1,4 +1,5 @@ use std::rc::Rc; +use std::cell::RefCell; use super::surface_builder::WindowStateBuilder; use super::component_state::ComponentState; use super::rendering_state::RenderingState; @@ -18,6 +19,7 @@ use crate::errors::{LayerShikaError, Result}; use core::result::Result as CoreResult; use layer_shika_domain::errors::DomainError; use layer_shika_domain::ports::windowing::RuntimeStatePort; +use layer_shika_domain::value_objects::popup_request::PopupHandle; use slint::{LogicalPosition, PhysicalSize}; use slint::platform::WindowEvent; use slint_interpreter::{ComponentInstance, CompilationResult}; @@ -31,6 +33,8 @@ pub struct WindowState { interaction: InteractionState, popup: PopupState, output_size: PhysicalSize, + active_popup_key: RefCell>, + main_surface: Rc, } impl WindowState { @@ -112,6 +116,8 @@ impl WindowState { interaction, popup, output_size: builder.output_size.unwrap_or_default(), + active_popup_key: RefCell::new(None), + main_surface: surface_rc, }) } @@ -206,12 +212,39 @@ impl WindowState { self.popup.set_popup_manager(popup_manager); } - pub fn find_window_for_surface(&mut self, surface: &WlSurface) { - self.interaction.find_window_for_surface(surface); + pub fn set_entered_surface(&self, surface: &WlSurface) { + if let Some(popup_service) = self.popup.popup_service() { + if let Some(popup_key) = popup_service + .manager() + .find_popup_key_by_surface_id(&surface.id()) + { + *self.active_popup_key.borrow_mut() = Some(popup_key); + return; + } + } + *self.active_popup_key.borrow_mut() = None; + } + + pub fn clear_entered_surface(&self) { + *self.active_popup_key.borrow_mut() = None; } pub fn dispatch_to_active_window(&self, event: WindowEvent) { - self.interaction.dispatch_to_active_window(event); + let active_popup = *self.active_popup_key.borrow(); + + if let Some(popup_key) = active_popup { + if let Some(popup_service) = self.popup.popup_service() { + if let Some(popup_window) = + popup_service.get_popup_window(PopupHandle::new(popup_key)) + { + popup_window.dispatch_event(event); + return; + } + } + } + + self.interaction + .dispatch_to_active_window(event, &self.main_surface); } #[allow(clippy::cast_precision_loss)] @@ -233,14 +266,6 @@ impl WindowState { .update_scale_for_fractional_scale_object(fractional_scale_proxy, scale_120ths); } - pub fn clear_active_window(&mut self) { - self.popup.clear_active_window(); - } - - pub fn clear_active_window_if_popup(&mut self, popup_key: usize) { - self.popup.clear_active_window_if_popup(popup_key); - } - pub fn popup_service(&self) -> &Option> { self.popup.popup_service() }