Add a widget abstraction to make drawing easier
Getty Ritter
5 years ago
1 | mod widgets; | |
1 | 2 | mod window; |
2 | 3 | |
3 | 4 | use std::os::unix::io::AsRawFd; |
4 | 5 | use pango::LayoutExt; |
5 | 6 | |
7 | use widgets::Widget; | |
6 | 8 | use window::{Display,Event,Size,Window}; |
7 | 9 | |
8 | 10 | fn main() { |
107 | 109 | |
108 | 110 | /// Do our Cairo drawing. This needs to be refactored to allow for |
109 | 111 | /// more configurability in terms of what gets written! |
110 | fn draw(ctx: &cairo::Context, left: &str, Size { wd: width, ..}: Size) { | |
111 | // for the current time on the right | |
112 | let now = time::now(); | |
113 | ||
112 | fn draw(ctx: &cairo::Context, left: &str, size: Size) { | |
114 | 113 | // the background is... gray-ish? this'll be configurable eventually |
115 | 114 | ctx.set_source_rgb(0.1, 0.1, 0.1); |
116 | 115 | ctx.paint(); |
120 | 119 | |
121 | 120 | // Our pango layout handles placing and drawing text |
122 | 121 | let layout = pangocairo::functions::create_layout(&ctx).unwrap(); |
123 | // for the time, we want to align it to the right | |
124 | layout.set_alignment(pango::Alignment::Right); | |
125 | 122 | // allow for the whole width of the bar, minus a small fixed amount |
126 |
layout.set_width(( |
|
123 | layout.set_width((size.wd - 20) * pango::SCALE); | |
127 | 124 | // this should also be configurable, but Fira Mono is a good font |
128 | 125 | let mut font = pango::FontDescription::from_string("Fira Mono 18"); |
129 | 126 | font.set_weight(pango::Weight::Bold); |
130 | 127 | layout.set_font_description(&font); |
131 | // start drawing in the top-left | |
132 | ctx.move_to(10.0, 4.0); | |
133 | //The text here is just the nicely-rendered current time | |
134 | layout.set_text(&time::strftime("%a %b %d %H:%M", &now).unwrap()); | |
135 | // and draw it | |
136 | pangocairo::functions::show_layout(&ctx, &layout); | |
137 | 128 | |
138 | // We can reuse the same layout, but starting from the left | |
139 | layout.set_alignment(pango::Alignment::Left); | |
140 | // and now we write whatever the "current text" is... | |
141 | layout.set_text(left); | |
142 | // and draw that! | |
143 | pangocairo::functions::show_layout(&ctx, &layout); | |
129 | // set up a struct with everything that widgets need to draw | |
130 | let drawing = widgets::Drawing { | |
131 | ctx: ctx, | |
132 | lyt: &layout, | |
133 | size, | |
134 | }; | |
135 | // set up our widgets | |
136 | let text = widgets::Text::new(left); | |
137 | let time = widgets::Time::new(); | |
138 | let sbox = widgets::SmallBox; | |
139 | ||
140 | // and create a 'config' which tells us which widgets to draw from | |
141 | // the left, and which from the right | |
142 | let config = widgets::Config { | |
143 | left: vec![ | |
144 | &text as &Widget, | |
145 | ], | |
146 | right: vec![ | |
147 | &sbox as &Widget, | |
148 | &time as &Widget, | |
149 | ], | |
150 | }; | |
151 | // and draw them! | |
152 | config.draw(&drawing); | |
153 | ||
144 | 154 | } |
1 | use crate::window::Size; | |
2 | use pango::LayoutExt; | |
3 | ||
4 | #[derive(Debug,Clone,Copy)] | |
5 | pub enum Located { | |
6 | FromLeft(i32), | |
7 | FromRight(i32), | |
8 | } | |
9 | ||
10 | pub struct Config<'r> { | |
11 | pub left: Vec<&'r Widget>, | |
12 | pub right: Vec<&'r Widget>, | |
13 | } | |
14 | ||
15 | impl<'r> Config<'r> { | |
16 | pub fn draw(&self, d: &Drawing) { | |
17 | let mut offset = 10; | |
18 | for w in self.left.iter() { | |
19 | offset += 10 + w.draw(d, Located::FromLeft(offset)); | |
20 | } | |
21 | offset = 10; | |
22 | for w in self.right.iter() { | |
23 | offset += 10 + w.draw(d, Located::FromRight(offset)); | |
24 | } | |
25 | } | |
26 | } | |
27 | ||
28 | impl Located { | |
29 | fn draw_text(&self, d: &Drawing, msg: &str) -> i32 { | |
30 | d.lyt.set_text(msg); | |
31 | let (w, _) = d.lyt.get_size(); | |
32 | d.ctx.move_to(self.target_x(d, w / pango::SCALE), 4.0); | |
33 | pangocairo::functions::show_layout(d.ctx, d.lyt); | |
34 | w / pango::SCALE | |
35 | } | |
36 | ||
37 | fn target_x(&self, d: &Drawing, w: i32) -> f64 { | |
38 | match self { | |
39 | Located::FromLeft(x) => *x as f64, | |
40 | Located::FromRight(x) => (d.size.wd - (x + w)) as f64, | |
41 | } | |
42 | } | |
43 | } | |
44 | ||
45 | pub struct Drawing<'t> { | |
46 | pub ctx: &'t cairo::Context, | |
47 | pub lyt: &'t pango::Layout, | |
48 | pub size: Size, | |
49 | } | |
50 | ||
51 | pub trait Widget { | |
52 | fn draw(&self, d: &Drawing, loc: Located) -> i32; | |
53 | } | |
54 | ||
55 | #[derive(Debug)] | |
56 | pub struct Time { | |
57 | fmt: String, | |
58 | } | |
59 | ||
60 | impl Time { | |
61 | pub fn new() -> Time { | |
62 | Time { | |
63 | fmt: format!("%a %b %d %H:%M") | |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | impl Widget for Time { | |
69 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
70 | let now = time::now(); | |
71 | loc.draw_text(d, &time::strftime(&self.fmt, &now).unwrap()) | |
72 | } | |
73 | } | |
74 | ||
75 | ||
76 | #[derive(Debug)] | |
77 | pub struct Text<'t> { | |
78 | text: &'t str, | |
79 | } | |
80 | ||
81 | impl<'t> Text<'t> { | |
82 | pub fn new(text: &str) -> Text { | |
83 | Text { text } | |
84 | } | |
85 | } | |
86 | ||
87 | impl<'t> Widget for Text<'t> { | |
88 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
89 | loc.draw_text(d, &self.text) | |
90 | } | |
91 | } | |
92 | ||
93 | ||
94 | pub struct SmallBox; | |
95 | ||
96 | impl Widget for SmallBox { | |
97 | fn draw(&self, d: &Drawing, loc: Located) -> i32 { | |
98 | let sz = d.size.ht - 8; | |
99 | let x = loc.target_x(d, sz); | |
100 | d.ctx.rectangle(x, 4.0, sz as f64, sz as f64); | |
101 | d.ctx.fill(); | |
102 | sz | |
103 | } | |
104 | } |