refactor: interior mutuability refactor

This commit is contained in:
drendog 2025-11-10 23:01:58 +01:00
parent 4022ca1484
commit d2930a7a85
Signed by: dwenya
GPG key ID: 8DD77074645332D0
10 changed files with 126 additions and 124 deletions

View file

@ -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<Weak<PopupManager>>,
popup_key: Cell<Option<usize>>,
configured: Cell<bool>,
component_instance: RefCell<Option<ComponentInstance>>,
component_instance: OnceCell<ComponentInstance>,
}
#[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) {

View file

@ -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<Rc<dyn WindowAdapter>, PlatformError>;
pub struct CustomSlintPlatform {
main_window: Weak<FemtoVGWindow>,
popup_creator: RefCell<Option<Rc<PopupCreator>>>,
popup_creator: OnceCell<Rc<PopupCreator>>,
first_call: Cell<bool>,
}
@ -20,7 +20,7 @@ impl CustomSlintPlatform {
pub fn new(window: &Rc<FemtoVGWindow>) -> Rc<Self> {
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<Rc<dyn WindowAdapter>, 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<dyn WindowAdapter>)
} 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)

View file

@ -170,7 +170,7 @@ impl Dispatch<WlPointer, ()> 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<WlPointer, ()> 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<XdgPopup, ()> 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);
}

View file

@ -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<PopupManager>,
active_window: RefCell<Option<ActiveWindow>>,
scale_factor: Cell<f32>,
}
@ -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<ActiveWindow> {
*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;

View file

@ -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 => {}
}
}
}

View file

@ -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 {

View file

@ -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<ActivePopup>,
scale_factor: f32,
output_size: PhysicalSize,
current_popup_key: Option<usize>,
pending_popup: Option<PendingPopup>,
}
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<Slab<ActivePopup>>,
state: RefCell<PopupState>,
current_popup_key: RefCell<Option<usize>>,
pending_popup: RefCell<Option<PendingPopup>>,
state: RefCell<PopupManagerState>,
}
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<usize> {
*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<usize> {
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<usize> {
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<Rc<PopupWindow>> {
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<usize> {
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<usize> {
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();
}
}

View file

@ -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<Rc<PopupService>> {
&self.popup_service
}

View file

@ -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<u32>,
serial: Cell<u32>,
}
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()
}
}

View file

@ -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<Option<usize>>,
main_surface: Rc<WlSurface>,
}
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<Rc<PopupService>> {
self.popup.popup_service()
}