diff --git a/Cargo.lock b/Cargo.lock index 700a5b8..507f93c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "arrayref" version = "0.3.8" @@ -570,6 +620,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "combine" version = "4.6.7" @@ -720,6 +776,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +[[package]] +name = "declarative-config" +version = "0.1.0" +dependencies = [ + "env_logger", + "layer-shika", + "log", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -873,6 +938,29 @@ dependencies = [ "syn", ] +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1748,6 +1836,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.13.0" @@ -1772,6 +1866,30 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jiff" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jni" version = "0.21.1" @@ -2121,6 +2239,15 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "multi-surface" +version = "0.1.0" +dependencies = [ + "env_logger", + "layer-shika", + "log", +] + [[package]] name = "ndk" version = "0.9.0" @@ -2604,6 +2731,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "orbclient" version = "0.3.47" @@ -2749,13 +2882,22 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" dependencies = [ "critical-section", ] +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "prettyplease" version = "0.2.20" @@ -2777,9 +2919,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -2801,9 +2943,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -3140,6 +3282,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simple-bar" +version = "0.1.0" +dependencies = [ + "env_logger", + "layer-shika", + "log", +] + [[package]] name = "simplecss" version = "0.2.1" @@ -3459,9 +3610,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3845,6 +3996,12 @@ dependencies = [ "xmlwriter", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.18.1" diff --git a/Cargo.toml b/Cargo.toml index da134d9..b8805ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,14 @@ layer-shika-composition.workspace = true [workspace] resolver = "2" -members = ["crates/domain", "crates/adapters", "crates/composition"] +members = [ + "crates/domain", + "crates/adapters", + "crates/composition", + "examples/simple-bar", + "examples/multi-surface", + "examples/declarative-config", +] [workspace.package] version = "0.1.0" diff --git a/README.md b/README.md index b696bc2..6e6988b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Please note that this library is currently in early development and is not yet r - Event handling system - **UI Integration**: Slint integration layer with custom rendering backend - **Documentation**: Work in progress -- **Examples**: Coming soon (examples directory prepared) +- **Examples**: Available - see [examples/](examples/) directory **What's Working:** @@ -34,13 +34,28 @@ Please note that this library is currently in early development and is not yet r - Not all features are fully implemented or tested - API is still unstable and may change -- Examples and comprehensive documentation pending +- Comprehensive documentation pending It's recommended to wait for a stable release before using this library in production projects. Development is trying to be as fast as a running skippy deer! +## Quick Start + +Check out the [examples/](examples/) directory for comprehensive demonstrations. +Each example includes detailed documentation and can be run with: + +```bash +cargo run -p simple-bar +cargo run -p multi-surface +cargo run -p declarative-config +... +... +``` + +See the [examples README](examples/README.md) for detailed usage instructions and patterns. + ## Usage -Examples and usage instructions are coming soon. Hoping that they will not be too deer-ifying, like the current state of this library. If you want to use it now, use this repo as dependency instead of crates.io outdated versions. +If you want to use it now, use this repo as dependency instead of crates.io outdated versions. ## First Stable Release diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a4e17f2 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,78 @@ +# layer-shika Examples + +This directory contains comprehensive examples demonstrating the key features and use cases of layer-shika. + +## Quick Start + +Each example is a standalone crate that can be run from anywhere in the workspace: + +```bash +# From workspace root +cargo run -p simple-bar +cargo run -p multi-surface +cargo run -p declarative-config + +# Or with logging +RUST_LOG=info cargo run -p simple-bar + +# Or from the example directory +cd examples/simple-bar +cargo run +``` + +## Building All Examples + +From the workspace root: + +```bash +cargo build --workspace +``` + +Or build a specific example: + +```bash +cargo build -p simple-bar +cargo build -p multi-surface +cargo build -p declarative-config +``` + +## Example Progression + +**Recommended learning path:** + +1. **simple-bar** - Start here to understand the basics +2. **multi-surface** - Learn about multiple surfaces and callbacks +3. **declarative-config** - See the alternative configuration approach + +## Common Patterns + +### UI Files + +Each example includes `.slint` files in its `ui/` directory. These demonstrate: + +- Window components for surfaces +- Property bindings for dynamic updates +- Callback definitions for event handling + +### Error Handling + +All examples use layer-shika's `Result<()>` type for error handling with the `?` operator. + +## Coming Soon + +Additional examples demonstrating: +- Event loop integration (timers, channels, custom event sources) +- Multi-output support (multiple monitors) with different surfaces per output +- Advanced popup patterns +- Dynamic UI loading + +## Contributing Examples + +When adding new examples: + +1. Create a new crate in `examples//` +2. Add to workspace members in root `Cargo.toml` +3. Include `Cargo.toml`, `src/main.rs`, `ui/*.slint`, and `README.md` +4. Follow the naming convention: kebab-case +5. Add entry to this README with clear description +6. Ensure code passes `cargo clippy --all-targets` diff --git a/examples/declarative-config/Cargo.toml b/examples/declarative-config/Cargo.toml new file mode 100644 index 0000000..c5d8b7f --- /dev/null +++ b/examples/declarative-config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "declarative-config" +version.workspace = true +edition.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +layer-shika = { path = "../.." } +env_logger = "0.11.7" +log.workspace = true diff --git a/examples/declarative-config/README.md b/examples/declarative-config/README.md new file mode 100644 index 0000000..8fab794 --- /dev/null +++ b/examples/declarative-config/README.md @@ -0,0 +1,27 @@ +# Declarative Config Example + +This example demonstrates the declarative configuration approach using `ShellConfig` instead of the fluent builder API. + +## What it demonstrates + +- Creating a shell from declarative configuration +- Using `ShellConfig` and `SurfaceComponentConfig` +- Specifying all surface properties explicitly via `SurfaceConfig` +- Separating configuration building from shell creation +- Loading UI from file path via `CompiledUiSource::file()` + +## Running + +```bash +cd examples/declarative-config +RUST_LOG=info cargo run +``` + +## When to use declarative config + +- Loading configuration from external sources +- Programmatically generating configurations +- When you need full control over all configuration fields +- Building configuration tools or editors + +For simple use cases, the fluent builder API is more ergonomic. diff --git a/examples/declarative-config/src/main.rs b/examples/declarative-config/src/main.rs new file mode 100644 index 0000000..e429f68 --- /dev/null +++ b/examples/declarative-config/src/main.rs @@ -0,0 +1,54 @@ +use std::path::PathBuf; + +use layer_shika::prelude::*; +use layer_shika::slint_interpreter::Value; + +fn main() -> Result<()> { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .init(); + + log::info!("Starting declarative-config example"); + + let config = build_config(); + + log::info!("Creating shell with {} surface(s)", config.surfaces.len()); + + let mut shell = Shell::from_config(config)?; + + log::info!("Shell has surfaces: {:?}", shell.surface_names()); + log::info!("Has StatusBar surface: {}", shell.has_surface("StatusBar")); + + shell.on("StatusBar", "settings-clicked", |_control| { + log::info!("Settings button clicked"); + Value::Void + })?; + + log::info!("Registered callback for settings-clicked"); + + shell.run()?; + + Ok(()) +} + +fn build_config() -> ShellConfig { + let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/bar.slint"); + + ShellConfig { + ui_source: CompiledUiSource::file(ui_path), + surfaces: vec![SurfaceComponentConfig::with_config( + "StatusBar", + SurfaceConfig { + dimensions: SurfaceDimension::new(0, 42), + anchor: AnchorEdges::top_bar(), + exclusive_zone: 42, + layer: Layer::Top, + margin: Margins::default(), + namespace: "declarative-config-example".to_string(), + scale_factor: ScaleFactor::default(), + keyboard_interactivity: KeyboardInteractivity::OnDemand, + output_policy: OutputPolicy::PrimaryOnly, + }, + )], + } +} diff --git a/examples/declarative-config/ui/bar.slint b/examples/declarative-config/ui/bar.slint new file mode 100644 index 0000000..a90472b --- /dev/null +++ b/examples/declarative-config/ui/bar.slint @@ -0,0 +1,40 @@ +import { Button } from "std-widgets.slint"; + +export component StatusBar inherits Window { + + callback settings-clicked(); + + HorizontalLayout { + padding: 8px; + spacing: 16px; + + Text { + text: "Declarative Config Example"; + font-size: 14px; + vertical-alignment: center; + } + + Rectangle { + horizontal-stretch: 1; + } + + HorizontalLayout { + spacing: 12px; + + Text { + text: "Layer: Top | Zone: 42px | Anchor: Top"; + color: #88c0d0; + font-size: 12px; + vertical-alignment: center; + } + + settings-btn := Button { + text: "⚙️"; + min-width: 32px; + clicked => { + settings-clicked(); + } + } + } + } +} diff --git a/examples/multi-surface/Cargo.toml b/examples/multi-surface/Cargo.toml new file mode 100644 index 0000000..1b7ec6e --- /dev/null +++ b/examples/multi-surface/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "multi-surface" +version.workspace = true +edition.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +layer-shika = { path = "../.." } +env_logger = "0.11.7" +log.workspace = true diff --git a/examples/multi-surface/README.md b/examples/multi-surface/README.md new file mode 100644 index 0000000..7bf01a4 --- /dev/null +++ b/examples/multi-surface/README.md @@ -0,0 +1,53 @@ +# Multi-Surface Example + +This example demonstrates creating a shell with multiple independent surfaces - a top bar and a bottom dock. + +## What it demonstrates + +- Multiple surface creation in a single shell +- Chaining surface configurations with `.surface()` +- Different anchoring for each surface (top and bottom) +- Independent exclusive zones per surface +- Separate namespaces for each surface +- Registering callbacks on specific surfaces +- Listing all surfaces with `shell.surface_names()` + +## Running + +```bash +cd examples/multi-surface +RUST_LOG=info cargo run +``` + +## Key concepts + +Each surface is configured independently via chained `.surface()` calls: + +```rust +let mut shell = Shell::from_file("ui/shell.slint") + .surface("TopBar") + .height(42) + .anchor(AnchorEdges::top_bar()) + .exclusive_zone(42) + .surface("Dock") + .height(64) + .anchor(AnchorEdges::bottom_bar()) + .exclusive_zone(64) + .build()?; +``` + +Callbacks are registered per-surface: + +```rust +shell.on("TopBar", "workspace_clicked", |control| { + // Handle TopBar events + Value::Void +})?; + +shell.on("Dock", "app_clicked", |control| { + // Handle Dock events + Value::Void +})?; +``` + +Both surfaces run in the same event loop and share the same Slint compilation result. diff --git a/examples/multi-surface/src/main.rs b/examples/multi-surface/src/main.rs new file mode 100644 index 0000000..0f1ff34 --- /dev/null +++ b/examples/multi-surface/src/main.rs @@ -0,0 +1,44 @@ +use layer_shika::prelude::*; +use layer_shika::slint_interpreter::Value; +use std::path::PathBuf; + +fn main() -> Result<()> { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .init(); + + log::info!("Starting multi-surface example"); + + let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/shell.slint"); + + let mut shell = Shell::from_file(ui_path) + .surface("TopBar") + .height(42) + .anchor(AnchorEdges::top_bar()) + .exclusive_zone(42) + .namespace("multi-surface-top") + .surface("Dock") + .height(64) + .anchor(AnchorEdges::bottom_bar()) + .exclusive_zone(64) + .namespace("multi-surface-dock") + .build()?; + + shell.on("TopBar", "workspace_clicked", |_control| { + log::info!("Workspace button clicked in TopBar"); + Value::Void + })?; + + shell.on_with_args("Dock", "app_clicked", |args, _control| { + if let Some(Value::String(app_name)) = args.first() { + log::info!("App clicked in Dock: {}", app_name.as_str()); + } + Value::Void + })?; + + log::info!("Running shell with surfaces: {:?}", shell.surface_names()); + + shell.run()?; + + Ok(()) +} diff --git a/examples/multi-surface/ui/shell.slint b/examples/multi-surface/ui/shell.slint new file mode 100644 index 0000000..6f8f60d --- /dev/null +++ b/examples/multi-surface/ui/shell.slint @@ -0,0 +1,104 @@ +import { HorizontalBox, Button } from "std-widgets.slint"; + +export component TopBar inherits Window { + + callback workspace-clicked <=> workspace-btn.clicked; + + HorizontalLayout { + padding: 8px; + spacing: 12px; + + // Workspace switcher + HorizontalLayout { + alignment: start; + spacing: 8px; + + workspace-btn := Button { + text: "Workspaces"; + } + + Text { + text: "1"; + color: #88c0d0; + font-size: 14px; + vertical-alignment: center; + horizontal-alignment: center; + min-width: 24px; + } + + Text { + text: "2"; + color: #4c566a; + font-size: 14px; + vertical-alignment: center; + horizontal-alignment: center; + min-width: 24px; + } + } + + Rectangle { + horizontal-stretch: 1; + } + + // System info + HorizontalLayout { + alignment: end; + spacing: 12px; + + Text { + text: "🕐 12:34"; + color: #88c0d0; + font-size: 14px; + vertical-alignment: center; + } + } + } +} + +export component Dock inherits Window { + + callback app-clicked(string); + + HorizontalBox { + padding: 12px; + spacing: 16px; + alignment: center; + + // App icons + Button { + text: "🌐"; + min-width: 48px; + min-height: 48px; + clicked => { + app-clicked("browser"); + } + } + + Button { + text: "📁"; + min-width: 48px; + min-height: 48px; + clicked => { + app-clicked("files"); + } + } + + Button { + text: "💻"; + min-width: 48px; + min-height: 48px; + clicked => { + app-clicked("terminal"); + } + } + + Button { + text: "🎵"; + min-width: 48px; + min-height: 48px; + clicked => { + app-clicked("music"); + } + } + } +} diff --git a/examples/simple-bar/Cargo.toml b/examples/simple-bar/Cargo.toml new file mode 100644 index 0000000..98209df --- /dev/null +++ b/examples/simple-bar/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "simple-bar" +version.workspace = true +edition.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +layer-shika = { path = "../.." } +env_logger = "0.11.7" +log.workspace = true diff --git a/examples/simple-bar/README.md b/examples/simple-bar/README.md new file mode 100644 index 0000000..361100c --- /dev/null +++ b/examples/simple-bar/README.md @@ -0,0 +1,10 @@ +# Simple Bar Example + +This example demonstrates the most basic use case of layer-shika: creating a status bar using the fluent builder API. + +## Running + +```bash +cd examples/simple-bar +cargo run +``` diff --git a/examples/simple-bar/src/main.rs b/examples/simple-bar/src/main.rs new file mode 100644 index 0000000..b224148 --- /dev/null +++ b/examples/simple-bar/src/main.rs @@ -0,0 +1,23 @@ +use layer_shika::prelude::*; +use std::path::PathBuf; + +fn main() -> Result<()> { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .init(); + + log::info!("Starting simple-bar example"); + + let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/bar.slint"); + + Shell::from_file(ui_path) + .surface("Bar") + .height(42) + .anchor(AnchorEdges::top_bar()) + .exclusive_zone(42) + .namespace("simple-bar-example") + .build()? + .run()?; + + Ok(()) +} diff --git a/examples/simple-bar/ui/bar.slint b/examples/simple-bar/ui/bar.slint new file mode 100644 index 0000000..f5e9f9a --- /dev/null +++ b/examples/simple-bar/ui/bar.slint @@ -0,0 +1,36 @@ +import { VerticalBox, HorizontalBox } from "std-widgets.slint"; + +export component Bar inherits Window { + default-font-size: 16px; + + HorizontalBox { + // Left section + Text { + text: "Simple Bar Example"; + vertical-alignment: center; + } + + // Center section (spacer) + Rectangle { + horizontal-stretch: 1; + } + + // Right section + HorizontalLayout { + alignment: end; + spacing: 12px; + + Text { + text: "🕐 12:34"; + color: #88c0d0; + vertical-alignment: center; + } + + Text { + text: "🔋 75%"; + color: #a3be8c; + vertical-alignment: center; + } + } + } +}