diff --git a/src/fonts.rs b/src/fonts.rs index 7171a16..49705d3 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -4,6 +4,10 @@ use crate::draw; use fontconfig::Fontconfig as FontConfig; use rusttype::Font; use std::{ + cell::RefCell, + mem, + rc::Rc, + thread, collections::HashMap, fs::File, hash, @@ -11,6 +15,7 @@ use std::{ path::{Path, PathBuf}, }; + /// FontRef is used to store Fonts on widgets. pub type FontRef<'a> = &'a rusttype::Font<'a>; @@ -126,3 +131,31 @@ impl FontMap { .expect("no font at specified size") } } + +pub enum MaybeFontMap { + Waiting(thread::JoinHandle), + Ready(Rc>), + Invalid, +} + +impl MaybeFontMap { + pub fn unwrap(&self) -> Rc> { + match self { + MaybeFontMap::Ready(f) => f.clone(), + _ => panic!("fontmap not yet ready"), + } + } + + pub fn resolve(&mut self) { + if matches!(self, MaybeFontMap::Waiting(_)) { + let s = mem::replace(self, MaybeFontMap::Invalid); + match s { + MaybeFontMap::Waiting(handle) => { + *self = MaybeFontMap::Ready(Rc::new(RefCell::new(handle.join().unwrap()))); + } + _ => unreachable!(), + } + } + } +} + diff --git a/src/main.rs b/src/main.rs index b33d0a5..a99243e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,19 +5,9 @@ mod fonts; mod keyboard; mod utils; mod widgets; +mod state; -use wayland_client::{ - protocol::{ - wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, - wl_surface, - }, - Connection, Dispatch, QueueHandle, WEnum, WaylandSource, -}; - -use wayland_protocols::xdg::{ - activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1}, - shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}, -}; +use wayland_client::{Connection, QueueHandle, WaylandSource,}; use calloop::{ timer::{TimeoutAction, Timer}, @@ -27,84 +17,14 @@ use chrono::{Duration, Local, Timelike}; use buffer::{BufferManager, BufferView}; use color::Color; -use fonts::FontMap; +use fonts::{MaybeFontMap, FontMap}; use keyboard::Keyboard; use widgets::{ - Backlight, Battery, Clock, Date, Geometry, HorizontalLayout, IndexedLayout, Interface, Layout, - Line, Margin, VerticalLayout, Widget, WidgetUpdater, + Backlight, Battery, Clock, Date, Geometry, Interface, Line, WidgetUpdater, }; +use state::State; -use std::{cell::RefCell, env, mem, process::exit, rc::Rc, thread}; - -enum MaybeFontMap { - Waiting(thread::JoinHandle), - Ready(Rc>), - Invalid, -} - -impl MaybeFontMap { - fn unwrap(&self) -> Rc> { - match self { - MaybeFontMap::Ready(f) => f.clone(), - _ => panic!("fontmap not yet ready"), - } - } - - fn resolve(&mut self) { - if matches!(self, MaybeFontMap::Waiting(_)) { - let s = mem::replace(self, MaybeFontMap::Invalid); - match s { - MaybeFontMap::Waiting(handle) => { - *self = MaybeFontMap::Ready(Rc::new(RefCell::new(handle.join().unwrap()))); - } - _ => unreachable!(), - } - } - } -} - -struct State { - running: bool, - dirty: bool, - activated: bool, - base_surface: Option, - wm_base: Option, - xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>, - wl_shm: Option, - xdg_activation: Option, - configured: bool, - dimensions: (i32, i32), - bufmgr: BufferManager, - widgets: Vec>, - keyboard: Keyboard, - fonts: MaybeFontMap, -} - -impl State { - fn add_buffer(&mut self, qh: &QueueHandle) { - self.bufmgr.add_buffer( - self.wl_shm.as_ref().expect("missing wl_shm"), - self.dimensions, - qh, - ); - } - - fn activate(&mut self, qh: &QueueHandle) { - let key = match env::var("XDG_ACTIVATION_TOKEN") { - Ok(token) => token, - Err(_) => return, - }; - match (&self.base_surface, &self.xdg_activation) { - (Some(surface), Some(activation)) => { - activation.activate(key, surface); - let activation_token = activation.get_activation_token(qh, ()); - activation_token.commit(); - env::remove_var("XDG_ACTIVATION_TOKEN"); - } - _ => (), - } - } -} +use std::thread; impl WidgetUpdater for State { fn geometry_update( @@ -121,8 +41,8 @@ fn main() { let font_thread = thread::spawn(move || { let mut fm = FontMap::new(); // Hard-coding font paths make things a lot faster - fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 96.); - fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 40.); + fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 128.); + fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 48.); fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 24.); fm.queue_font_path( "monospace", @@ -146,12 +66,12 @@ fn main() { let (ping_sender, ping_source) = calloop::ping::make_ping().unwrap(); - let clock = Box::new(Clock::new()); - let date = Box::new(Date::new()); + let clock = Box::new(Clock::new("sans", 128.)); + let date = Box::new(Date::new("sans", 48.)); let line = Box::new(Line::new()); - let launcher = Box::new(Interface::new()); - let battery = Box::new(Battery::new(ping_sender)); - let backlight = Box::new(Backlight::new("intel_backlight")); + let launcher = Box::new(Interface::new("monospace", 32.)); + let battery = Box::new(Battery::new(ping_sender, "sans", 24.)); + let backlight = Box::new(Backlight::new("intel_backlight", "sans", 24.)); let mut event_loop: EventLoop = EventLoop::try_new().expect("Failed to initialize the event loop!"); @@ -288,345 +208,3 @@ fn main() { } } -impl Dispatch for State { - fn event( - state: &mut Self, - registry: &wl_registry::WlRegistry, - event: wl_registry::Event, - _: &(), - _: &Connection, - qh: &QueueHandle, - ) { - if let wl_registry::Event::Global { - name, interface, .. - } = event - { - match &interface[..] { - "wl_compositor" => { - let compositor = - registry.bind::(name, 4, qh, ()); - let surface = compositor.create_surface(qh, ()); - state.base_surface = Some(surface); - state.activate(qh); - - if state.wm_base.is_some() && state.xdg_surface.is_none() { - state.init_xdg_surface(qh); - } - } - "wl_shm" => { - state.wl_shm = Some(registry.bind::(name, 1, qh, ())); - } - "wl_seat" => { - registry.bind::(name, 1, qh, ()); - } - "xdg_wm_base" => { - let wm_base = registry.bind::(name, 1, qh, ()); - state.wm_base = Some(wm_base); - - if state.base_surface.is_some() && state.xdg_surface.is_none() { - state.init_xdg_surface(qh); - } - } - "xdg_activation_v1" => { - state.xdg_activation = Some( - registry.bind::(name, 1, qh, ()), - ); - state.activate(qh); - } - _ => {} - } - } - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - _: &wl_compositor::WlCompositor, - _: wl_compositor::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - _: &wl_surface::WlSurface, - _: wl_surface::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - _: &wl_shm::WlShm, - _: wl_shm::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - _: &wl_shm_pool::WlShmPool, - _: wl_shm_pool::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - } -} - -impl Dispatch for State { - fn event( - state: &mut Self, - buffer: &wl_buffer::WlBuffer, - _: wl_buffer::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - for buf in state.bufmgr.buffers.iter_mut() { - if &buf.buffer == buffer { - buf.release(); - } - } - } -} - -impl State { - fn init_xdg_surface(&mut self, qh: &QueueHandle) { - let wm_base = self.wm_base.as_ref().unwrap(); - let base_surface = self.base_surface.as_ref().unwrap(); - - let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ()); - let toplevel = xdg_surface.get_toplevel(qh, ()); - toplevel.set_title("wldash".into()); - - base_surface.commit(); - - self.xdg_surface = Some((xdg_surface, toplevel)); - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - wm_base: &xdg_wm_base::XdgWmBase, - event: xdg_wm_base::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - if let xdg_wm_base::Event::Ping { serial } = event { - wm_base.pong(serial); - } - } -} - -impl Dispatch for State { - fn event( - state: &mut Self, - xdg_surface: &xdg_surface::XdgSurface, - event: xdg_surface::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - if let xdg_surface::Event::Configure { serial, .. } = event { - xdg_surface.ack_configure(serial); - state.configured = true; - state.dirty = true; - } - } -} - -impl Dispatch for State { - fn event( - state: &mut Self, - _: &xdg_toplevel::XdgToplevel, - event: xdg_toplevel::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - match event { - xdg_toplevel::Event::Configure { - width, - height, - states, - .. - } => { - if (width, height) == (0, 0) { - return; - } - let activated = states - .iter() - .find(|&st| *st as u32 == xdg_toplevel::State::Activated as u32) - .is_some(); - - if activated { - state.activated = true; - } else if state.activated && !activated { - exit(0); - } - - if state.dimensions != (width, height) { - state.bufmgr.clear_buffers(); - 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(); - let fonts = state.fonts.unwrap(); - layout.geometry_update( - &mut fonts.borrow_mut(), - &Geometry { - x: 0, - y: 0, - width: width as u32, - height: height as u32, - }, - state, - ); - } - } - xdg_toplevel::Event::Close => { - state.running = false; - } - _ => (), - } - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - seat: &wl_seat::WlSeat, - event: wl_seat::Event, - _: &(), - _: &Connection, - qh: &QueueHandle, - ) { - if let wl_seat::Event::Capabilities { - capabilities: WEnum::Value(capabilities), - } = event - { - if capabilities.contains(wl_seat::Capability::Keyboard) { - seat.get_keyboard(qh, ()); - } - } - } -} - -impl Dispatch for State { - fn event( - state: &mut Self, - _: &wl_keyboard::WlKeyboard, - event: wl_keyboard::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - match event { - wl_keyboard::Event::Key { - serial, - time, - key, - state: kbstate, - } => { - if key == 1 { - state.running = false; - return; - } - let k = state.keyboard.key(serial, time, key, kbstate); - for widget in state.widgets.iter_mut() { - widget.keyboard_input(&k); - } - state.dirty = true; - } - wl_keyboard::Event::Modifiers { - mods_depressed, - mods_latched, - mods_locked, - group, - .. - } => { - state - .keyboard - .modifiers(mods_depressed, mods_latched, mods_locked, group); - } - wl_keyboard::Event::Keymap { format, fd, size } => { - state.keyboard.keymap(format, fd, size); - } - _ => (), - } - } -} - -impl Dispatch for State { - fn event( - _: &mut Self, - _: &xdg_activation_v1::XdgActivationV1, - _: xdg_activation_v1::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - } -} - -impl Dispatch for State { - fn event( - state: &mut Self, - _: &xdg_activation_token_v1::XdgActivationTokenV1, - event: xdg_activation_token_v1::Event, - _: &(), - _: &Connection, - _: &QueueHandle, - ) { - match event { - xdg_activation_token_v1::Event::Done { token } => { - for widget in state.widgets.iter_mut() { - widget.token_update(&token); - } - } - _ => (), - } - } -} diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..d22d1e9 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,412 @@ +use std::{ + env, + process::exit, +}; + +use wayland_client::{ + protocol::{ + wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, + wl_surface, + }, + Connection, Dispatch, QueueHandle, WEnum, +}; + +use wayland_protocols::xdg::{ + activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1}, + shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}, +}; + +use crate::{ + buffer::BufferManager, + fonts::MaybeFontMap, + keyboard::Keyboard, + widgets::{ + Geometry, HorizontalLayout, IndexedLayout, Layout, Margin, VerticalLayout, Widget, + }, +}; + +pub struct State { + pub running: bool, + pub dirty: bool, + pub activated: bool, + pub base_surface: Option, + pub wm_base: Option, + pub xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>, + pub wl_shm: Option, + pub xdg_activation: Option, + pub configured: bool, + pub dimensions: (i32, i32), + pub bufmgr: BufferManager, + pub widgets: Vec>, + pub keyboard: Keyboard, + pub fonts: MaybeFontMap, +} + +impl State { + pub fn add_buffer(&mut self, qh: &QueueHandle) { + self.bufmgr.add_buffer( + self.wl_shm.as_ref().expect("missing wl_shm"), + self.dimensions, + qh, + ); + } + + pub fn activate(&mut self, qh: &QueueHandle) { + let key = match env::var("XDG_ACTIVATION_TOKEN") { + Ok(token) => token, + Err(_) => return, + }; + match (&self.base_surface, &self.xdg_activation) { + (Some(surface), Some(activation)) => { + activation.activate(key, surface); + let activation_token = activation.get_activation_token(qh, ()); + activation_token.commit(); + env::remove_var("XDG_ACTIVATION_TOKEN"); + } + _ => (), + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + registry: &wl_registry::WlRegistry, + event: wl_registry::Event, + _: &(), + _: &Connection, + qh: &QueueHandle, + ) { + if let wl_registry::Event::Global { + name, interface, .. + } = event + { + match &interface[..] { + "wl_compositor" => { + let compositor = + registry.bind::(name, 4, qh, ()); + let surface = compositor.create_surface(qh, ()); + state.base_surface = Some(surface); + state.activate(qh); + + if state.wm_base.is_some() && state.xdg_surface.is_none() { + state.init_xdg_surface(qh); + } + } + "wl_shm" => { + state.wl_shm = Some(registry.bind::(name, 1, qh, ())); + } + "wl_seat" => { + registry.bind::(name, 1, qh, ()); + } + "xdg_wm_base" => { + let wm_base = registry.bind::(name, 1, qh, ()); + state.wm_base = Some(wm_base); + + if state.base_surface.is_some() && state.xdg_surface.is_none() { + state.init_xdg_surface(qh); + } + } + "xdg_activation_v1" => { + state.xdg_activation = Some( + registry.bind::(name, 1, qh, ()), + ); + state.activate(qh); + } + _ => {} + } + } + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + _: &wl_compositor::WlCompositor, + _: wl_compositor::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + _: &wl_surface::WlSurface, + _: wl_surface::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + _: &wl_shm::WlShm, + _: wl_shm::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + _: &wl_shm_pool::WlShmPool, + _: wl_shm_pool::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + buffer: &wl_buffer::WlBuffer, + _: wl_buffer::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + for buf in state.bufmgr.buffers.iter_mut() { + if &buf.buffer == buffer { + buf.release(); + } + } + } +} + +impl State { + fn init_xdg_surface(&mut self, qh: &QueueHandle) { + let wm_base = self.wm_base.as_ref().unwrap(); + let base_surface = self.base_surface.as_ref().unwrap(); + + let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ()); + let toplevel = xdg_surface.get_toplevel(qh, ()); + toplevel.set_title("wldash".into()); + + base_surface.commit(); + + self.xdg_surface = Some((xdg_surface, toplevel)); + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + wm_base: &xdg_wm_base::XdgWmBase, + event: xdg_wm_base::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_wm_base::Event::Ping { serial } = event { + wm_base.pong(serial); + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + xdg_surface: &xdg_surface::XdgSurface, + event: xdg_surface::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let xdg_surface::Event::Configure { serial, .. } = event { + xdg_surface.ack_configure(serial); + state.configured = true; + state.dirty = true; + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &xdg_toplevel::XdgToplevel, + event: xdg_toplevel::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + match event { + xdg_toplevel::Event::Configure { + width, + height, + states, + .. + } => { + if (width, height) == (0, 0) { + return; + } + let activated = states + .iter() + .find(|&st| *st as u32 == xdg_toplevel::State::Activated as u32) + .is_some(); + + if activated { + state.activated = true; + } else if state.activated && !activated { + exit(0); + } + + if state.dimensions != (width, height) { + state.bufmgr.clear_buffers(); + 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(); + let fonts = state.fonts.unwrap(); + layout.geometry_update( + &mut fonts.borrow_mut(), + &Geometry { + x: 0, + y: 0, + width: width as u32, + height: height as u32, + }, + state, + ); + } + } + xdg_toplevel::Event::Close => { + state.running = false; + } + _ => (), + } + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + seat: &wl_seat::WlSeat, + event: wl_seat::Event, + _: &(), + _: &Connection, + qh: &QueueHandle, + ) { + if let wl_seat::Event::Capabilities { + capabilities: WEnum::Value(capabilities), + } = event + { + if capabilities.contains(wl_seat::Capability::Keyboard) { + seat.get_keyboard(qh, ()); + } + } + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &wl_keyboard::WlKeyboard, + event: wl_keyboard::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + match event { + wl_keyboard::Event::Key { + serial, + time, + key, + state: kbstate, + } => { + if key == 1 { + state.running = false; + return; + } + let k = state.keyboard.key(serial, time, key, kbstate); + for widget in state.widgets.iter_mut() { + widget.keyboard_input(&k); + } + state.dirty = true; + } + wl_keyboard::Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => { + state + .keyboard + .modifiers(mods_depressed, mods_latched, mods_locked, group); + } + wl_keyboard::Event::Keymap { format, fd, size } => { + state.keyboard.keymap(format, fd, size); + } + _ => (), + } + } +} + +impl Dispatch for State { + fn event( + _: &mut Self, + _: &xdg_activation_v1::XdgActivationV1, + _: xdg_activation_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &xdg_activation_token_v1::XdgActivationTokenV1, + event: xdg_activation_token_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + match event { + xdg_activation_token_v1::Event::Done { token } => { + for widget in state.widgets.iter_mut() { + widget.token_update(&token); + } + } + _ => (), + } + } +} diff --git a/src/widgets/backlight.rs b/src/widgets/backlight.rs index b046d6b..02064a9 100644 --- a/src/widgets/backlight.rs +++ b/src/widgets/backlight.rs @@ -29,7 +29,7 @@ impl Backlight { self.max = read_file_as_u64(self.device_path.join("max_brightness").as_path()).unwrap(); } - pub fn new(path: &str) -> BarWidget { + pub fn new(path: &str, font: &'static str, size: f32) -> BarWidget { let mut dev = Backlight { device_path: Path::new("/sys/class/backlight").to_path_buf().join(path), cur: 0, @@ -38,7 +38,7 @@ impl Backlight { }; dev.update(); - BarWidget::new(Box::new(dev)) + BarWidget::new(Box::new(dev), font, size) } } diff --git a/src/widgets/bar_widget.rs b/src/widgets/bar_widget.rs index 77e73b7..f57febb 100644 --- a/src/widgets/bar_widget.rs +++ b/src/widgets/bar_widget.rs @@ -8,9 +8,6 @@ use crate::{ widgets::{Geometry, Widget}, }; -const BAR_FONT: &str = "sans"; -const BAR_SIZE: f32 = 24.; - pub trait BarWidgetImpl { fn get_dirty(&self) -> bool; fn set_dirty(&mut self, dirty: bool); @@ -20,15 +17,17 @@ pub trait BarWidgetImpl { } pub struct BarWidget { + font: &'static str, + size: f32, geometry: Geometry, inner_widget: Box, } impl BarWidget { - pub fn new(inner_widget: Box) -> BarWidget { + pub fn new(inner_widget: Box, font: &'static str, size: f32) -> BarWidget { BarWidget { geometry: Default::default(), - inner_widget, + inner_widget, font, size, } } } @@ -47,13 +46,13 @@ impl Widget for BarWidget { } fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry { - let font = fonts.get_font(BAR_FONT, BAR_SIZE); + let font = fonts.get_font(self.font, self.size); let fg = Color::new(1., 1., 1., 1.); let bg = Color::new(0., 0., 0., 1.); font.auto_draw_text(view, &bg, &fg, self.inner_widget.name()) .unwrap(); - let size = BAR_SIZE.ceil() as u32; + let size = self.size.ceil() as u32; let bar_offset = 4 * size; let val = self.inner_widget.value(); @@ -86,7 +85,7 @@ impl Widget for BarWidget { x: x, y: geometry.y, width: w, - height: BAR_SIZE.ceil() as u32, + height: self.size.ceil() as u32, }; self.geometry } diff --git a/src/widgets/battery.rs b/src/widgets/battery.rs index 776a9ad..e448871 100644 --- a/src/widgets/battery.rs +++ b/src/widgets/battery.rs @@ -111,7 +111,7 @@ pub struct Battery { } impl Battery { - pub fn new(ping: Ping) -> BarWidget { + pub fn new(ping: Ping, font: &'static str, size: f32) -> BarWidget { let battery = Battery { inner: Arc::new(Mutex::new(InnerBattery { value: 0., @@ -120,7 +120,7 @@ impl Battery { dirty: false, }; start_monitor(battery.inner.clone(), ping); - BarWidget::new(Box::new(battery)) + BarWidget::new(Box::new(battery), font, size) } } diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index 91b4fce..63ee961 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -9,10 +9,9 @@ use crate::{ use chrono::{Local, Timelike}; -const CLOCK_FONT: &str = "sans"; -const CLOCK_SIZE: f32 = 96.; - pub struct Clock { + font: &'static str, + size: f32, dirty: bool, geometry: Geometry, digit_width: u32, @@ -20,8 +19,9 @@ pub struct Clock { } impl<'a> Clock { - pub fn new() -> Clock { + pub fn new(font: &'static str, size: f32) -> Clock { Clock { + font, size, dirty: true, geometry: Default::default(), digit_width: 0, @@ -49,7 +49,7 @@ impl<'a> Widget for Clock { let digit = self.digit_width; let colon = self.colon_width; - let font = fonts.get_font(CLOCK_FONT, CLOCK_SIZE); + let font = fonts.get_font(self.font, self.size); font.draw_text_fixed_width( view, &bg, @@ -62,14 +62,14 @@ impl<'a> Widget for Clock { } fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry { - let font = fonts.get_font(CLOCK_FONT, CLOCK_SIZE); + let font = fonts.get_font(self.font, self.size); self.digit_width = font.auto_widest("0123456789").unwrap(); self.colon_width = font.auto_widest(":").unwrap(); self.geometry = Geometry { x: geometry.x, y: geometry.y, width: self.digit_width * 4 + self.colon_width, - height: CLOCK_SIZE.ceil() as u32, + height: self.size.ceil() as u32, }; self.geometry } diff --git a/src/widgets/date.rs b/src/widgets/date.rs index 7544308..b56c5f0 100644 --- a/src/widgets/date.rs +++ b/src/widgets/date.rs @@ -9,17 +9,17 @@ use crate::{ use chrono::{Datelike, Local}; -const DATE_FONT: &str = "sans"; -const DATE_SIZE: f32 = 40.; - pub struct Date { + font: &'static str, + size: f32, dirty: bool, geometry: Geometry, } impl Date { - pub fn new() -> Date { + pub fn new(font: &'static str, size: f32) -> Date { Date { + font, size, dirty: true, geometry: Default::default(), } @@ -43,7 +43,7 @@ impl<'a> Widget for Date { let fg = Color::new(1., 1., 1., 1.); let bg = Color::new(0., 0., 0., 1.); let mut date_line = view.offset((0, 8)).unwrap(); - let font = fonts.get_font(DATE_FONT, DATE_SIZE); + let font = fonts.get_font(self.font, self.size); font.auto_draw_text( &mut date_line, &bg, @@ -61,11 +61,12 @@ impl<'a> Widget for Date { } fn geometry_update(&mut self, _fonts: &mut FontMap, geometry: &Geometry) -> Geometry { + let width = (3. + 2. + 2. + 2. + 4.) * self.size / 2.; self.geometry = Geometry { x: geometry.x, y: geometry.y, - width: 256, - height: DATE_SIZE.ceil() as u32, + width: width.ceil() as u32, + height: self.size.ceil() as u32, }; self.geometry } diff --git a/src/widgets/launcher.rs b/src/widgets/launcher.rs index ca39206..7afa221 100644 --- a/src/widgets/launcher.rs +++ b/src/widgets/launcher.rs @@ -1,7 +1,11 @@ -use std::cmp::{min, Ordering}; -use std::collections::HashMap; -use std::default::Default; -use std::process::{exit, Command}; +use std::{ + cmp::{min, max, Ordering}, + collections::HashMap, + default::Default, + process::{exit, Command}, + sync::{Arc, Mutex}, + thread, +}; use crate::{ buffer::BufferView, @@ -17,12 +21,6 @@ use fuzzy_matcher::FuzzyMatcher; use unicode_segmentation::UnicodeSegmentation; use wayland_client::{protocol::wl_keyboard, WEnum}; -use std::sync::{Arc, Mutex}; -use std::thread; - -const LAUNCHER_FONT: &str = "monospace"; -const LAUNCHER_SIZE: f32 = 32.; - struct Matcher { matches: HashMap, } @@ -284,7 +282,7 @@ impl InterfaceWidget for Launcher { let fg = Color::new(1., 1., 1., 1.); let bg = Color::new(0., 0., 0., 1.); - let line_height = LAUNCHER_SIZE.ceil() as u32; + let line_height = intf.size.ceil() as u32; // Draw line let mut prompt_offset = intf.geometry.height - line_height; @@ -292,9 +290,8 @@ impl InterfaceWidget for Launcher { let mut prompt_border = prompt_line.limit((intf.geometry.width, 1)).unwrap(); prompt_border.memset(&fg); - // Draw prompt - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); - font.auto_draw_text_with_cursor( + let font = fonts.get_font(intf.font, intf.size); + let mut x_max = font.auto_draw_text_with_cursor( &mut prompt_line, &bg, &fg, @@ -303,7 +300,6 @@ impl InterfaceWidget for Launcher { ) .unwrap(); - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); font.auto_draw_text(&mut prompt_line, &bg, &fg, ">") .unwrap(); @@ -332,11 +328,11 @@ impl InterfaceWidget for Launcher { colors.push(Color::new(0.75, 0.75, 0.75, 1.0)); } } - font.auto_draw_text_individual_colors(&mut line, &bg, &colors, &m.name) - .unwrap(); + x_max = max(x_max, font.auto_draw_text_individual_colors(&mut line, &bg, &colors, &m.name) + .unwrap()); } else { - font.auto_draw_text(&mut line, &bg, &dimfg, &m.name) - .unwrap(); + x_max = max(x_max, font.auto_draw_text(&mut line, &bg, &dimfg, &m.name) + .unwrap()); } } @@ -347,7 +343,7 @@ impl InterfaceWidget for Launcher { Geometry { x: intf.geometry.x, y: intf.geometry.y + intf.geometry.height - content_height, - width: intf.geometry.width, + width: x_max.0+1, height: content_height, } } @@ -380,7 +376,7 @@ impl InterfaceWidget for Shell { let fg = Color::new(1., 1., 1., 1.); let bg = Color::new(0., 0., 0., 1.); - let line_height = LAUNCHER_SIZE.ceil() as u32; + let line_height = intf.size.ceil() as u32; // Draw line let prompt_offset = intf.geometry.height - line_height; @@ -389,8 +385,8 @@ impl InterfaceWidget for Shell { prompt_border.memset(&fg); // Draw prompt - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); - font.auto_draw_text_with_cursor( + let font = fonts.get_font(intf.font, intf.size); + let x_max = font.auto_draw_text_with_cursor( &mut prompt_line, &bg, &fg, @@ -398,14 +394,13 @@ impl InterfaceWidget for Shell { intf.prompt.cursor + 3, ) .unwrap(); - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); font.auto_draw_text(&mut prompt_line, &bg, &Color::new(1., 0.75, 0.5, 1.), "!") .unwrap(); Geometry { x: intf.geometry.x, y: intf.geometry.y + intf.geometry.height - line_height, - width: intf.geometry.width, + width: x_max.0 + 1, height: line_height, } } @@ -443,16 +438,16 @@ impl InterfaceWidget for Calc { let fg = Color::new(1., 1., 1., 1.); let bg = Color::new(0., 0., 0., 1.); - let line_height = LAUNCHER_SIZE.ceil() as u32; + let line_height = intf.size.ceil() as u32; // Draw line - let mut prompt_offset = intf.geometry.height - (LAUNCHER_SIZE.ceil() as u32); + let mut prompt_offset = intf.geometry.height - (line_height); 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 - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); + let font = fonts.get_font(intf.font, intf.size); let res_off = font .auto_draw_text_with_cursor( &mut prompt_line, @@ -463,7 +458,6 @@ impl InterfaceWidget for Calc { ) .unwrap(); - let font = fonts.get_font(LAUNCHER_FONT, LAUNCHER_SIZE); font.auto_draw_text(&mut prompt_line, &bg, &Color::new(1., 0.75, 0.5, 1.), "=") .unwrap(); @@ -476,10 +470,10 @@ impl InterfaceWidget for Calc { for m in self.old.iter().rev() { let dimfg = Color::new(0.5, 0.5, 0.5, 1.0); - if prompt_offset < LAUNCHER_SIZE.ceil() as u32 { + if prompt_offset < line_height { break; } - prompt_offset -= LAUNCHER_SIZE.ceil() as u32; + prompt_offset -= line_height; let mut result_line = view.offset((0, prompt_offset)).unwrap(); font.auto_draw_text(&mut result_line, &bg, &dimfg, &m) .unwrap(); @@ -499,6 +493,8 @@ impl InterfaceWidget for Calc { } struct InnerInterface { + font: &'static str, + size: f32, dirty: bool, geometry: Geometry, selection: usize, @@ -513,7 +509,7 @@ pub struct Interface { } impl Interface { - pub fn new() -> Interface { + pub fn new(font: &'static str, size: f32) -> Interface { Interface { launcher: Launcher::new(), shell: Shell {}, @@ -522,6 +518,7 @@ impl Interface { result: None, }, inner: InnerInterface { + font, size, dirty: false, geometry: Default::default(), selection: 0,