Skip to content

Commit dfaedf7

Browse files
author
Innes Anderson-Morrison
committed
adding a simple layout renderer
1 parent 355cc7c commit dfaedf7

File tree

5 files changed

+170
-2
lines changed

5 files changed

+170
-2
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use penrose::{
2+
builtin::layout::{CenteredMain, Grid, MainAndStack},
3+
extensions::layout::{Fibonacci, Tatami},
4+
pure::geometry::Rect,
5+
stack,
6+
};
7+
use penrose_ui::layout_viewer::LayoutViewer;
8+
9+
const BLACK: u32 = 0x252535ff; // #252535
10+
const WHITE: u32 = 0xdcd7baff; // #dcd7ba
11+
const BLUE: u32 = 0x658594ff; // #658594
12+
const R: Rect = Rect::new(0, 0, 640, 480);
13+
const FRAME_MS: u64 = 400;
14+
const GPX: u32 = 5;
15+
16+
fn main() -> anyhow::Result<()> {
17+
let layouts = vec![
18+
MainAndStack::boxed_default(),
19+
MainAndStack::boxed_default_rotated(),
20+
CenteredMain::boxed_default(),
21+
CenteredMain::boxed_default_rotated(),
22+
Fibonacci::boxed_default(),
23+
Tatami::boxed_default(),
24+
Grid::boxed(),
25+
];
26+
27+
let mut v = LayoutViewer::new(R, BLACK, BLUE, WHITE)?;
28+
let s = stack!(1, 2, 3, 4, 5, 6).map(Into::into);
29+
v.showcase_layouts(s, layouts, GPX, FRAME_MS)?;
30+
31+
Ok(())
32+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//! A simple UI for view the results of a given Layout implementation
2+
use crate::{Draw, Result};
3+
use penrose::{
4+
builtin::layout::transformers::Gaps,
5+
core::layout::Layout,
6+
pure::{geometry::Rect, Stack},
7+
x::{Atom, WinType, XConn},
8+
x11rb::RustConn,
9+
Color, Xid,
10+
};
11+
use std::{thread::sleep, time::Duration};
12+
13+
const 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)]
18+
pub struct LayoutViewer {
19+
drw: Draw,
20+
win: Xid,
21+
r: Rect,
22+
focused: Color,
23+
unfocused: Color,
24+
}
25+
26+
impl LayoutViewer {
27+
/// Construct a new [LayoutViewer] with a specified color scheme and window size.
28+
pub fn new(
29+
r: Rect,
30+
bg: impl Into<Color>,
31+
focused: impl Into<Color>,
32+
unfocused: impl Into<Color>,
33+
) -> Result<Self> {
34+
let conn = RustConn::new()?;
35+
let screen_rects = conn.screen_details()?;
36+
let r_screen = screen_rects.last().unwrap();
37+
38+
let mut drw = Draw::new(FONT, 14, bg)?;
39+
let win = drw.new_window(
40+
WinType::InputOutput(Atom::NetWindowTypeDock),
41+
r.centered_in(r_screen).unwrap_or(r_screen.shrink_in(30)),
42+
false,
43+
)?;
44+
45+
Ok(Self {
46+
drw,
47+
win,
48+
r,
49+
focused: focused.into(),
50+
unfocused: unfocused.into(),
51+
})
52+
}
53+
54+
/// Run a [Layout] for a given client stack and display the result for specified number of
55+
/// milliseconds.
56+
pub fn render_layout_with_stack(
57+
&mut self,
58+
layout: &mut Box<dyn Layout>,
59+
stack: &Stack<Xid>,
60+
display_ms: u64,
61+
) -> Result<()> {
62+
let focus = *stack.focused();
63+
let (_, positions) = layout.layout(stack, self.r);
64+
65+
let mut ctx = self.drw.context_for(self.win)?;
66+
ctx.fill_bg(self.r)?;
67+
68+
for (id, r_w) in positions {
69+
let color = if id == focus {
70+
self.focused
71+
} else {
72+
self.unfocused
73+
};
74+
ctx.fill_rect(r_w, color)?;
75+
}
76+
77+
ctx.flush();
78+
sleep(Duration::from_millis(display_ms));
79+
80+
Ok(())
81+
}
82+
83+
/// Show the layout result for a set of [Layout]s using a given stack while rotating focus
84+
/// between the clients.
85+
pub fn showcase_layouts(
86+
&mut self,
87+
mut s: Stack<Xid>,
88+
layouts: Vec<Box<dyn Layout>>,
89+
gap_px: u32,
90+
display_ms: u64,
91+
) -> Result<()> {
92+
for mut l in layouts.into_iter().map(|l| Gaps::wrap(l, gap_px, gap_px)) {
93+
for _ in 0..s.len() {
94+
self.render_layout_with_stack(&mut l, &s, display_ms)?;
95+
s.focus_down();
96+
}
97+
}
98+
99+
Ok(())
100+
}
101+
}

