mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 11:25:54 +00:00
refactor: remove the convention over configuration concept and decouple popup management
This commit is contained in:
parent
dac315b589
commit
58278cecef
8 changed files with 340 additions and 342 deletions
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(clippy::pub_use)]
|
||||
|
||||
mod builder;
|
||||
mod slint_callbacks;
|
||||
mod system;
|
||||
|
||||
use layer_shika_adapters::errors::LayerShikaError;
|
||||
|
|
@ -22,8 +21,7 @@ pub use layer_shika_domain::value_objects::popup_positioning_mode::PopupPosition
|
|||
pub use layer_shika_domain::value_objects::popup_request::{
|
||||
PopupAt, PopupHandle, PopupRequest, PopupSize,
|
||||
};
|
||||
pub use slint_callbacks::{SlintCallbackContract, SlintCallbackNames};
|
||||
pub use system::{App, EventLoopHandle, ShellContext};
|
||||
pub use system::{App, EventLoopHandle, ShellContext, ShellControl};
|
||||
|
||||
pub mod calloop {
|
||||
pub use layer_shika_adapters::platform::calloop::{
|
||||
|
|
@ -50,7 +48,7 @@ pub mod prelude {
|
|||
AnchorEdges, App, EventLoopHandle, KeyboardInteractivity, Layer, LayerShika,
|
||||
OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupAt,
|
||||
PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result,
|
||||
ShellContext, SlintCallbackContract, SlintCallbackNames,
|
||||
ShellContext, ShellControl,
|
||||
};
|
||||
|
||||
pub use crate::calloop::{Generic, Interest, Mode, PostAction, RegistrationToken, Timer};
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
use crate::system::PopupCommand;
|
||||
use crate::{Error, Result};
|
||||
use layer_shika_adapters::PopupManager;
|
||||
use layer_shika_adapters::platform::calloop::channel;
|
||||
use layer_shika_adapters::platform::slint::SharedString;
|
||||
use layer_shika_adapters::platform::slint_interpreter::{ComponentInstance, Value};
|
||||
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::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct SlintCallbackNames;
|
||||
|
||||
impl SlintCallbackNames {
|
||||
pub const SHOW_POPUP: &'static str = "show_popup";
|
||||
pub const CHANGE_POPUP_SIZE: &'static str = "change_popup_size";
|
||||
pub const SET_POPUP_POSITIONING_MODE: &'static str = "set_popup_positioning_mode";
|
||||
pub const POPUP_CLOSED: &'static str = "closed";
|
||||
}
|
||||
|
||||
pub struct SlintCallbackContract {
|
||||
popup_positioning_mode: Rc<RefCell<PopupPositioningMode>>,
|
||||
popup_command_sender: channel::Sender<PopupCommand>,
|
||||
}
|
||||
|
||||
impl SlintCallbackContract {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
popup_positioning_mode: Rc<RefCell<PopupPositioningMode>>,
|
||||
popup_command_sender: channel::Sender<PopupCommand>,
|
||||
) -> Self {
|
||||
Self {
|
||||
popup_positioning_mode,
|
||||
popup_command_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_on_main_component(&self, component_instance: &ComponentInstance) -> Result<()> {
|
||||
self.register_set_popup_positioning_mode_callback(component_instance)?;
|
||||
self.register_show_popup_callback(component_instance)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_on_popup_component(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
) -> Result<()> {
|
||||
Self::register_popup_closed_callback(instance, popup_manager)?;
|
||||
Self::register_change_popup_size_callback(
|
||||
instance,
|
||||
popup_manager,
|
||||
resize_sender,
|
||||
popup_key_cell,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_set_popup_positioning_mode_callback(
|
||||
&self,
|
||||
component_instance: &ComponentInstance,
|
||||
) -> Result<()> {
|
||||
let popup_mode_clone = Rc::clone(&self.popup_positioning_mode);
|
||||
component_instance
|
||||
.set_callback(
|
||||
SlintCallbackNames::SET_POPUP_POSITIONING_MODE,
|
||||
move |args| {
|
||||
let center_x: bool = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
let center_y: bool = args
|
||||
.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let mode = PopupPositioningMode::from_flags(center_x, center_y);
|
||||
*popup_mode_clone.borrow_mut() = mode;
|
||||
log::info!(
|
||||
"Popup positioning mode set to: {:?} (center_x: {}, center_y: {})",
|
||||
mode,
|
||||
center_x,
|
||||
center_y
|
||||
);
|
||||
Value::Void
|
||||
},
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!(
|
||||
"Failed to register {} callback: {}",
|
||||
SlintCallbackNames::SET_POPUP_POSITIONING_MODE,
|
||||
e
|
||||
),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn register_show_popup_callback(&self, component_instance: &ComponentInstance) -> Result<()> {
|
||||
let sender = self.popup_command_sender.clone();
|
||||
let popup_mode_for_callback = Rc::clone(&self.popup_positioning_mode);
|
||||
|
||||
component_instance
|
||||
.set_callback(SlintCallbackNames::SHOW_POPUP, move |args| {
|
||||
let component_name: SharedString = args
|
||||
.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or_else(|| SharedString::from(""));
|
||||
|
||||
if component_name.is_empty() {
|
||||
log::error!(
|
||||
"{} called without component name",
|
||||
SlintCallbackNames::SHOW_POPUP
|
||||
);
|
||||
return Value::Void;
|
||||
}
|
||||
|
||||
let x: f32 = args
|
||||
.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(0.0);
|
||||
let y: f32 = args
|
||||
.get(2)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(0.0);
|
||||
|
||||
let mode = *popup_mode_for_callback.borrow();
|
||||
|
||||
let request = PopupRequest::builder(component_name.to_string())
|
||||
.at(PopupAt::absolute(x, y))
|
||||
.size(PopupSize::content())
|
||||
.mode(mode)
|
||||
.build();
|
||||
|
||||
if sender.send(PopupCommand::Show(request)).is_err() {
|
||||
log::error!("Failed to send popup show command through channel");
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!(
|
||||
"Failed to register {} callback: {}",
|
||||
SlintCallbackNames::SHOW_POPUP,
|
||||
e
|
||||
),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn register_popup_closed_callback(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
) -> Result<()> {
|
||||
let popup_manager_weak = Rc::downgrade(popup_manager);
|
||||
instance
|
||||
.set_callback(SlintCallbackNames::POPUP_CLOSED, move |_| {
|
||||
if let Some(popup_manager) = popup_manager_weak.upgrade() {
|
||||
popup_manager.close_current_popup();
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!(
|
||||
"Failed to set {} callback: {}",
|
||||
SlintCallbackNames::POPUP_CLOSED,
|
||||
e
|
||||
),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn register_change_popup_size_callback(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
) {
|
||||
let result = if let Some(sender) = resize_sender {
|
||||
let key_cell = Rc::clone(popup_key_cell);
|
||||
instance.set_callback(SlintCallbackNames::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);
|
||||
|
||||
let popup_key = key_cell.get();
|
||||
log::info!(
|
||||
"{} callback invoked: {}x{} for key {}",
|
||||
SlintCallbackNames::CHANGE_POPUP_SIZE,
|
||||
width,
|
||||
height,
|
||||
popup_key
|
||||
);
|
||||
|
||||
if sender
|
||||
.send(PopupCommand::Resize {
|
||||
handle: PopupHandle::new(popup_key),
|
||||
width,
|
||||
height,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Failed to send popup resize command through channel");
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
} else {
|
||||
let popup_manager_for_resize = Rc::downgrade(popup_manager);
|
||||
let key_cell = Rc::clone(popup_key_cell);
|
||||
instance.set_callback(SlintCallbackNames::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);
|
||||
|
||||
let popup_key = key_cell.get();
|
||||
log::info!(
|
||||
"{} callback invoked: {}x{} for key {}",
|
||||
SlintCallbackNames::CHANGE_POPUP_SIZE,
|
||||
width,
|
||||
height,
|
||||
popup_key
|
||||
);
|
||||
|
||||
if let Some(popup_manager) = popup_manager_for_resize.upgrade() {
|
||||
if let Some(popup_window) = popup_manager.get_popup_window(popup_key) {
|
||||
popup_window.request_resize(width, height);
|
||||
|
||||
#[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;
|
||||
|
||||
popup_manager.update_popup_viewport(popup_key, logical_width, logical_height);
|
||||
log::debug!(
|
||||
"Updated popup viewport to logical size: {}x{} (from direct resize to {}x{})",
|
||||
logical_width,
|
||||
logical_height,
|
||||
width,
|
||||
height
|
||||
);
|
||||
}
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
log::warn!(
|
||||
"Failed to set {} callback: {}",
|
||||
SlintCallbackNames::CHANGE_POPUP_SIZE,
|
||||
e
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"{} callback registered successfully",
|
||||
SlintCallbackNames::CHANGE_POPUP_SIZE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::slint_callbacks::SlintCallbackContract;
|
||||
use crate::{Error, Result};
|
||||
use layer_shika_adapters::errors::EventLoopError;
|
||||
use layer_shika_adapters::platform::calloop::{
|
||||
|
|
@ -7,7 +6,7 @@ use layer_shika_adapters::platform::calloop::{
|
|||
};
|
||||
use layer_shika_adapters::platform::slint::ComponentHandle;
|
||||
use layer_shika_adapters::platform::slint_interpreter::{
|
||||
CompilationResult, ComponentDefinition, ComponentInstance,
|
||||
CompilationResult, ComponentDefinition, ComponentInstance, Value,
|
||||
};
|
||||
use layer_shika_adapters::{
|
||||
AppState, PopupManager, WaylandWindowConfig, WindowState, WindowingSystemFacade,
|
||||
|
|
@ -17,7 +16,7 @@ use layer_shika_domain::entities::output_registry::OutputRegistry;
|
|||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
use layer_shika_domain::value_objects::dimensions::PopupDimensions;
|
||||
use layer_shika_domain::value_objects::popup_request::{PopupHandle, PopupRequest, PopupSize};
|
||||
use std::cell::Cell;
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -36,6 +35,47 @@ pub enum PopupCommand {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ShellControl {
|
||||
sender: channel::Sender<PopupCommand>,
|
||||
}
|
||||
|
||||
impl ShellControl {
|
||||
pub fn show_popup(&self, request: &PopupRequest) -> Result<()> {
|
||||
self.sender
|
||||
.send(PopupCommand::Show(request.clone()))
|
||||
.map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup show command: channel closed".to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
|
||||
self.sender
|
||||
.send(PopupCommand::Close(handle))
|
||||
.map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup close command: channel closed".to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resize_popup(&self, handle: PopupHandle, width: f32, height: f32) -> Result<()> {
|
||||
self.sender
|
||||
.send(PopupCommand::Resize {
|
||||
handle,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
.map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup resize command: channel closed".to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopHandle {
|
||||
system: Weak<RefCell<WindowingSystemFacade>>,
|
||||
}
|
||||
|
|
@ -128,6 +168,18 @@ pub struct ShellContext<'a> {
|
|||
app_state: &'a mut AppState,
|
||||
}
|
||||
|
||||
fn extract_dimensions_from_callback(args: &[Value]) -> PopupDimensions {
|
||||
let defaults = PopupDimensions::default();
|
||||
PopupDimensions::new(
|
||||
args.first()
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(defaults.width),
|
||||
args.get(1)
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(defaults.height),
|
||||
)
|
||||
}
|
||||
|
||||
impl ShellContext<'_> {
|
||||
#[must_use]
|
||||
pub fn component_instance(&self) -> Option<&ComponentInstance> {
|
||||
|
|
@ -204,8 +256,8 @@ impl ShellContext<'_> {
|
|||
|
||||
pub fn show_popup(
|
||||
&mut self,
|
||||
req: PopupRequest,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
req: &PopupRequest,
|
||||
resize_control: Option<ShellControl>,
|
||||
) -> Result<PopupHandle> {
|
||||
let compilation_result = self.compilation_result().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -266,10 +318,10 @@ impl ShellContext<'_> {
|
|||
);
|
||||
|
||||
let popup_handle =
|
||||
popup_manager.request_popup(req, initial_dimensions.0, initial_dimensions.1);
|
||||
popup_manager.request_popup(req.clone(), initial_dimensions.0, initial_dimensions.1);
|
||||
|
||||
let (instance, popup_key_cell) =
|
||||
Self::create_popup_instance(&definition, &popup_manager, resize_sender)?;
|
||||
Self::create_popup_instance(&definition, &popup_manager, resize_control, req)?;
|
||||
|
||||
popup_key_cell.set(popup_handle.key());
|
||||
|
||||
|
|
@ -307,7 +359,7 @@ impl ShellContext<'_> {
|
|||
handle: PopupHandle,
|
||||
width: f32,
|
||||
height: f32,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
resize_control: Option<ShellControl>,
|
||||
) -> Result<()> {
|
||||
let active_window = self.active_or_primary_output().ok_or_else(|| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -345,13 +397,21 @@ impl ShellContext<'_> {
|
|||
|
||||
self.close_popup(handle)?;
|
||||
|
||||
let new_request = PopupRequest::builder(request.component)
|
||||
let mut builder = PopupRequest::builder(request.component)
|
||||
.at(request.at)
|
||||
.size(PopupSize::fixed(width, height))
|
||||
.mode(request.mode)
|
||||
.build();
|
||||
.mode(request.mode);
|
||||
|
||||
self.show_popup(new_request, resize_sender)?;
|
||||
if let Some(close_cb) = &request.close_callback {
|
||||
builder = builder.close_on(close_cb.clone());
|
||||
}
|
||||
if let Some(resize_cb) = &request.resize_callback {
|
||||
builder = builder.resize_on(resize_cb.clone());
|
||||
}
|
||||
|
||||
let new_request = builder.build();
|
||||
|
||||
self.show_popup(&new_request, resize_control)?;
|
||||
} else if size_changed {
|
||||
if let Some(popup_window) = popup_manager.get_popup_window(handle.key()) {
|
||||
popup_window.request_resize(width, height);
|
||||
|
|
@ -380,7 +440,8 @@ impl ShellContext<'_> {
|
|||
fn create_popup_instance(
|
||||
definition: &ComponentDefinition,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
resize_sender: Option<channel::Sender<PopupCommand>>,
|
||||
resize_control: Option<ShellControl>,
|
||||
req: &PopupRequest,
|
||||
) -> Result<(ComponentInstance, Rc<Cell<usize>>)> {
|
||||
let instance = definition.create().map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
|
|
@ -390,11 +451,12 @@ impl ShellContext<'_> {
|
|||
|
||||
let popup_key_cell = Rc::new(Cell::new(0));
|
||||
|
||||
SlintCallbackContract::register_on_popup_component(
|
||||
Self::register_popup_callbacks(
|
||||
&instance,
|
||||
popup_manager,
|
||||
resize_sender,
|
||||
resize_control,
|
||||
&popup_key_cell,
|
||||
req,
|
||||
)?;
|
||||
|
||||
instance.show().map_err(|e| {
|
||||
|
|
@ -405,12 +467,158 @@ impl ShellContext<'_> {
|
|||
|
||||
Ok((instance, popup_key_cell))
|
||||
}
|
||||
|
||||
fn register_popup_callbacks(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
resize_control: Option<ShellControl>,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
req: &PopupRequest,
|
||||
) -> Result<()> {
|
||||
if let Some(close_callback_name) = &req.close_callback {
|
||||
Self::register_close_callback(instance, popup_manager, close_callback_name)?;
|
||||
}
|
||||
|
||||
if let Some(resize_callback_name) = &req.resize_callback {
|
||||
Self::register_resize_callback(
|
||||
instance,
|
||||
popup_manager,
|
||||
resize_control,
|
||||
popup_key_cell,
|
||||
resize_callback_name,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_close_callback(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
callback_name: &str,
|
||||
) -> Result<()> {
|
||||
let popup_manager_weak = Rc::downgrade(popup_manager);
|
||||
instance
|
||||
.set_callback(callback_name, move |_| {
|
||||
if let Some(popup_manager) = popup_manager_weak.upgrade() {
|
||||
popup_manager.close_current_popup();
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to set '{}' callback: {}", callback_name, e),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn register_resize_callback(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
resize_control: Option<ShellControl>,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
callback_name: &str,
|
||||
) -> Result<()> {
|
||||
if let Some(control) = resize_control {
|
||||
Self::register_resize_with_control(instance, popup_key_cell, &control, callback_name)
|
||||
} else {
|
||||
Self::register_resize_direct(instance, popup_manager, popup_key_cell, callback_name)
|
||||
}
|
||||
}
|
||||
|
||||
fn register_resize_with_control(
|
||||
instance: &ComponentInstance,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
control: &ShellControl,
|
||||
callback_name: &str,
|
||||
) -> Result<()> {
|
||||
let key_cell = Rc::clone(popup_key_cell);
|
||||
let control = control.clone();
|
||||
instance
|
||||
.set_callback(callback_name, move |args| {
|
||||
let dimensions = extract_dimensions_from_callback(args);
|
||||
let popup_key = key_cell.get();
|
||||
|
||||
log::info!(
|
||||
"Resize callback invoked: {}x{} for key {}",
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
popup_key
|
||||
);
|
||||
|
||||
if control
|
||||
.resize_popup(PopupHandle::new(popup_key), dimensions.width, dimensions.height)
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Failed to resize popup through control");
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to set '{}' callback: {}", callback_name, e),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn register_resize_direct(
|
||||
instance: &ComponentInstance,
|
||||
popup_manager: &Rc<PopupManager>,
|
||||
popup_key_cell: &Rc<Cell<usize>>,
|
||||
callback_name: &str,
|
||||
) -> Result<()> {
|
||||
let popup_manager_weak = Rc::downgrade(popup_manager);
|
||||
let key_cell = Rc::clone(popup_key_cell);
|
||||
instance
|
||||
.set_callback(callback_name, move |args| {
|
||||
let dimensions = extract_dimensions_from_callback(args);
|
||||
let popup_key = key_cell.get();
|
||||
|
||||
log::info!(
|
||||
"Resize callback invoked: {}x{} for key {}",
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
popup_key
|
||||
);
|
||||
|
||||
if let Some(popup_manager) = popup_manager_weak.upgrade() {
|
||||
if let Some(popup_window) = popup_manager.get_popup_window(popup_key) {
|
||||
popup_window.request_resize(dimensions.width, dimensions.height);
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
let logical_width = dimensions.width as i32;
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
let logical_height = dimensions.height as i32;
|
||||
|
||||
popup_manager.update_popup_viewport(
|
||||
popup_key,
|
||||
logical_width,
|
||||
logical_height,
|
||||
);
|
||||
log::debug!(
|
||||
"Updated popup viewport to logical size: {}x{} (from direct resize to {}x{})",
|
||||
logical_width,
|
||||
logical_height,
|
||||
dimensions.width,
|
||||
dimensions.height
|
||||
);
|
||||
}
|
||||
}
|
||||
Value::Void
|
||||
})
|
||||
.map_err(|e| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: format!("Failed to set '{}' callback: {}", callback_name, e),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
inner: Rc<RefCell<WindowingSystemFacade>>,
|
||||
popup_command_sender: channel::Sender<PopupCommand>,
|
||||
callback_contract: SlintCallbackContract,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -430,25 +638,19 @@ impl App {
|
|||
|
||||
let (sender, receiver) = channel::channel();
|
||||
|
||||
let popup_positioning_mode = Rc::new(RefCell::new(PopupPositioningMode::Center));
|
||||
let callback_contract =
|
||||
SlintCallbackContract::new(Rc::clone(&popup_positioning_mode), sender.clone());
|
||||
|
||||
let system = Self {
|
||||
inner: Rc::clone(&inner_rc),
|
||||
popup_command_sender: sender,
|
||||
callback_contract,
|
||||
};
|
||||
|
||||
system.setup_popup_command_handler(receiver)?;
|
||||
system.register_popup_callbacks();
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
||||
fn setup_popup_command_handler(&self, receiver: channel::Channel<PopupCommand>) -> Result<()> {
|
||||
let loop_handle = self.inner.borrow().inner_ref().event_loop_handle();
|
||||
let sender_for_handler = self.popup_command_sender.clone();
|
||||
let control = self.control();
|
||||
|
||||
loop_handle
|
||||
.insert_source(receiver, move |event, (), app_state| {
|
||||
|
|
@ -457,8 +659,7 @@ impl App {
|
|||
|
||||
match command {
|
||||
PopupCommand::Show(request) => {
|
||||
if let Err(e) =
|
||||
shell_context.show_popup(request, Some(sender_for_handler.clone()))
|
||||
if let Err(e) = shell_context.show_popup(&request, Some(control.clone()))
|
||||
{
|
||||
log::error!("Failed to show popup: {}", e);
|
||||
}
|
||||
|
|
@ -473,12 +674,9 @@ impl App {
|
|||
width,
|
||||
height,
|
||||
} => {
|
||||
if let Err(e) = shell_context.resize_popup(
|
||||
handle,
|
||||
width,
|
||||
height,
|
||||
Some(sender_for_handler.clone()),
|
||||
) {
|
||||
if let Err(e) =
|
||||
shell_context.resize_popup(handle, width, height, Some(control.clone()))
|
||||
{
|
||||
log::error!("Failed to resize popup: {}", e);
|
||||
}
|
||||
}
|
||||
|
|
@ -497,15 +695,11 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn register_popup_callbacks(&self) {
|
||||
self.with_all_component_instances(|component_instance| {
|
||||
if let Err(e) = self
|
||||
.callback_contract
|
||||
.register_on_main_component(component_instance)
|
||||
{
|
||||
log::error!("Failed to register popup callbacks on output: {}", e);
|
||||
#[must_use]
|
||||
pub fn control(&self) -> ShellControl {
|
||||
ShellControl {
|
||||
sender: self.popup_command_sender.clone(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -515,24 +709,55 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn request_show_popup(&self, request: PopupRequest) -> Result<()> {
|
||||
self.popup_command_sender
|
||||
.send(PopupCommand::Show(request))
|
||||
.map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup show command: channel closed".to_string(),
|
||||
})
|
||||
})
|
||||
pub fn on_callback<F>(&self, callback_name: &str, handler: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&[Value], ShellControl) -> Value + 'static,
|
||||
{
|
||||
let control = self.control();
|
||||
let handler = Rc::new(handler);
|
||||
self.with_all_component_instances(|instance| {
|
||||
let handler_rc = Rc::clone(&handler);
|
||||
let control_clone = control.clone();
|
||||
if let Err(e) = instance.set_callback(callback_name, move |args| {
|
||||
handler_rc(args, control_clone.clone())
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to register callback '{}' on component: {}",
|
||||
callback_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn request_close_popup(&self, handle: PopupHandle) -> Result<()> {
|
||||
self.popup_command_sender
|
||||
.send(PopupCommand::Close(handle))
|
||||
.map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup close command: channel closed".to_string(),
|
||||
})
|
||||
})
|
||||
pub fn bind_popup<F>(&self, callback_name: &str, config_builder: F) -> Result<()>
|
||||
where
|
||||
F: Fn() -> PopupRequest + 'static,
|
||||
{
|
||||
let control = self.control();
|
||||
let builder = Rc::new(config_builder);
|
||||
|
||||
self.with_all_component_instances(|instance| {
|
||||
let builder_clone = Rc::clone(&builder);
|
||||
let control_clone = control.clone();
|
||||
|
||||
if let Err(e) = instance.set_callback(callback_name, move |_args| {
|
||||
let request = builder_clone();
|
||||
if let Err(e) = control_clone.show_popup(&request) {
|
||||
log::error!("Failed to show popup: {}", e);
|
||||
}
|
||||
Value::Void
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to bind popup callback '{}': {}",
|
||||
callback_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub use crate::entities::window::WindowHandle;
|
|||
pub use crate::errors::{DomainError, Result};
|
||||
pub use crate::surface_dimensions::SurfaceDimensions;
|
||||
pub use crate::value_objects::anchor::AnchorEdges;
|
||||
pub use crate::value_objects::dimensions::WindowHeight;
|
||||
pub use crate::value_objects::dimensions::{PopupDimensions, WindowHeight};
|
||||
pub use crate::value_objects::keyboard_interactivity::KeyboardInteractivity;
|
||||
pub use crate::value_objects::layer::Layer;
|
||||
pub use crate::value_objects::margins::Margins;
|
||||
|
|
|
|||
|
|
@ -31,3 +31,35 @@ impl From<u32> for WindowHeight {
|
|||
Self::new(height)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PopupDimensions {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
impl Default for PopupDimensions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 200.0,
|
||||
height: 150.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ pub struct PopupRequest {
|
|||
pub size: PopupSize,
|
||||
pub mode: PopupPositioningMode,
|
||||
pub grab: bool,
|
||||
pub close_callback: Option<String>,
|
||||
pub resize_callback: Option<String>,
|
||||
}
|
||||
|
||||
impl PopupRequest {
|
||||
|
|
@ -38,6 +40,8 @@ impl PopupRequest {
|
|||
size,
|
||||
mode,
|
||||
grab: false,
|
||||
close_callback: None,
|
||||
resize_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +115,8 @@ pub struct PopupRequestBuilder {
|
|||
size: PopupSize,
|
||||
mode: PopupPositioningMode,
|
||||
grab: bool,
|
||||
close_callback: Option<String>,
|
||||
resize_callback: Option<String>,
|
||||
}
|
||||
|
||||
impl PopupRequestBuilder {
|
||||
|
|
@ -122,6 +128,8 @@ impl PopupRequestBuilder {
|
|||
size: PopupSize::Content,
|
||||
mode: PopupPositioningMode::default(),
|
||||
grab: false,
|
||||
close_callback: None,
|
||||
resize_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,6 +157,18 @@ impl PopupRequestBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn close_on(mut self, callback_name: impl Into<String>) -> Self {
|
||||
self.close_callback = Some(callback_name.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn resize_on(mut self, callback_name: impl Into<String>) -> Self {
|
||||
self.resize_callback = Some(callback_name.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn build(self) -> PopupRequest {
|
||||
PopupRequest {
|
||||
|
|
@ -157,6 +177,8 @@ impl PopupRequestBuilder {
|
|||
size: self.size,
|
||||
mode: self.mode,
|
||||
grab: self.grab,
|
||||
close_callback: self.close_callback,
|
||||
resize_callback: self.resize_callback,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ pub mod prelude;
|
|||
pub use layer_shika_composition::{
|
||||
AnchorEdges, App, Error, EventLoopHandle, KeyboardInteractivity, Layer, LayerShika,
|
||||
OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupAt, PopupHandle,
|
||||
PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, ShellContext,
|
||||
SlintCallbackContract, SlintCallbackNames,
|
||||
PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, ShellContext, ShellControl,
|
||||
};
|
||||
|
||||
pub use layer_shika_composition::{slint, slint_interpreter};
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@
|
|||
|
||||
// Core API types
|
||||
pub use crate::{
|
||||
App, Error, EventLoopHandle, LayerShika, PopupWindow, Result, ShellContext,
|
||||
SlintCallbackContract, SlintCallbackNames,
|
||||
App, Error, EventLoopHandle, LayerShika, PopupWindow, Result, ShellContext, ShellControl,
|
||||
};
|
||||
|
||||
// Domain value objects
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue