From d70a0abda5fdbab865343cd64fb1c12ff59fc988 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Fri, 14 Jun 2024 21:29:19 -0500 Subject: [PATCH] Add builtin quit prompt --- Cargo.lock | 71 +++++++++++++- api/rust/examples/default_config/main.rs | 35 +------ api/rust/src/lib.rs | 38 ++++---- api/rust/src/snowcap.rs | 29 ++++++ api/rust/src/snowcap/integration.rs | 115 +++++++++++++++++++++++ snowcap | 2 +- 6 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 api/rust/src/snowcap.rs create mode 100644 api/rust/src/snowcap/integration.rs diff --git a/Cargo.lock b/Cargo.lock index 9d4ae9d..d0370a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,7 +601,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -937,6 +937,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1219,6 +1254,27 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "from_variants" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e859c8f2057687618905dbe99fc76e836e0a69738865ef90e46fc214a41bbf2" +dependencies = [ + "from_variants_impl", +] + +[[package]] +name = "from_variants_impl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a5e644a80e6d96b2b4910fa7993301d7b7926c045b475b62202b20a36ce69e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "futures" version = "0.3.30" @@ -1834,6 +1890,12 @@ dependencies = [ "objc2 0.4.1", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.25.1" @@ -3614,6 +3676,7 @@ dependencies = [ name = "snowcap-api" version = "0.1.0" dependencies = [ + "from_variants", "futures", "snowcap-api-defs", "tokio", @@ -3698,6 +3761,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" diff --git a/api/rust/examples/default_config/main.rs b/api/rust/examples/default_config/main.rs index addc8de..815a8f5 100644 --- a/api/rust/examples/default_config/main.rs +++ b/api/rust/examples/default_config/main.rs @@ -11,10 +11,6 @@ use pinnacle_api::{ input::{Mod, MouseButton, MouseEdge}, ApiModules, }; -#[cfg(feature = "snowcap")] -use snowcap_api::layer::{ExclusiveZone, KeyboardInteractivity, ZLayer}; -#[cfg(feature = "snowcap")] -use snowcap_api::widget::{Alignment, Column, Container, Length, Text}; // Pinnacle needs to perform some setup before and after your config. // The `#[pinnacle_api::config(modules)]` attribute does so and @@ -76,36 +72,7 @@ async fn main() { // `mod_key + alt + q` quits Pinnacle input.keybind([mod_key, Mod::Alt], 'q', || { #[cfg(feature = "snowcap")] - { - let widget = Container::new(Column::new_with_children([ - Text::new("Quit Pinnacle?").into(), - Text::new("Press ENTER to confirm.").into(), - ])) - .with_vertical_alignment(Alignment::Center) - .with_horizontal_alignment(Alignment::Center) - .with_width(Length::Fill) - .with_height(Length::Fill) - .with_border_thickness(4.0) - .with_border_radius(16.0); - - snowcap - .new_widget( - widget.into(), - 200, - 100, - None, - KeyboardInteractivity::Exclusive, - ExclusiveZone::Respect, - ZLayer::Overlay, - ) - .on_key_press(|widget, key, _mods| { - if key == Keysym::Return { - pinnacle.quit(); - } else { - widget.close(); - } - }); - } + snowcap.integration.quit_prompt().show(); #[cfg(not(feature = "snowcap"))] pinnacle.quit(); diff --git a/api/rust/src/lib.rs b/api/rust/src/lib.rs index df9f1a4..a602be4 100644 --- a/api/rust/src/lib.rs +++ b/api/rust/src/lib.rs @@ -85,6 +85,7 @@ use pinnacle::Pinnacle; use process::Process; use render::Render; use signal::SignalState; +use snowcap::Snowcap; use tag::Tag; use tokio::{ sync::{ @@ -98,9 +99,6 @@ use tonic::transport::{Endpoint, Uri}; use tower::service_fn; use window::Window; -#[cfg(feature = "snowcap")] -use snowcap_api::layer::Layer; - pub mod input; pub mod layout; pub mod output; @@ -108,6 +106,8 @@ pub mod pinnacle; pub mod process; pub mod render; pub mod signal; +#[cfg(feature = "snowcap")] +pub mod snowcap; pub mod tag; pub mod util; pub mod window; @@ -142,7 +142,7 @@ pub struct ApiModules { #[cfg(feature = "snowcap")] /// The snowcap widget system. - pub snowcap: &'static Layer, + pub snowcap: &'static Snowcap, } impl std::fmt::Debug for ApiModules { @@ -215,20 +215,21 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box Result<(ApiModules, Receivers), Box Self { + Self { + layer: Box::leak(Box::new(layer)), + integration: Box::leak(Box::new(Integration::new())), + } + } + + pub(crate) fn finish_init(&self, api: ApiModules) { + self.integration.finish_init(api); + } +} diff --git a/api/rust/src/snowcap/integration.rs b/api/rust/src/snowcap/integration.rs new file mode 100644 index 0000000..9be1f48 --- /dev/null +++ b/api/rust/src/snowcap/integration.rs @@ -0,0 +1,115 @@ +//! Pinnacle-specific integrations with Snowcap. +//! +//! This module includes builtin widgets like the exit prompt and keybind list. + +use std::sync::OnceLock; + +use snowcap_api::{ + layer::{ExclusiveZone, KeyboardInteractivity, ZLayer}, + widget::{ + font::{Family, Font, Weight}, + Alignment, Color, Column, Container, Length, Text, + }, +}; +use xkbcommon::xkb::Keysym; + +use crate::ApiModules; + +/// Builtin widgets for Pinnacle. +pub struct Integration { + api: OnceLock, +} + +impl Integration { + pub(crate) fn new() -> Self { + Self { + api: OnceLock::new(), + } + } + + pub(crate) fn finish_init(&self, api: ApiModules) { + self.api.set(api).unwrap(); + } + + /// Create the default quit prompt. + /// + /// Some of its characteristics can be changed by setting its fields. + pub fn quit_prompt(&self) -> QuitPrompt { + QuitPrompt { + api: self.api.get().cloned().unwrap(), + border_radius: 12.0, + border_thickness: 6.0, + background_color: [0.15, 0.03, 0.1, 0.65].into(), + border_color: [0.8, 0.2, 0.4].into(), + font: Font::new_with_family(Family::Name("Ubuntu".into())), + width: 220, + height: 120, + } + } +} + +/// A quit prompt. +/// +/// When opened, pressing ENTER will quit the compositor. +pub struct QuitPrompt { + api: ApiModules, + /// The radius of the prompt's corners. + pub border_radius: f32, + /// The thickness of the prompt border. + pub border_thickness: f32, + /// The color of the prompt background. + pub background_color: Color, + /// The color of the prompt border. + pub border_color: Color, + /// The font of the prompt. + pub font: Font, + /// The height of the prompt. + pub width: u32, + /// The width of the prompt. + pub height: u32, +} + +impl QuitPrompt { + /// Show this quit prompt. + pub fn show(&self) { + let widget = Container::new(Column::new_with_children([ + Text::new("Quit Pinnacle?") + .font(self.font.clone().weight(Weight::Bold)) + .size(20.0) + .into(), + Text::new("").size(8.0).into(), // Spacing because I haven't impl'd that yet + Text::new("Press ENTER to confirm, or\nany other key to close this") + .font(self.font.clone()) + .size(14.0) + .into(), + ])) + .width(Length::Fill) + .height(Length::Fill) + .vertical_alignment(Alignment::Center) + .horizontal_alignment(Alignment::Center) + .border_radius(self.border_radius) + .border_thickness(self.border_thickness) + .border_color(self.border_color) + .background_color(self.background_color); + + self.api + .snowcap + .layer + .new_widget( + widget, + self.width, + self.height, + None, + KeyboardInteractivity::Exclusive, + ExclusiveZone::Respect, + ZLayer::Overlay, + ) + .on_key_press(|handle, key, _mods| { + if key == Keysym::Return { + self.api.pinnacle.quit(); + } else { + handle.close(); + } + }); + } +} diff --git a/snowcap b/snowcap index 81f61f7..e685077 160000 --- a/snowcap +++ b/snowcap @@ -1 +1 @@ -Subproject commit 81f61f7fbbec91673caf6c56df438b64eab90181 +Subproject commit e6850779511da33bbdd4be646bcc21be39495ffa