Add calendar, refactor more

This commit is contained in:
Kenny Levinsen
2023-01-16 00:25:34 +01:00
parent 3a40bb18c5
commit 7593cd48a4
15 changed files with 689 additions and 286 deletions
Generated
-28
View File
@@ -105,12 +105,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "const-cstr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.3" version = "0.8.3"
@@ -243,15 +237,6 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fontconfig"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b0653582d9c82645f95bacd0dc87b49a3126f7a9633be8cafa9c6d17d525847"
dependencies = [
"yeslogic-fontconfig-sys",
]
[[package]] [[package]]
name = "fuzzy-matcher" name = "fuzzy-matcher"
version = "0.3.7" version = "0.3.7"
@@ -1102,7 +1087,6 @@ dependencies = [
"calloop", "calloop",
"chrono", "chrono",
"dbus", "dbus",
"fontconfig",
"fuzzy-matcher", "fuzzy-matcher",
"itertools", "itertools",
"memmap", "memmap",
@@ -1128,15 +1112,3 @@ dependencies = [
"libc", "libc",
"memmap2", "memmap2",
] ]
[[package]]
name = "yeslogic-fontconfig-sys"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec657fd32bbcbeaef5c7bc8e10b3db95b143fab8db0a50079773dbf936fd4f73"
dependencies = [
"const-cstr",
"dlib",
"once_cell",
"pkg-config",
]
+5 -1
View File
@@ -3,6 +3,10 @@ name = "wldash2"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[profile.release]
lto = "fat"
panic = "abort"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
@@ -11,7 +15,7 @@ wayland-protocols = { version = "0.30", features = ["client", "unstable", "stagi
tempfile = "3.3" tempfile = "3.3"
nix = "0.26" nix = "0.26"
memmap = "0.7" memmap = "0.7"
fontconfig = "0.6.0" #fontconfig = "0.6.0"
rusttype = "^0.9" rusttype = "^0.9"
calloop = { version = "0.10" } calloop = { version = "0.10" }
chrono = "0.4" chrono = "0.4"
+6
View File
@@ -125,6 +125,12 @@ pub struct BufferManager {
} }
impl BufferManager { impl BufferManager {
pub fn new() -> BufferManager {
BufferManager {
buffers: Vec::new(),
}
}
pub fn clear_buffers(&mut self) { pub fn clear_buffers(&mut self) {
for buf in self.buffers.iter_mut() { for buf in self.buffers.iter_mut() {
buf.buffer.destroy() buf.buffer.destroy()
+8 -8
View File
@@ -1,24 +1,24 @@
//! Utility module for fonts //! Utility module for fonts
use crate::draw; use crate::draw;
use fontconfig::Fontconfig as FontConfig; //use fontconfig::Fontconfig as FontConfig;
use rusttype::Font; use rusttype::Font;
use std::{ use std::{
cell::RefCell, cell::RefCell,
mem,
rc::Rc,
thread,
collections::HashMap, collections::HashMap,
fs::File, fs::File,
hash, hash,
io::Read, io::Read,
mem,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc,
thread,
}; };
/// FontRef is used to store Fonts on widgets. /// FontRef is used to store Fonts on widgets.
pub type FontRef<'a> = &'a rusttype::Font<'a>; pub type FontRef<'a> = &'a rusttype::Font<'a>;
/*
/// FontSeeker is a marker struct that is used to look up fonts /// FontSeeker is a marker struct that is used to look up fonts
pub(crate) struct FontSeeker; pub(crate) struct FontSeeker;
@@ -30,7 +30,7 @@ impl FontSeeker {
fc.find(name, None).unwrap().path fc.find(name, None).unwrap().path
} }
} }
*/
/// FontLoader is a marker struct that is used to load files /// FontLoader is a marker struct that is used to load files
pub(crate) struct FontLoader; pub(crate) struct FontLoader;
@@ -113,7 +113,8 @@ impl FontMap {
let path = if v.0.starts_with("/") { let path = if v.0.starts_with("/") {
std::path::PathBuf::from(v.0) std::path::PathBuf::from(v.0)
} else { } else {
FontSeeker::from_string(v.0) //FontSeeker::from_string(v.0)
panic!("required FontSeeker");
}; };
let fontref = Box::leak(Box::new( let fontref = Box::leak(Box::new(
FontLoader::from_path(path).expect("unable to load font"), FontLoader::from_path(path).expect("unable to load font"),
@@ -158,4 +159,3 @@ impl MaybeFontMap {
} }
} }
} }
+92 -75
View File
@@ -3,11 +3,11 @@ mod color;
mod draw; mod draw;
mod fonts; mod fonts;
mod keyboard; mod keyboard;
mod state;
mod utils; mod utils;
mod widgets; mod widgets;
mod state;
use wayland_client::{Connection, QueueHandle, WaylandSource,}; use wayland_client::{Connection, QueueHandle, WaylandSource};
use calloop::{ use calloop::{
timer::{TimeoutAction, Timer}, timer::{TimeoutAction, Timer},
@@ -15,27 +15,16 @@ use calloop::{
}; };
use chrono::{Duration, Local, Timelike}; use chrono::{Duration, Local, Timelike};
use buffer::{BufferManager, BufferView}; use buffer::BufferView;
use color::Color; use color::Color;
use fonts::{MaybeFontMap, FontMap}; use fonts::{FontMap, MaybeFontMap};
use keyboard::Keyboard; use state::{Event, Events, State};
use widgets::{ use widgets::{
Backlight, Battery, Clock, Date, Geometry, Interface, Line, WidgetUpdater, Backlight, Battery, Calendar, Clock, Date, Geometry, HorizontalLayout, IndexedLayout,
Interface, InvertedHorizontalLayout, Line, Margin, VerticalLayout, Widget,
}; };
use state::State;
use std::thread; use std::{rc::Rc, thread};
impl WidgetUpdater for State {
fn geometry_update(
&mut self,
idx: usize,
fonts: &mut FontMap,
geometry: &Geometry,
) -> Geometry {
self.widgets[idx].geometry_update(fonts, geometry)
}
}
fn main() { fn main() {
let font_thread = thread::spawn(move || { let font_thread = thread::spawn(move || {
@@ -49,6 +38,21 @@ fn main() {
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf", "/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
32., 32.,
); );
fm.queue_font_path(
"monospace",
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
12.,
);
fm.queue_font_path(
"monospace",
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
12. * 1.5,
);
fm.queue_font_path(
"monospace",
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
12. * 2.0,
);
fm.load_fonts(); fm.load_fonts();
fm fm
}); });
@@ -60,19 +64,34 @@ fn main() {
let display = conn.display(); let display = conn.display();
display.get_registry(&qhandle, ()); display.get_registry(&qhandle, ());
let bufmgr = BufferManager {
buffers: Vec::new(),
};
let (ping_sender, ping_source) = calloop::ping::make_ping().unwrap(); let (ping_sender, ping_source) = calloop::ping::make_ping().unwrap();
let events = Events::new(ping_sender);
let clock = Box::new(Clock::new("sans", 128.)); let clock = Box::new(Clock::new("sans", 128.));
// let calendar = Box::new(Calendar::new("monospace", 12.0, 1, -1));
let date = Box::new(Date::new("sans", 48.)); let date = Box::new(Date::new("sans", 48.));
let line = Box::new(Line::new()); let line = Box::new(Line::new());
let launcher = Box::new(Interface::new("monospace", 32.)); let launcher = Box::new(Interface::new("monospace", 32.));
let battery = Box::new(Battery::new(ping_sender, "sans", 24.)); let battery = Box::new(Battery::new(events.clone(), "sans", 24.));
let backlight = Box::new(Backlight::new("intel_backlight", "sans", 24.)); let backlight = Box::new(Backlight::new("intel_backlight", "sans", 24.));
let widgets: Vec<Box<dyn Widget>> =
vec![clock, date, battery, backlight, line, launcher];
let layout = Rc::new(VerticalLayout::new(vec![
HorizontalLayout::new(vec![
IndexedLayout::new(0),
Margin::new(IndexedLayout::new(1), (16, 8, 0, 0)),
VerticalLayout::new(vec![
Margin::new(IndexedLayout::new(2), (16, 8, 8, 0)),
Margin::new(IndexedLayout::new(3), (16, 8, 8, 0)),
]),
]),
IndexedLayout::new(4),
IndexedLayout::new(5),
// InvertedHorizontalLayout::new(vec![IndexedLayout::new(5), IndexedLayout::new(6)]),
]));
let mut event_loop: EventLoop<State> = let mut event_loop: EventLoop<State> =
EventLoop::try_new().expect("Failed to initialize the event loop!"); EventLoop::try_new().expect("Failed to initialize the event loop!");
@@ -86,7 +105,12 @@ fn main() {
handle handle
.insert_source(ping_source, |(), &mut (), state| { .insert_source(ping_source, |(), &mut (), state| {
state.widgets[2].set_dirty(true); let events = state.events.lock().unwrap().flush();
for widget in state.widgets.iter_mut() {
for event in events.iter() {
widget.event(event);
}
}
state.dirty = true; state.dirty = true;
}) })
.expect("Failed to insert ping source!"); .expect("Failed to insert ping source!");
@@ -101,8 +125,9 @@ fn main() {
.with_nanosecond(0) .with_nanosecond(0)
.unwrap(); .unwrap();
let d = target - now; let d = target - now;
state.widgets[0].set_dirty(true); for widget in state.widgets.iter_mut() {
state.widgets[1].set_dirty(true); widget.event(&Event::NewMinute);
}
state.dirty = true; state.dirty = true;
// The timer event source requires us to return a TimeoutAction to // The timer event source requires us to return a TimeoutAction to
@@ -111,22 +136,7 @@ fn main() {
}) })
.expect("Failed to insert event source!"); .expect("Failed to insert event source!");
let mut state = State { let mut state = State::new(widgets, layout, MaybeFontMap::Waiting(font_thread), events);
running: true,
dirty: true,
activated: false,
base_surface: None,
wm_base: None,
xdg_surface: None,
wl_shm: None,
xdg_activation: None,
configured: false,
dimensions: (320, 240),
bufmgr: bufmgr,
widgets: vec![clock, date, battery, backlight, line, launcher],
keyboard: Keyboard::new(),
fonts: MaybeFontMap::Waiting(font_thread),
};
let mut damage = Vec::new(); let mut damage = Vec::new();
for _ in 0..state.widgets.len() { for _ in 0..state.widgets.len() {
@@ -142,7 +152,6 @@ fn main() {
continue; continue;
} }
let mut drew = false;
let mut force = false; let mut force = false;
if state.bufmgr.buffers.len() == 0 { if state.bufmgr.buffers.len() == 0 {
state.add_buffer(&qhandle); state.add_buffer(&qhandle);
@@ -168,43 +177,51 @@ fn main() {
let bg = Color::new(0., 0., 0., 1.); let bg = Color::new(0., 0., 0., 1.);
let surface = state.base_surface.as_ref().unwrap().clone(); let surface = state.base_surface.as_ref().unwrap().clone();
for (idx, widget) in state.widgets.iter_mut().enumerate() { if force {
if force || widget.get_dirty() { for (idx, widget) in state.widgets.iter_mut().enumerate() {
widget.set_dirty(false); if force || widget.get_dirty() {
drew = true; let geo = widget.geometry();
let geo = widget.geometry();
let old_damage = damage[idx];
if !force {
bufview.subgeometry(old_damage).unwrap().memset(&bg);
} else {
bufview.subgeometry(geo).unwrap().memset(&bg); bufview.subgeometry(geo).unwrap().memset(&bg);
}
let mut subview = bufview.subgeometry(geo).unwrap(); let mut subview = bufview.subgeometry(geo).unwrap();
let new_damage = widget.draw(&mut state.fonts.unwrap().borrow_mut(), &mut subview); damage[idx] = widget.draw(&mut state.fonts.unwrap().borrow_mut(), &mut subview);
let combined_damage = new_damage.expand(old_damage); //draw_box(&mut subview, &Color::new(1.0, 0.5, 0.0, 1.0), (geo.width, geo.height));
damage[idx] = new_damage; }
surface.damage_buffer( }
combined_damage.x as i32, surface.damage_buffer(0, 0, 0x7FFFFFFF, 0x7FFFFFFF)
combined_damage.y as i32, } else {
combined_damage.width as i32, let mut drew = false;
combined_damage.height as i32, for (idx, widget) in state.widgets.iter_mut().enumerate() {
); if force || widget.get_dirty() {
//draw_box(&mut subview, &Color::new(1.0, 0.5, 0.0, 1.0), (geo.width, geo.height)); drew = true;
let old_damage = damage[idx];
bufview.subgeometry(old_damage).unwrap().memset(&bg);
let geo = widget.geometry();
let mut subview = bufview.subgeometry(geo).unwrap();
let new_damage =
widget.draw(&mut state.fonts.unwrap().borrow_mut(), &mut subview);
let combined_damage = new_damage.expand(old_damage);
damage[idx] = new_damage;
surface.damage_buffer(
combined_damage.x as i32,
combined_damage.y as i32,
combined_damage.width as i32,
combined_damage.height as i32,
);
//draw_box(&mut subview, &Color::new(1.0, 0.5, 0.0, 1.0), (geo.width, geo.height));
}
}
if !drew {
buf.release();
continue;
} }
} }
if !drew {
buf.release();
continue;
}
if force {
surface.damage_buffer(0, 0, 0x7FFFFFFF, 0x7FFFFFFF)
}
surface.attach(Some(&buf.buffer), 0, 0); surface.attach(Some(&buf.buffer), 0, 0);
surface.commit(); surface.commit();
} }
} }
+94 -41
View File
@@ -1,8 +1,12 @@
use std::{ use std::{
env, env,
process::exit, process::exit,
rc::Rc,
sync::{Arc, Mutex},
}; };
use calloop::ping::Ping;
use wayland_client::{ use wayland_client::{
protocol::{ protocol::{
wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool,
@@ -18,31 +22,93 @@ use wayland_protocols::xdg::{
use crate::{ use crate::{
buffer::BufferManager, buffer::BufferManager,
fonts::MaybeFontMap, fonts::{FontMap, MaybeFontMap},
keyboard::Keyboard, keyboard::{KeyEvent, Keyboard},
widgets::{ widgets::{Geometry, Layout, Widget, WidgetUpdater},
Geometry, HorizontalLayout, IndexedLayout, Layout, Margin, VerticalLayout, Widget,
},
}; };
pub enum Event {
NewMinute,
PowerUpdate,
KeyEvent(KeyEvent),
TokenUpdate(String),
}
pub struct Events {
dirty: bool,
events: Vec<Event>,
ping: Ping,
}
impl Events {
pub fn new(ping: Ping) -> Arc<Mutex<Events>> {
Arc::new(Mutex::new(Events {
dirty: false,
events: Vec::new(),
ping,
}))
}
pub fn add_event(&mut self, ev: Event) {
self.events.push(ev);
if !self.dirty {
self.dirty = true;
self.ping.ping();
}
}
pub fn flush(&mut self) -> Vec<Event> {
self.dirty = false;
self.events.drain(..).collect()
}
}
pub struct State { pub struct State {
pub running: bool, pub running: bool,
pub dirty: bool, pub dirty: bool,
pub activated: bool, activated: bool,
pub base_surface: Option<wl_surface::WlSurface>, pub base_surface: Option<wl_surface::WlSurface>,
pub wm_base: Option<xdg_wm_base::XdgWmBase>, wm_base: Option<xdg_wm_base::XdgWmBase>,
pub xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>, xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>,
pub wl_shm: Option<wl_shm::WlShm>, wl_shm: Option<wl_shm::WlShm>,
pub xdg_activation: Option<xdg_activation_v1::XdgActivationV1>, xdg_activation: Option<xdg_activation_v1::XdgActivationV1>,
pub configured: bool, pub configured: bool,
pub dimensions: (i32, i32), pub dimensions: (i32, i32),
pub bufmgr: BufferManager, pub bufmgr: BufferManager,
pub widgets: Vec<Box<dyn Widget>>, pub widgets: Vec<Box<dyn Widget>>,
pub keyboard: Keyboard, keyboard: Keyboard,
pub fonts: MaybeFontMap, pub fonts: MaybeFontMap,
pub events: Arc<Mutex<Events>>,
layout: Rc<Box<dyn Layout>>,
} }
impl State { impl State {
pub fn new(
widgets: Vec<Box<dyn Widget>>,
layout: Rc<Box<dyn Layout>>,
fonts: MaybeFontMap,
events: Arc<Mutex<Events>>,
) -> State {
State {
running: true,
dirty: true,
activated: false,
base_surface: None,
wm_base: None,
xdg_surface: None,
wl_shm: None,
xdg_activation: None,
configured: false,
dimensions: (320, 240),
bufmgr: BufferManager::new(),
keyboard: Keyboard::new(),
widgets,
fonts,
events,
layout,
}
}
pub fn add_buffer(&mut self, qh: &QueueHandle<Self>) { pub fn add_buffer(&mut self, qh: &QueueHandle<Self>) {
self.bufmgr.add_buffer( self.bufmgr.add_buffer(
self.wl_shm.as_ref().expect("missing wl_shm"), self.wl_shm.as_ref().expect("missing wl_shm"),
@@ -68,6 +134,17 @@ impl State {
} }
} }
impl WidgetUpdater for State {
fn geometry_update(
&mut self,
idx: usize,
fonts: &mut FontMap,
geometry: &Geometry,
) -> Geometry {
self.widgets[idx].geometry_update(fonts, geometry)
}
}
impl Dispatch<wl_registry::WlRegistry, ()> for State { impl Dispatch<wl_registry::WlRegistry, ()> for State {
fn event( fn event(
state: &mut Self, state: &mut Self,
@@ -264,36 +341,9 @@ impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
if state.dimensions != (width, height) { if state.dimensions != (width, height) {
state.bufmgr.clear_buffers(); state.bufmgr.clear_buffers();
state.dimensions = (width, height); state.dimensions = (width, height);
let mut layout = VerticalLayout {
widgets: vec![
Box::new(HorizontalLayout {
widgets: vec![
Box::new(IndexedLayout { widget_idx: 0 }),
Box::new(Margin {
widget: Box::new(IndexedLayout { widget_idx: 1 }),
margin: (16, 8, 0, 0),
}),
Box::new(VerticalLayout {
widgets: vec![
Box::new(Margin {
widget: Box::new(IndexedLayout { widget_idx: 2 }),
margin: (16, 8, 8, 0),
}),
Box::new(Margin {
widget: Box::new(IndexedLayout { widget_idx: 3 }),
margin: (16, 8, 8, 0),
}),
],
}),
],
}),
Box::new(IndexedLayout { widget_idx: 4 }),
Box::new(IndexedLayout { widget_idx: 5 }),
],
};
state.fonts.resolve(); state.fonts.resolve();
let fonts = state.fonts.unwrap(); let fonts = state.fonts.unwrap();
let layout = state.layout.clone();
layout.geometry_update( layout.geometry_update(
&mut fonts.borrow_mut(), &mut fonts.borrow_mut(),
&Geometry { &Geometry {
@@ -355,8 +405,9 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
return; return;
} }
let k = state.keyboard.key(serial, time, key, kbstate); let k = state.keyboard.key(serial, time, key, kbstate);
let ev = Event::KeyEvent(k);
for widget in state.widgets.iter_mut() { for widget in state.widgets.iter_mut() {
widget.keyboard_input(&k); widget.event(&ev);
} }
state.dirty = true; state.dirty = true;
} }
@@ -372,6 +423,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
.modifiers(mods_depressed, mods_latched, mods_locked, group); .modifiers(mods_depressed, mods_latched, mods_locked, group);
} }
wl_keyboard::Event::Keymap { format, fd, size } => { wl_keyboard::Event::Keymap { format, fd, size } => {
// TODO: Keymap loading blocks xdg configure, consider delaying
state.keyboard.keymap(format, fd, size); state.keyboard.keymap(format, fd, size);
} }
_ => (), _ => (),
@@ -402,8 +454,9 @@ impl Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, ()> for State {
) { ) {
match event { match event {
xdg_activation_token_v1::Event::Done { token } => { xdg_activation_token_v1::Event::Done { token } => {
let ev = Event::TokenUpdate(token);
for widget in state.widgets.iter_mut() { for widget in state.widgets.iter_mut() {
widget.token_update(&token); widget.event(&ev);
} }
} }
_ => (), _ => (),
-8
View File
@@ -20,7 +20,6 @@ pub struct Backlight {
device_path: PathBuf, device_path: PathBuf,
cur: u64, cur: u64,
max: u64, max: u64,
dirty: bool,
} }
impl Backlight { impl Backlight {
@@ -34,7 +33,6 @@ impl Backlight {
device_path: Path::new("/sys/class/backlight").to_path_buf().join(path), device_path: Path::new("/sys/class/backlight").to_path_buf().join(path),
cur: 0, cur: 0,
max: 0, max: 0,
dirty: false,
}; };
dev.update(); dev.update();
@@ -43,12 +41,6 @@ impl Backlight {
} }
impl BarWidgetImpl for Backlight { impl BarWidgetImpl for Backlight {
fn get_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty
}
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"backlight" "backlight"
} }
+6 -7
View File
@@ -9,8 +9,9 @@ use crate::{
}; };
pub trait BarWidgetImpl { pub trait BarWidgetImpl {
fn get_dirty(&self) -> bool; fn get_dirty(&self) -> bool {
fn set_dirty(&mut self, dirty: bool); false
}
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
fn value(&self) -> f32; fn value(&self) -> f32;
fn color(&self) -> Color; fn color(&self) -> Color;
@@ -27,7 +28,9 @@ impl BarWidget {
pub fn new(inner_widget: Box<dyn BarWidgetImpl>, font: &'static str, size: f32) -> BarWidget { pub fn new(inner_widget: Box<dyn BarWidgetImpl>, font: &'static str, size: f32) -> BarWidget {
BarWidget { BarWidget {
geometry: Default::default(), geometry: Default::default(),
inner_widget, font, size, inner_widget,
font,
size,
} }
} }
} }
@@ -41,10 +44,6 @@ impl Widget for BarWidget {
self.inner_widget.get_dirty() self.inner_widget.get_dirty()
} }
fn set_dirty(&mut self, dirty: bool) {
self.inner_widget.set_dirty(dirty)
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry { fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
let font = fonts.get_font(self.font, self.size); let font = fonts.get_font(self.font, self.size);
let fg = Color::new(1., 1., 1., 1.); let fg = Color::new(1., 1., 1., 1.);
+15 -14
View File
@@ -4,8 +4,6 @@ use std::{
time::Duration, time::Duration,
}; };
use calloop::ping::Ping;
use dbus::blocking::{ use dbus::blocking::{
stdintf::org_freedesktop_dbus::{Properties, PropertiesPropertiesChanged}, stdintf::org_freedesktop_dbus::{Properties, PropertiesPropertiesChanged},
LocalConnection, LocalConnection,
@@ -13,6 +11,7 @@ use dbus::blocking::{
use crate::{ use crate::{
color::Color, color::Color,
state::{Event, Events},
widgets::bar_widget::{BarWidget, BarWidgetImpl}, widgets::bar_widget::{BarWidget, BarWidgetImpl},
}; };
@@ -28,9 +27,10 @@ enum UpowerBatteryState {
struct InnerBattery { struct InnerBattery {
value: f32, value: f32,
state: UpowerBatteryState, state: UpowerBatteryState,
dirty: bool,
} }
fn start_monitor(inner: Arc<Mutex<InnerBattery>>, ping: Ping) { fn start_monitor(inner: Arc<Mutex<InnerBattery>>, events: Arc<Mutex<Events>>) {
thread::Builder::new() thread::Builder::new()
.name("battmon".to_string()) .name("battmon".to_string())
.spawn(move || { .spawn(move || {
@@ -84,11 +84,15 @@ fn start_monitor(inner: Arc<Mutex<InnerBattery>>, ping: Ping) {
6 => UpowerBatteryState::Discharging, 6 => UpowerBatteryState::Discharging,
_ => UpowerBatteryState::Unknown, _ => UpowerBatteryState::Unknown,
}; };
ping.ping(); inner.dirty = true;
let mut events = events.lock().unwrap();
events.add_event(Event::PowerUpdate);
} }
"Percentage" => { "Percentage" => {
inner.value = value.0.as_f64().unwrap() as f32 / 100.; inner.value = value.0.as_f64().unwrap() as f32 / 100.;
ping.ping(); inner.dirty = true;
let mut events = events.lock().unwrap();
events.add_event(Event::PowerUpdate);
} }
_ => (), _ => (),
} }
@@ -107,35 +111,32 @@ fn start_monitor(inner: Arc<Mutex<InnerBattery>>, ping: Ping) {
pub struct Battery { pub struct Battery {
inner: Arc<Mutex<InnerBattery>>, inner: Arc<Mutex<InnerBattery>>,
dirty: bool,
} }
impl Battery { impl Battery {
pub fn new(ping: Ping, font: &'static str, size: f32) -> BarWidget { pub fn new(events: Arc<Mutex<Events>>, font: &'static str, size: f32) -> BarWidget {
let battery = Battery { let battery = Battery {
inner: Arc::new(Mutex::new(InnerBattery { inner: Arc::new(Mutex::new(InnerBattery {
value: 0., value: 0.,
state: UpowerBatteryState::Unknown, state: UpowerBatteryState::Unknown,
dirty: false,
})), })),
dirty: false,
}; };
start_monitor(battery.inner.clone(), ping); start_monitor(battery.inner.clone(), events);
BarWidget::new(Box::new(battery), font, size) BarWidget::new(Box::new(battery), font, size)
} }
} }
impl BarWidgetImpl for Battery { impl BarWidgetImpl for Battery {
fn get_dirty(&self) -> bool { fn get_dirty(&self) -> bool {
self.dirty self.inner.lock().unwrap().dirty
}
fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty
} }
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"battery" "battery"
} }
fn value(&self) -> f32 { fn value(&self) -> f32 {
let inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
inner.dirty = false;
inner.value inner.value
} }
fn color(&self) -> Color { fn color(&self) -> Color {
+273
View File
@@ -0,0 +1,273 @@
use std::default::Default;
use crate::{
buffer::BufferView,
color::Color,
fonts::FontMap,
state::Event,
widgets::{Geometry, Widget},
};
use chrono::{Datelike, Local, NaiveDate};
pub struct Calendar {
font: &'static str,
sections_x: i32,
sections_y: i32,
size: f32,
dirty: bool,
geometry: Geometry,
}
impl Calendar {
pub fn new(font: &'static str, size: f32, sections_x: i32, sections_y: i32) -> Calendar {
Calendar {
font,
size,
sections_x,
sections_y,
dirty: true,
geometry: Default::default(),
}
}
fn draw_month(
&mut self,
fonts: &mut FontMap,
mut view: &mut BufferView,
orig: NaiveDate,
mut time: NaiveDate,
) {
let black = Color::new(0., 0., 0., 1.);
let white = Color::new(1., 1., 1., 1.);
let dim = Color::new(0.8, 0.8, 0.8, 1.);
let dimmer = Color::new(0.75, 0.75, 0.75, 1.);
let month_str = match time.month() {
1 => "January",
2 => "February",
3 => "March",
4 => "April",
5 => "May",
6 => "June",
7 => "July",
8 => "August",
9 => "September",
10 => "October",
11 => "November",
12 => "December",
_ => panic!("impossible value"),
};
let mut y_off = 1;
// TODO: Fix offsets:
// - Headline font is not correctly accounted for
//
// Draw the week day
//
for idx in 1..8 {
let day_font = fonts.get_font(self.font, self.size);
let wk_chr = match idx {
1 => "MON",
2 => "TUE",
3 => "WED",
4 => "THU",
5 => "FRI",
6 => "SAT",
7 => "SUN",
_ => panic!("impossible value"),
};
day_font
.auto_draw_text(
&mut view
.offset((
idx * (self.size * 2.5).ceil() as u32 + (self.size / 5.).ceil() as u32,
y_off * (self.size * 1.8).ceil() as u32
+ (self.size * 1.).ceil() as u32,
))
.unwrap(),
&black,
&white,
&wk_chr,
)
.unwrap();
}
//
// Draw the month
//
if time.year() != orig.year() {
let year_font = fonts.get_font(self.font, self.size * 1.5);
year_font
.auto_draw_text(
&mut view.offset(((self.size * 17.).ceil() as u32, 0)).unwrap(),
&black,
&dim,
&format!("{:}", time.year()),
)
.unwrap();
}
let cal_font = fonts.get_font(self.font, self.size * 2.);
cal_font
.auto_draw_text(&mut view, &black, &white, month_str)
.unwrap();
let mut done = false;
while !done {
let mut x_pos = 0;
//
// Find the start of this week
//
let mut wkday = time.weekday();
while wkday != chrono::Weekday::Mon {
x_pos += 1;
wkday = wkday.pred();
}
//
// Draw the week number
//
let wk = time.iso_week();
cal_font
.auto_draw_text(
&mut view
.offset((
0,
y_off * (self.size * 1.8).ceil() as u32
+ (self.size * 2.).ceil() as u32,
))
.unwrap(),
&black,
&dimmer,
&format!("{:02}", wk.week()),
)
.unwrap();
x_pos += 1;
//
// Draw the dates
//
while x_pos < 8 {
let c = if time.day() == orig.day() && time.month() == orig.month() {
Color::new(1.0, 1.0, 1.0, 1.0)
} else {
Color::new(0.5, 0.5, 0.5, 1.0)
};
cal_font
.auto_draw_text(
&mut view
.offset((
x_pos * (self.size * 2.5).ceil() as u32,
y_off * (self.size * 1.8).ceil() as u32 + (self.size * 2.) as u32,
))
.unwrap(),
&black,
&c,
&format!("{:02}", time.day()),
)
.unwrap();
let t = time.with_day(time.day() + 1);
if t.is_none() {
done = true;
break;
}
time = t.unwrap();
x_pos += 1;
}
y_off += 1;
}
}
}
impl<'a> Widget for Calendar {
fn get_dirty(&self) -> bool {
self.dirty
}
fn geometry(&self) -> Geometry {
self.geometry
}
fn event(&mut self, event: &Event) {
match event {
Event::NewMinute => {
self.dirty = true;
}
_ => (),
}
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
let time = Local::now().naive_local().date();
let cal_height = (self.size * 16.25).ceil() as u32;
let cal_width = (7. * self.size * 3. + self.size * 2.).ceil() as u32;
let cal_pad = (3. * self.size).ceil() as u32;
let sections_y = if self.sections_y > 0 {
self.sections_y as u32
} else {
self.geometry.height / cal_height
};
let sections_x = if self.sections_x > 0 {
self.sections_x as u32
} else {
self.geometry.width / (cal_height + cal_pad)
};
let mut t = time.with_day(1).unwrap();
let cals = sections_y * sections_x - 1;
if cals >=3 {
t = t.pred_opt().unwrap().with_day(1).unwrap();
}
for ydx in 0..sections_y {
for idx in 0..sections_x {
self.draw_month(
fonts,
&mut view
.offset(((cal_width + cal_pad) * idx, (cal_height) * ydx))
.unwrap(),
time,
t,
);
t = if t.month() == 12 {
t.with_year(t.year() + 1).unwrap().with_month(1).unwrap()
} else {
t.with_month(t.month() + 1).unwrap()
};
}
}
self.dirty = false;
self.geometry
}
fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry {
let cal_width = (7. * self.size * 3. + self.size * 2.).ceil() as u32;
let cal_height = (self.size * 16.25).ceil() as u32; // TODO: Calculate from font sizes
let cal_pad = (self.size * 3.).ceil() as u32;
let width = if self.sections_x > 0 {
cal_width * self.sections_x as u32 + cal_pad * (self.sections_x as u32 - 1)
} else {
(geometry.width / (cal_width + cal_pad)) * (cal_width + cal_pad)
};
let height = if self.sections_y > 0 {
cal_height * self.sections_y as u32
} else {
(geometry.height / cal_height) * cal_height
};
self.geometry = Geometry {
x: geometry.x,
y: geometry.y,
width: width,
height: height,
};
self.geometry
}
}
+13 -4
View File
@@ -4,6 +4,7 @@ use crate::{
buffer::BufferView, buffer::BufferView,
color::Color, color::Color,
fonts::FontMap, fonts::FontMap,
state::Event,
widgets::{Geometry, Widget}, widgets::{Geometry, Widget},
}; };
@@ -21,7 +22,8 @@ pub struct Clock {
impl<'a> Clock { impl<'a> Clock {
pub fn new(font: &'static str, size: f32) -> Clock { pub fn new(font: &'static str, size: f32) -> Clock {
Clock { Clock {
font, size, font,
size,
dirty: true, dirty: true,
geometry: Default::default(), geometry: Default::default(),
digit_width: 0, digit_width: 0,
@@ -31,9 +33,6 @@ impl<'a> Clock {
} }
impl<'a> Widget for Clock { impl<'a> Widget for Clock {
fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty;
}
fn get_dirty(&self) -> bool { fn get_dirty(&self) -> bool {
self.dirty self.dirty
} }
@@ -42,6 +41,15 @@ impl<'a> Widget for Clock {
self.geometry self.geometry
} }
fn event(&mut self, event: &Event) {
match event {
Event::NewMinute => {
self.dirty = true;
}
_ => (),
}
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry { fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
let time = Local::now().naive_local(); let time = Local::now().naive_local();
let fg = Color::new(1., 1., 1., 1.); let fg = Color::new(1., 1., 1., 1.);
@@ -58,6 +66,7 @@ impl<'a> Widget for Clock {
&format!("{:02}:{:02}", time.hour(), time.minute()), &format!("{:02}:{:02}", time.hour(), time.minute()),
) )
.unwrap(); .unwrap();
self.dirty = false;
self.geometry self.geometry
} }
+13 -4
View File
@@ -4,6 +4,7 @@ use crate::{
buffer::BufferView, buffer::BufferView,
color::Color, color::Color,
fonts::FontMap, fonts::FontMap,
state::Event,
widgets::{Geometry, Widget}, widgets::{Geometry, Widget},
}; };
@@ -19,7 +20,8 @@ pub struct Date {
impl Date { impl Date {
pub fn new(font: &'static str, size: f32) -> Date { pub fn new(font: &'static str, size: f32) -> Date {
Date { Date {
font, size, font,
size,
dirty: true, dirty: true,
geometry: Default::default(), geometry: Default::default(),
} }
@@ -27,9 +29,6 @@ impl Date {
} }
impl<'a> Widget for Date { impl<'a> Widget for Date {
fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty;
}
fn get_dirty(&self) -> bool { fn get_dirty(&self) -> bool {
self.dirty self.dirty
} }
@@ -38,6 +37,15 @@ impl<'a> Widget for Date {
self.geometry self.geometry
} }
fn event(&mut self, event: &Event) {
match event {
Event::NewMinute => {
self.dirty = true;
}
_ => (),
}
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry { fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
let time = Local::now().naive_local(); let time = Local::now().naive_local();
let fg = Color::new(1., 1., 1., 1.); let fg = Color::new(1., 1., 1., 1.);
@@ -57,6 +65,7 @@ impl<'a> Widget for Date {
), ),
) )
.unwrap(); .unwrap();
self.dirty = false;
self.geometry self.geometry
} }
+63 -57
View File
@@ -1,5 +1,5 @@
use std::{ use std::{
cmp::{min, max, Ordering}, cmp::{max, min, Ordering},
collections::HashMap, collections::HashMap,
default::Default, default::Default,
process::{exit, Command}, process::{exit, Command},
@@ -12,6 +12,7 @@ use crate::{
color::Color, color::Color,
fonts::FontMap, fonts::FontMap,
keyboard::{keysyms, KeyEvent}, keyboard::{keysyms, KeyEvent},
state::Event,
utils::desktop::{load_desktop_files, Desktop}, utils::desktop::{load_desktop_files, Desktop},
widgets::{Geometry, Widget}, widgets::{Geometry, Widget},
}; };
@@ -287,18 +288,17 @@ impl InterfaceWidget for Launcher {
// Draw line // Draw line
let mut prompt_offset = intf.geometry.height - line_height; let mut prompt_offset = intf.geometry.height - line_height;
let mut prompt_line = view.offset((0, prompt_offset)).unwrap(); let mut prompt_line = view.offset((0, prompt_offset)).unwrap();
let mut prompt_border = prompt_line.limit((intf.geometry.width, 1)).unwrap();
prompt_border.memset(&fg);
let font = fonts.get_font(intf.font, intf.size); let font = fonts.get_font(intf.font, intf.size);
let mut x_max = font.auto_draw_text_with_cursor( let mut x_max = font
&mut prompt_line, .auto_draw_text_with_cursor(
&bg, &mut prompt_line,
&fg, &bg,
&format!(" {}", &intf.prompt.input), &fg,
intf.prompt.cursor + 3, &format!(" {}", &intf.prompt.input),
) intf.prompt.cursor + 3,
.unwrap(); )
.unwrap();
font.auto_draw_text(&mut prompt_line, &bg, &fg, ">") font.auto_draw_text(&mut prompt_line, &bg, &fg, ">")
.unwrap(); .unwrap();
@@ -328,11 +328,17 @@ impl InterfaceWidget for Launcher {
colors.push(Color::new(0.75, 0.75, 0.75, 1.0)); colors.push(Color::new(0.75, 0.75, 0.75, 1.0));
} }
} }
x_max = max(x_max, font.auto_draw_text_individual_colors(&mut line, &bg, &colors, &m.name) x_max = max(
.unwrap()); x_max,
font.auto_draw_text_individual_colors(&mut line, &bg, &colors, &m.name)
.unwrap(),
);
} else { } else {
x_max = max(x_max, font.auto_draw_text(&mut line, &bg, &dimfg, &m.name) x_max = max(
.unwrap()); x_max,
font.auto_draw_text(&mut line, &bg, &dimfg, &m.name)
.unwrap(),
);
} }
} }
@@ -343,7 +349,7 @@ impl InterfaceWidget for Launcher {
Geometry { Geometry {
x: intf.geometry.x, x: intf.geometry.x,
y: intf.geometry.y + intf.geometry.height - content_height, y: intf.geometry.y + intf.geometry.height - content_height,
width: x_max.0+1, width: x_max.0 + 1,
height: content_height, height: content_height,
} }
} }
@@ -381,19 +387,18 @@ impl InterfaceWidget for Shell {
// Draw line // Draw line
let prompt_offset = intf.geometry.height - line_height; let prompt_offset = intf.geometry.height - line_height;
let mut prompt_line = view.offset((0, prompt_offset)).unwrap(); let mut prompt_line = view.offset((0, prompt_offset)).unwrap();
let mut prompt_border = prompt_line.limit((intf.geometry.width, 1)).unwrap();
prompt_border.memset(&fg);
// Draw prompt // Draw prompt
let font = fonts.get_font(intf.font, intf.size); let font = fonts.get_font(intf.font, intf.size);
let x_max = font.auto_draw_text_with_cursor( let x_max = font
&mut prompt_line, .auto_draw_text_with_cursor(
&bg, &mut prompt_line,
&fg, &bg,
&format!(" {}", &intf.prompt.input), &fg,
intf.prompt.cursor + 3, &format!(" {}", &intf.prompt.input),
) intf.prompt.cursor + 3,
.unwrap(); )
.unwrap();
font.auto_draw_text(&mut prompt_line, &bg, &Color::new(1., 0.75, 0.5, 1.), "!") font.auto_draw_text(&mut prompt_line, &bg, &Color::new(1., 0.75, 0.5, 1.), "!")
.unwrap(); .unwrap();
@@ -443,8 +448,6 @@ impl InterfaceWidget for Calc {
// Draw line // Draw line
let mut prompt_offset = intf.geometry.height - (line_height); let mut prompt_offset = intf.geometry.height - (line_height);
let mut prompt_line = view.offset((0, prompt_offset)).unwrap(); let mut prompt_line = view.offset((0, prompt_offset)).unwrap();
let mut prompt_border = prompt_line.limit((intf.geometry.width, 1)).unwrap();
prompt_border.memset(&fg);
// Draw prompt // Draw prompt
let font = fonts.get_font(intf.font, intf.size); let font = fonts.get_font(intf.font, intf.size);
@@ -518,7 +521,8 @@ impl Interface {
result: None, result: None,
}, },
inner: InnerInterface { inner: InnerInterface {
font, size, font,
size,
dirty: false, dirty: false,
geometry: Default::default(), geometry: Default::default(),
selection: 0, selection: 0,
@@ -526,32 +530,6 @@ impl Interface {
}, },
} }
} }
}
impl Widget for Interface {
fn set_dirty(&mut self, dirty: bool) {
self.inner.dirty = dirty;
}
fn get_dirty(&self) -> bool {
self.inner.dirty
}
fn geometry(&self) -> Geometry {
self.inner.geometry
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
match self.inner.prompt.mode {
PromptMode::Shell => self.shell.draw(&self.inner, fonts, view),
PromptMode::Normal => self.launcher.draw(&self.inner, fonts, view),
PromptMode::Calc => self.calc.draw(&self.inner, fonts, view),
}
}
fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry {
self.inner.geometry = geometry.clone();
self.inner.geometry
}
fn keyboard_input(&mut self, event: &KeyEvent) { fn keyboard_input(&mut self, event: &KeyEvent) {
if event.state != WEnum::Value(wl_keyboard::KeyState::Pressed) { if event.state != WEnum::Value(wl_keyboard::KeyState::Pressed) {
@@ -610,8 +588,36 @@ impl Widget for Interface {
} }
} }
} }
}
fn token_update(&mut self, token: &str) { impl Widget for Interface {
self.launcher.next_token = Some(token.to_string()); fn get_dirty(&self) -> bool {
self.inner.dirty
}
fn geometry(&self) -> Geometry {
self.inner.geometry
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
self.inner.dirty = false;
match self.inner.prompt.mode {
PromptMode::Shell => self.shell.draw(&self.inner, fonts, view),
PromptMode::Normal => self.launcher.draw(&self.inner, fonts, view),
PromptMode::Calc => self.calc.draw(&self.inner, fonts, view),
}
}
fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry {
self.inner.geometry = geometry.clone();
self.inner.geometry
}
fn event(&mut self, event: &Event) {
match event {
Event::KeyEvent(e) => self.keyboard_input(e),
Event::TokenUpdate(t) => self.launcher.next_token = Some(t.to_string()),
_ => (),
}
} }
} }
+4 -2
View File
@@ -1,6 +1,7 @@
mod backlight; mod backlight;
mod bar_widget; mod bar_widget;
mod battery; mod battery;
mod calendar;
mod clock; mod clock;
mod date; mod date;
mod launcher; mod launcher;
@@ -9,11 +10,12 @@ mod widget;
pub use backlight::Backlight; pub use backlight::Backlight;
pub use battery::Battery; pub use battery::Battery;
pub use calendar::Calendar;
pub use clock::Clock; pub use clock::Clock;
pub use date::Date; pub use date::Date;
pub use launcher::Interface; pub use launcher::Interface;
pub use line::Line; pub use line::Line;
pub use widget::{ pub use widget::{
Geometry, HorizontalLayout, IndexedLayout, Layout, Margin, VerticalLayout, Widget, Geometry, HorizontalLayout, IndexedLayout, InvertedHorizontalLayout, Layout, Margin,
WidgetUpdater, VerticalLayout, Widget, WidgetUpdater,
}; };
+97 -37
View File
@@ -1,7 +1,13 @@
use std::cmp::{max, min}; use std::{
use std::fmt; cmp::{max, min},
fmt,
};
use crate::{buffer::BufferView, fonts::FontMap, keyboard::KeyEvent}; use crate::{
buffer::BufferView,
fonts::FontMap,
state::{Event, State},
};
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub struct Geometry { pub struct Geometry {
@@ -47,38 +53,42 @@ pub trait Widget {
fn get_dirty(&self) -> bool { fn get_dirty(&self) -> bool {
false false
} }
fn set_dirty(&mut self, _dirty: bool) {}
fn geometry(&self) -> Geometry; fn geometry(&self) -> Geometry;
fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry; fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry;
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry; fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry;
fn keyboard_input(&mut self, _event: &KeyEvent) {} fn event(&mut self, _event: &Event) {}
fn token_update(&mut self, _token: &str) {}
} }
pub trait Layout<U> { pub trait Layout {
fn geometry_update( fn geometry_update(
&mut self, &self,
fonts: &mut FontMap, fonts: &mut FontMap,
geometry: &Geometry, geometry: &Geometry,
user_data: &mut U, state: &mut State,
) -> Geometry; ) -> Geometry;
} }
pub struct HorizontalLayout<U> { pub struct HorizontalLayout {
pub widgets: Vec<Box<dyn Layout<U>>>, pub widgets: Vec<Box<dyn Layout>>,
} }
impl<U> Layout<U> for HorizontalLayout<U> { impl HorizontalLayout {
pub fn new(widgets: Vec<Box<dyn Layout>>) -> Box<dyn Layout> {
Box::new(HorizontalLayout { widgets })
}
}
impl Layout for HorizontalLayout {
fn geometry_update( fn geometry_update(
&mut self, &self,
fonts: &mut FontMap, fonts: &mut FontMap,
geometry: &Geometry, geometry: &Geometry,
user_data: &mut U, state: &mut State,
) -> Geometry { ) -> Geometry {
let mut geo = geometry.clone(); let mut geo = geometry.clone();
let mut max_width = 0; let mut max_width = 0;
let mut max_height = 0; let mut max_height = 0;
for w in self.widgets.iter_mut() { for w in self.widgets.iter() {
let result = w.geometry_update(fonts, &geo, user_data); let result = w.geometry_update(fonts, &geo, state);
geo.x = result.x + result.width; geo.x = result.x + result.width;
geo.width -= result.width; geo.width -= result.width;
max_width += result.width; max_width += result.width;
@@ -90,22 +100,28 @@ impl<U> Layout<U> for HorizontalLayout<U> {
} }
} }
pub struct VerticalLayout<U> { pub struct VerticalLayout {
pub widgets: Vec<Box<dyn Layout<U>>>, pub widgets: Vec<Box<dyn Layout>>,
} }
impl<U> Layout<U> for VerticalLayout<U> { impl VerticalLayout {
pub fn new(widgets: Vec<Box<dyn Layout>>) -> Box<dyn Layout> {
Box::new(VerticalLayout { widgets })
}
}
impl Layout for VerticalLayout {
fn geometry_update( fn geometry_update(
&mut self, &self,
fonts: &mut FontMap, fonts: &mut FontMap,
geometry: &Geometry, geometry: &Geometry,
user_data: &mut U, state: &mut State,
) -> Geometry { ) -> Geometry {
let mut geo = geometry.clone(); let mut geo = geometry.clone();
let mut max_width = 0; let mut max_width = 0;
let mut max_height = 0; let mut max_height = 0;
for w in self.widgets.iter_mut() { for w in self.widgets.iter() {
let result = w.geometry_update(fonts, &geo, user_data); let result = w.geometry_update(fonts, &geo, state);
geo.y = result.y + result.height; geo.y = result.y + result.height;
geo.height -= result.height; geo.height -= result.height;
max_width = max(result.width, max_width); max_width = max(result.width, max_width);
@@ -126,28 +142,40 @@ pub struct IndexedLayout {
pub widget_idx: usize, pub widget_idx: usize,
} }
impl<U: WidgetUpdater> Layout<U> for IndexedLayout { impl IndexedLayout {
fn geometry_update( pub fn new(widget_idx: usize) -> Box<dyn Layout> {
&mut self, Box::new(IndexedLayout { widget_idx })
fonts: &mut FontMap,
geometry: &Geometry,
user_data: &mut U,
) -> Geometry {
user_data.geometry_update(self.widget_idx, fonts, geometry)
} }
} }
pub struct Margin<U> { impl Layout for IndexedLayout {
pub widget: Box<dyn Layout<U>>, fn geometry_update(
&self,
fonts: &mut FontMap,
geometry: &Geometry,
state: &mut State,
) -> Geometry {
state.geometry_update(self.widget_idx, fonts, geometry)
}
}
pub struct Margin {
pub widget: Box<dyn Layout>,
pub margin: (u32, u32, u32, u32), pub margin: (u32, u32, u32, u32),
} }
impl<U> Layout<U> for Margin<U> { impl Margin {
pub fn new(widget: Box<dyn Layout>, margin: (u32, u32, u32, u32)) -> Box<dyn Layout> {
Box::new(Margin { widget, margin })
}
}
impl Layout for Margin {
fn geometry_update( fn geometry_update(
&mut self, &self,
fonts: &mut FontMap, fonts: &mut FontMap,
geometry: &Geometry, geometry: &Geometry,
user_data: &mut U, state: &mut State,
) -> Geometry { ) -> Geometry {
let geo = Geometry { let geo = Geometry {
x: geometry.x + self.margin.0, x: geometry.x + self.margin.0,
@@ -156,7 +184,7 @@ impl<U> Layout<U> for Margin<U> {
height: geometry.height - self.margin.1 - self.margin.3, height: geometry.height - self.margin.1 - self.margin.3,
}; };
let out = self.widget.geometry_update(fonts, &geo, user_data); let out = self.widget.geometry_update(fonts, &geo, state);
Geometry { Geometry {
x: out.x - self.margin.0, x: out.x - self.margin.0,
y: out.y - self.margin.1, y: out.y - self.margin.1,
@@ -165,3 +193,35 @@ impl<U> Layout<U> for Margin<U> {
} }
} }
} }
pub struct InvertedHorizontalLayout {
pub widgets: Vec<Box<dyn Layout>>,
}
impl InvertedHorizontalLayout {
pub fn new(widgets: Vec<Box<dyn Layout>>) -> Box<dyn Layout> {
Box::new(InvertedHorizontalLayout { widgets })
}
}
impl Layout for InvertedHorizontalLayout {
fn geometry_update(
&self,
fonts: &mut FontMap,
geometry: &Geometry,
state: &mut State,
) -> Geometry {
let mut geo = geometry.clone();
let mut max_height = 0;
for w in self.widgets.iter() {
let mut temp_geo = geo.clone();
let temp_result = w.geometry_update(fonts, &temp_geo, state);
temp_geo.x = geo.x + (geo.width - temp_result.width);
let result = w.geometry_update(fonts, &temp_geo, state);
geo.width -= result.width;
max_height = max(result.height, max_height);
}
geo.height = max_height;
geo
}
}