feat: add declarative config support

This commit is contained in:
drendog 2025-12-04 03:48:59 +01:00
parent 983056abfc
commit aa536c9487
Signed by: dwenya
GPG key ID: 8DD77074645332D0
9 changed files with 261 additions and 18 deletions

View file

@ -4,6 +4,7 @@ mod event_loop;
mod layer_surface; mod layer_surface;
mod popup_builder; mod popup_builder;
mod shell; mod shell;
mod shell_config;
mod shell_runtime; mod shell_runtime;
mod system; mod system;
pub mod value_conversion; pub mod value_conversion;
@ -38,6 +39,8 @@ pub use shell::{
SurfaceConfigBuilder, SurfaceDefinition, SurfaceConfigBuilder, SurfaceDefinition,
}; };
pub use shell_config::{CompiledUiSource, ShellConfig, SurfaceComponentConfig};
pub mod calloop { pub mod calloop {
pub use layer_shika_adapters::platform::calloop::{ pub use layer_shika_adapters::platform::calloop::{
Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, channel, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, channel,
@ -60,12 +63,13 @@ pub enum Error {
pub mod prelude { pub mod prelude {
pub use crate::{ pub use crate::{
AnchorEdges, AnchorStrategy, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, EventContext, AnchorEdges, AnchorStrategy, CompiledUiSource, DEFAULT_COMPONENT_NAME,
EventLoopHandle, IntoValue, KeyboardInteractivity, Layer, LayerSurfaceHandle, DEFAULT_SURFACE_NAME, EventContext, EventLoopHandle, IntoValue, KeyboardInteractivity,
OutputGeometry, OutputHandle, OutputInfo, OutputPolicy, OutputRegistry, PopupBuilder, Layer, LayerSurfaceHandle, OutputGeometry, OutputHandle, OutputInfo, OutputPolicy,
PopupHandle, PopupPlacement, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, OutputRegistry, PopupBuilder, PopupHandle, PopupPlacement, PopupPositioningMode,
Result, Shell, ShellBuilder, ShellControl, ShellEventContext, ShellEventLoopHandle, PopupRequest, PopupSize, PopupWindow, Result, Shell, ShellBuilder, ShellConfig,
ShellRuntime, ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime,
ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig,
SurfaceConfigBuilder, SurfaceDefinition, SurfaceConfigBuilder, SurfaceDefinition,
}; };
@ -74,7 +78,7 @@ pub mod prelude {
pub use crate::{slint, slint_interpreter}; pub use crate::{slint, slint_interpreter};
pub use layer_shika_domain::prelude::{ 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; pub use layer_shika_adapters::platform::wayland::Anchor;

View file

@ -1,6 +1,7 @@
use crate::event_loop::{EventLoopHandleBase, FromAppState}; use crate::event_loop::{EventLoopHandleBase, FromAppState};
use crate::layer_surface::LayerSurfaceHandle; use crate::layer_surface::LayerSurfaceHandle;
use crate::popup_builder::PopupBuilder; use crate::popup_builder::PopupBuilder;
use crate::shell_config::{CompiledUiSource, ShellConfig};
use crate::shell_runtime::ShellRuntime; use crate::shell_runtime::ShellRuntime;
use crate::system::{PopupCommand, ShellControl}; use crate::system::{PopupCommand, ShellControl};
use crate::value_conversion::IntoValue; use crate::value_conversion::IntoValue;
@ -314,6 +315,32 @@ impl Shell {
Ok(Rc::new(result)) Ok(Rc::new(result))
} }
pub fn from_config(config: ShellConfig) -> Result<Self> {
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<SurfaceDefinition> = 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( pub(crate) fn new(
compilation_result: Rc<CompilationResult>, compilation_result: Rc<CompilationResult>,
definitions: Vec<SurfaceDefinition>, definitions: Vec<SurfaceDefinition>,

View file

@ -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<CompilationResult>),
}
impl CompiledUiSource {
pub fn file(path: impl Into<PathBuf>) -> Self {
Self::File(path.into())
}
pub fn source(code: impl Into<String>) -> Self {
Self::Source(code.into())
}
pub fn compiled(result: Rc<CompilationResult>) -> Self {
Self::Compiled(result)
}
}
impl From<UiSource> for CompiledUiSource {
fn from(source: UiSource) -> Self {
match source {
UiSource::File(path) => Self::File(path),
UiSource::Source(code) => Self::Source(code),
}
}
}
impl From<Rc<CompilationResult>> for CompiledUiSource {
fn from(result: Rc<CompilationResult>) -> Self {
Self::Compiled(result)
}
}
impl From<&str> for CompiledUiSource {
fn from(s: &str) -> Self {
Self::File(PathBuf::from(s))
}
}
impl From<String> for CompiledUiSource {
fn from(s: String) -> Self {
Self::File(PathBuf::from(s))
}
}
impl From<PathBuf> for CompiledUiSource {
fn from(path: PathBuf) -> Self {
Self::File(path)
}
}
pub struct ShellConfig {
pub ui_source: CompiledUiSource,
pub surfaces: Vec<SurfaceComponentConfig>,
}
#[derive(Debug, Clone)]
pub struct SurfaceComponentConfig {
pub component: String,
pub config: SurfaceConfig,
}
impl ShellConfig {
pub fn new(ui_source: impl Into<CompiledUiSource>) -> Self {
Self {
ui_source: ui_source.into(),
surfaces: Vec::new(),
}
}
#[must_use]
pub fn with_surface(mut self, component: impl Into<String>) -> Self {
self.surfaces.push(SurfaceComponentConfig {
component: component.into(),
config: SurfaceConfig::default(),
});
self
}
#[must_use]
pub fn with_surface_config(
mut self,
component: impl Into<String>,
config: SurfaceConfig,
) -> Self {
self.surfaces.push(SurfaceComponentConfig {
component: component.into(),
config,
});
self
}
pub fn add_surface(&mut self, component: impl Into<String>) -> &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<String>,
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<String>) -> Self {
Self {
component: component.into(),
config: SurfaceConfig::default(),
}
}
pub fn with_config(component: impl Into<String>, config: SurfaceConfig) -> Self {
Self {
component: component.into(),
config,
}
}
}

View file

@ -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_handle::OutputHandle;
pub use crate::value_objects::output_info::{OutputGeometry, OutputInfo}; pub use crate::value_objects::output_info::{OutputGeometry, OutputInfo};
pub use crate::value_objects::output_policy::OutputPolicy; pub use crate::value_objects::output_policy::OutputPolicy;
pub use crate::value_objects::ui_source::UiSource;

View file

@ -10,3 +10,4 @@ pub mod output_policy;
pub mod popup_config; pub mod popup_config;
pub mod popup_positioning_mode; pub mod popup_positioning_mode;
pub mod popup_request; pub mod popup_request;
pub mod ui_source;

View file

@ -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<PathBuf>) -> Self {
Self::File(path.into())
}
pub fn source(code: impl Into<String>) -> Self {
Self::Source(code.into())
}
}
impl From<&str> for UiSource {
fn from(s: &str) -> Self {
Self::File(PathBuf::from(s))
}
}
impl From<String> for UiSource {
fn from(s: String) -> Self {
Self::File(PathBuf::from(s))
}
}
impl From<PathBuf> for UiSource {
fn from(path: PathBuf) -> Self {
Self::File(path)
}
}

View file

@ -28,9 +28,9 @@
//! - [`slint_integration`] Slint framework re-exports and wrappers //! - [`slint_integration`] Slint framework re-exports and wrappers
//! - [`calloop`] Event loop types for custom event sources //! - [`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 //! ```rust,no_run
//! use layer_shika::prelude::*; //! use layer_shika::prelude::*;
@ -45,6 +45,29 @@
//! # Ok::<(), layer_shika::Error>(()) //! # 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 //! # Multi-Surface Shell
//! //!
//! Same API naturally extends to multiple surfaces: //! Same API naturally extends to multiple surfaces:
@ -98,9 +121,10 @@ pub mod window;
pub use layer_shika_composition::{Error, Result}; pub use layer_shika_composition::{Error, Result};
pub use shell::{ pub use shell::{
DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell,
ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime,
ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig,
SurfaceConfigBuilder, SurfaceDefinition,
}; };
pub use window::{ pub use window::{

View file

@ -9,9 +9,10 @@
#![allow(clippy::pub_use)] #![allow(clippy::pub_use)]
pub use crate::shell::{ pub use crate::shell::{
DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell,
ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime,
ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig,
SurfaceConfigBuilder, SurfaceDefinition,
}; };
pub use crate::window::{ pub use crate::window::{
@ -29,6 +30,7 @@ pub use crate::{Error, Result};
pub use layer_shika_composition::prelude::{ pub use layer_shika_composition::prelude::{
Anchor, LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension, Anchor, LogicalSize, Margins, PhysicalSize, ScaleFactor, SurfaceConfig, SurfaceDimension,
UiSource,
}; };
pub use crate::calloop; pub use crate::calloop;

View file

@ -1,5 +1,6 @@
pub use layer_shika_composition::{ pub use layer_shika_composition::{
DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell,
ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, ShellSurfaceConfigHandler, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime,
ShellSurfaceHandle, SingleWindowShell, SurfaceConfigBuilder, SurfaceDefinition, ShellSurfaceConfigHandler, ShellSurfaceHandle, SingleWindowShell, SurfaceComponentConfig,
SurfaceConfigBuilder, SurfaceDefinition,
}; };