Skip to main content

Module layer_shell

Module layer_shell 

Source
Expand description

Wayland layer-shell surfaces, such as panels and overlays.

Run it with cargo run --example layer_shell. It uses desktop platform services, so it doesn’t run in a browser.

//! A clock pinned to the bottom edge of the screen as a Wayland layer-shell surface.
//!
//! `WindowKind::LayerShell` places the window on a compositor layer instead of a normal
//! toplevel. `LayerShellOptions` picks the anchor edges and margins. The view re-renders
//! twice a second to keep the clock current.
//!
//! Runs on Linux with the `wayland` feature only. Elsewhere `main` panics with an explanation.
//!
//! Run it with `cargo run --example layer_shell --features wayland`.

#[cfg(all(target_os = "linux", feature = "wayland"))]
fn main() {
    linux::main();
}

#[cfg(not(all(target_os = "linux", feature = "wayland")))]
fn main() {
    panic!("This example requires Linux and the `wayland` feature.");
}

#[cfg(all(target_os = "linux", feature = "wayland"))]
mod linux {
    use std::time::{Duration, SystemTime, UNIX_EPOCH};

    use gpui::{
        App, Bounds, Context, FontWeight, LinuxWindowBackground, QuitMode, Size, Window,
        WindowBounds, WindowKind, WindowOptions, div, layer_shell::*, point, prelude::*, px, rems,
        rgba, white,
    };

    struct Clock;

    impl Clock {
        fn new(cx: &mut Context<Self>) -> Self {
            cx.spawn(async move |this, cx| {
                loop {
                    if this.update(cx, |_, cx| cx.notify()).is_err() {
                        break;
                    }
                    cx.background_executor()
                        .timer(Duration::from_millis(500))
                        .await;
                }
            })
            .detach();
            Clock
        }
    }

    impl Render for Clock {
        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
            let seconds = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs();

            div()
                .size_full()
                .flex()
                .items_center()
                .justify_center()
                .text_size(rems(4.5))
                .font_weight(FontWeight::EXTRA_BOLD)
                .text_color(white())
                .bg(rgba(0x00000044))
                .rounded_xl()
                .child(format!(
                    "{:02}:{:02}:{:02}",
                    (seconds / 3600) % 24,
                    (seconds / 60) % 60,
                    seconds % 60
                ))
        }
    }

    pub fn main() {
        gpui_platform::application()
            .with_quit_mode(QuitMode::LastWindowClosed)
            .run(run);
    }

    /// Opens the layer-shell clock. The browser player and the example tests call this too.
    pub fn run(cx: &mut App) {
        let options = WindowOptions::new()
            .titlebar(None)
            .window_bounds(Some(WindowBounds::Windowed(Bounds {
                origin: point(px(0.), px(0.)),
                size: Size::new(px(500.), px(200.)),
            })))
            .app_id(Some("gpui-layer-shell-example".to_string()))
            .kind(WindowKind::LayerShell(LayerShellOptions {
                namespace: "gpui".to_string(),
                anchor: Anchor::LEFT | Anchor::RIGHT | Anchor::BOTTOM,
                margin: Some((px(0.), px(0.), px(40.), px(0.))),
                keyboard_interactivity: KeyboardInteractivity::None,
                ..Default::default()
            }))
            .linux_window_background(LinuxWindowBackground::Transparent);
        cx.open_window(options, |_, cx| cx.new(Clock::new)).unwrap();
    }
}