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 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;

View file

@ -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<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(
compilation_result: Rc<CompilationResult>,
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_info::{OutputGeometry, OutputInfo};
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_positioning_mode;
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
//! - [`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::{

View file

@ -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;

View file

@ -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,
};