From 6e90b1f0e82c3cd798a903df134c51487f9adfb1 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 8 Feb 2023 22:53:41 +0100 Subject: [PATCH] More better --- src/buffer.rs | 42 +++--- src/color.rs | 119 ++++------------- src/draw.rs | 36 ++--- src/event.rs | 1 - src/fonts.rs | 20 ++- src/keyboard/mod.rs | 5 +- src/main.rs | 68 ++++++---- src/state.rs | 176 ++++++++++++++++--------- src/widgets/bar_widget.rs | 24 +++- src/widgets/battery.rs | 54 ++++---- src/widgets/calendar.rs | 271 +++++++++++++++++++++++--------------- src/widgets/clock.rs | 14 +- src/widgets/date.rs | 13 +- src/widgets/launcher.rs | 59 +++++---- src/widgets/layout.rs | 71 +++++++++- src/widgets/line.rs | 13 +- src/widgets/mod.rs | 13 +- src/widgets/pulseaudio.rs | 177 +++++++++++++------------ src/widgets/widget.rs | 16 +-- 19 files changed, 681 insertions(+), 511 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index 07117a8..e63363a 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -199,15 +199,16 @@ impl<'a> BufferView<'a> { } } - pub fn subdimensions( - &mut self, - subdimensions: (u32, u32, u32, u32), - ) -> BufferView { + pub fn subdimensions(&mut self, subdimensions: (u32, u32, u32, u32)) -> BufferView { let bounds = self.get_bounds(); - if cfg!(debug_assertions) && (subdimensions.0 + subdimensions.2 > bounds.2 - || subdimensions.1 + subdimensions.3 > bounds.3) + if cfg!(debug_assertions) + && (subdimensions.0 + subdimensions.2 > bounds.2 + || subdimensions.1 + subdimensions.3 > bounds.3) { - panic!("cannot create subdimensions larger than buffer: {:?} > {:?}", subdimensions, bounds); + panic!( + "cannot create subdimensions larger than buffer: {:?} > {:?}", + subdimensions, bounds + ); } BufferView { @@ -229,7 +230,10 @@ impl<'a> BufferView<'a> { pub fn offset(&mut self, offset: (u32, u32)) -> BufferView { let bounds = self.get_bounds(); if cfg!(debug_assertions) && (offset.0 > bounds.2 || offset.1 > bounds.3) { - panic!("cannot create offset outside buffer: {:?} > {:?}", offset, bounds); + panic!( + "cannot create offset outside buffer: {:?} > {:?}", + offset, bounds + ); } BufferView { @@ -268,19 +272,15 @@ impl<'a> BufferView<'a> { } } - pub fn put(&mut self, pos: (u32, u32), c: Color) { - let true_pos = if let Some(subdim) = self.subdimensions { - if cfg!(debug_assertions) && (pos.0 >= subdim.2 || pos.1 >= subdim.3) { - panic!("put({:?}) is not within subdimensions of buffer ({:?})", pos, subdim); - } - (pos.0 + subdim.0, pos.1 + subdim.1) - } else { - if cfg!(debug_assertions) && (pos.0 >= self.dimensions.0 || pos.1 >= self.dimensions.1) { - panic!("put({:?}) is not within dimensions of buffer ({:?})", pos, self.dimensions); - } - pos - }; + #[inline] + pub fn put_raw(&mut self, pos: (u32, u32), c: Color) { + self.view[(pos.0 + pos.1 * self.dimensions.0) as usize] = c.0 + } - self.view[(true_pos.0 + (true_pos.1 * self.dimensions.0)) as usize] = c.0; + #[inline] + pub fn put_line_raw(&mut self, pos: (u32, u32), len: u32, c: Color) { + let start = (pos.0 + (pos.1 * self.dimensions.0)) as usize; + let len = len as usize; + self.view[start..start + len].fill(c.0); } } diff --git a/src/color.rs b/src/color.rs index 88d5989..5694ae8 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,113 +1,42 @@ -//use serde::{Deserialize, Serialize}; -/* -//#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)] -pub struct Color { - red: f32, - green: f32, - blue: f32, - opacity: f32, -} - -impl Color { - pub fn new(red: f32, green: f32, blue: f32, opacity: f32) -> Color { - let (red, green, blue, opacity) = clamp(red, green, blue, opacity); - Color { - red, - green, - blue, - opacity, - } - } - - pub fn blend(&self, other: &Color, ratio: f32) -> Color { - let ratio = clamp_f32(ratio, 0.0, 1.0); - - Color { - red: blend_f32(self.red, other.red, ratio), - green: blend_f32(self.green, other.green, ratio), - blue: blend_f32(self.blue, other.blue, ratio), - opacity: blend_f32(self.opacity, other.opacity, ratio), - } - } - - #[inline] - pub fn as_argb8888(&self) -> u32 { - ((255.0 * self.opacity) as u32 & 0xFF) << 24 - | ((255.0 * self.red) as u32 & 0xFF) << 16 - | ((255.0 * self.green) as u32 & 0xFF) << 8 - | ((255.0 * self.blue) as u32 & 0xFF) - } - - #[inline] - pub fn as_integer(&self) -> IntegerColor { - IntegerColor(self.as_argb8888()) - } -} - -#[inline] -fn clamp(r: f32, g: f32, b: f32, o: f32) -> (f32, f32, f32, f32) { - clamp_naive(r, g, b, o) -} - -#[inline] -fn clamp_naive(r: f32, g: f32, b: f32, o: f32) -> (f32, f32, f32, f32) { - ( - clamp_f32(r, 0.0, 1.0), - clamp_f32(g, 0.0, 1.0), - clamp_f32(b, 0.0, 1.0), - clamp_f32(o, 0.0, 1.0), - ) -} - -#[inline] -fn clamp_f32(v: f32, a: f32, b: f32) -> f32 { - if v > b { - b - } else if v < a { - a - } else { - v - } -} - -#[inline] -fn blend_f32(a: f32, b: f32, r: f32) -> f32 { - a + ((b - a) * r) -} -*/ - #[derive(Copy, Clone)] pub struct Color(pub u32); impl Color { - pub const WHITE: Color = Color(0xFFFFFFFF); - pub const GREY50: Color = Color(0xFF7F7F7F); - pub const GREY75: Color = Color(0xFFBFBFBF); - pub const GREY80: Color = Color(0xFFCCCCCC); - pub const RED: Color = Color(0xFFFF0000); - pub const YELLOW: Color = Color(0xFFFFFF00); - pub const LIGHTGREEN: Color = Color(0xFF7FFF7F); - pub const LIGHTRED: Color = Color(0xFFFF7F7F); + pub const WHITE: Color = Color(0xFFFFFFFF); + pub const GREY35: Color = Color(0xFF595959); + pub const GREY50: Color = Color(0xFF7F7F7F); + pub const GREY75: Color = Color(0xFFBFBFBF); + pub const GREY80: Color = Color(0xFFCCCCCC); + pub const RED: Color = Color(0xFFFF0000); + pub const YELLOW: Color = Color(0xFFFFFF00); + pub const LIGHTGREEN: Color = Color(0xFF7FFF7F); pub const LIGHTORANGE: Color = Color(0xFFFFBF00); - pub const DARKORANGE: Color = Color(0xFFFF7F00); - pub const BUFF: Color = Color(0xFFFFBF7F); + pub const DARKORANGE: Color = Color(0xFFFF7F00); + pub const BUFF: Color = Color(0xFFFFBF7F); #[allow(unused)] pub fn new(red: u8, green: u8, blue: u8, opacity: u8) -> Color { Color((opacity as u32) << 24 | (red as u32) << 16 | (green as u32) << 8 | (blue as u32)) } - fn red(self) -> u8 {((self.0 >> 16) & 0xFF) as u8 } - fn green(self) -> u8 {((self.0 >> 8) & 0xFF) as u8 } - fn blue(self) -> u8 {(self.0 & 0xFF) as u8 } + fn red(self) -> u8 { + ((self.0 >> 16) & 0xFF) as u8 + } + fn green(self) -> u8 { + ((self.0 >> 8) & 0xFF) as u8 + } + fn blue(self) -> u8 { + (self.0 & 0xFF) as u8 + } pub fn alpha(self, alpha: f32) -> Color { unsafe { Color( - (255. * alpha).to_int_unchecked::() << 24 | - (self.red() as f32 * alpha).to_int_unchecked::() << 16 | - (self.green() as f32 * alpha).to_int_unchecked::() << 8 | - (self.blue() as f32 * alpha).to_int_unchecked::()) + (255. * alpha).to_int_unchecked::() << 24 + | (self.red() as f32 * alpha).to_int_unchecked::() << 16 + | (self.green() as f32 * alpha).to_int_unchecked::() << 8 + | (self.blue() as f32 * alpha).to_int_unchecked::(), + ) } } } diff --git a/src/draw.rs b/src/draw.rs index 37b71d8..5f46a1d 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -21,7 +21,6 @@ impl CachedGlyph { if let Some(bounding_box) = glyph.pixel_bounding_box() { let origin = (bounding_box.min.x, bounding_box.min.y); - let dimensions = ( (bounding_box.max.x - bounding_box.min.x) as u32, (bounding_box.max.y - bounding_box.min.y) as u32, @@ -50,13 +49,15 @@ impl CachedGlyph { fn draw(&self, buf: &mut BufferView, pos: (i32, i32), c: Color) { let mut x = 0; let mut y = 0; + let bounds = buf.get_bounds(); + let offset = (bounds.0 as i32, bounds.1 as i32); for v in &self.render { - let _ = buf.put( + buf.put_raw( ( - (x + pos.0 + self.origin.0) as u32, - (y + pos.1 + self.origin.1) as u32, + (x + pos.0 + self.origin.0 + offset.0) as u32, + (y + pos.1 + self.origin.1 + offset.1) as u32, ), - c.alpha(*v) + c.alpha(*v), ); if x == self.dimensions.0 as i32 - 1 { @@ -180,8 +181,9 @@ impl<'a> Font<'a> { height: u32, ) -> Result<(), ::std::io::Error> { // draw cursor - for i in 1..height { - buf.put((offset, i), c); + let bounds = buf.get_bounds(); + for i in bounds.1 + 1..height + bounds.1 { + buf.put_raw((bounds.0 + offset, i), c); } Ok(()) } @@ -305,13 +307,12 @@ impl<'a> Font<'a> { } pub fn draw_box(buf: &mut BufferView, c: Color, dim: (u32, u32)) -> Result<(), ::std::io::Error> { - for x in 0..dim.0 { - buf.put((x, 0), c); - buf.put((x, dim.1 - 1), c); - } - for y in 0..dim.1 { - buf.put((0, y), c); - buf.put((dim.0 - 1, y), c); + let bounds = buf.get_bounds(); + buf.put_line_raw((bounds.0, bounds.1), dim.0, c); + buf.put_line_raw((bounds.0, bounds.1 + dim.1 - 1), dim.0, c); + for y in bounds.1..dim.1 + bounds.1 { + buf.put_raw((bounds.0, y), c); + buf.put_raw((bounds.0 + dim.0 - 1, y), c); } Ok(()) @@ -328,10 +329,9 @@ pub fn draw_bar( if fill_pos > length { fill_pos = length; } - for y in 0..height { - for x in 0..fill_pos { - let _ = buf.put((x, y), color); - } + let bounds = buf.get_bounds(); + for y in bounds.1..height + bounds.1 { + buf.put_line_raw((bounds.0, y), fill_pos, color); } Ok(()) diff --git a/src/event.rs b/src/event.rs index eb2bab0..eb0811e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -58,4 +58,3 @@ impl Events { self.events.drain(..).collect() } } - diff --git a/src/fonts.rs b/src/fonts.rs index 29c13a2..308cbe3 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -3,15 +3,7 @@ use crate::draw; use rusttype::Font; use std::{ - cell::RefCell, - collections::HashMap, - fs::File, - hash, - io::Read, - mem, - path::Path, - rc::Rc, - thread, + cell::RefCell, collections::HashMap, fs::File, hash, io::Read, mem, path::Path, rc::Rc, thread, }; /// FontRef is used to store Fonts on widgets. @@ -24,7 +16,12 @@ mod fc { /// Given a string, it matches it to a font file and returns its path. pub(crate) fn from_string(name: &str) -> String { let fc = FontConfig::new().unwrap(); - fc.find(name, None).unwrap().path.to_str().unwrap().to_string() + fc.find(name, None) + .unwrap() + .path + .to_str() + .unwrap() + .to_string() } } @@ -98,7 +95,8 @@ impl FontMap { match self.required_fonts.get_mut(font_name) { Some(v) => v.1.push((size, preload)), None => { - self.required_fonts.insert(font_name, (font_name, vec![(size, preload)])); + self.required_fonts + .insert(font_name, (font_name, vec![(size, preload)])); } } } diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index aef408b..7281a50 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -45,7 +45,10 @@ pub struct Keyboard { impl Keyboard { pub fn new() -> Keyboard { - Keyboard { state: None, fd: None, } + Keyboard { + state: None, + fd: None, + } } pub fn keymap(&mut self, format: WEnum, fd: OwnedFd, size: u32) { diff --git a/src/main.rs b/src/main.rs index db27d26..1d1a619 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ mod buffer; mod color; mod draw; +mod event; mod fonts; mod keyboard; mod state; mod utils; mod widgets; -mod event; use wayland_client::{Connection, QueueHandle, WaylandSource}; @@ -17,19 +17,15 @@ use calloop::{ use chrono::{Duration, Local, Timelike}; use buffer::BufferView; -use fonts::{FontMap, MaybeFontMap}; -use state::{State, OperationMode}; use event::{Event, Events}; +use fonts::{FontMap, MaybeFontMap}; +use state::{OperationMode, State}; use widgets::{ - PulseAudio, Backlight, Battery, Calendar, Clock, Date, Geometry, HorizontalLayout, IndexedLayout, - Interface, InvertedHorizontalLayout, Line, Margin, VerticalLayout, Widget, Layout, + Backlight, Battery, Calendar, Clock, Date, Geometry, HorizontalLayout, IndexedLayout, + Interface, InvertedHorizontalLayout, Layout, Line, Margin, PulseAudio, VerticalLayout, Widget, }; -use std::{ - env, - rc::Rc, - thread -}; +use std::{env, rc::Rc, thread}; enum UIVersion { V1, @@ -52,7 +48,7 @@ fn main() { (Some(w), Some(h)) => mode = OperationMode::LayerSurface((w, h)), _ => panic!("invalid arguments"), } - }, + } Some(ref s) if s == "xdg_shell" => mode = OperationMode::XdgToplevel, Some(ref s) if s == "v1" => version = UIVersion::V1, Some(ref s) if s == "v2" => version = UIVersion::V2, @@ -120,27 +116,31 @@ fn main() { let mut fm = FontMap::new(); // TODO: Cache in a file - fm.add_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf".to_string()); - fm.add_font_path("monospace", "/usr/share/fonts/noto/NotoSansMono-Regular.ttf".to_string()); + fm.add_font_path( + "sans", + "/usr/share/fonts/noto/NotoSans-Regular.ttf".to_string(), + ); + fm.add_font_path( + "monospace", + "/usr/share/fonts/noto/NotoSansMono-Regular.ttf".to_string(), + ); let (widgets, layout): (Vec>, Rc>) = match version { UIVersion::V1 => { let clock = Box::new(Clock::new(&mut fm, "sans", 256.)); - let calendar = Box::new(Calendar::new(&mut fm, "monospace", 18.0, -1, 1)); + let calendar = Box::new(Calendar::new(&mut fm, "monospace", "sans", 36.0, 3, 1)); let date = Box::new(Date::new(&mut fm, "sans", 48.)); let launcher = Box::new(Interface::new(events.clone(), &mut fm, "monospace", 32.)); let battery = Box::new(Battery::new(events.clone(), &mut fm, "sans", 24.)); - let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.)); + let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.)); let audio = Box::new(PulseAudio::new(events.clone(), &mut fm, "sans", 24.)); - let v: Vec> = vec![clock, date, battery, backlight, audio, calendar, launcher]; + let v: Vec> = + vec![clock, date, battery, backlight, audio, calendar, launcher]; let l = Rc::new(Margin::new( VerticalLayout::new(vec![ HorizontalLayout::new(vec![ - VerticalLayout::new(vec![ - IndexedLayout::new(1), - IndexedLayout::new(0), - ]), + VerticalLayout::new(vec![IndexedLayout::new(1), IndexedLayout::new(0)]), Margin::new( VerticalLayout::new(vec![ Margin::new(IndexedLayout::new(2), (0, 0, 0, 8)), @@ -152,20 +152,23 @@ fn main() { ]), IndexedLayout::new(5), IndexedLayout::new(6), - ]), (20, 20, 20, 20)) - ); + ]), + (20, 20, 20, 20), + )); (v, l) } UIVersion::V2 => { let clock = Box::new(Clock::new(&mut fm, "sans", 128.)); - let calendar = Box::new(Calendar::new(&mut fm, "monospace", 12.0, 1, -1)); + let calendar = Box::new(Calendar::new(&mut fm, "monospace", "sans", 24.0, 1, -1)); let date = Box::new(Date::new(&mut fm, "sans", 48.)); let line = Box::new(Line::new(1)); let launcher = Box::new(Interface::new(events.clone(), &mut fm, "monospace", 32.)); let battery = Box::new(Battery::new(events.clone(), &mut fm, "sans", 24.)); - let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.)); + let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.)); let audio = Box::new(PulseAudio::new(events.clone(), &mut fm, "sans", 24.)); - let v: Vec> = vec![clock, date, battery, backlight, audio, line, calendar, launcher]; + let v: Vec> = vec![ + clock, date, battery, backlight, audio, line, calendar, launcher, + ]; let l = Rc::new(VerticalLayout::new(vec![ HorizontalLayout::new(vec![ IndexedLayout::new(0), @@ -186,11 +189,18 @@ fn main() { let font_thread = thread::Builder::new() .name("fontloader".to_string()) .spawn(move || { - fm.load_fonts(); - fm - }).unwrap(); + fm.load_fonts(); + fm + }) + .unwrap(); - let mut state = State::new(mode, widgets, layout, MaybeFontMap::Waiting(font_thread), events); + let mut state = State::new( + mode, + widgets, + layout, + MaybeFontMap::Waiting(font_thread), + events, + ); let mut damage = Vec::new(); for _ in 0..state.widgets.len() { diff --git a/src/state.rs b/src/state.rs index 5831c38..d8e0b32 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,43 +1,39 @@ use std::{ + cmp::max, + default::Default, env, process::exit, rc::Rc, sync::{Arc, Mutex}, - default::Default, }; use wayland_client::{ protocol::{ - wl_buffer, wl_compositor, wl_subcompositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, - wl_surface, wl_subsurface, wl_pointer, + wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm, + wl_shm_pool, wl_subcompositor, wl_subsurface, wl_surface, }, Connection, Dispatch, QueueHandle, WEnum, }; use wayland_protocols::{ + wp::{ + single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1, + viewporter::client::{wp_viewport, wp_viewporter}, + }, xdg::{ activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1}, shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}, }, - wp::{ - single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1, - viewporter::client::{ - wp_viewport, - wp_viewporter, - }, - }, }; -use wayland_protocols_wlr::layer_shell::v1::client::{ - zwlr_layer_shell_v1, zwlr_layer_surface_v1, -}; +use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}; use crate::{ buffer::BufferManager, + event::{Event, Events, PointerButton, PointerEvent}, fonts::{FontMap, MaybeFontMap}, keyboard::Keyboard, widgets::{Geometry, Layout, Widget, WidgetUpdater}, - event::{Event, Events, PointerEvent, PointerButton}, }; #[derive(Debug)] @@ -127,7 +123,10 @@ impl State { Ok(token) => token, Err(_) => return, }; - match (&self.main_surface.wl_surface, &self.protocols.xdg_activation) { + match ( + &self.main_surface.wl_surface, + &self.protocols.xdg_activation, + ) { (Some(surface), Some(activation)) => { activation.activate(key, surface); let activation_token = activation.get_activation_token(qh, ()); @@ -148,6 +147,10 @@ impl WidgetUpdater for State { ) -> Geometry { self.widgets[idx].geometry_update(fonts, geometry) } + + fn minimum_size(&mut self, idx: usize, fonts: &mut FontMap) -> Geometry { + self.widgets[idx].minimum_size(fonts) + } } impl Dispatch for State { @@ -182,30 +185,34 @@ impl Dispatch for State { let subsurface = subcompositor.get_subsurface(child, parent, qh, ()); subsurface.place_below(parent); state.bg_surface.subsurface = Some(subsurface); - }, + } _ => todo!("handle early subcompositor creation"), } } "wl_shm" => { - state.protocols.wl_shm = Some(registry.bind::(name, 1, qh, ())); + state.protocols.wl_shm = + Some(registry.bind::(name, 1, qh, ())); } "wl_seat" => { registry.bind::(name, 8, qh, ()); } - "xdg_wm_base" => if let OperationMode::XdgToplevel = state.mode { - let wm_base = registry.bind::(name, 2, qh, ()); + "xdg_wm_base" => { + if let OperationMode::XdgToplevel = state.mode { + let wm_base = + registry.bind::(name, 2, qh, ()); - match &state.main_surface.wl_surface { - Some(surface) => { - let xdg_surface = wm_base.get_xdg_surface(surface, qh, ()); - let toplevel = xdg_surface.get_toplevel(qh, ()); - toplevel.set_title("wldash".into()); + match &state.main_surface.wl_surface { + Some(surface) => { + let xdg_surface = wm_base.get_xdg_surface(surface, qh, ()); + let toplevel = xdg_surface.get_toplevel(qh, ()); + toplevel.set_title("wldash".into()); - surface.commit(); + surface.commit(); - state.main_surface.xdg_surface = Some((xdg_surface, toplevel)); + state.main_surface.xdg_surface = Some((xdg_surface, toplevel)); + } + _ => todo!("handle early xdg_shell creation"), } - _ => todo!("handle early xdg_shell creation"), } } "xdg_activation_v1" => { @@ -214,41 +221,63 @@ impl Dispatch for State { ); state.activate(qh); } - "zwlr_layer_shell_v1" => if let OperationMode::LayerSurface(size) = state.mode { - let layer_shell = registry.bind::(name, 4, qh, ()); - match &state.main_surface.wl_surface { - Some(surface) => { - let layer_surface = layer_shell.get_layer_surface(surface, None, zwlr_layer_shell_v1::Layer::Top, "launcher".to_string(), qh, ()); - layer_surface.set_keyboard_interactivity(zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive); - layer_surface.set_size(size.0, size.1); + "zwlr_layer_shell_v1" => { + if let OperationMode::LayerSurface(size) = state.mode { + let layer_shell = registry + .bind::(name, 4, qh, ()); + state.fonts.resolve(); + let fonts = state.fonts.unwrap(); + let layout = state.layout.clone(); + let min_size = layout.minimum_size(&mut fonts.borrow_mut(), state); + match &state.main_surface.wl_surface { + Some(surface) => { + let layer_surface = layer_shell.get_layer_surface( + surface, + None, + zwlr_layer_shell_v1::Layer::Top, + "launcher".to_string(), + qh, + (), + ); + layer_surface.set_keyboard_interactivity( + zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive, + ); + layer_surface.set_size( + max(size.0, min_size.width), + max(size.1, min_size.height), + ); - surface.commit(); - state.main_surface.layer_surface = Some(layer_surface); + surface.commit(); + state.main_surface.layer_surface = Some(layer_surface); + } + _ => todo!("handle early layer_shell creation"), } - _ => todo!("handle early layer_shell creation"), } - }, + } "wp_viewporter" => { - let viewporter = registry.bind::(name, 1, qh, ()); + let viewporter = + registry.bind::(name, 1, qh, ()); match &state.bg_surface.wl_surface { Some(surface) => { - state.bg_surface.viewport = Some(viewporter.get_viewport(surface, qh, ())); - }, + state.bg_surface.viewport = + Some(viewporter.get_viewport(surface, qh, ())); + } _ => todo!("handle early viewporter creation"), } state.protocols.viewporter = Some(viewporter); - }, + } "wp_single_pixel_buffer_manager_v1" => { let singlepixel = registry.bind::(name, 1, qh, ()); match &state.bg_surface.wl_surface { Some(surface) => { - let buffer = singlepixel.create_u32_rgba_buffer(0, 0, 0, 0xFFFFFFFF, qh, ()); + let buffer = + singlepixel.create_u32_rgba_buffer(0, 0, 0, 0xFFFFFFFF, qh, ()); surface.attach(Some(&buffer), 0, 0); surface.damage_buffer(0, 0, 1, 1); - }, + } _ => todo!("handle early single_pixel_buffer creation"), } - }, + } _ => {} } } @@ -352,7 +381,8 @@ impl Dispatch for State { _: &(), _: &Connection, _: &QueueHandle, - ) {} + ) { + } } impl Dispatch for State { @@ -364,7 +394,12 @@ impl Dispatch for State { _: &Connection, _: &QueueHandle, ) { - if let zwlr_layer_surface_v1::Event::Configure { serial, width, height } = event { + if let zwlr_layer_surface_v1::Event::Configure { + serial, + width, + height, + } = event + { if (width, height) == (0, 0) { return; } @@ -397,7 +432,7 @@ impl Dispatch for State { (Some(surface), Some(viewport)) => { viewport.set_destination(dim.0, dim.1); surface.commit(); - }, + } _ => todo!("handle early layer shell creation"), } } @@ -577,20 +612,30 @@ impl Dispatch for State { _: &QueueHandle, ) { match event { - wl_pointer::Event::Enter { surface_x, surface_y, .. } => { + wl_pointer::Event::Enter { + surface_x, + surface_y, + .. + } => { state.pointer = Some((surface_x, surface_y)); - }, - wl_pointer::Event::Motion { surface_x, surface_y, .. } => { + } + wl_pointer::Event::Motion { + surface_x, + surface_y, + .. + } => { state.pointer = Some((surface_x, surface_y)); - }, - wl_pointer::Event::Leave { .. } => { - state.pointer = None - }, - wl_pointer::Event::Axis { axis, value, .. } => match state.pointer { + } + wl_pointer::Event::Leave { .. } => state.pointer = None, + wl_pointer::Event::Axis { axis, value, .. } => match state.pointer { Some((x, y)) => { let axis = match axis { - WEnum::Value(wl_pointer::Axis::VerticalScroll) => PointerButton::ScrollVertical(value), - WEnum::Value(wl_pointer::Axis::HorizontalScroll) => PointerButton::ScrollHorizontal(value), + WEnum::Value(wl_pointer::Axis::VerticalScroll) => { + PointerButton::ScrollVertical(value) + } + WEnum::Value(wl_pointer::Axis::HorizontalScroll) => { + PointerButton::ScrollHorizontal(value) + } _ => return, }; let pos = (x as u32, y as u32); @@ -598,7 +643,7 @@ impl Dispatch for State { let geo = widget.geometry(); if geo.contains(pos) { let pos = (pos.0 - geo.x, pos.1 - geo.y); - let ev = Event::PointerEvent(PointerEvent{ + let ev = Event::PointerEvent(PointerEvent { button: axis, pos: pos, }); @@ -607,10 +652,14 @@ impl Dispatch for State { break; } } - }, + } _ => (), }, - wl_pointer::Event::Button { button, state: button_state, .. } => match (button, button_state, state.pointer) { + wl_pointer::Event::Button { + button, + state: button_state, + .. + } => match (button, button_state, state.pointer) { (button, WEnum::Value(wl_pointer::ButtonState::Pressed), Some((x, y))) => { let button = match button { BTN_LEFT => PointerButton::Left, @@ -622,9 +671,8 @@ impl Dispatch for State { for widget in state.widgets.iter_mut() { let geo = widget.geometry(); if geo.contains(pos) { - let pos = (pos.0 - geo.x, pos.1 - geo.y); - let ev = Event::PointerEvent(PointerEvent{ + let ev = Event::PointerEvent(PointerEvent { button: button, pos: pos, }); @@ -633,7 +681,7 @@ impl Dispatch for State { break; } } - }, + } _ => (), }, _ => (), diff --git a/src/widgets/bar_widget.rs b/src/widgets/bar_widget.rs index 6ee3f39..a4adc37 100644 --- a/src/widgets/bar_widget.rs +++ b/src/widgets/bar_widget.rs @@ -4,9 +4,9 @@ use crate::{ buffer::BufferView, color::Color, draw::{draw_bar, draw_box}, + event::{Event, PointerButton}, fonts::FontMap, widgets::{Geometry, Widget}, - event::{Event, PointerButton}, }; pub trait BarWidgetImpl { @@ -27,7 +27,12 @@ pub struct BarWidget { } impl BarWidget { - pub fn new(inner_widget: Box, fm: &mut FontMap, font: &'static str, size: f32) -> BarWidget { + pub fn new( + inner_widget: Box, + fm: &mut FontMap, + font: &'static str, + size: f32, + ) -> BarWidget { fm.queue_font(font, size, inner_widget.name()); BarWidget { geometry: Default::default(), @@ -51,8 +56,7 @@ impl Widget for BarWidget { let font = fonts.get_font(self.font, self.size); let fg = Color::WHITE; - font.draw_text(view, fg, self.inner_widget.name()) - .unwrap(); + font.draw_text(view, fg, self.inner_widget.name()).unwrap(); let size = self.size.ceil() as u32; let bar_offset = 4 * size; let val = self.inner_widget.value(); @@ -91,6 +95,15 @@ impl Widget for BarWidget { self.geometry } + fn minimum_size(&mut self, _fonts: &mut FontMap) -> Geometry { + Geometry { + x: 0, + y: 0, + width: self.size.ceil() as u32 * 6, + height: self.size.ceil() as u32, + } + } + fn event(&mut self, event: &Event) { match event { Event::PointerEvent(ev) => { @@ -99,9 +112,8 @@ impl Widget for BarWidget { let val = (ev.pos.0 - offset) as f32 / (self.geometry.width - offset) as f32; self.inner_widget.click(val, ev.button); } - }, + } _ => (), } } - } diff --git a/src/widgets/battery.rs b/src/widgets/battery.rs index 69cca10..53c1dc3 100644 --- a/src/widgets/battery.rs +++ b/src/widgets/battery.rs @@ -11,18 +11,33 @@ use dbus::blocking::{ use crate::{ color::Color, - fonts::FontMap, event::{Event, Events}, + fonts::FontMap, widgets::bar_widget::{BarWidget, BarWidgetImpl}, }; enum UpowerBatteryState { + Unknown, Charging, Discharging, Empty, - Full, - NotCharging, - Unknown, + FullyCharged, + PendingCharge, + PendingDischarge, +} + +impl UpowerBatteryState { + fn from_dbus(val: u64) -> UpowerBatteryState { + match val { + 1 => UpowerBatteryState::Charging, + 2 => UpowerBatteryState::Discharging, + 3 => UpowerBatteryState::Empty, + 4 => UpowerBatteryState::FullyCharged, + 5 => UpowerBatteryState::PendingCharge, + 6 => UpowerBatteryState::PendingDischarge, + _ => UpowerBatteryState::Unknown, + } + } } struct InnerBattery { @@ -53,15 +68,7 @@ fn start_monitor(inner: Arc>, events: Arc>) { { let mut inner = inner.lock().unwrap(); inner.value = capacity as f32 / 100.; - inner.state = match state { - 1 => UpowerBatteryState::Charging, - 2 => UpowerBatteryState::Discharging, - 3 => UpowerBatteryState::Empty, - 4 => UpowerBatteryState::Full, - 5 => UpowerBatteryState::NotCharging, - 6 => UpowerBatteryState::Discharging, - _ => UpowerBatteryState::Unknown, - } + inner.state = UpowerBatteryState::from_dbus(state as u64); } proxy @@ -76,15 +83,8 @@ fn start_monitor(inner: Arc>, events: Arc>) { for (key, value) in c.changed_properties { match key.as_str() { "State" => { - inner.state = match value.0.as_u64().unwrap() { - 1 => UpowerBatteryState::Charging, - 2 => UpowerBatteryState::Discharging, - 3 => UpowerBatteryState::Empty, - 4 => UpowerBatteryState::Full, - 5 => UpowerBatteryState::NotCharging, - 6 => UpowerBatteryState::Discharging, - _ => UpowerBatteryState::Unknown, - }; + inner.state = + UpowerBatteryState::from_dbus(value.0.as_u64().unwrap()); inner.dirty = true; let mut events = events.lock().unwrap(); events.add_event(Event::PowerUpdate); @@ -115,7 +115,12 @@ pub struct Battery { } impl Battery { - pub fn new(events: Arc>, fm: &mut FontMap, font: &'static str, size: f32) -> BarWidget { + pub fn new( + events: Arc>, + fm: &mut FontMap, + font: &'static str, + size: f32, + ) -> BarWidget { let battery = Battery { inner: Arc::new(Mutex::new(InnerBattery { value: 0., @@ -143,8 +148,7 @@ impl BarWidgetImpl for Battery { fn color(&self) -> Color { let inner = self.inner.lock().unwrap(); match inner.state { - UpowerBatteryState::Charging | UpowerBatteryState::Full => Color::LIGHTGREEN, - UpowerBatteryState::NotCharging => Color::LIGHTRED, + UpowerBatteryState::Charging | UpowerBatteryState::FullyCharged => Color::LIGHTGREEN, _ if inner.value > 0.25 => Color::WHITE, _ if inner.value > 0.1 => Color::DARKORANGE, _ => Color::RED, diff --git a/src/widgets/calendar.rs b/src/widgets/calendar.rs index 79b2d8f..ef9b73e 100644 --- a/src/widgets/calendar.rs +++ b/src/widgets/calendar.rs @@ -3,8 +3,8 @@ use std::default::Default; use crate::{ buffer::BufferView, color::Color, - fonts::FontMap, event::Event, + fonts::FontMap, widgets::{Geometry, Widget}, }; @@ -12,6 +12,7 @@ use chrono::{Datelike, Local, NaiveDate}; pub struct Calendar { font: &'static str, + month_font: &'static str, sections_x: i32, sections_y: i32, shown_sections_x: u32, @@ -19,22 +20,35 @@ pub struct Calendar { size: f32, dirty: bool, geometry: Geometry, + year_width: u32, + date_width: u32, + day_width: u32, } -const DAY_FACTOR: f32 = 1.; -const YEAR_FACTOR: f32 = 1.5; -const MONTH_FACTOR: f32 = 2.; -const DATE_FACTOR: f32 = 2.; -const LINE_HEIGHT: f32 = 1.8; +const DAY_FACTOR: f32 = 0.6; +const YEAR_FACTOR: f32 = 0.75; +const MONTH_FACTOR: f32 = 1.25; +const DATE_FACTOR: f32 = 1.; + +const LINE_HEIGHT: f32 = 0.85; impl Calendar { - pub fn new(fm: &mut FontMap, font: &'static str, size: f32, sections_x: i32, sections_y: i32) -> Calendar { - fm.queue_font(font, size, "ADEFHIMNORSTUW"); - fm.queue_font(font, size * 1.5, "0123456789"); - fm.queue_font(font, size * 2.0, "0123456789 ADFJMOSabcehgilmnoprstuvy"); + pub fn new( + fm: &mut FontMap, + font: &'static str, + month_font: &'static str, + size: f32, + sections_x: i32, + sections_y: i32, + ) -> Calendar { + fm.queue_font(font, size * DAY_FACTOR, "ADEFHIMNORSTUW"); + fm.queue_font(font, size * YEAR_FACTOR, "0123456789"); + fm.queue_font(font, size * DATE_FACTOR, "0123456789"); + fm.queue_font(month_font, size * MONTH_FACTOR, "ADFJMOSabcehgilmnoprstuvy"); Calendar { font, + month_font, size, sections_x, sections_y, @@ -42,6 +56,9 @@ impl Calendar { shown_sections_y: 0, dirty: true, geometry: Default::default(), + date_width: 0, + day_width: 0, + year_width: 0, } } @@ -52,39 +69,33 @@ impl Calendar { orig: NaiveDate, mut time: NaiveDate, ) { - let white = Color::WHITE; - let dim = Color::GREY80; - let dimmer = Color::GREY75; - - let mut y_off = 1; - // TODO: Fix offsets: - // - Headline font is not correctly accounted for - + let cal_width = 8 * self.date_width + 7 * (self.date_width / 2); // // Draw the week day // + let day_offset = (self.date_width - self.day_width) / 2; + let date_offset = self.date_width + self.date_width / 2; for idx in 1..8 { let day_font = fonts.get_font(self.font, self.size * DAY_FACTOR); - let wk_chr = match idx { - 1 => "MON", - 2 => "TUE", - 3 => "WED", - 4 => "THU", - 5 => "FRI", - 6 => "SAT", - 7 => "SUN", + let (wk_chr, c) = match idx { + 1 => ("MON", Color::WHITE), + 2 => ("TUE", Color::WHITE), + 3 => ("WED", Color::WHITE), + 4 => ("THU", Color::WHITE), + 5 => ("FRI", Color::WHITE), + 6 => ("SAT", Color::GREY80), + 7 => ("SUN", Color::GREY80), _ => panic!("impossible value"), }; day_font .draw_text( - &mut view - .offset(( - idx * (self.size * 2.5).ceil() as u32 + (self.size / 5.).ceil() as u32, - y_off * (self.size * LINE_HEIGHT).ceil() as u32 - + (self.size * 1.).ceil() as u32, - )), - white, + &mut view.offset(( + idx * date_offset + day_offset, + (self.size * MONTH_FACTOR).ceil() as u32 + + (self.size * DAY_FACTOR * 0.2).ceil() as u32, + )), + c, &wk_chr, ) .unwrap(); @@ -93,18 +104,7 @@ impl Calendar { // // Draw the month // - if time.year() != orig.year() { - let year_font = fonts.get_font(self.font, self.size * YEAR_FACTOR); - year_font - .draw_text( - &mut view.offset(((self.size * 17.).ceil() as u32, 0)), - dim, - &format!("{:}", time.year()), - ) - .unwrap(); - } - - let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR); + let month_font = fonts.get_font(self.month_font, self.size * MONTH_FACTOR); let month_str = match time.month() { 1 => "January", 2 => "February", @@ -120,59 +120,62 @@ impl Calendar { 12 => "December", _ => panic!("impossible value"), }; - cal_font - .draw_text(&mut view, white, month_str) + month_font + .draw_text(&mut view, Color::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 year + // + if time.year() != orig.year() { + let year_font = fonts.get_font(self.font, self.size * YEAR_FACTOR); + year_font + .draw_text( + &mut view.offset((cal_width - self.year_width, 0)), + Color::GREY80, + &format!("{:}", time.year()), + ) + .unwrap(); + } + let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR); + 'row: for y_off in 1.. { // // Draw the week number // let wk = time.iso_week(); cal_font .draw_text( - &mut view - .offset(( - 0, - y_off * (self.size * LINE_HEIGHT).ceil() as u32 - + (self.size * MONTH_FACTOR).ceil() as u32, - )), - dimmer, + &mut view.offset(( + 0, + y_off * (self.size * LINE_HEIGHT).ceil() as u32 + + (self.size * MONTH_FACTOR).ceil() as u32, + )), + Color::GREY75, &format!("{:02}", wk.week()), ) .unwrap(); - x_pos += 1; // // Draw the dates // - while x_pos < 8 { + let x_off = time.weekday().number_from_monday(); + for x_pos in x_off..8 { let c = if time.day() == orig.day() && time.month() == orig.month() { Color::WHITE - } else { + } else if x_pos < 6 { Color::GREY50 + } else { + Color::GREY35 }; cal_font .draw_text( - &mut view - .offset(( - x_pos * (self.size * 2.5).ceil() as u32, - y_off * (self.size * LINE_HEIGHT).ceil() as u32 - + (self.size * MONTH_FACTOR).ceil() as u32, - )), + &mut view.offset(( + x_pos * date_offset, + y_off * (self.size * LINE_HEIGHT).ceil() as u32 + + (self.size * MONTH_FACTOR).ceil() as u32, + )), c, &format!("{:02}", time.day()), ) @@ -180,14 +183,10 @@ impl Calendar { let t = time.with_day(time.day() + 1); if t.is_none() { - done = true; - break; + break 'row; } time = t.unwrap(); - x_pos += 1; } - - y_off += 1; } } } @@ -222,9 +221,10 @@ impl<'a> Widget for Calendar { let time = Local::now().naive_local().date(); - let cal_height = (self.size * 16.25).ceil() as u32; - let cal_width = (7. * self.size * 2.5 + self.size * 2.).ceil() as u32; - let cal_pad = (3. * self.size).ceil() as u32; + let cal_height = + (self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32; + let cal_width = 8 * self.date_width + 7 * (self.date_width / 2); + let cal_pad = self.date_width * 2; let mut t = time.with_day(1).unwrap(); let cals = self.shown_sections_y * self.shown_sections_x; @@ -236,8 +236,7 @@ impl<'a> Widget for Calendar { for idx in 0..self.shown_sections_x { self.draw_month( fonts, - &mut view - .offset(((cal_width + cal_pad) * idx, (cal_height) * ydx)), + &mut view.offset(((cal_width + cal_pad) * idx, cal_height * ydx)), time, t, ); @@ -252,35 +251,91 @@ impl<'a> Widget for Calendar { self.geometry } - fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry { - let cal_width = 7 * (self.size * 2.5).ceil() as u32 + (self.size * 2.).ceil() as u32; - let cal_height = (self.size * MONTH_FACTOR).ceil() as u32 - + (self.size * LINE_HEIGHT * 8.).ceil() as u32; - let cal_pad = (self.size * 3.).ceil() as u32; + fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry { + let year_font = fonts.get_font(self.font, self.size * YEAR_FACTOR); + self.year_width = year_font.auto_widest("0123456789").unwrap() * 4; + let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR); + self.date_width = cal_font.auto_widest("0123456789").unwrap() * 2; + let day_font = fonts.get_font(self.font, self.size * DAY_FACTOR); + self.day_width = day_font.auto_widest("ADEFHIMNORSTUW").unwrap() * 3; - let possible_sections_x = geometry.width / (cal_width + cal_pad); - let possible_sections_y = geometry.height / cal_height; + let cal_width = 8 * self.date_width + 7 * (self.date_width / 2); + let cal_height = + (self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32; + + let cal_pad = self.date_width * 2; + + if geometry.width < cal_width || geometry.height < cal_height { + self.geometry = Default::default(); + return self.geometry; + } + + let possible_sections_x = 1 + (geometry.width - cal_width) / (cal_width + cal_pad); + let possible_sections_y = 1 + (geometry.height - cal_height) / cal_height; + + self.shown_sections_x = + if self.sections_x > 0 && self.sections_x < possible_sections_x as i32 { + self.sections_x as u32 + } else { + possible_sections_x + }; + self.shown_sections_y = + if self.sections_y > 0 && self.sections_y < possible_sections_y as i32 { + self.sections_y as u32 + } else { + possible_sections_y + }; - let width = if self.sections_x > 0 && self.sections_x < possible_sections_x as i32 { - self.shown_sections_x = self.sections_x as u32; - cal_width * self.sections_x as u32 + cal_pad * (self.sections_x as u32 - 1) - } else { - self.shown_sections_x = possible_sections_x; - (geometry.width / (cal_width + cal_pad)) * (cal_width + cal_pad) - }; - let height = if self.sections_y > 0 && self.sections_y < possible_sections_y as i32 { - self.shown_sections_y = self.sections_y as u32; - cal_height * self.sections_y as u32 - } else { - self.shown_sections_y = possible_sections_y; - (geometry.height / cal_height) * cal_height - }; self.geometry = Geometry { x: geometry.x, y: geometry.y, - width: width, - height: height, + width: cal_width * self.shown_sections_x as u32 + + cal_pad * (self.shown_sections_x as u32 - 1), + height: cal_height * self.shown_sections_y as u32, }; self.geometry } + + fn minimum_size(&mut self, fonts: &mut FontMap) -> Geometry { + let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR); + let date_width = cal_font.auto_widest("0123456789").unwrap() * 2; + + let cal_width = 8 * date_width + 7 * (date_width / 2); + let cal_height = + (self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32; + let cal_pad = date_width * 2; + + let sections_x = match self.sections_x { + -1 => 1, + 0 => { + return Geometry { + x: 0, + y: 0, + width: 0, + height: 0, + } + } + x => x, + }; + let sections_y = match self.sections_y { + -1 => 1, + 0 => { + return Geometry { + x: 0, + y: 0, + width: 0, + height: 0, + } + } + y => y, + }; + let width = cal_width * sections_x as u32 + cal_pad * (sections_x as u32 - 1); + let height = cal_height * sections_y as u32; + Geometry { + x: 0, + y: 0, + width: width, + height: height, + } + } } diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index 074a151..9f932ce 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -3,8 +3,8 @@ use std::default::Default; use crate::{ buffer::BufferView, color::Color, - fonts::FontMap, event::Event, + fonts::FontMap, widgets::{Geometry, Widget}, }; @@ -81,4 +81,16 @@ impl<'a> Widget for Clock { }; self.geometry } + + fn minimum_size(&mut self, fonts: &mut FontMap) -> Geometry { + let font = fonts.get_font(self.font, self.size); + let digit_width = font.auto_widest("0123456789").unwrap(); + let colon_width = font.auto_widest(":").unwrap(); + Geometry { + x: 0, + y: 0, + width: digit_width * 4 + colon_width, + height: self.size.ceil() as u32, + } + } } diff --git a/src/widgets/date.rs b/src/widgets/date.rs index ee89797..2dce5c9 100644 --- a/src/widgets/date.rs +++ b/src/widgets/date.rs @@ -3,8 +3,8 @@ use std::default::Default; use crate::{ buffer::BufferView, color::Color, - fonts::FontMap, event::Event, + fonts::FontMap, widgets::{Geometry, Widget}, }; @@ -78,4 +78,15 @@ impl<'a> Widget for Date { }; self.geometry } + + fn minimum_size(&mut self, fonts: &mut FontMap) -> Geometry { + let width = (3. + 2. + 2. + 2. + 4.) * self.size / 2.; + let font = fonts.get_font(self.font, self.size); + Geometry { + x: 0, + y: 0, + width: width.ceil() as u32, + height: font.height().ceil() as u32, + } + } } diff --git a/src/widgets/launcher.rs b/src/widgets/launcher.rs index 19983b7..4df5af8 100644 --- a/src/widgets/launcher.rs +++ b/src/widgets/launcher.rs @@ -1,10 +1,10 @@ use std::{ cmp::{max, min, Ordering}, collections::HashMap, - env, - io::Write, - fs::{read_to_string, File}, default::Default, + env, + fs::{read_to_string, File}, + io::Write, process::{exit, Command}, sync::{Arc, Mutex}, thread, @@ -13,9 +13,9 @@ use std::{ use crate::{ buffer::BufferView, color::Color, + event::{Event, Events, PointerButton, PointerEvent}, fonts::FontMap, keyboard::{keysyms, KeyEvent}, - event::{Event, Events, PointerEvent, PointerButton}, utils::desktop::{load_desktop_files, Desktop}, widgets::{Geometry, Widget}, }; @@ -312,8 +312,7 @@ impl InterfaceWidget for Launcher { ) .unwrap(); - font.auto_draw_text(&mut prompt_line, fg, ">") - .unwrap(); + font.auto_draw_text(&mut prompt_line, fg, ">").unwrap(); // Draw entries let dimfg = Color::GREY50; @@ -348,8 +347,7 @@ impl InterfaceWidget for Launcher { } else { x_max = max( x_max, - font.auto_draw_text(&mut line, dimfg, &m.name) - .unwrap(), + font.auto_draw_text(&mut line, dimfg, &m.name).unwrap(), ); } } @@ -452,10 +450,7 @@ impl Calc { }) .unwrap(); - Calc{ - old, - result: None, - } + Calc { old, result: None } } fn sync(&self) { @@ -521,18 +516,18 @@ impl InterfaceWidget for Calc { // Draw prompt let font = fonts.get_font(intf.font, intf.size); - font - .auto_draw_text_with_cursor( - &mut prompt_line, - fg, - &format!(" {} ", &intf.prompt.input), - intf.prompt.cursor + 3, - ) - .unwrap(); + font.auto_draw_text_with_cursor( + &mut prompt_line, + fg, + &format!(" {} ", &intf.prompt.input), + intf.prompt.cursor + 3, + ) + .unwrap(); font.auto_draw_text(&mut prompt_line, Color::BUFF, "=") .unwrap(); + prompt_offset -= 16; prompt_offset -= line_height; if let Some(res) = &self.result { let c = if intf.selection == 0 { @@ -555,7 +550,8 @@ impl InterfaceWidget for Calc { }; prompt_offset -= line_height; let mut result_line = view.offset((0, prompt_offset)); - font.auto_draw_text(&mut result_line, c, &format!("{} = {}", input, res)).unwrap(); + font.auto_draw_text(&mut result_line, c, &format!("{} = {}", input, res)) + .unwrap(); } let content_height = min( @@ -588,9 +584,15 @@ pub struct Interface { } impl Interface { - pub fn new(events: Arc>, fm: &mut FontMap, font: &'static str, size: f32) -> Interface { + pub fn new( + events: Arc>, + fm: &mut FontMap, + font: &'static str, + size: f32, + ) -> Interface { fm.queue_font( - font, size, + font, + size, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 >=!,.-/()", ); @@ -708,6 +710,15 @@ impl Widget for Interface { self.inner.geometry } + fn minimum_size(&mut self, _fonts: &mut FontMap) -> Geometry { + Geometry { + x: 0, + y: 0, + width: 256, + height: (self.inner.size.ceil() as u32) * 4 + 16, + } + } + fn event(&mut self, event: &Event) { match event { Event::KeyEvent(e) => self.keyboard_input(e), @@ -715,7 +726,7 @@ impl Widget for Interface { Event::LauncherUpdate => { self.inner.dirty = true; self.launcher.update(&self.inner); - }, + } Event::TokenUpdate(t) => self.launcher.next_token = Some(t.to_string()), _ => (), } diff --git a/src/widgets/layout.rs b/src/widgets/layout.rs index c952a97..fb958ed 100644 --- a/src/widgets/layout.rs +++ b/src/widgets/layout.rs @@ -1,10 +1,6 @@ use std::cmp::max; -use crate::{ - fonts::FontMap, - state::State, - widgets::{Geometry}, -}; +use crate::{fonts::FontMap, state::State, widgets::Geometry}; pub trait Layout { fn geometry_update( @@ -13,6 +9,8 @@ pub trait Layout { geometry: &Geometry, state: &mut State, ) -> Geometry; + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry; } pub struct HorizontalLayout { @@ -46,6 +44,22 @@ impl Layout for HorizontalLayout { geo.height = max_height; geo } + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry { + let mut max_width = 0; + let mut max_height = 0; + for w in self.widgets.iter() { + let result = w.minimum_size(fonts, state); + max_width += result.x + result.width; + max_height = max(result.y + result.height, max_height); + } + Geometry { + width: max_width, + height: max_height, + x: 0, + y: 0, + } + } } pub struct VerticalLayout { @@ -79,11 +93,28 @@ impl Layout for VerticalLayout { geo.height = max_height; geo } + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry { + let mut max_width = 0; + let mut max_height = 0; + for w in self.widgets.iter() { + let result = w.minimum_size(fonts, state); + max_width = max(result.x + result.width, max_width); + max_height += result.y + result.height; + } + Geometry { + width: max_width, + height: max_height, + x: 0, + y: 0, + } + } } pub trait WidgetUpdater { fn geometry_update(&mut self, idx: usize, fonts: &mut FontMap, geometry: &Geometry) -> Geometry; + fn minimum_size(&mut self, idx: usize, fonts: &mut FontMap) -> Geometry; } pub struct IndexedLayout { @@ -105,6 +136,10 @@ impl Layout for IndexedLayout { ) -> Geometry { state.geometry_update(self.widget_idx, fonts, geometry) } + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry { + state.minimum_size(self.widget_idx, fonts) + } } pub struct Margin { @@ -139,6 +174,16 @@ impl Layout for Margin { height: out.height + self.margin.1 + self.margin.3, } } + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry { + let size = self.widget.minimum_size(fonts, state); + Geometry { + width: size.x + size.width + self.margin.0 + self.margin.2, + height: size.y + size.height + self.margin.1 + self.margin.3, + x: 0, + y: 0, + } + } } pub struct InvertedHorizontalLayout { @@ -171,4 +216,20 @@ impl Layout for InvertedHorizontalLayout { geo.height = max_height; geo } + + fn minimum_size(&self, fonts: &mut FontMap, state: &mut State) -> Geometry { + let mut max_width = 0; + let mut max_height = 0; + for w in self.widgets.iter() { + let result = w.minimum_size(fonts, state); + max_width += result.x + result.width; + max_height = max(result.y + result.height, max_height); + } + Geometry { + width: max_width, + height: max_height, + x: 0, + y: 0, + } + } } diff --git a/src/widgets/line.rs b/src/widgets/line.rs index faa9750..354752b 100644 --- a/src/widgets/line.rs +++ b/src/widgets/line.rs @@ -14,7 +14,7 @@ pub struct Line { impl Line { pub fn new(thickness: u32) -> Line { - Line{ + Line { geometry: Default::default(), thickness, } @@ -25,10 +25,12 @@ impl Widget for Line { fn geometry(&self) -> Geometry { self.geometry } + fn draw(&mut self, _fonts: &mut FontMap, view: &mut BufferView) -> Geometry { view.memset(Color::WHITE); self.geometry } + fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry { self.geometry = Geometry { x: geometry.x, @@ -38,4 +40,13 @@ impl Widget for Line { }; self.geometry } + + fn minimum_size(&mut self, _fonts: &mut FontMap) -> Geometry { + Geometry { + x: 0, + y: 0, + width: 0, + height: self.thickness, + } + } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 6f51598..4b7dd68 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,5 +1,4 @@ //mod alsaaudio; -mod pulseaudio; mod backlight; mod bar_widget; mod battery; @@ -7,18 +6,22 @@ mod calendar; mod clock; mod date; mod launcher; -mod line; -mod widget; mod layout; +mod line; +mod pulseaudio; +mod widget; //pub use alsaaudio::AlsaAudio; -pub use pulseaudio::PulseAudio; pub use backlight::Backlight; pub use battery::Battery; pub use calendar::Calendar; pub use clock::Clock; pub use date::Date; pub use launcher::Interface; +pub use layout::{ + HorizontalLayout, IndexedLayout, InvertedHorizontalLayout, Layout, Margin, VerticalLayout, + WidgetUpdater, +}; pub use line::Line; -pub use layout::{VerticalLayout, HorizontalLayout, IndexedLayout, InvertedHorizontalLayout, Layout, Margin, WidgetUpdater}; +pub use pulseaudio::PulseAudio; pub use widget::{Geometry, Widget}; diff --git a/src/widgets/pulseaudio.rs b/src/widgets/pulseaudio.rs index b59f952..0b03e44 100644 --- a/src/widgets/pulseaudio.rs +++ b/src/widgets/pulseaudio.rs @@ -1,16 +1,13 @@ - use std::{ + cell::RefCell, + error::Error, + os::fd::RawFd, + rc::Rc, sync::{Arc, Mutex}, thread, - error::Error, - rc::Rc, - cell::RefCell, - os::fd::RawFd, }; -use nix::unistd::{ - pipe, write, read, -}; +use nix::unistd::{pipe, read, write}; use crate::{ color::Color, @@ -21,22 +18,19 @@ use crate::{ use libpulse_binding::volume::ChannelVolumes; -fn monitor(events: Arc>, inner: Arc>, pipe: RawFd) -> Result<(), Box> { +fn monitor( + events: Arc>, + inner: Arc>, + pipe: RawFd, +) -> Result<(), Box> { use libpulse_binding::{ - mainloop::{ - self, - standard::{ - IterateResult, - }, - api::Mainloop, - events::io::FlagSet as IoFlagSet, - }, callbacks::ListResult, context::{ - Context, - FlagSet, - subscribe::{InterestMaskSet, Facility}, - State, + subscribe::{Facility, InterestMaskSet}, + Context, FlagSet, State, + }, + mainloop::{ + self, api::Mainloop, events::io::FlagSet as IoFlagSet, standard::IterateResult, }, proplist::{properties, Proplist}, volume::Volume, @@ -48,7 +42,9 @@ fn monitor(events: Arc>, inner: Arc>, pipe: RawF .unwrap(); let mut mainloop = mainloop::standard::Mainloop::new().unwrap(); - let context = Rc::new(RefCell::new(Context::new_with_proplist(&mainloop, "wldash_context", &proplist).unwrap())); + let context = Rc::new(RefCell::new( + Context::new_with_proplist(&mainloop, "wldash_context", &proplist).unwrap(), + )); context.borrow_mut().connect(None, FlagSet::NOFLAGS, None)?; loop { @@ -63,33 +59,31 @@ fn monitor(events: Arc>, inner: Arc>, pipe: RawF let local_context = context.clone(); let local_inner = inner.clone(); let local_events = events.clone(); - context - .borrow_mut() - .set_subscribe_callback(Some(Box::new(move |facility, _operation, index| { - match facility { - Some(Facility::Sink) => { - let introspector = local_context.borrow_mut().introspect(); - let inner = local_inner.clone(); - let events = local_events.clone(); - introspector.get_sink_info_by_index(index, move |res| { - match res { - ListResult::End | ListResult::Error => (), - ListResult::Item(sink_info) => if let Some(_) = &sink_info.name { - let avg = sink_info.volume.avg().0 as f32 / Volume::NORMAL.0 as f32; - let mut inner = inner.lock().unwrap(); - inner.volume = avg; - inner.mute = sink_info.mute; - inner.dirty = true; - drop(inner); - let mut events = events.lock().unwrap(); - events.add_event(Event::AudioUpdate); - }, + context.borrow_mut().set_subscribe_callback(Some(Box::new( + move |facility, _operation, index| match facility { + Some(Facility::Sink) => { + let introspector = local_context.borrow_mut().introspect(); + let inner = local_inner.clone(); + let events = local_events.clone(); + introspector.get_sink_info_by_index(index, move |res| match res { + ListResult::End | ListResult::Error => (), + ListResult::Item(sink_info) => { + if let Some(_) = &sink_info.name { + let avg = sink_info.volume.avg().0 as f32 / Volume::NORMAL.0 as f32; + let mut inner = inner.lock().unwrap(); + inner.volume = avg; + inner.mute = sink_info.mute; + inner.dirty = true; + drop(inner); + let mut events = events.lock().unwrap(); + events.add_event(Event::AudioUpdate); } - }); - }, - _ => (), + } + }); } - }))); + _ => (), + }, + ))); context .borrow_mut() @@ -97,10 +91,10 @@ fn monitor(events: Arc>, inner: Arc>, pipe: RawF let introspector = context.borrow_mut().introspect(); let local_inner = inner.clone(); - introspector.get_sink_info_by_name("@DEFAULT_SINK@", move |res| { - match res { - ListResult::End | ListResult::Error => (), - ListResult::Item(sink_info) => if let Some(_) = &sink_info.name { + introspector.get_sink_info_by_name("@DEFAULT_SINK@", move |res| match res { + ListResult::End | ListResult::Error => (), + ListResult::Item(sink_info) => { + if let Some(_) = &sink_info.name { let avg = sink_info.volume.avg().0 as f32 / Volume::NORMAL.0 as f32; let mut inner = local_inner.lock().unwrap(); inner.volume = avg; @@ -110,46 +104,50 @@ fn monitor(events: Arc>, inner: Arc>, pipe: RawF drop(inner); let mut events = events.lock().unwrap(); events.add_event(Event::AudioUpdate); - }, + } } }); drop(introspector); let local_context = context.clone(); let local_inner = inner.clone(); - let event_source = mainloop.new_io_event(pipe as i32, IoFlagSet::INPUT, Box::new(move |_, _, _| { - let mut buf = [0; 8]; - _ = read(pipe, &mut buf); + let event_source = mainloop.new_io_event( + pipe as i32, + IoFlagSet::INPUT, + Box::new(move |_, _, _| { + let mut buf = [0; 8]; + _ = read(pipe, &mut buf); - let mut inner = local_inner.lock().unwrap(); - let mut introspector = local_context.borrow_mut().introspect(); - match inner.last_click.take() { - Some(Change::Volume(volume)) => { - // do something - let mut vol = inner.channel_volume.unwrap(); - vol.scale(Volume((volume * Volume::NORMAL.0 as f32).round() as u32)); - introspector.set_sink_volume_by_name("@DEFAULT_SINK@", &vol, None); - }, - Some(Change::VolumeInc(value)) => { - let mut vol = inner.channel_volume.unwrap(); - // apply step to volumes - let step = (value * Volume::NORMAL.0 as f32).round() as i32; - if step > 0 { - vol.inc_clamp(Volume(step as u32), Volume::NORMAL); - } else { - vol.decrease(Volume(-step as u32)); + let mut inner = local_inner.lock().unwrap(); + let mut introspector = local_context.borrow_mut().introspect(); + match inner.last_click.take() { + Some(Change::Volume(volume)) => { + // do something + let mut vol = inner.channel_volume.unwrap(); + vol.scale(Volume((volume * Volume::NORMAL.0 as f32).round() as u32)); + introspector.set_sink_volume_by_name("@DEFAULT_SINK@", &vol, None); } - // HACK: It would be better to only update the volume from feedback, but axis - // events fire fast and require the last set volume immediately for reference. - inner.channel_volume = Some(vol); - introspector.set_sink_volume_by_name("@DEFAULT_SINK@", &vol, None); - }, - Some(Change::ToggleMute) => { - introspector.set_sink_mute_by_name("@DEFAULT_SINK@", !inner.mute, None); - }, - None => (), - } - })); + Some(Change::VolumeInc(value)) => { + let mut vol = inner.channel_volume.unwrap(); + // apply step to volumes + let step = (value * Volume::NORMAL.0 as f32).round() as i32; + if step > 0 { + vol.inc_clamp(Volume(step as u32), Volume::NORMAL); + } else { + vol.decrease(Volume(-step as u32)); + } + // HACK: It would be better to only update the volume from feedback, but axis + // events fire fast and require the last set volume immediately for reference. + inner.channel_volume = Some(vol); + introspector.set_sink_volume_by_name("@DEFAULT_SINK@", &vol, None); + } + Some(Change::ToggleMute) => { + introspector.set_sink_mute_by_name("@DEFAULT_SINK@", !inner.mute, None); + } + None => (), + } + }), + ); loop { match mainloop.iterate(true) { @@ -167,7 +165,7 @@ fn monitor(events: Arc>, inner: Arc>, pipe: RawF fn start_monitor(events: Arc>, inner: Arc>, pipe: RawFd) { thread::Builder::new() .name("audiomon".to_string()) - .spawn(move || { monitor(events, inner, pipe).unwrap() }) + .spawn(move || monitor(events, inner, pipe).unwrap()) .unwrap(); } @@ -191,10 +189,15 @@ pub struct PulseAudio { } impl PulseAudio { - pub fn new(events: Arc>, fm: &mut FontMap, font: &'static str, size: f32) -> BarWidget { + pub fn new( + events: Arc>, + fm: &mut FontMap, + font: &'static str, + size: f32, + ) -> BarWidget { let (a, b) = pipe().unwrap(); let audio = PulseAudio { - inner: Arc::new(Mutex::new(InnerAudio{ + inner: Arc::new(Mutex::new(InnerAudio { volume: 0.0, channel_volume: None, mute: false, @@ -229,7 +232,7 @@ impl BarWidgetImpl for PulseAudio { fn click(&mut self, pos: f32, btn: PointerButton) { let mut inner = self.inner.lock().unwrap(); inner.last_click = match btn { - PointerButton::Left => Some(Change::Volume(pos)), + PointerButton::Left => Some(Change::Volume(pos)), PointerButton::Right => Some(Change::ToggleMute), PointerButton::ScrollVertical(val) => Some(Change::VolumeInc((val / 512.) as f32)), PointerButton::ScrollHorizontal(val) => Some(Change::VolumeInc((val / 100.) as f32)), diff --git a/src/widgets/widget.rs b/src/widgets/widget.rs index b097854..69341cb 100644 --- a/src/widgets/widget.rs +++ b/src/widgets/widget.rs @@ -3,11 +3,7 @@ use std::{ fmt, }; -use crate::{ - buffer::BufferView, - fonts::FontMap, - event::Event, -}; +use crate::{buffer::BufferView, event::Event, fonts::FontMap}; #[derive(Debug, Default, Clone, Copy)] pub struct Geometry { @@ -49,8 +45,10 @@ impl Geometry { } pub fn contains(&self, position: (u32, u32)) -> bool { - position.0 >= self.x && position.0 < self.x + self.width && - position.1 >= self.y && position.1 < self.y + self.height + position.0 >= self.x + && position.0 < self.x + self.width + && position.1 >= self.y + && position.1 < self.y + self.height } } @@ -60,7 +58,9 @@ pub trait Widget { } fn geometry(&self) -> Geometry; fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry; + fn minimum_size(&mut self, _fonts: &mut FontMap) -> Geometry { + Default::default() + } fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry; fn event(&mut self, _event: &Event) {} } -