mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-04 15:14:33 +00:00
refactor: remove thread local global state and centralize popup state management
This commit is contained in:
parent
aa5052b566
commit
9808eeb819
6 changed files with 123 additions and 181 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Rc<dyn WindowAdapter>, PlatformError>;
|
||||
type PopupConfigData = (f32, f32, f32, f32, PopupPositioningMode);
|
||||
|
||||
thread_local! {
|
||||
static CURRENT_PLATFORM: RefCell<Option<Weak<CustomSlintPlatform>>> = 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<PopupConfigData> {
|
||||
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<FemtoVGWindow>,
|
||||
popup_creator: RefCell<Option<Rc<PopupCreator>>>,
|
||||
first_call: Cell<bool>,
|
||||
last_popup: RefCell<Option<Weak<PopupWindow>>>,
|
||||
popup_config: RefCell<Option<PopupConfigData>>,
|
||||
}
|
||||
|
||||
impl CustomSlintPlatform {
|
||||
#[must_use]
|
||||
pub fn new(window: &Rc<FemtoVGWindow>) -> Rc<Self> {
|
||||
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<F>(&self, creator: F)
|
||||
where
|
||||
F: Fn() -> Result<Rc<dyn WindowAdapter>, PlatformError> + 'static,
|
||||
{
|
||||
*self.popup_creator.borrow_mut() = Some(Rc::new(creator));
|
||||
}
|
||||
|
||||
pub fn set_last_popup(&self, popup: &Rc<PopupWindow>) {
|
||||
*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<PopupConfigData> {
|
||||
*self.popup_config.borrow()
|
||||
}
|
||||
|
||||
pub fn clear_popup_config(&self) {
|
||||
*self.popup_config.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for CustomSlintPlatform {
|
||||
|
|
|
|||
|
|
@ -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 params = popup_manager_clone.take_pending_popup_config();
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
)
|
||||
});
|
||||
|
||||
clear_popup_config();
|
||||
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<dyn WindowAdapter>);
|
||||
|
||||
match &result {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ pub struct PopupManager {
|
|||
popups: RefCell<Slab<ActivePopup>>,
|
||||
current_scale_factor: RefCell<f32>,
|
||||
current_output_size: RefCell<PhysicalSize>,
|
||||
pending_popup_config: RefCell<Option<CreatePopupParams>>,
|
||||
last_popup_key: RefCell<Option<usize>>,
|
||||
}
|
||||
|
||||
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<CreatePopupParams> {
|
||||
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}");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,16 +159,20 @@ impl RuntimeState<'_> {
|
|||
})
|
||||
})?;
|
||||
|
||||
close_current_popup();
|
||||
self.close_current_popup()?;
|
||||
|
||||
let (width, height) = if let Some(explicit_size) = size {
|
||||
explicit_size
|
||||
} else {
|
||||
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| {
|
||||
|
||||
temp_instance.show().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to hide temporary popup instance: {}", e),
|
||||
message: format!("Failed to show temporary popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
|
|
@ -170,24 +180,38 @@ impl RuntimeState<'_> {
|
|||
.get_property("popup-width")
|
||||
.ok()
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.or(size.map(|(w, _)| w))
|
||||
.unwrap_or(300.0);
|
||||
.unwrap_or(120.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);
|
||||
.unwrap_or(120.0);
|
||||
|
||||
drop(temp_instance);
|
||||
close_current_popup();
|
||||
self.close_current_popup()?;
|
||||
|
||||
if let Some((reference_x, reference_y)) = position {
|
||||
set_popup_config(reference_x, reference_y, width, height, positioning_mode);
|
||||
} else {
|
||||
clear_popup_config();
|
||||
}
|
||||
(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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue