mirror of
				https://codeberg.org/waydeer/layer-shika.git
				synced 2025-11-04 00:54:22 +00:00 
			
		
		
		
	feat: make popup requests explicit and typed
This commit is contained in:
		
							parent
							
								
									f604a49268
								
							
						
					
					
						commit
						a32140d41d
					
				
					 5 changed files with 252 additions and 101 deletions
				
			
		| 
						 | 
				
			
			@ -180,11 +180,25 @@ impl WaylandWindowingSystem {
 | 
			
		|||
 | 
			
		||||
            let serial = serial_holder.get();
 | 
			
		||||
 | 
			
		||||
            let params = popup_manager_clone.take_pending_popup_config();
 | 
			
		||||
            let params = if let Some((request, width, height)) = popup_manager_clone.take_pending_popup_request() {
 | 
			
		||||
                log::info!(
 | 
			
		||||
                    "Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}",
 | 
			
		||||
                    request.component,
 | 
			
		||||
                    request.at.position().0,
 | 
			
		||||
                    request.at.position().1,
 | 
			
		||||
                    width,
 | 
			
		||||
                    height,
 | 
			
		||||
                    request.mode
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
            let params = if let Some(mut p) = params {
 | 
			
		||||
                p.last_pointer_serial = serial;
 | 
			
		||||
                p
 | 
			
		||||
                CreatePopupParams {
 | 
			
		||||
                    last_pointer_serial: serial,
 | 
			
		||||
                    reference_x: request.at.position().0,
 | 
			
		||||
                    reference_y: request.at.position().1,
 | 
			
		||||
                    width,
 | 
			
		||||
                    height,
 | 
			
		||||
                    positioning_mode: request.mode,
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                let output_size = popup_manager_clone.output_size();
 | 
			
		||||
                #[allow(clippy::cast_precision_loss)]
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +206,7 @@ impl WaylandWindowingSystem {
 | 
			
		|||
                #[allow(clippy::cast_precision_loss)]
 | 
			
		||||
                let default_height = output_size.height as f32;
 | 
			
		||||
 | 
			
		||||
                log::warn!("No popup config provided, using output size ({default_width}x{default_height}) as defaults");
 | 
			
		||||
                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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ use crate::rendering::egl::context::EGLContext;
 | 
			
		|||
use crate::rendering::femtovg::popup_window::PopupWindow;
 | 
			
		||||
use layer_shika_domain::value_objects::popup_config::PopupConfig;
 | 
			
		||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
 | 
			
		||||
use layer_shika_domain::value_objects::popup_request::PopupRequest;
 | 
			
		||||
use log::info;
 | 
			
		||||
use slab::Slab;
 | 
			
		||||
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize};
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +73,7 @@ pub struct PopupManager {
 | 
			
		|||
    popups: RefCell<Slab<ActivePopup>>,
 | 
			
		||||
    current_scale_factor: RefCell<f32>,
 | 
			
		||||
    current_output_size: RefCell<PhysicalSize>,
 | 
			
		||||
    pending_popup_config: RefCell<Option<CreatePopupParams>>,
 | 
			
		||||
    pending_popup_request: RefCell<Option<(PopupRequest, f32, f32)>>,
 | 
			
		||||
    last_popup_key: RefCell<Option<usize>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -84,33 +85,18 @@ 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),
 | 
			
		||||
            pending_popup_request: 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,
 | 
			
		||||
        });
 | 
			
		||||
    pub fn set_pending_popup_request(&self, request: PopupRequest, width: f32, height: f32) {
 | 
			
		||||
        *self.pending_popup_request.borrow_mut() = Some((request, width, height));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn take_pending_popup_config(&self) -> Option<CreatePopupParams> {
 | 
			
		||||
        self.pending_popup_config.borrow_mut().take()
 | 
			
		||||
    pub fn take_pending_popup_request(&self) -> Option<(PopupRequest, f32, f32)> {
 | 
			
		||||
        self.pending_popup_request.borrow_mut().take()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn close_current_popup(&self) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,9 @@ use layer_shika_adapters::wayland::{
 | 
			
		|||
use layer_shika_domain::config::WindowConfig;
 | 
			
		||||
use layer_shika_domain::errors::DomainError;
 | 
			
		||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
 | 
			
		||||
use layer_shika_domain::value_objects::popup_request::{
 | 
			
		||||
    PopupAt, PopupHandle, PopupRequest, PopupSize,
 | 
			
		||||
};
 | 
			
		||||
use std::cell::{Ref, RefCell};
 | 
			
		||||
use std::os::unix::io::AsFd;
 | 
			
		||||
use std::rc::{Rc, Weak};
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +132,69 @@ impl RuntimeState<'_> {
 | 
			
		|||
        self.window_state.compilation_result()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn show_popup(&mut self, req: PopupRequest) -> Result<()> {
 | 
			
		||||
        let compilation_result = self.compilation_result().ok_or_else(|| {
 | 
			
		||||
            Error::Domain(DomainError::Configuration {
 | 
			
		||||
                message: "No compilation result available for popup creation".to_string(),
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let definition = compilation_result
 | 
			
		||||
            .component(&req.component)
 | 
			
		||||
            .ok_or_else(|| {
 | 
			
		||||
                Error::Domain(DomainError::Configuration {
 | 
			
		||||
                    message: format!(
 | 
			
		||||
                        "{} component not found in compilation result",
 | 
			
		||||
                        req.component
 | 
			
		||||
                    ),
 | 
			
		||||
                })
 | 
			
		||||
            })?;
 | 
			
		||||
 | 
			
		||||
        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()
 | 
			
		||||
            .as_ref()
 | 
			
		||||
            .ok_or_else(|| {
 | 
			
		||||
                Error::Domain(DomainError::Configuration {
 | 
			
		||||
                    message: "No popup manager available".to_string(),
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
            .map(Rc::clone)?;
 | 
			
		||||
 | 
			
		||||
        log::debug!(
 | 
			
		||||
            "Setting pending popup request for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
 | 
			
		||||
            req.component,
 | 
			
		||||
            width,
 | 
			
		||||
            height,
 | 
			
		||||
            req.at.position().0,
 | 
			
		||||
            req.at.position().1,
 | 
			
		||||
            req.mode
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        popup_manager.set_pending_popup_request(req, width, height);
 | 
			
		||||
 | 
			
		||||
        Self::create_popup_instance(&definition, &popup_manager)?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn close_popup(&mut self, handle: PopupHandle) -> Result<()> {
 | 
			
		||||
        if let Some(popup_manager) = self.window_state.popup_manager() {
 | 
			
		||||
            popup_manager.destroy_popup(handle.key());
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn close_current_popup(&mut self) -> Result<()> {
 | 
			
		||||
        if let Some(popup_manager) = self.window_state.popup_manager() {
 | 
			
		||||
            popup_manager.close_current_popup();
 | 
			
		||||
| 
						 | 
				
			
			@ -208,78 +274,6 @@ impl RuntimeState<'_> {
 | 
			
		|||
 | 
			
		||||
        Ok(instance)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn show_popup_component(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        component_name: &str,
 | 
			
		||||
        position: Option<(f32, f32)>,
 | 
			
		||||
        size: Option<(f32, f32)>,
 | 
			
		||||
        positioning_mode: PopupPositioningMode,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        let compilation_result = self.compilation_result().ok_or_else(|| {
 | 
			
		||||
            Error::Domain(DomainError::Configuration {
 | 
			
		||||
                message: "No compilation result available for popup creation".to_string(),
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let definition = compilation_result
 | 
			
		||||
            .component(component_name)
 | 
			
		||||
            .ok_or_else(|| {
 | 
			
		||||
                Error::Domain(DomainError::Configuration {
 | 
			
		||||
                    message: format!(
 | 
			
		||||
                        "{} component not found in compilation result",
 | 
			
		||||
                        component_name
 | 
			
		||||
                    ),
 | 
			
		||||
                })
 | 
			
		||||
            })?;
 | 
			
		||||
 | 
			
		||||
        self.close_current_popup()?;
 | 
			
		||||
 | 
			
		||||
        let (width, height) = if let Some(explicit_size) = size {
 | 
			
		||||
            log::debug!(
 | 
			
		||||
                "Using explicit popup size: {}x{}",
 | 
			
		||||
                explicit_size.0,
 | 
			
		||||
                explicit_size.1
 | 
			
		||||
            );
 | 
			
		||||
            explicit_size
 | 
			
		||||
        } else {
 | 
			
		||||
            self.measure_popup_dimensions(&definition)?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        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,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        log::debug!(
 | 
			
		||||
            "Creating final popup instance with dimensions {}x{} at position ({}, {}), mode: {:?}",
 | 
			
		||||
            width,
 | 
			
		||||
            height,
 | 
			
		||||
            reference_x,
 | 
			
		||||
            reference_y,
 | 
			
		||||
            positioning_mode
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Self::create_popup_instance(&definition, &popup_manager)?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct WindowingSystem {
 | 
			
		||||
| 
						 | 
				
			
			@ -344,9 +338,14 @@ impl WindowingSystem {
 | 
			
		|||
        let (_token, sender) = event_loop_handle.add_channel(
 | 
			
		||||
            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)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                let request = PopupRequest::builder(component_name)
 | 
			
		||||
                    .at(PopupAt::absolute(x, y))
 | 
			
		||||
                    .size(PopupSize::content())
 | 
			
		||||
                    .mode(mode)
 | 
			
		||||
                    .build();
 | 
			
		||||
 | 
			
		||||
                if let Err(e) = state.show_popup(request) {
 | 
			
		||||
                    log::error!("Failed to show popup: {}", e);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,3 +5,4 @@ pub mod layer;
 | 
			
		|||
pub mod margins;
 | 
			
		||||
pub mod popup_config;
 | 
			
		||||
pub mod popup_positioning_mode;
 | 
			
		||||
pub mod popup_request;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										151
									
								
								domain/src/value_objects/popup_request.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								domain/src/value_objects/popup_request.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
use super::popup_positioning_mode::PopupPositioningMode;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
pub struct PopupHandle(usize);
 | 
			
		||||
 | 
			
		||||
impl PopupHandle {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn new(key: usize) -> Self {
 | 
			
		||||
        Self(key)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn key(self) -> usize {
 | 
			
		||||
        self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct PopupRequest {
 | 
			
		||||
    pub component: String,
 | 
			
		||||
    pub at: PopupAt,
 | 
			
		||||
    pub size: PopupSize,
 | 
			
		||||
    pub mode: PopupPositioningMode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PopupRequest {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        component: String,
 | 
			
		||||
        at: PopupAt,
 | 
			
		||||
        size: PopupSize,
 | 
			
		||||
        mode: PopupPositioningMode,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            component,
 | 
			
		||||
            at,
 | 
			
		||||
            size,
 | 
			
		||||
            mode,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn builder(component: String) -> PopupRequestBuilder {
 | 
			
		||||
        PopupRequestBuilder::new(component)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum PopupAt {
 | 
			
		||||
    Absolute { x: f32, y: f32 },
 | 
			
		||||
    Cursor,
 | 
			
		||||
    AnchorRect { x: f32, y: f32, w: f32, h: f32 },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PopupAt {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn absolute(x: f32, y: f32) -> Self {
 | 
			
		||||
        Self::Absolute { x, y }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn cursor() -> Self {
 | 
			
		||||
        Self::Cursor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn anchor_rect(x: f32, y: f32, w: f32, h: f32) -> Self {
 | 
			
		||||
        Self::AnchorRect { x, y, w, h }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn position(&self) -> (f32, f32) {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Self::Absolute { x, y } | Self::AnchorRect { x, y, .. } => (x, y),
 | 
			
		||||
            Self::Cursor => (0.0, 0.0),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum PopupSize {
 | 
			
		||||
    Fixed { w: f32, h: f32 },
 | 
			
		||||
    Content,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PopupSize {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn fixed(w: f32, h: f32) -> Self {
 | 
			
		||||
        Self::Fixed { w, h }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn content() -> Self {
 | 
			
		||||
        Self::Content
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn dimensions(&self) -> Option<(f32, f32)> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Self::Fixed { w, h } => Some((w, h)),
 | 
			
		||||
            Self::Content => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct PopupRequestBuilder {
 | 
			
		||||
    component: String,
 | 
			
		||||
    at: PopupAt,
 | 
			
		||||
    size: PopupSize,
 | 
			
		||||
    mode: PopupPositioningMode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PopupRequestBuilder {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn new(component: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            component,
 | 
			
		||||
            at: PopupAt::Cursor,
 | 
			
		||||
            size: PopupSize::Content,
 | 
			
		||||
            mode: PopupPositioningMode::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn at(mut self, at: PopupAt) -> Self {
 | 
			
		||||
        self.at = at;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn size(mut self, size: PopupSize) -> Self {
 | 
			
		||||
        self.size = size;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub const fn mode(mut self, mode: PopupPositioningMode) -> Self {
 | 
			
		||||
        self.mode = mode;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn build(self) -> PopupRequest {
 | 
			
		||||
        PopupRequest {
 | 
			
		||||
            component: self.component,
 | 
			
		||||
            at: self.at,
 | 
			
		||||
            size: self.size,
 | 
			
		||||
            mode: self.mode,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue