mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Add builtin quit prompt
This commit is contained in:
parent
d58c8571ca
commit
d70a0abda5
6 changed files with 236 additions and 54 deletions
71
Cargo.lock
generated
71
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<dyn std::error::Er
|
|||
};
|
||||
|
||||
#[cfg(feature = "snowcap")]
|
||||
let (snowcap, snowcap_recv) = snowcap_api::connect().await.unwrap();
|
||||
|
||||
#[cfg(feature = "snowcap")]
|
||||
let modules = ApiModules {
|
||||
pinnacle,
|
||||
process,
|
||||
window,
|
||||
input,
|
||||
output,
|
||||
tag,
|
||||
layout,
|
||||
render,
|
||||
signal: signal.clone(),
|
||||
snowcap: Box::leak(Box::new(snowcap)),
|
||||
let (modules, snowcap_recv) = {
|
||||
let (snowcap, snowcap_recv) = snowcap_api::connect().await.unwrap();
|
||||
let api_modules = ApiModules {
|
||||
pinnacle,
|
||||
process,
|
||||
window,
|
||||
input,
|
||||
output,
|
||||
tag,
|
||||
layout,
|
||||
render,
|
||||
signal: signal.clone(),
|
||||
snowcap: Box::leak(Box::new(Snowcap::new(snowcap))),
|
||||
};
|
||||
(api_modules, snowcap_recv)
|
||||
};
|
||||
|
||||
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());
|
||||
layout.finish_init(modules.clone());
|
||||
signal.read().await.finish_init(modules.clone());
|
||||
modules.snowcap.finish_init(modules.clone());
|
||||
|
||||
#[cfg(feature = "snowcap")]
|
||||
let receivers = Receivers {
|
||||
|
|
29
api/rust/src/snowcap.rs
Normal file
29
api/rust/src/snowcap.rs
Normal 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);
|
||||
}
|
||||
}
|
115
api/rust/src/snowcap/integration.rs
Normal file
115
api/rust/src/snowcap/integration.rs
Normal 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
2
snowcap
2
snowcap
|
@ -1 +1 @@
|
|||
Subproject commit 81f61f7fbbec91673caf6c56df438b64eab90181
|
||||
Subproject commit e6850779511da33bbdd4be646bcc21be39495ffa
|
Loading…
Reference in a new issue