layer_shika_adapters/rendering/femtovg/
popup_window.rs

1use super::renderable_window::{RenderState, RenderableWindow};
2use crate::errors::{RenderingError, Result};
3use crate::wayland::surfaces::popup_manager::OnCloseCallback;
4use core::ops::Deref;
5use layer_shika_domain::value_objects::popup_request::PopupHandle;
6use log::info;
7use slint::{
8    PhysicalSize, Window, WindowSize,
9    platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer},
10};
11use slint_interpreter::ComponentInstance;
12use std::cell::{Cell, OnceCell, RefCell};
13use std::rc::{Rc, Weak};
14
15pub struct PopupWindow {
16    window: Window,
17    renderer: FemtoVGRenderer,
18    render_state: Cell<RenderState>,
19    size: Cell<PhysicalSize>,
20    scale_factor: Cell<f32>,
21    popup_handle: Cell<Option<PopupHandle>>,
22    on_close: OnceCell<OnCloseCallback>,
23    configured: Cell<bool>,
24    repositioning: Cell<bool>,
25    needs_relayout: Cell<bool>,
26    component_instance: RefCell<Option<ComponentInstance>>,
27}
28
29impl PopupWindow {
30    #[must_use]
31    pub fn new(renderer: FemtoVGRenderer) -> Rc<Self> {
32        Rc::new_cyclic(|weak_self| {
33            let window = Window::new(Weak::clone(weak_self) as Weak<dyn WindowAdapter>);
34            Self {
35                window,
36                renderer,
37                render_state: Cell::new(RenderState::Clean),
38                size: Cell::new(PhysicalSize::default()),
39                scale_factor: Cell::new(1.),
40                popup_handle: Cell::new(None),
41                on_close: OnceCell::new(),
42                configured: Cell::new(false),
43                repositioning: Cell::new(false),
44                needs_relayout: Cell::new(false),
45                component_instance: RefCell::new(None),
46            }
47        })
48    }
49
50    #[must_use]
51    pub fn new_with_callback(renderer: FemtoVGRenderer, on_close: OnCloseCallback) -> Rc<Self> {
52        let window = Self::new(renderer);
53        window.on_close.set(on_close).ok();
54        window
55    }
56
57    pub fn set_popup_id(&self, handle: PopupHandle) {
58        self.popup_handle.set(Some(handle));
59    }
60
61    pub(crate) fn cleanup_resources(&self) {
62        info!("Cleaning up popup window resources to break reference cycles");
63
64        if let Err(e) = self.window.hide() {
65            info!("Failed to hide popup window: {e}");
66        }
67
68        if let Some(component) = self.component_instance.borrow_mut().take() {
69            info!("Dropping ComponentInstance to break reference cycle");
70            drop(component);
71        }
72
73        info!("Popup window resource cleanup complete");
74    }
75
76    pub fn close_popup(&self) {
77        info!("Closing popup window - cleaning up resources");
78
79        self.cleanup_resources();
80
81        if let Some(handle) = self.popup_handle.get() {
82            info!("Destroying popup with handle {:?}", handle);
83            if let Some(on_close) = self.on_close.get() {
84                on_close(handle);
85            }
86        }
87
88        self.popup_handle.set(None);
89
90        info!("Popup window cleanup complete");
91    }
92
93    pub fn popup_key(&self) -> Option<usize> {
94        self.popup_handle.get().map(PopupHandle::key)
95    }
96
97    pub fn mark_configured(&self) {
98        info!("Popup window marked as configured");
99        self.configured.set(true);
100    }
101
102    pub fn is_configured(&self) -> bool {
103        self.configured.get()
104    }
105
106    pub fn set_component_instance(&self, instance: ComponentInstance) {
107        info!("Setting component instance for popup window");
108        let mut comp = self.component_instance.borrow_mut();
109        if comp.is_some() {
110            info!("Component instance already set for popup window - replacing");
111        }
112        *comp = Some(instance);
113    }
114
115    pub fn request_resize(&self, width: f32, height: f32) {
116        info!("Requesting popup resize to {}x{}", width, height);
117        self.set_size(WindowSize::Logical(slint::LogicalSize::new(width, height)));
118        RenderableWindow::request_redraw(self);
119    }
120
121    pub fn begin_repositioning(&self) {
122        self.repositioning.set(true);
123    }
124
125    pub fn end_repositioning(&self) {
126        self.repositioning.set(false);
127        self.needs_relayout.set(true);
128    }
129}
130
131impl RenderableWindow for PopupWindow {
132    fn render_frame_if_dirty(&self) -> Result<()> {
133        if !self.configured.get() {
134            info!("Popup not yet configured, skipping render");
135            return Ok(());
136        }
137
138        if self.repositioning.get() {
139            info!("Popup repositioning in progress, skipping render");
140            return Ok(());
141        }
142
143        if matches!(
144            self.render_state.replace(RenderState::Clean),
145            RenderState::Dirty
146        ) {
147            info!(
148                "Rendering popup frame (size: {:?}, scale: {})",
149                self.size.get(),
150                self.scale_factor.get()
151            );
152            self.renderer
153                .render()
154                .map_err(|e| RenderingError::Operation {
155                    message: format!("Error rendering popup frame: {e}"),
156                })?;
157            info!("Popup frame rendered successfully");
158
159            if self.needs_relayout.get() {
160                info!("Popup needs relayout, requesting additional render");
161                self.needs_relayout.set(false);
162                RenderableWindow::request_redraw(self);
163            }
164        }
165        Ok(())
166    }
167
168    fn set_scale_factor(&self, scale_factor: f32) {
169        info!("Setting popup scale factor to {scale_factor}");
170        self.scale_factor.set(scale_factor);
171        self.window()
172            .dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
173    }
174
175    fn scale_factor(&self) -> f32 {
176        self.scale_factor.get()
177    }
178
179    fn render_state(&self) -> &Cell<RenderState> {
180        &self.render_state
181    }
182
183    fn size_cell(&self) -> &Cell<PhysicalSize> {
184        &self.size
185    }
186}
187
188impl WindowAdapter for PopupWindow {
189    fn window(&self) -> &Window {
190        &self.window
191    }
192
193    fn renderer(&self) -> &dyn Renderer {
194        &self.renderer
195    }
196
197    fn size(&self) -> PhysicalSize {
198        self.size_impl()
199    }
200
201    fn set_size(&self, size: WindowSize) {
202        self.set_size_impl(size);
203    }
204
205    fn request_redraw(&self) {
206        RenderableWindow::request_redraw(self);
207    }
208}
209
210impl Deref for PopupWindow {
211    type Target = Window;
212    fn deref(&self) -> &Self::Target {
213        &self.window
214    }
215}
216
217impl Drop for PopupWindow {
218    fn drop(&mut self) {
219        info!("PopupWindow being dropped - cleaning up resources");
220
221        if let Some(component) = self.component_instance.borrow_mut().take() {
222            info!("Dropping any remaining ComponentInstance in PopupWindow::drop");
223            drop(component);
224        }
225
226        info!("PopupWindow drop complete");
227    }
228}