Add builtin quit prompt

This commit is contained in:
Ottatop 2024-06-14 21:29:19 -05:00
parent d58c8571ca
commit d70a0abda5
6 changed files with 236 additions and 54 deletions

71
Cargo.lock generated
View file

@ -601,7 +601,7 @@ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim 0.11.1",
] ]
[[package]] [[package]]
@ -937,6 +937,41 @@ dependencies = [
"winapi", "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]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -1219,6 +1254,27 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 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]] [[package]]
name = "futures" name = "futures"
version = "0.3.30" version = "0.3.30"
@ -1834,6 +1890,12 @@ dependencies = [
"objc2 0.4.1", "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]] [[package]]
name = "image" name = "image"
version = "0.25.1" version = "0.25.1"
@ -3614,6 +3676,7 @@ dependencies = [
name = "snowcap-api" name = "snowcap-api"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"from_variants",
"futures", "futures",
"snowcap-api-defs", "snowcap-api-defs",
"tokio", "tokio",
@ -3698,6 +3761,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"

View file

@ -11,10 +11,6 @@ use pinnacle_api::{
input::{Mod, MouseButton, MouseEdge}, input::{Mod, MouseButton, MouseEdge},
ApiModules, 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. // Pinnacle needs to perform some setup before and after your config.
// The `#[pinnacle_api::config(modules)]` attribute does so and // The `#[pinnacle_api::config(modules)]` attribute does so and
@ -76,36 +72,7 @@ async fn main() {
// `mod_key + alt + q` quits Pinnacle // `mod_key + alt + q` quits Pinnacle
input.keybind([mod_key, Mod::Alt], 'q', || { input.keybind([mod_key, Mod::Alt], 'q', || {
#[cfg(feature = "snowcap")] #[cfg(feature = "snowcap")]
{ snowcap.integration.quit_prompt().show();
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();
}
});
}
#[cfg(not(feature = "snowcap"))] #[cfg(not(feature = "snowcap"))]
pinnacle.quit(); pinnacle.quit();

View file

@ -85,6 +85,7 @@ use pinnacle::Pinnacle;
use process::Process; use process::Process;
use render::Render; use render::Render;
use signal::SignalState; use signal::SignalState;
use snowcap::Snowcap;
use tag::Tag; use tag::Tag;
use tokio::{ use tokio::{
sync::{ sync::{
@ -98,9 +99,6 @@ use tonic::transport::{Endpoint, Uri};
use tower::service_fn; use tower::service_fn;
use window::Window; use window::Window;
#[cfg(feature = "snowcap")]
use snowcap_api::layer::Layer;
pub mod input; pub mod input;
pub mod layout; pub mod layout;
pub mod output; pub mod output;
@ -108,6 +106,8 @@ pub mod pinnacle;
pub mod process; pub mod process;
pub mod render; pub mod render;
pub mod signal; pub mod signal;
#[cfg(feature = "snowcap")]
pub mod snowcap;
pub mod tag; pub mod tag;
pub mod util; pub mod util;
pub mod window; pub mod window;
@ -142,7 +142,7 @@ pub struct ApiModules {
#[cfg(feature = "snowcap")] #[cfg(feature = "snowcap")]
/// The snowcap widget system. /// The snowcap widget system.
pub snowcap: &'static Layer, pub snowcap: &'static Snowcap,
} }
impl std::fmt::Debug for ApiModules { impl std::fmt::Debug for ApiModules {
@ -215,10 +215,9 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box<dyn std::error::Er
}; };
#[cfg(feature = "snowcap")] #[cfg(feature = "snowcap")]
let (modules, snowcap_recv) = {
let (snowcap, snowcap_recv) = snowcap_api::connect().await.unwrap(); let (snowcap, snowcap_recv) = snowcap_api::connect().await.unwrap();
let api_modules = ApiModules {
#[cfg(feature = "snowcap")]
let modules = ApiModules {
pinnacle, pinnacle,
process, process,
window, window,
@ -228,7 +227,9 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box<dyn std::error::Er
layout, layout,
render, render,
signal: signal.clone(), signal: signal.clone(),
snowcap: Box::leak(Box::new(snowcap)), snowcap: Box::leak(Box::new(Snowcap::new(snowcap))),
};
(api_modules, snowcap_recv)
}; };
window.finish_init(modules.clone()); window.finish_init(modules.clone());
@ -236,6 +237,7 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box<dyn std::error::Er
tag.finish_init(modules.clone()); tag.finish_init(modules.clone());
layout.finish_init(modules.clone()); layout.finish_init(modules.clone());
signal.read().await.finish_init(modules.clone()); signal.read().await.finish_init(modules.clone());
modules.snowcap.finish_init(modules.clone());
#[cfg(feature = "snowcap")] #[cfg(feature = "snowcap")]
let receivers = Receivers { let receivers = Receivers {

29
api/rust/src/snowcap.rs Normal file
View file

@ -0,0 +1,29 @@
//! The Snowcap widget system.
//! // TODO: these docs
use integration::Integration;
use snowcap_api::layer::Layer;
use crate::ApiModules;
pub mod integration;
/// Snowcap modules and Pinnacle integration.
pub struct Snowcap {
/// Create layer surface widgets.
pub layer: &'static Layer,
pub integration: &'static Integration,
}
impl Snowcap {
pub(crate) fn new(layer: Layer) -> 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);
}
}

View file

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

@ -1 +1 @@
Subproject commit 81f61f7fbbec91673caf6c56df438b64eab90181 Subproject commit e6850779511da33bbdd4be646bcc21be39495ffa