fix: popup flicker due render before configuration event

This commit is contained in:
drendog 2025-11-04 23:56:25 +01:00
parent f6ea47cbba
commit a5e48f1b9f
Signed by: dwenya
GPG key ID: 8DD77074645332D0
4 changed files with 50 additions and 50 deletions

View file

@ -3,7 +3,7 @@ use crate::wayland::surfaces::popup_manager::PopupManager;
use core::ops::Deref; use core::ops::Deref;
use log::info; use log::info;
use slint::{ use slint::{
ComponentHandle, PhysicalSize, Window, WindowSize, PhysicalSize, Window, WindowSize,
platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer}, platform::{Renderer, WindowAdapter, WindowEvent, femtovg_renderer::FemtoVGRenderer},
}; };
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
@ -21,6 +21,7 @@ pub struct PopupWindow {
scale_factor: Cell<f32>, scale_factor: Cell<f32>,
popup_manager: RefCell<Weak<PopupManager>>, popup_manager: RefCell<Weak<PopupManager>>,
popup_key: Cell<Option<usize>>, popup_key: Cell<Option<usize>>,
configured: Cell<bool>,
component_instance: RefCell<Option<ComponentInstance>>, component_instance: RefCell<Option<ComponentInstance>>,
} }
@ -38,15 +39,12 @@ impl PopupWindow {
scale_factor: Cell::new(1.), scale_factor: Cell::new(1.),
popup_manager: RefCell::new(Weak::new()), popup_manager: RefCell::new(Weak::new()),
popup_key: Cell::new(None), popup_key: Cell::new(None),
configured: Cell::new(false),
component_instance: RefCell::new(None), component_instance: RefCell::new(None),
} }
}) })
} }
pub fn set_component_instance(&self, instance: ComponentInstance) {
*self.component_instance.borrow_mut() = Some(instance);
}
pub fn set_popup_manager(&self, popup_manager: Weak<PopupManager>, key: usize) { pub fn set_popup_manager(&self, popup_manager: Weak<PopupManager>, key: usize) {
*self.popup_manager.borrow_mut() = popup_manager; *self.popup_manager.borrow_mut() = popup_manager;
self.popup_key.set(Some(key)); self.popup_key.set(Some(key));
@ -55,13 +53,6 @@ impl PopupWindow {
pub fn close_popup(&self) { pub fn close_popup(&self) {
info!("Closing popup window - cleaning up resources"); info!("Closing popup window - cleaning up resources");
if let Some(instance) = self.component_instance.borrow_mut().take() {
info!("Hiding ComponentInstance to release strong reference from show()");
if let Err(e) = instance.hide() {
info!("Failed to hide component instance: {e}");
}
}
if let Err(e) = self.window.hide() { if let Err(e) = self.window.hide() {
info!("Failed to hide popup window: {e}"); info!("Failed to hide popup window: {e}");
} }
@ -80,6 +71,11 @@ impl PopupWindow {
} }
pub fn render_frame_if_dirty(&self) -> Result<()> { pub fn render_frame_if_dirty(&self) -> Result<()> {
if !self.configured.get() {
info!("Popup not yet configured, skipping render");
return Ok(());
}
if matches!( if matches!(
self.render_state.replace(RenderState::Clean), self.render_state.replace(RenderState::Clean),
RenderState::Dirty RenderState::Dirty
@ -114,46 +110,25 @@ impl PopupWindow {
self.popup_key.get() self.popup_key.get()
} }
pub fn cleanup_component_instance(&self) { pub fn mark_configured(&self) {
if let Some(instance) = self.component_instance.borrow_mut().take() { info!("Popup window marked as configured");
info!("Cleaning up component instance to break reference cycles"); self.configured.set(true);
if let Err(e) = instance.hide() {
info!("Failed to hide component instance during cleanup: {e}");
} }
drop(instance);
pub fn is_configured(&self) -> bool {
self.configured.get()
} }
pub fn set_component_instance(&self, instance: ComponentInstance) {
info!("Setting component instance for popup window");
*self.component_instance.borrow_mut() = Some(instance);
} }
pub fn request_resize(&self, width: f32, height: f32) { pub fn request_resize(&self, width: f32, height: f32) {
info!("Requesting popup resize to {}x{}", width, height); info!("Requesting popup resize to {}x{}", width, height);
let logical_size = slint::LogicalSize::new(width, height); self.set_size(WindowSize::Logical(slint::LogicalSize::new(width, height)));
self.set_size(WindowSize::Logical(logical_size));
if let Some(popup_manager) = self.popup_manager.borrow().upgrade() {
if let Some(key) = self.popup_key.get() {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
let logical_width = width as i32;
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
let logical_height = height as i32;
info!("Updating popup viewport to match Slint size: {}x{}", logical_width, logical_height);
popup_manager.update_popup_viewport(key, logical_width, logical_height);
}
}
self.request_redraw(); self.request_redraw();
} }
pub fn with_component_instance<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&ComponentInstance) -> R,
{
self.component_instance
.borrow()
.as_ref()
.map(f)
}
} }
impl WindowAdapter for PopupWindow { impl WindowAdapter for PopupWindow {

View file

@ -268,6 +268,17 @@ impl Dispatch<XdgPopup, ()> for WindowState {
height, height,
} => { } => {
info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})"); info!("XdgPopup Configure: position=({x}, {y}), size=({width}x{height})");
if let Some(popup_manager) = &state.popup_manager() {
let popup_id = xdg_popup.id();
if let Some(key) = popup_manager.find_popup_key_by_xdg_popup_id(&popup_id) {
info!(
"Marking popup with key {key} as configured after XdgPopup::Configure"
);
popup_manager.mark_popup_configured(key);
popup_manager.mark_all_popups_dirty();
}
}
} }
xdg_popup::Event::PopupDone => { xdg_popup::Event::PopupDone => {
info!("XdgPopup dismissed by compositor"); info!("XdgPopup dismissed by compositor");

View file

@ -73,7 +73,6 @@ struct ActivePopup {
impl Drop for ActivePopup { impl Drop for ActivePopup {
fn drop(&mut self) { fn drop(&mut self) {
info!("ActivePopup being dropped - cleaning up resources"); info!("ActivePopup being dropped - cleaning up resources");
self.window.cleanup_component_instance();
} }
} }
@ -290,8 +289,6 @@ impl PopupManager {
if let Some(popup) = self.popups.borrow_mut().try_remove(key) { if let Some(popup) = self.popups.borrow_mut().try_remove(key) {
info!("Destroying popup with key {key}"); info!("Destroying popup with key {key}");
popup.window.cleanup_component_instance();
popup.surface.destroy(); popup.surface.destroy();
} }
} }
@ -303,9 +300,17 @@ impl PopupManager {
.find_map(|(key, popup)| (popup.surface.xdg_popup.id() == *xdg_popup_id).then_some(key)) .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)| {
(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) { 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.popups.borrow().get(key) {
popup.surface.update_viewport_size(logical_width, logical_height); popup
.surface
.update_viewport_size(logical_width, logical_height);
} }
} }
@ -315,4 +320,10 @@ impl PopupManager {
.get(key) .get(key)
.map(|popup| (popup.request.clone(), popup.last_serial)) .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) {
popup.window.mark_configured();
}
}
} }

View file

@ -137,6 +137,9 @@ impl PopupSurface {
pub fn grab(&self, seat: &WlSeat, serial: u32) { pub fn grab(&self, seat: &WlSeat, serial: u32) {
info!("Grabbing popup with serial {serial}"); info!("Grabbing popup with serial {serial}");
self.xdg_popup.grab(seat, serial); self.xdg_popup.grab(seat, serial);
info!("Committing popup surface to trigger configure event");
self.surface.commit();
} }
pub fn update_viewport_size(&self, logical_width: i32, logical_height: i32) { pub fn update_viewport_size(&self, logical_width: i32, logical_height: i32) {