From aa536c94870c0363d98c7fa7cb70b3acc58d6267 Mon Sep 17 00:00:00 2001 From: drendog Date: Thu, 4 Dec 2025 03:48:59 +0100 Subject: [PATCH] feat: add declarative config support --- crates/composition/src/lib.rs | 18 ++- crates/composition/src/shell.rs | 27 ++++ crates/composition/src/shell_config.rs | 148 +++++++++++++++++++ crates/domain/src/prelude.rs | 1 + crates/domain/src/value_objects/mod.rs | 1 + crates/domain/src/value_objects/ui_source.rs | 35 +++++ src/lib.rs | 34 ++++- src/prelude.rs | 8 +- src/shell.rs | 7 +- 9 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 crates/composition/src/shell_config.rs create mode 100644 crates/domain/src/value_objects/ui_source.rs diff --git a/crates/composition/src/lib.rs b/crates/composition/src/lib.rs index 1ea3728..8d30b46 100644 --- a/crates/composition/src/lib.rs +++ b/crates/composition/src/lib.rs @@ -4,6 +4,7 @@ mod event_loop; mod layer_surface; mod popup_builder; mod shell; +mod shell_config; mod shell_runtime; mod system; pub mod value_conversion; @@ -38,6 +39,8 @@ pub use shell::{ SurfaceConfigBuilder, SurfaceDefinition, }; +pub use shell_config::{CompiledUiSource, ShellConfig, SurfaceComponentConfig}; + pub mod calloop { pub use layer_shika_adapters::platform::calloop::{ Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, channel, @@ -60,12 +63,13 @@ pub enum Error { pub mod prelude { pub use crate::{ - AnchorEdges, AnchorStrategy, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, EventContext, - EventLoopHandle, IntoValue, KeyboardInteractivity, Layer, LayerSurfaceHandle, - OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupBuilder, - PopupHandle, PopupPlacement, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, - Result, Shell, ShellBuilder, ShellControl, ShellEventContext, ShellEventLoopHandle, - ShellRuntime, ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, + AnchorEdges, AnchorStrategy, CompiledUiSource, DEFAULT_COMPONENT_NAME, + DEFAULT_SURFACE_NAME, EventContext, EventLoopHandle, IntoValue, KeyboardInteractivity, + Layer, LayerSurfaceHandle, OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, + OutputRegistry, PopupBuilder, PopupHandle, PopupPlacement, PopupPositioningMode, + PopupRequest, PopupSize, PopupWindow, Result, Shell, ShellBuilder, ShellConfig, + ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, + ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, }; @@ -74,7 +78,7 @@ pub mod prelude { pub use crate::{slint, slint_interpreter}; pub use layer_shika_domain::prelude::{ - LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension, + LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension, UiSource, }; pub use layer_shika_adapters::platform::wayland::Anchor; diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index 61f86d8..247023e 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -1,6 +1,7 @@ use crate::event_loop::{EventLoopHandleBase, FromAppState}; use crate::layer_surface::LayerSurfaceHandle; use crate::popup_builder::PopupBuilder; +use crate::shell_config::{CompiledUiSource, ShellConfig}; use crate::shell_runtime::ShellRuntime; use crate::system::{PopupCommand, ShellControl}; use crate::value_conversion::IntoValue; @@ -314,6 +315,32 @@ impl Shell { Ok(Rc::new(result)) } + pub fn from_config(config: ShellConfig) -> Result { + let compilation_result = match config.ui_source { + CompiledUiSource::File(path) => Self::compile_file(&path)?, + CompiledUiSource::Source(code) => Self::compile_source(code)?, + CompiledUiSource::Compiled(result) => result, + }; + + let surfaces: Vec = if config.surfaces.is_empty() { + vec![SurfaceDefinition { + component: DEFAULT_COMPONENT_NAME.to_string(), + config: SurfaceConfig::default(), + }] + } else { + config + .surfaces + .into_iter() + .map(|s| SurfaceDefinition { + component: s.component, + config: s.config, + }) + .collect() + }; + + Self::new(compilation_result, surfaces) + } + pub(crate) fn new( compilation_result: Rc, definitions: Vec, diff --git a/crates/composition/src/shell_config.rs b/crates/composition/src/shell_config.rs new file mode 100644 index 0000000..f657352 --- /dev/null +++ b/crates/composition/src/shell_config.rs @@ -0,0 +1,148 @@ +use layer_shika_adapters::platform::slint_interpreter::CompilationResult; +use layer_shika_domain::prelude::{SurfaceConfig, UiSource}; +use std::path::PathBuf; +use std::rc::Rc; + +pub enum CompiledUiSource { + File(PathBuf), + Source(String), + Compiled(Rc), +} + +impl CompiledUiSource { + pub fn file(path: impl Into) -> Self { + Self::File(path.into()) + } + + pub fn source(code: impl Into) -> Self { + Self::Source(code.into()) + } + + pub fn compiled(result: Rc) -> Self { + Self::Compiled(result) + } +} + +impl From for CompiledUiSource { + fn from(source: UiSource) -> Self { + match source { + UiSource::File(path) => Self::File(path), + UiSource::Source(code) => Self::Source(code), + } + } +} + +impl From> for CompiledUiSource { + fn from(result: Rc) -> Self { + Self::Compiled(result) + } +} + +impl From<&str> for CompiledUiSource { + fn from(s: &str) -> Self { + Self::File(PathBuf::from(s)) + } +} + +impl From for CompiledUiSource { + fn from(s: String) -> Self { + Self::File(PathBuf::from(s)) + } +} + +impl From for CompiledUiSource { + fn from(path: PathBuf) -> Self { + Self::File(path) + } +} + +pub struct ShellConfig { + pub ui_source: CompiledUiSource, + pub surfaces: Vec, +} + +#[derive(Debug, Clone)] +pub struct SurfaceComponentConfig { + pub component: String, + pub config: SurfaceConfig, +} + +impl ShellConfig { + pub fn new(ui_source: impl Into) -> Self { + Self { + ui_source: ui_source.into(), + surfaces: Vec::new(), + } + } + + #[must_use] + pub fn with_surface(mut self, component: impl Into) -> Self { + self.surfaces.push(SurfaceComponentConfig { + component: component.into(), + config: SurfaceConfig::default(), + }); + self + } + + #[must_use] + pub fn with_surface_config( + mut self, + component: impl Into, + config: SurfaceConfig, + ) -> Self { + self.surfaces.push(SurfaceComponentConfig { + component: component.into(), + config, + }); + self + } + + pub fn add_surface(&mut self, component: impl Into) -> &mut SurfaceComponentConfig { + self.surfaces.push(SurfaceComponentConfig { + component: component.into(), + config: SurfaceConfig::default(), + }); + self.surfaces + .last_mut() + .unwrap_or_else(|| unreachable!("just pushed")) + } + + pub fn add_surface_config( + &mut self, + component: impl Into, + config: SurfaceConfig, + ) -> &mut SurfaceComponentConfig { + self.surfaces.push(SurfaceComponentConfig { + component: component.into(), + config, + }); + self.surfaces + .last_mut() + .unwrap_or_else(|| unreachable!("just pushed")) + } +} + +impl Default for ShellConfig { + fn default() -> Self { + Self { + ui_source: CompiledUiSource::Source(String::new()), + surfaces: Vec::new(), + } + } +} + +impl SurfaceComponentConfig { + pub fn new(component: impl Into) -> Self { + Self { + component: component.into(), + config: SurfaceConfig::default(), + } + } + + pub fn with_config(component: impl Into, config: SurfaceConfig) -> Self { + Self { + component: component.into(), + config, + } + } +} diff --git a/crates/domain/src/prelude.rs b/crates/domain/src/prelude.rs index 653ad78..1208a0a 100644 --- a/crates/domain/src/prelude.rs +++ b/crates/domain/src/prelude.rs @@ -16,3 +16,4 @@ pub use crate::value_objects::margins::Margins; pub use crate::value_objects::output_handle::OutputHandle; pub use crate::value_objects::output_info::{OutputGeometry, OutputInfo}; pub use crate::value_objects::output_policy::OutputPolicy; +pub use crate::value_objects::ui_source::UiSource; diff --git a/crates/domain/src/value_objects/mod.rs b/crates/domain/src/value_objects/mod.rs index 5c0e313..4f14a64 100644 --- a/crates/domain/src/value_objects/mod.rs +++ b/crates/domain/src/value_objects/mod.rs @@ -10,3 +10,4 @@ pub mod output_policy; pub mod popup_config; pub mod popup_positioning_mode; pub mod popup_request; +pub mod ui_source; diff --git a/crates/domain/src/value_objects/ui_source.rs b/crates/domain/src/value_objects/ui_source.rs new file mode 100644 index 0000000..cf16cd9 --- /dev/null +++ b/crates/domain/src/value_objects/ui_source.rs @@ -0,0 +1,35 @@ +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub enum UiSource { + File(PathBuf), + Source(String), +} + +impl UiSource { + pub fn file(path: impl Into) -> Self { + Self::File(path.into()) + } + + pub fn source(code: impl Into) -> Self { + Self::Source(code.into()) + } +} + +impl From<&str> for UiSource { + fn from(s: &str) -> Self { + Self::File(PathBuf::from(s)) + } +} + +impl From for UiSource { + fn from(s: String) -> Self { + Self::File(PathBuf::from(s)) + } +} + +impl From for UiSource { + fn from(path: PathBuf) -> Self { + Self::File(path) + } +} diff --git a/src/lib.rs b/src/lib.rs index 06b7437..28b423b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,9 +28,9 @@ //! - [`slint_integration`] – Slint framework re-exports and wrappers //! - [`calloop`] – Event loop types for custom event sources //! -//! # Quick Start +//! # Quick Start (Fluent Builder) //! -//! Single-surface use case: +//! Single-surface use case with the fluent builder API: //! //! ```rust,no_run //! use layer_shika::prelude::*; @@ -45,6 +45,29 @@ //! # Ok::<(), layer_shika::Error>(()) //! ``` //! +//! # Declarative Configuration +//! +//! For reusable, programmatically generated, or externally sourced configurations: +//! +//! ```rust,no_run +//! use layer_shika::prelude::*; +//! +//! let config = ShellConfig { +//! ui_source: UiSource::file("ui/bar.slint"), +//! surfaces: vec![ +//! SurfaceComponentConfig::with_config("Bar", SurfaceConfig { +//! dimensions: SurfaceDimension::new(0, 42), +//! anchor: AnchorEdges::top_bar(), +//! exclusive_zone: 42, +//! ..Default::default() +//! }), +//! ], +//! }; +//! +//! Shell::from_config(config)?.run()?; +//! # Ok::<(), layer_shika::Error>(()) +//! ``` +//! //! # Multi-Surface Shell //! //! Same API naturally extends to multiple surfaces: @@ -98,9 +121,10 @@ pub mod window; pub use layer_shika_composition::{Error, Result}; pub use shell::{ - DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, - ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, - ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, + CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, + ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, + ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig, + SurfaceConfigBuilder, SurfaceDefinition, }; pub use window::{ diff --git a/src/prelude.rs b/src/prelude.rs index 04693e5..5764fb2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,9 +9,10 @@ #![allow(clippy::pub_use)] pub use crate::shell::{ - DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, - ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, - ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, + CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, + ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, + ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig, + SurfaceConfigBuilder, SurfaceDefinition, }; pub use crate::window::{ @@ -29,6 +30,7 @@ pub use crate::{Error, Result}; pub use layer_shika_composition::prelude::{ Anchor, LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension, + UiSource, }; pub use crate::calloop; diff --git a/src/shell.rs b/src/shell.rs index c55b681..2d29ca4 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,5 +1,6 @@ pub use layer_shika_composition::{ - DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, - ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, - ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, + CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, + ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, + ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig, + SurfaceConfigBuilder, SurfaceDefinition, };