mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-04 18:44:24 +00:00
refactor:extract popup builder and dimensions
This commit is contained in:
parent
a32140d41d
commit
6e5c0259e9
8 changed files with 315 additions and 31 deletions
|
|
@ -95,6 +95,10 @@ impl PopupWindow {
|
||||||
pub fn scale_factor(&self) -> f32 {
|
pub fn scale_factor(&self) -> f32 {
|
||||||
self.scale_factor.get()
|
self.scale_factor.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn popup_key(&self) -> Option<usize> {
|
||||||
|
self.popup_key.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowAdapter for PopupWindow {
|
impl WindowAdapter for PopupWindow {
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ impl WaylandWindowingSystem {
|
||||||
|
|
||||||
let serial = serial_holder.get();
|
let serial = serial_holder.get();
|
||||||
|
|
||||||
let params = if let Some((request, width, height)) = popup_manager_clone.take_pending_popup_request() {
|
let params = if let Some((request, width, height)) = popup_manager_clone.take_pending_popup() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}",
|
"Using popup request: component='{}', position=({}, {}), size={}x{}, mode={:?}",
|
||||||
request.component,
|
request.component,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod dimensions;
|
pub mod dimensions;
|
||||||
pub mod layer_surface;
|
pub mod layer_surface;
|
||||||
|
pub mod popup_builder;
|
||||||
pub mod popup_manager;
|
pub mod popup_manager;
|
||||||
pub mod popup_surface;
|
pub mod popup_surface;
|
||||||
pub mod surface_builder;
|
pub mod surface_builder;
|
||||||
|
|
|
||||||
200
adapters/src/wayland/surfaces/popup_builder.rs
Normal file
200
adapters/src/wayland/surfaces/popup_builder.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
use crate::errors::{LayerShikaError, Result};
|
||||||
|
use crate::rendering::femtovg::popup_window::PopupWindow;
|
||||||
|
use layer_shika_domain::value_objects::popup_dimensions::PopupDimensions;
|
||||||
|
use layer_shika_domain::value_objects::popup_request::{PopupHandle, PopupRequest, PopupSize};
|
||||||
|
use log::{debug, info};
|
||||||
|
use slint::ComponentHandle;
|
||||||
|
use slint_interpreter::{ComponentDefinition, ComponentInstance, Value};
|
||||||
|
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wayland_client::QueueHandle;
|
||||||
|
|
||||||
|
use super::popup_manager::{CreatePopupParams, PopupManager};
|
||||||
|
use super::surface_state::WindowState;
|
||||||
|
|
||||||
|
pub struct PopupBuilder<'a> {
|
||||||
|
popup_manager: &'a Rc<PopupManager>,
|
||||||
|
queue_handle: &'a QueueHandle<WindowState>,
|
||||||
|
parent_layer_surface: &'a ZwlrLayerSurfaceV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PopupBuilder<'a> {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(
|
||||||
|
popup_manager: &'a Rc<PopupManager>,
|
||||||
|
queue_handle: &'a QueueHandle<WindowState>,
|
||||||
|
parent_layer_surface: &'a ZwlrLayerSurfaceV1,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
popup_manager,
|
||||||
|
queue_handle,
|
||||||
|
parent_layer_surface,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
&self,
|
||||||
|
component_def: &ComponentDefinition,
|
||||||
|
request: &PopupRequest,
|
||||||
|
serial: u32,
|
||||||
|
) -> Result<PopupHandle> {
|
||||||
|
info!(
|
||||||
|
"Building popup for component '{}' at position ({}, {}) with mode {:?}",
|
||||||
|
request.component,
|
||||||
|
request.at.position().0,
|
||||||
|
request.at.position().1,
|
||||||
|
request.mode
|
||||||
|
);
|
||||||
|
|
||||||
|
let dimensions = Self::resolve_dimensions(component_def, &request.size)?;
|
||||||
|
dimensions
|
||||||
|
.validate()
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!("Invalid popup dimensions: {e}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Resolved popup dimensions: {}x{}",
|
||||||
|
dimensions.width(),
|
||||||
|
dimensions.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
let params = CreatePopupParams {
|
||||||
|
last_pointer_serial: serial,
|
||||||
|
reference_x: request.at.position().0,
|
||||||
|
reference_y: request.at.position().1,
|
||||||
|
width: dimensions.width(),
|
||||||
|
height: dimensions.height(),
|
||||||
|
positioning_mode: request.mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
let popup_window = self.popup_manager.create_popup(
|
||||||
|
self.queue_handle,
|
||||||
|
self.parent_layer_surface,
|
||||||
|
params,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let instance = Self::create_component_instance(component_def, &popup_window)?;
|
||||||
|
|
||||||
|
self.setup_popup_callbacks(&instance, &popup_window)?;
|
||||||
|
|
||||||
|
let handle = PopupHandle::new(popup_window.popup_key().ok_or_else(|| {
|
||||||
|
LayerShikaError::WindowConfiguration {
|
||||||
|
message: "Popup window has no key assigned".to_string(),
|
||||||
|
}
|
||||||
|
})?);
|
||||||
|
|
||||||
|
info!("Popup built successfully with handle {:?}", handle);
|
||||||
|
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_dimensions(
|
||||||
|
component_def: &ComponentDefinition,
|
||||||
|
size: &PopupSize,
|
||||||
|
) -> Result<PopupDimensions> {
|
||||||
|
match size {
|
||||||
|
PopupSize::Fixed { w, h } => {
|
||||||
|
debug!("Using fixed popup size: {}x{}", w, h);
|
||||||
|
Ok(PopupDimensions::new(*w, *h))
|
||||||
|
}
|
||||||
|
PopupSize::Content => {
|
||||||
|
debug!("Measuring popup dimensions from component content");
|
||||||
|
Self::measure_component_dimensions(component_def)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure_component_dimensions(
|
||||||
|
component_def: &ComponentDefinition,
|
||||||
|
) -> Result<PopupDimensions> {
|
||||||
|
debug!("Creating temporary component instance to measure dimensions");
|
||||||
|
|
||||||
|
let temp_instance =
|
||||||
|
component_def
|
||||||
|
.create()
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!(
|
||||||
|
"Failed to create temporary instance for dimension measurement: {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
temp_instance
|
||||||
|
.show()
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!("Failed to show temporary instance: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let width = Self::read_property(&temp_instance, "popup-width", 120.0);
|
||||||
|
let height = Self::read_property(&temp_instance, "popup-height", 120.0);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Measured dimensions from component properties: {}x{}",
|
||||||
|
width, height
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(PopupDimensions::new(width, height))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_property(instance: &ComponentInstance, name: &str, default: f32) -> f32 {
|
||||||
|
instance
|
||||||
|
.get_property(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.try_into().ok())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
debug!(
|
||||||
|
"Property '{}' not found or invalid, using default: {}",
|
||||||
|
name, default
|
||||||
|
);
|
||||||
|
default
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_component_instance(
|
||||||
|
component_def: &ComponentDefinition,
|
||||||
|
_popup_window: &Rc<PopupWindow>,
|
||||||
|
) -> Result<ComponentInstance> {
|
||||||
|
debug!("Creating popup component instance");
|
||||||
|
|
||||||
|
let instance =
|
||||||
|
component_def
|
||||||
|
.create()
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!("Failed to create popup instance: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
instance
|
||||||
|
.show()
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!("Failed to show popup instance: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!("Popup component instance created and shown successfully");
|
||||||
|
|
||||||
|
Ok(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_popup_callbacks(
|
||||||
|
&self,
|
||||||
|
instance: &ComponentInstance,
|
||||||
|
_popup_window: &Rc<PopupWindow>,
|
||||||
|
) -> Result<()> {
|
||||||
|
debug!("Setting up popup callbacks");
|
||||||
|
|
||||||
|
let popup_manager = Rc::clone(self.popup_manager);
|
||||||
|
instance
|
||||||
|
.set_callback("closed", move |_| {
|
||||||
|
info!("Popup 'closed' callback triggered");
|
||||||
|
popup_manager.close_current_popup();
|
||||||
|
Value::Void
|
||||||
|
})
|
||||||
|
.map_err(|e| LayerShikaError::WindowConfiguration {
|
||||||
|
message: format!("Failed to set popup 'closed' callback: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!("Popup callbacks configured successfully");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,54 +68,84 @@ struct ActivePopup {
|
||||||
window: Rc<PopupWindow>,
|
window: Rc<PopupWindow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PopupState {
|
||||||
|
scale_factor: f32,
|
||||||
|
output_size: PhysicalSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PendingPopup {
|
||||||
|
request: PopupRequest,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PopupManager {
|
pub struct PopupManager {
|
||||||
context: PopupContext,
|
context: PopupContext,
|
||||||
popups: RefCell<Slab<ActivePopup>>,
|
popups: RefCell<Slab<ActivePopup>>,
|
||||||
current_scale_factor: RefCell<f32>,
|
state: RefCell<PopupState>,
|
||||||
current_output_size: RefCell<PhysicalSize>,
|
current_popup_key: RefCell<Option<usize>>,
|
||||||
pending_popup_request: RefCell<Option<(PopupRequest, f32, f32)>>,
|
pending_popup: RefCell<Option<PendingPopup>>,
|
||||||
last_popup_key: RefCell<Option<usize>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PopupManager {
|
impl PopupManager {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(context: PopupContext, initial_scale_factor: f32) -> Self {
|
pub fn new(context: PopupContext, initial_scale_factor: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
context,
|
context,
|
||||||
popups: RefCell::new(Slab::new()),
|
popups: RefCell::new(Slab::new()),
|
||||||
current_scale_factor: RefCell::new(initial_scale_factor),
|
state: RefCell::new(PopupState {
|
||||||
current_output_size: RefCell::new(PhysicalSize::new(0, 0)),
|
scale_factor: initial_scale_factor,
|
||||||
pending_popup_request: RefCell::new(None),
|
output_size: PhysicalSize::new(0, 0),
|
||||||
last_popup_key: RefCell::new(None),
|
}),
|
||||||
|
current_popup_key: RefCell::new(None),
|
||||||
|
pending_popup: RefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pending_popup_request(&self, request: PopupRequest, width: f32, height: f32) {
|
pub fn set_pending_popup(&self, request: PopupRequest, width: f32, height: f32) {
|
||||||
*self.pending_popup_request.borrow_mut() = Some((request, width, height));
|
*self.pending_popup.borrow_mut() = Some(PendingPopup {
|
||||||
|
request,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn take_pending_popup_request(&self) -> Option<(PopupRequest, f32, f32)> {
|
pub fn take_pending_popup(&self) -> Option<(PopupRequest, f32, f32)> {
|
||||||
self.pending_popup_request.borrow_mut().take()
|
self.pending_popup
|
||||||
|
.borrow_mut()
|
||||||
|
.take()
|
||||||
|
.map(|p| (p.request, p.width, p.height))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn scale_factor(&self) -> f32 {
|
||||||
|
self.state.borrow().scale_factor
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn output_size(&self) -> PhysicalSize {
|
||||||
|
self.state.borrow().output_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_scale_factor(&self, scale_factor: f32) {
|
||||||
|
self.state.borrow_mut().scale_factor = scale_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_output_size(&self, output_size: PhysicalSize) {
|
||||||
|
self.state.borrow_mut().output_size = output_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_current_popup(&self) {
|
pub fn close_current_popup(&self) {
|
||||||
let key = self.last_popup_key.borrow_mut().take();
|
let key = self.current_popup_key.borrow_mut().take();
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
self.destroy_popup(key);
|
self.destroy_popup(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_scale_factor(&self, scale_factor: f32) {
|
#[must_use]
|
||||||
*self.current_scale_factor.borrow_mut() = scale_factor;
|
pub fn current_popup_key(&self) -> Option<usize> {
|
||||||
}
|
*self.current_popup_key.borrow()
|
||||||
|
|
||||||
pub fn update_output_size(&self, output_size: PhysicalSize) {
|
|
||||||
*self.current_output_size.borrow_mut() = output_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn output_size(&self) -> PhysicalSize {
|
|
||||||
*self.current_output_size.borrow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_popup(
|
pub fn create_popup(
|
||||||
|
|
@ -130,7 +160,7 @@ impl PopupManager {
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let scale_factor = *self.current_scale_factor.borrow();
|
let scale_factor = self.scale_factor();
|
||||||
info!(
|
info!(
|
||||||
"Creating popup window with scale factor {scale_factor}, reference=({}, {}), size=({} x {}), mode={:?}",
|
"Creating popup window with scale factor {scale_factor}, reference=({}, {}), size=({} x {}), mode={:?}",
|
||||||
params.reference_x,
|
params.reference_x,
|
||||||
|
|
@ -192,7 +222,7 @@ impl PopupManager {
|
||||||
window: Rc::clone(&popup_window),
|
window: Rc::clone(&popup_window),
|
||||||
});
|
});
|
||||||
popup_window.set_popup_manager(Rc::downgrade(self), key);
|
popup_window.set_popup_manager(Rc::downgrade(self), key);
|
||||||
*self.last_popup_key.borrow_mut() = Some(key);
|
*self.current_popup_key.borrow_mut() = Some(key);
|
||||||
|
|
||||||
info!("Popup window created successfully with key {key}");
|
info!("Popup window created successfully with key {key}");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl RuntimeState<'_> {
|
||||||
self.window_state.compilation_result()
|
self.window_state.compilation_result()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_popup(&mut self, req: PopupRequest) -> Result<()> {
|
pub fn show_popup(&mut self, req: PopupRequest) -> Result<PopupHandle> {
|
||||||
let compilation_result = self.compilation_result().ok_or_else(|| {
|
let compilation_result = self.compilation_result().ok_or_else(|| {
|
||||||
Error::Domain(DomainError::Configuration {
|
Error::Domain(DomainError::Configuration {
|
||||||
message: "No compilation result available for popup creation".to_string(),
|
message: "No compilation result available for popup creation".to_string(),
|
||||||
|
|
@ -172,7 +172,7 @@ impl RuntimeState<'_> {
|
||||||
.map(Rc::clone)?;
|
.map(Rc::clone)?;
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Setting pending popup request for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
|
"Creating popup for '{}' with dimensions {}x{} at position ({}, {}), mode: {:?}",
|
||||||
req.component,
|
req.component,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|
@ -181,11 +181,17 @@ impl RuntimeState<'_> {
|
||||||
req.mode
|
req.mode
|
||||||
);
|
);
|
||||||
|
|
||||||
popup_manager.set_pending_popup_request(req, width, height);
|
popup_manager.set_pending_popup(req.clone(), width, height);
|
||||||
|
|
||||||
Self::create_popup_instance(&definition, &popup_manager)?;
|
Self::create_popup_instance(&definition, &popup_manager)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(PopupHandle::new(
|
||||||
|
popup_manager.current_popup_key().ok_or_else(|| {
|
||||||
|
Error::Domain(DomainError::Configuration {
|
||||||
|
message: "No popup key available after creation".to_string(),
|
||||||
|
})
|
||||||
|
})?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_popup(&mut self, handle: PopupHandle) -> Result<()> {
|
pub fn close_popup(&mut self, handle: PopupHandle) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,6 @@ pub mod keyboard_interactivity;
|
||||||
pub mod layer;
|
pub mod layer;
|
||||||
pub mod margins;
|
pub mod margins;
|
||||||
pub mod popup_config;
|
pub mod popup_config;
|
||||||
|
pub mod popup_dimensions;
|
||||||
pub mod popup_positioning_mode;
|
pub mod popup_positioning_mode;
|
||||||
pub mod popup_request;
|
pub mod popup_request;
|
||||||
|
|
|
||||||
42
domain/src/value_objects/popup_dimensions.rs
Normal file
42
domain/src/value_objects/popup_dimensions.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::errors::{DomainError, Result};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct PopupDimensions {
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupDimensions {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(width: f32, height: f32) -> Self {
|
||||||
|
Self { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn width(&self) -> f32 {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn height(&self) -> f32 {
|
||||||
|
self.height
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(&self) -> Result<()> {
|
||||||
|
if self.width <= 0.0 || self.height <= 0.0 {
|
||||||
|
return Err(DomainError::Configuration {
|
||||||
|
message: format!(
|
||||||
|
"Invalid popup dimensions: width={}, height={}. Both must be positive.",
|
||||||
|
self.width, self.height
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PopupDimensions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(120.0, 120.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue