penrose_ui/
layout_viewer.rs

1//! A simple UI for view the results of a given Layout implementation
2use crate::{Draw, Result};
3use penrose::{
4    Color, WinId,
5    builtin::layout::transformers::Gaps,
6    core::layout::Layout,
7    pure::{Stack, geometry::Rect},
8    x::{Atom, WinType, XConn},
9    x11rb::RustConn,
10};
11use std::{thread::sleep, time::Duration};
12
13const FONT: &str = "mono";
14
15/// A simple way to view the output of specific [Layout] implementations outside of a running
16/// window manager.
17#[derive(Debug)]
18pub struct LayoutViewer {
19    drw: Draw,
20    win: WinId,
21    r: Rect,
22    focused: Color,
23    unfocused: Color,
24    text: Color,
25}
26
27impl LayoutViewer {
28    /// Construct a new [LayoutViewer] with a specified color scheme and window size.
29    pub fn new(
30        r: Rect,
31        bg: impl Into<Color>,
32        focused: impl Into<Color>,
33        unfocused: impl Into<Color>,
34        text: impl Into<Color>,
35    ) -> Result<Self> {
36        let mut conn = RustConn::new()?;
37        let screen_rects = conn.screen_details()?;
38        let r_screen = screen_rects.last().unwrap();
39
40        let mut drw = Draw::new(FONT, 14, bg)?;
41        let win = drw.new_window(
42            WinType::InputOutput(Atom::NetWindowTypeDock),
43            r.centered_in(r_screen).unwrap_or(r_screen.shrink_in(30)),
44            false,
45        )?;
46
47        Ok(Self {
48            drw,
49            win,
50            r,
51            focused: focused.into(),
52            unfocused: unfocused.into(),
53            text: text.into(),
54        })
55    }
56
57    /// Run a [Layout] for a given client stack and display the result for specified number of
58    /// milliseconds.
59    pub fn render_layout_with_stack(
60        &mut self,
61        layout: &mut Box<dyn Layout>,
62        stack: &Stack<WinId>,
63        display_ms: u64,
64    ) -> Result<()> {
65        let focus = *stack.focused();
66        let (_, positions) = layout.layout(stack, self.r);
67
68        let mut ctx = self.drw.context_for(self.win)?;
69        ctx.fill_bg(self.r)?;
70
71        for (id, r_w) in positions {
72            let color = if id == focus {
73                self.focused
74            } else {
75                self.unfocused
76            };
77            ctx.fill_rect(r_w, color)?;
78
79            ctx.set_offset(r_w.x + r_w.w as i32 / 2, r_w.y + r_w.h as i32 / 2);
80            ctx.draw_text(&id.to_string(), 0, (0, 0), self.text)?;
81            ctx.reset_offset();
82        }
83
84        self.drw.flush(self.win)?;
85        sleep(Duration::from_millis(display_ms));
86
87        Ok(())
88    }
89
90    /// Show the layout result for a set of [Layout]s using a given stack while rotating focus
91    /// between the clients.
92    pub fn showcase_layouts(
93        &mut self,
94        mut s: Stack<WinId>,
95        layouts: &[Box<dyn Layout>],
96        gap_px: u32,
97        display_ms: u64,
98    ) -> Result<()> {
99        for l in layouts.iter() {
100            let mut l = Gaps::wrap(l.boxed_clone(), gap_px, gap_px);
101            for _ in 0..s.len() {
102                self.render_layout_with_stack(&mut l, &s, display_ms)?;
103                s.focus_down();
104            }
105        }
106
107        Ok(())
108    }
109}