mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-06 09:54:22 +00:00
refactor: migrate to callback popup resizing
This commit is contained in:
parent
0ff8d19fe3
commit
bb26ce9e6b
6 changed files with 116 additions and 66 deletions
|
|
@ -113,6 +113,47 @@ impl PopupWindow {
|
|||
pub fn popup_key(&self) -> Option<usize> {
|
||||
self.popup_key.get()
|
||||
}
|
||||
|
||||
pub fn cleanup_component_instance(&self) {
|
||||
if let Some(instance) = self.component_instance.borrow_mut().take() {
|
||||
info!("Cleaning up component instance to break reference cycles");
|
||||
if let Err(e) = instance.hide() {
|
||||
info!("Failed to hide component instance during cleanup: {e}");
|
||||
}
|
||||
drop(instance);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_resize(&self, width: f32, height: f32) {
|
||||
info!("Requesting popup resize to {}x{}", width, height);
|
||||
let logical_size = 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();
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use crate::{
|
|||
use core::result::Result as CoreResult;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::ports::windowing::WindowingSystemPort;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
use log::{error, info};
|
||||
use slint::{
|
||||
LogicalPosition, PhysicalSize, PlatformError, WindowPosition,
|
||||
|
|
@ -200,21 +199,10 @@ impl WaylandWindowingSystem {
|
|||
positioning_mode: request.mode,
|
||||
}
|
||||
} 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;
|
||||
|
||||
log::warn!("No popup request provided, using output size ({default_width}x{default_height}) as defaults");
|
||||
CreatePopupParams {
|
||||
last_pointer_serial: serial,
|
||||
reference_x: 0.0,
|
||||
reference_y: 0.0,
|
||||
width: default_width,
|
||||
height: default_height,
|
||||
positioning_mode: PopupPositioningMode::TopLeft,
|
||||
}
|
||||
log::warn!("Popup creator called without pending popup request - aborting");
|
||||
return Err(PlatformError::Other(
|
||||
"No popup request available - cannot create popup without parameters".to_string()
|
||||
));
|
||||
};
|
||||
|
||||
let popup_window = popup_manager_clone
|
||||
|
|
|
|||
|
|
@ -68,6 +68,13 @@ struct ActivePopup {
|
|||
window: Rc<PopupWindow>,
|
||||
}
|
||||
|
||||
impl Drop for ActivePopup {
|
||||
fn drop(&mut self) {
|
||||
info!("ActivePopup being dropped - cleaning up resources");
|
||||
self.window.cleanup_component_instance();
|
||||
}
|
||||
}
|
||||
|
||||
struct PopupState {
|
||||
scale_factor: f32,
|
||||
output_size: PhysicalSize,
|
||||
|
|
@ -277,6 +284,9 @@ impl PopupManager {
|
|||
pub fn destroy_popup(&self, key: usize) {
|
||||
if let Some(popup) = self.popups.borrow_mut().try_remove(key) {
|
||||
info!("Destroying popup with key {key}");
|
||||
|
||||
popup.window.cleanup_component_instance();
|
||||
|
||||
popup.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
|
@ -287,4 +297,10 @@ impl PopupManager {
|
|||
.iter()
|
||||
.find_map(|(key, popup)| (popup.surface.xdg_popup.id() == *xdg_popup_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) {
|
||||
popup.surface.update_viewport_size(logical_width, logical_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,16 @@ impl PopupSurface {
|
|||
self.xdg_popup.grab(seat, serial);
|
||||
}
|
||||
|
||||
pub fn update_viewport_size(&self, logical_width: i32, logical_height: i32) {
|
||||
if let Some(ref vp) = self.viewport {
|
||||
info!(
|
||||
"Updating popup viewport destination to logical size: {}x{}",
|
||||
logical_width, logical_height
|
||||
);
|
||||
vp.set_destination(logical_width, logical_height);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
info!("Destroying popup surface");
|
||||
self.xdg_popup.destroy();
|
||||
|
|
|
|||
|
|
@ -157,14 +157,6 @@ impl RuntimeState<'_> {
|
|||
|
||||
self.close_current_popup()?;
|
||||
|
||||
let (width, height) = match req.size {
|
||||
PopupSize::Fixed { w, h } => {
|
||||
log::debug!("Using fixed popup size: {}x{}", w, h);
|
||||
(w, h)
|
||||
}
|
||||
PopupSize::Content => self.measure_popup_dimensions(&definition)?,
|
||||
};
|
||||
|
||||
let popup_manager = self
|
||||
.window_state
|
||||
.popup_manager()
|
||||
|
|
@ -176,19 +168,30 @@ impl RuntimeState<'_> {
|
|||
})
|
||||
.map(Rc::clone)?;
|
||||
|
||||
let initial_dimensions = match req.size {
|
||||
PopupSize::Fixed { w, h } => {
|
||||
log::debug!("Using fixed popup size: {}x{}", w, h);
|
||||
(w, h)
|
||||
}
|
||||
PopupSize::Content => {
|
||||
log::debug!("Using content-based sizing - will measure after instance creation");
|
||||
(600.0, 300.0)
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"Creating popup for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
|
||||
req.component,
|
||||
width,
|
||||
height,
|
||||
initial_dimensions.0,
|
||||
initial_dimensions.1,
|
||||
req.at.position().0,
|
||||
req.at.position().1,
|
||||
req.mode
|
||||
);
|
||||
|
||||
popup_manager.set_pending_popup(req, width, height);
|
||||
popup_manager.set_pending_popup(req, initial_dimensions.0, initial_dimensions.1);
|
||||
|
||||
let instance = Self::create_popup_instance(&definition, &popup_manager)?;
|
||||
let instance = Self::create_popup_instance(&definition, &popup_manager, 0)?;
|
||||
|
||||
let popup_key = popup_manager.current_popup_key().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -221,45 +224,10 @@ impl RuntimeState<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn measure_popup_dimensions(&mut self, definition: &ComponentDefinition) -> Result<(f32, f32)> {
|
||||
log::debug!(
|
||||
"Creating temporary popup instance to read dimensions from component properties"
|
||||
);
|
||||
|
||||
let temp_instance = definition.create().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to create temporary popup instance: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
let width: f32 = temp_instance
|
||||
.get_property("popup-width")
|
||||
.ok()
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.unwrap_or(120.0);
|
||||
|
||||
let height: f32 = temp_instance
|
||||
.get_property("popup-height")
|
||||
.ok()
|
||||
.and_then(|v| v.try_into().ok())
|
||||
.unwrap_or(120.0);
|
||||
|
||||
log::debug!(
|
||||
"Read popup dimensions from component properties: {}x{} (popup-width, popup-height)",
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
drop(temp_instance);
|
||||
self.close_current_popup()?;
|
||||
log::debug!("Destroyed temporary popup instance");
|
||||
|
||||
Ok((width, height))
|
||||
}
|
||||
|
||||
fn create_popup_instance(
|
||||
definition: &ComponentDefinition,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
popup_key: usize,
|
||||
) -> Result<ComponentInstance> {
|
||||
let instance = definition.create().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -281,6 +249,33 @@ impl RuntimeState<'_> {
|
|||
})
|
||||
})?;
|
||||
|
||||
let popup_manager_for_resize = Rc::downgrade(popup_manager);
|
||||
let result = instance.set_callback("change_popup_size", move |args| {
|
||||
let width: f32 = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(200.0);
|
||||
let height: f32 = args
|
||||
.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(150.0);
|
||||
|
||||
log::info!("change_popup_size callback invoked: {}x{}", width, height);
|
||||
|
||||
if let Some(popup_mgr) = popup_manager_for_resize.upgrade() {
|
||||
if let Some(popup_window) = popup_mgr.get_popup_window(popup_key) {
|
||||
popup_window.request_resize(width, height);
|
||||
}
|
||||
}
|
||||
Value::Void
|
||||
});
|
||||
|
||||
if let Err(e) = result {
|
||||
log::warn!("Failed to set change_popup_size callback: {}", e);
|
||||
} else {
|
||||
log::info!("change_popup_size callback registered successfully");
|
||||
}
|
||||
|
||||
instance.show().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to show popup instance: {}", e),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl PopupRequest {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PopupAt {
|
||||
Absolute { x: f32, y: f32 },
|
||||
Cursor,
|
||||
|
|
@ -77,7 +77,7 @@ impl PopupAt {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PopupSize {
|
||||
Fixed { w: f32, h: f32 },
|
||||
Content,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue