diff --git a/Cargo.lock b/Cargo.lock index 8080e0b..de80f10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3316,6 +3316,15 @@ dependencies = [ "log", ] +[[package]] +name = "simple-popup" +version = "0.2.0" +dependencies = [ + "env_logger", + "layer-shika", + "log", +] + [[package]] name = "simplecss" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 10005b9..44b6801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ members = [ "examples/declarative-config", "examples/event-loop", "examples/runtime-surface-config", + "examples/simple-popup", ] [workspace.package] diff --git a/examples/.gitkeep b/examples/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/examples/README.md b/examples/README.md index daa953a..ad99f70 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,6 +12,7 @@ cargo run -p simple-bar cargo run -p multi-surface cargo run -p declarative-config cargo run -p runtime-surface-config +cargo run -p simple-popup # Or from the example directory cd examples/simple-bar @@ -27,6 +28,7 @@ cargo run 3. **declarative-config** - See the alternative configuration approach 4. **event-loop** - Explore event loop integration with timers and channels 5. **runtime-surface-config** - Surface configuration manipulation at runtime +6. **simple-popup** - Showing popups and content sizing ## Common Patterns diff --git a/examples/simple-popup/Cargo.toml b/examples/simple-popup/Cargo.toml new file mode 100644 index 0000000..85e2adc --- /dev/null +++ b/examples/simple-popup/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "simple-popup" +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-popup/README.md b/examples/simple-popup/README.md new file mode 100644 index 0000000..583ae22 --- /dev/null +++ b/examples/simple-popup/README.md @@ -0,0 +1,23 @@ +# simple-popup + +Small example that demonstrates: + +- Showing a popup with `shell.popups().builder(...)` +- Content-based sizing (manual) using a Slint `Timer` to call `resize_popup(width, height)` +- Multiple simultaneous popups (one at cursor, one centered) + +This example intentionally keeps the popup “resize callback” pattern, since the popup window size cannot be tracked automatically. + +## Run + +From the layer-shika workspace root: + +```bash +cargo run -p simple-popup +``` + +## Notes + +- Popups are transient windows intended for menus/tooltips/dialogs. +- For `PopupSize::Content`, the popup starts small and then requests a resize once layout stabilizes. +- If you don’t see the popup, make sure your compositor supports `xdg-shell` popups and that the surface is focused. diff --git a/examples/simple-popup/src/main.rs b/examples/simple-popup/src/main.rs new file mode 100644 index 0000000..7370e50 --- /dev/null +++ b/examples/simple-popup/src/main.rs @@ -0,0 +1,69 @@ +use layer_shika::prelude::*; +use std::path::PathBuf; + +fn main() -> Result<()> { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .init(); + + log::info!("Starting simple-popup example"); + + let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/ui.slint"); + + let mut shell = Shell::from_file(ui_path) + .surface("MainWindow") + .height(42) + .anchor(AnchorEdges::top_bar()) + .exclusive_zone(42) + .namespace("simple-popup-example") + .build()?; + + shell + .select(Surface::named("MainWindow")) + .on_callback("open_popup", |ctx| { + if let Err(e) = ctx + .popups() + .builder("ExamplePopup") + .at_cursor() + .content_sized() + .grab(true) + .close_on("close_popup") + .resize_on("resize_popup") + .show() + { + log::error!("Failed to show popup: {e}"); + } + }); + + shell + .select(Surface::named("MainWindow")) + .on_callback("open_two_popups", |ctx| { + if let Err(e) = ctx + .popups() + .builder("ExamplePopup") + .at_cursor() + .content_sized() + .grab(false) + .close_on("close_popup") + .resize_on("resize_popup") + .show() + { + log::error!("Failed to show first popup: {e}"); + } + + if let Err(e) = ctx + .popups() + .builder("ExamplePopup") + .centered() + .fixed_size(360.0, 140.0) + .grab(false) + .close_on("close_popup") + .show() + { + log::error!("Failed to show second popup: {e}"); + } + }); + + shell.run()?; + Ok(()) +} diff --git a/examples/simple-popup/ui/ui.slint b/examples/simple-popup/ui/ui.slint new file mode 100644 index 0000000..a3cc41e --- /dev/null +++ b/examples/simple-popup/ui/ui.slint @@ -0,0 +1,58 @@ +import { Button, HorizontalBox, VerticalBox } from "std-widgets.slint"; + +export component MainWindow inherits Window { + callback open_popup(); + callback open_two_popups(); + + HorizontalBox { + spacing: 12px; + alignment: center; + + Button { + text: "Open popup"; + clicked => { + root.open_popup(); + } + } + + Button { + text: "Open two popups"; + clicked => { + root.open_two_popups(); + } + } + } +} + +export component ExamplePopup inherits Window { + callback close_popup(); + callback resize_popup(length, length); + + VerticalBox { + padding: 12px; + spacing: 8px; + + Text { + text: "Hello from a layer-shika popup!"; + } + + Text { + text: "This popup uses content sizing via an one-shot 1ms Timer."; + } + + Button { + text: "Close"; + clicked => { + root.close_popup(); + } + } + } + + resize_timer := Timer { + interval: 1ms; + running: true; + triggered => { + root.resize_popup(root.preferred-width, root.preferred-height); + } + } +}