crates/penrose_ui/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use std::ffi::NulError;
3939

4040
pub mod bar;
4141
pub mod core;
42+
pub mod layout_viewer;
4243

4344
pub use crate::core::{Context, Draw, TextStyle};
4445
pub use bar::{Position, StatusBar};

src/builtin/layout/mod.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ impl MainAndStack {
8282
Box::<Self>::default()
8383
}
8484

85+
/// Create a new rotated default [MainAndStack] [Layout] as a trait object ready to be added to
86+
/// your [LayoutStack][crate::core::layout::LayoutStack].
87+
pub fn boxed_default_rotated() -> Box<dyn Layout> {
88+
let mut l = Self::default();
89+
l.rotate();
90+
91+
Box::new(l)
92+
}
93+
8594
/// Create a new [MainAndStack] [Layout] with the main area on the left and remaining windows
8695
/// stacked to the right.
8796
pub fn side(max_main: u32, ratio: f32, ratio_step: f32) -> Box<dyn Layout> {
@@ -130,6 +139,11 @@ impl MainAndStack {
130139
}
131140
}
132141

142+
/// Rotate the main axis of this layout
143+
pub fn rotate(&mut self) {
144+
self.pos = self.pos.rotate();
145+
}
146+
133147
fn ratio(&self) -> f32 {
134148
if self.mirrored {
135149
1.0 - self.ratio
@@ -237,7 +251,7 @@ impl Layout for MainAndStack {
237251
} else if let Some(&Mirror) = m.downcast_ref() {
238252
self.mirrored = !self.mirrored;
239253
} else if let Some(&Rotate) = m.downcast_ref() {
240-
self.pos = self.pos.rotate();
254+
self.rotate();
241255
}
242256

243257
None
@@ -297,6 +311,15 @@ impl CenteredMain {
297311
Box::<Self>::default()
298312
}
299313

314+
/// Create a new rotated default [CenteredMain] [Layout] as a trait object ready to be added to
315+
/// your [LayoutStack][crate::core::layout::LayoutStack].
316+
pub fn boxed_default_rotated() -> Box<dyn Layout> {
317+
let mut l = Self::default();
318+
l.rotate();
319+
320+
Box::new(l)
321+
}
322+
300323
/// Create a new [CenteredMain] [Layout] with a vertical main area and remaining windows
301324
/// tiled to the left and right.
302325
pub fn vertical(max_main: u32, ratio: f32, ratio_step: f32) -> Box<dyn Layout> {
@@ -331,6 +354,11 @@ impl CenteredMain {
331354
}
332355
}
333356

357+
/// Rotate the main axis of this layout
358+
pub fn rotate(&mut self) {
359+
self.pos = self.pos.rotate();
360+
}
361+
334362
fn single_stack(&self, n: u32) -> bool {
335363
n <= self.max_main || self.ratio == 1.0 || self.ratio == 0.0
336364
}
@@ -455,7 +483,7 @@ impl Layout for CenteredMain {
455483
self.max_main += n as u32;
456484
}
457485
} else if let Some(&Rotate) = m.downcast_ref() {
458-
self.pos = self.pos.rotate();
486+
self.rotate();
459487
}
460488

461489
None

src/extensions/layout/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ impl Tatami {
193193
pub fn boxed(ratio: f32, ratio_step: f32) -> Box<dyn Layout> {
194194
Box::new(Tatami { ratio, ratio_step })
195195
}
196+
197+
/// Create a new default [Tatami] layout returned as a trait object ready to be added to your
198+
/// layout stack.
199+
pub fn boxed_default() -> Box<dyn Layout> {
200+
Box::<Tatami>::default()
201+
}
196202
}
197203

198204
impl Default for Tatami {

0 commit comments

Comments
 (0)