mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-12-23 11:25:54 +00:00
refactor: extract popup builder and consolidate popup config
This commit is contained in:
parent
30c3100ca2
commit
0756fc0ef7
4 changed files with 343 additions and 33 deletions
|
|
@ -1,7 +1,9 @@
|
|||
#![allow(clippy::pub_use)]
|
||||
|
||||
mod builder;
|
||||
mod popup_builder;
|
||||
mod system;
|
||||
mod value_conversion;
|
||||
|
||||
use layer_shika_adapters::errors::LayerShikaError;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
|
|
@ -21,6 +23,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 popup_builder::PopupBuilder;
|
||||
pub use system::{App, EventLoopHandle, ShellContext, ShellControl};
|
||||
|
||||
pub mod calloop {
|
||||
|
|
@ -47,8 +50,8 @@ pub mod prelude {
|
|||
pub use crate::{
|
||||
AnchorEdges, App, EventLoopHandle, KeyboardInteractivity, Layer, LayerShika,
|
||||
OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupAt,
|
||||
PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result,
|
||||
ShellContext, ShellControl,
|
||||
PopupBuilder, PopupHandle, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow,
|
||||
Result, ShellContext, ShellControl,
|
||||
};
|
||||
|
||||
pub use crate::calloop::{Generic, Interest, Mode, PostAction, RegistrationToken, Timer};
|
||||
|
|
|
|||
216
crates/composition/src/popup_builder.rs
Normal file
216
crates/composition/src/popup_builder.rs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
use crate::Result;
|
||||
use crate::system::App;
|
||||
use layer_shika_adapters::platform::slint_interpreter::Value;
|
||||
use layer_shika_domain::value_objects::popup_positioning_mode::PopupPositioningMode;
|
||||
use layer_shika_domain::value_objects::popup_request::{PopupAt, PopupRequest, PopupSize};
|
||||
|
||||
pub struct PopupBuilder<'a> {
|
||||
app: &'a App,
|
||||
component: String,
|
||||
reference: PopupAt,
|
||||
anchor: PopupPositioningMode,
|
||||
size: PopupSize,
|
||||
grab: bool,
|
||||
close_callback: Option<String>,
|
||||
resize_callback: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> PopupBuilder<'a> {
|
||||
pub(crate) fn new(app: &'a App, component: String) -> Self {
|
||||
Self {
|
||||
app,
|
||||
component,
|
||||
reference: PopupAt::Cursor,
|
||||
anchor: PopupPositioningMode::TopLeft,
|
||||
size: PopupSize::Content,
|
||||
grab: false,
|
||||
close_callback: None,
|
||||
resize_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn relative_to_cursor(mut self) -> Self {
|
||||
self.reference = PopupAt::Cursor;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn relative_to_point(mut self, x: f32, y: f32) -> Self {
|
||||
self.reference = PopupAt::Absolute { x, y };
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn relative_to_rect(mut self, x: f32, y: f32, w: f32, h: f32) -> Self {
|
||||
self.reference = PopupAt::AnchorRect { x, y, w, h };
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn anchor(mut self, anchor: PopupPositioningMode) -> Self {
|
||||
self.anchor = anchor;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_top_left(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::TopLeft;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_top_center(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::TopCenter;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_top_right(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::TopRight;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_center_left(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::CenterLeft;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_center(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::Center;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_center_right(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::CenterRight;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_bottom_left(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::BottomLeft;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_bottom_center(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::BottomCenter;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn anchor_bottom_right(mut self) -> Self {
|
||||
self.anchor = PopupPositioningMode::BottomRight;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn size(mut self, size: PopupSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn fixed_size(mut self, w: f32, h: f32) -> Self {
|
||||
self.size = PopupSize::Fixed { w, h };
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn content_size(mut self) -> Self {
|
||||
self.size = PopupSize::Content;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn grab(mut self, enable: bool) -> Self {
|
||||
self.grab = enable;
|
||||
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
|
||||
}
|
||||
|
||||
pub fn bind(self, trigger_callback: &str) -> Result<()> {
|
||||
let request = self.build_request();
|
||||
let control = self.app.control();
|
||||
|
||||
self.app.with_all_component_instances(|instance| {
|
||||
let request_clone = request.clone();
|
||||
let control_clone = control.clone();
|
||||
|
||||
if let Err(e) = instance.set_callback(trigger_callback, move |_args| {
|
||||
if let Err(e) = control_clone.show_popup(&request_clone) {
|
||||
log::error!("Failed to show popup: {}", e);
|
||||
}
|
||||
Value::Void
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to bind popup callback '{}': {}",
|
||||
trigger_callback,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn toggle(self, trigger_callback: &str) -> Result<()> {
|
||||
let request = self.build_request();
|
||||
let control = self.app.control();
|
||||
let component_name = request.component.clone();
|
||||
|
||||
self.app.with_all_component_instances(|instance| {
|
||||
let request_clone = request.clone();
|
||||
let control_clone = control.clone();
|
||||
let component_clone = component_name.clone();
|
||||
|
||||
if let Err(e) = instance.set_callback(trigger_callback, move |_args| {
|
||||
log::debug!("Toggle callback for component: {}", component_clone);
|
||||
if let Err(e) = control_clone.show_popup(&request_clone) {
|
||||
log::error!("Failed to toggle popup: {}", e);
|
||||
}
|
||||
Value::Void
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to bind toggle popup callback '{}': {}",
|
||||
trigger_callback,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_request(&self) -> PopupRequest {
|
||||
let mut builder = PopupRequest::builder(self.component.clone())
|
||||
.at(self.reference)
|
||||
.size(self.size)
|
||||
.mode(self.anchor)
|
||||
.grab(self.grab);
|
||||
|
||||
if let Some(ref close_cb) = self.close_callback {
|
||||
builder = builder.close_on(close_cb.clone());
|
||||
}
|
||||
|
||||
if let Some(ref resize_cb) = self.resize_callback {
|
||||
builder = builder.resize_on(resize_cb.clone());
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
use crate::popup_builder::PopupBuilder;
|
||||
use crate::value_conversion::IntoValue;
|
||||
use crate::{Error, Result};
|
||||
use layer_shika_adapters::errors::EventLoopError;
|
||||
use layer_shika_adapters::platform::calloop::{
|
||||
|
|
@ -14,10 +16,13 @@ use layer_shika_adapters::{
|
|||
use layer_shika_domain::config::WindowConfig;
|
||||
use layer_shika_domain::entities::output_registry::OutputRegistry;
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::value_objects::dimensions::PopupDimensions;
|
||||
use layer_shika_domain::value_objects::output_handle::OutputHandle;
|
||||
use layer_shika_domain::value_objects::output_info::OutputInfo;
|
||||
use layer_shika_domain::value_objects::dimensions::PopupDimensions;
|
||||
use layer_shika_domain::value_objects::popup_request::{PopupHandle, PopupRequest, PopupSize};
|
||||
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;
|
||||
use std::cell::RefCell;
|
||||
use std::os::unix::io::AsFd;
|
||||
|
|
@ -51,10 +56,35 @@ impl ShellControl {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn show_popup_at_cursor(&self, component: impl Into<String>) -> Result<()> {
|
||||
let request = PopupRequest::builder(component.into())
|
||||
.at(PopupAt::Cursor)
|
||||
.build();
|
||||
self.show_popup(&request)
|
||||
}
|
||||
|
||||
pub fn show_popup_centered(&self, component: impl Into<String>) -> Result<()> {
|
||||
let request = PopupRequest::builder(component.into())
|
||||
.at(PopupAt::Cursor)
|
||||
.mode(PopupPositioningMode::Center)
|
||||
.build();
|
||||
self.show_popup(&request)
|
||||
}
|
||||
|
||||
pub fn show_popup_at_position(
|
||||
&self,
|
||||
component: impl Into<String>,
|
||||
x: f32,
|
||||
y: f32,
|
||||
) -> Result<()> {
|
||||
let request = PopupRequest::builder(component.into())
|
||||
.at(PopupAt::Absolute { x, y })
|
||||
.build();
|
||||
self.show_popup(&request)
|
||||
}
|
||||
|
||||
pub fn close_popup(&self, handle: PopupHandle) -> Result<()> {
|
||||
self.sender
|
||||
.send(PopupCommand::Close(handle))
|
||||
.map_err(|_| {
|
||||
self.sender.send(PopupCommand::Close(handle)).map_err(|_| {
|
||||
Error::Domain(DomainError::Configuration {
|
||||
message: "Failed to send popup close command: channel closed".to_string(),
|
||||
})
|
||||
|
|
@ -547,7 +577,11 @@ impl ShellContext<'_> {
|
|||
);
|
||||
|
||||
if control
|
||||
.resize_popup(PopupHandle::new(popup_key), dimensions.width, dimensions.height)
|
||||
.resize_popup(
|
||||
PopupHandle::new(popup_key),
|
||||
dimensions.width,
|
||||
dimensions.height,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Failed to resize popup through control");
|
||||
|
|
@ -659,7 +693,8 @@ impl App {
|
|||
|
||||
match command {
|
||||
PopupCommand::Show(request) => {
|
||||
if let Err(e) = shell_context.show_popup(&request, Some(control.clone()))
|
||||
if let Err(e) =
|
||||
shell_context.show_popup(&request, Some(control.clone()))
|
||||
{
|
||||
log::error!("Failed to show popup: {}", e);
|
||||
}
|
||||
|
|
@ -674,9 +709,12 @@ impl App {
|
|||
width,
|
||||
height,
|
||||
} => {
|
||||
if let Err(e) =
|
||||
shell_context.resize_popup(handle, width, height, Some(control.clone()))
|
||||
{
|
||||
if let Err(e) = shell_context.resize_popup(
|
||||
handle,
|
||||
width,
|
||||
height,
|
||||
Some(control.clone()),
|
||||
) {
|
||||
log::error!("Failed to resize popup: {}", e);
|
||||
}
|
||||
}
|
||||
|
|
@ -709,17 +747,18 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_callback<F>(&self, callback_name: &str, handler: F) -> Result<()>
|
||||
pub fn on<F, R>(&self, callback_name: &str, handler: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&[Value], ShellControl) -> Value + 'static,
|
||||
F: Fn(ShellControl) -> R + 'static,
|
||||
R: IntoValue,
|
||||
{
|
||||
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())
|
||||
if let Err(e) = instance.set_callback(callback_name, move |_args| {
|
||||
handler_rc(control_clone.clone()).into_value()
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to register callback '{}' on component: {}",
|
||||
|
|
@ -731,35 +770,34 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bind_popup<F>(&self, callback_name: &str, config_builder: F) -> Result<()>
|
||||
pub fn on_with_args<F, R>(&self, callback_name: &str, handler: F) -> Result<()>
|
||||
where
|
||||
F: Fn() -> PopupRequest + 'static,
|
||||
F: Fn(&[Value], ShellControl) -> R + 'static,
|
||||
R: IntoValue,
|
||||
{
|
||||
let control = self.control();
|
||||
let builder = Rc::new(config_builder);
|
||||
|
||||
let handler = Rc::new(handler);
|
||||
self.with_all_component_instances(|instance| {
|
||||
let builder_clone = Rc::clone(&builder);
|
||||
let handler_rc = Rc::clone(&handler);
|
||||
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
|
||||
if let Err(e) = instance.set_callback(callback_name, move |args| {
|
||||
handler_rc(args, control_clone.clone()).into_value()
|
||||
}) {
|
||||
log::error!(
|
||||
"Failed to bind popup callback '{}': {}",
|
||||
"Failed to register callback '{}' on component: {}",
|
||||
callback_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn popup(&self, component_name: impl Into<String>) -> PopupBuilder<'_> {
|
||||
PopupBuilder::new(self, component_name.into())
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
self.inner.borrow_mut().run()?;
|
||||
Ok(())
|
||||
|
|
|
|||
53
crates/composition/src/value_conversion.rs
Normal file
53
crates/composition/src/value_conversion.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use layer_shika_adapters::platform::slint_interpreter::Value;
|
||||
|
||||
pub trait IntoValue {
|
||||
fn into_value(self) -> Value;
|
||||
}
|
||||
|
||||
impl IntoValue for () {
|
||||
fn into_value(self) -> Value {
|
||||
Value::Void
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for Value {
|
||||
fn into_value(self) -> Value {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for bool {
|
||||
fn into_value(self) -> Value {
|
||||
Value::Bool(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for i32 {
|
||||
fn into_value(self) -> Value {
|
||||
Value::Number(f64::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for f32 {
|
||||
fn into_value(self) -> Value {
|
||||
Value::Number(f64::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for f64 {
|
||||
fn into_value(self) -> Value {
|
||||
Value::Number(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for String {
|
||||
fn into_value(self) -> Value {
|
||||
Value::String(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for &str {
|
||||
fn into_value(self) -> Value {
|
||||
Value::String(self.into())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue