Configurable font support
Use fontconfig to load fonts. Fonts can be added to the `fonts` map in the config, and referenced from individual widgets' configurations for customizability. Fonts default to values "mono" and "sans" if nothing is set.
This commit is contained in:
committed by
Kenny Levinsen
parent
1a62696662
commit
48985c470a
+6
-6
@@ -199,19 +199,19 @@ impl AppInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub struct App<'a> {
|
||||
pools: DoubleMemPool,
|
||||
display: Display,
|
||||
event_queue: EventQueue,
|
||||
cmd_queue: Arc<Mutex<VecDeque<Cmd>>>,
|
||||
widget: Option<Box<dyn Widget + Send>>,
|
||||
widget: Option<Box<dyn Widget + Send + 'a>>,
|
||||
bg: Color,
|
||||
inner: Arc<Mutex<AppInner>>,
|
||||
last_damage: Option<Vec<(i32, i32, i32, i32)>>,
|
||||
last_dim: (u32, u32),
|
||||
}
|
||||
|
||||
impl App {
|
||||
impl<'a> App<'a> {
|
||||
pub fn redraw(&mut self, force: bool) -> Result<(), ::std::io::Error> {
|
||||
let widget = match self.widget {
|
||||
Some(ref mut widget) => widget,
|
||||
@@ -341,16 +341,16 @@ impl App {
|
||||
&mut self.event_queue
|
||||
}
|
||||
|
||||
pub fn get_widget(&mut self) -> &mut Box<dyn Widget + Send> {
|
||||
pub fn get_widget(&mut self) -> &mut Box<dyn Widget + Send + 'a> {
|
||||
self.widget.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_widget(&mut self, w: Box<dyn Widget + Send>) -> Result<(), ::std::io::Error> {
|
||||
pub fn set_widget(&mut self, w: Box<dyn Widget + Send + 'a>) -> Result<(), ::std::io::Error> {
|
||||
self.widget = Some(w);
|
||||
self.redraw(true)
|
||||
}
|
||||
|
||||
pub fn new(tx: Sender<Cmd>, output_mode: OutputMode, bg: Color, scale: u32) -> App {
|
||||
pub fn new(tx: Sender<Cmd>, output_mode: OutputMode, bg: Color, scale: u32) -> App<'a> {
|
||||
let inner = Arc::new(Mutex::new(AppInner::new(tx, output_mode, scale)));
|
||||
|
||||
//
|
||||
|
||||
+117
-21
@@ -1,13 +1,16 @@
|
||||
use crate::cmd::Cmd;
|
||||
use crate::color::Color;
|
||||
use crate::widget;
|
||||
use crate::widgets;
|
||||
use crate::{
|
||||
fonts::{FontMap, FontRef},
|
||||
widgets,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::default::Default;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::{collections::HashMap, sync::mpsc::Sender};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Widget {
|
||||
Margin {
|
||||
@@ -22,16 +25,21 @@ pub enum Widget {
|
||||
HorizontalLayout(Vec<Widget>),
|
||||
VerticalLayout(Vec<Widget>),
|
||||
Clock {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
},
|
||||
Date {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
},
|
||||
Calendar {
|
||||
font_primary: Option<String>,
|
||||
font_secondary: Option<String>,
|
||||
font_size: f32,
|
||||
sections: u32,
|
||||
},
|
||||
Launcher {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
#[serde(default)]
|
||||
@@ -42,35 +50,40 @@ pub enum Widget {
|
||||
url_opener: String,
|
||||
},
|
||||
Battery {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
},
|
||||
Backlight {
|
||||
#[serde(default)]
|
||||
device: String,
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
},
|
||||
#[cfg(feature = "pulseaudio-widget")]
|
||||
PulseAudio {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
},
|
||||
#[cfg(feature = "alsa-widget")]
|
||||
AlsaSound {
|
||||
font: Option<String>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Widget {
|
||||
pub fn construct(
|
||||
pub fn construct<'a>(
|
||||
self,
|
||||
time: NaiveDateTime,
|
||||
tx: Sender<Cmd>,
|
||||
) -> Option<Box<dyn widget::Widget + Send>> {
|
||||
fonts: &'a FontMap,
|
||||
) -> Option<Box<dyn widget::Widget + Send + 'a>> {
|
||||
match self {
|
||||
Widget::Margin { margins, widget } => match widget.construct(time, tx) {
|
||||
Widget::Margin { margins, widget } => match widget.construct(time, tx, fonts) {
|
||||
Some(w) => Some(widget::Margin::new(margins, w)),
|
||||
None => None,
|
||||
},
|
||||
@@ -78,14 +91,14 @@ impl Widget {
|
||||
width,
|
||||
height,
|
||||
widget,
|
||||
} => match widget.construct(time, tx) {
|
||||
} => match widget.construct(time, tx, fonts) {
|
||||
Some(w) => Some(widget::Fixed::new((width, height), w)),
|
||||
None => None,
|
||||
},
|
||||
Widget::HorizontalLayout(widgets) => Some(widget::HorizontalLayout::new(
|
||||
widgets
|
||||
.into_iter()
|
||||
.map(|x| x.construct(time, tx.clone()))
|
||||
.map(|x| x.construct(time, tx.clone(), fonts))
|
||||
.filter(|x| x.is_some())
|
||||
.map(|x| x.unwrap())
|
||||
.collect(),
|
||||
@@ -93,24 +106,48 @@ impl Widget {
|
||||
Widget::VerticalLayout(widgets) => Some(widget::VerticalLayout::new(
|
||||
widgets
|
||||
.into_iter()
|
||||
.map(|x| x.construct(time, tx.clone()))
|
||||
.map(|x| x.construct(time, tx.clone(), fonts))
|
||||
.filter(|x| x.is_some())
|
||||
.map(|x| x.unwrap())
|
||||
.collect(),
|
||||
)),
|
||||
Widget::Clock { font_size } => Some(widgets::clock::Clock::new(time, font_size)),
|
||||
Widget::Date { font_size } => Some(widgets::date::Date::new(time, font_size)),
|
||||
Widget::Clock { font, font_size } => Some(widgets::clock::Clock::new(
|
||||
time,
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
)),
|
||||
Widget::Date { font, font_size } => Some(widgets::date::Date::new(
|
||||
time,
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
)),
|
||||
Widget::Calendar {
|
||||
font_primary,
|
||||
font_secondary,
|
||||
font_size,
|
||||
sections,
|
||||
} => Some(widgets::calendar::Calendar::new(time, font_size, sections)),
|
||||
} => Some(widgets::calendar::Calendar::new(
|
||||
time,
|
||||
get_font(
|
||||
&font_primary.or_else(|| Some("sans".to_string())).unwrap(),
|
||||
&fonts,
|
||||
),
|
||||
get_font(
|
||||
&font_secondary.or_else(|| Some("mono".to_string())).unwrap(),
|
||||
&fonts,
|
||||
),
|
||||
font_size,
|
||||
sections,
|
||||
)),
|
||||
Widget::Launcher {
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
app_opener,
|
||||
term_opener,
|
||||
url_opener,
|
||||
} => Some(widgets::launcher::Launcher::new(
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
length,
|
||||
tx,
|
||||
@@ -122,14 +159,24 @@ impl Widget {
|
||||
url_opener
|
||||
},
|
||||
)),
|
||||
Widget::Battery { font_size, length } => {
|
||||
match widgets::battery::UpowerBattery::new(font_size, length, tx) {
|
||||
Widget::Battery {
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
} => {
|
||||
match widgets::battery::UpowerBattery::new(
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
length,
|
||||
tx,
|
||||
) {
|
||||
Ok(w) => Some(w),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
Widget::Backlight {
|
||||
device,
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
} => {
|
||||
@@ -138,21 +185,43 @@ impl Widget {
|
||||
} else {
|
||||
&device
|
||||
};
|
||||
match widgets::backlight::Backlight::new(d, font_size, length) {
|
||||
match widgets::backlight::Backlight::new(
|
||||
d,
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
length,
|
||||
) {
|
||||
Ok(w) => Some(w),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "pulseaudio-widget")]
|
||||
Widget::PulseAudio { font_size, length } => {
|
||||
match widgets::audio::PulseAudio::new(font_size, length, tx) {
|
||||
Widget::PulseAudio {
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
} => {
|
||||
match widgets::audio::PulseAudio::new(
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
length,
|
||||
tx,
|
||||
) {
|
||||
Ok(w) => Some(w),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alsa-widget")]
|
||||
Widget::AlsaSound { font_size, length } => {
|
||||
match widgets::audio::Alsa::new(font_size, length) {
|
||||
Widget::AlsaSound {
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
} => {
|
||||
match widgets::audio::Alsa::new(
|
||||
get_font(&font.or_else(|| Some("sans".to_string())).unwrap(), &fonts),
|
||||
font_size,
|
||||
length,
|
||||
) {
|
||||
Ok(w) => Some(w),
|
||||
Err(_) => None,
|
||||
}
|
||||
@@ -181,6 +250,7 @@ pub struct Config {
|
||||
pub scale: u32,
|
||||
pub background: Color,
|
||||
pub widget: Widget,
|
||||
pub fonts: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -193,14 +263,21 @@ impl Default for Config {
|
||||
Widget::Margin {
|
||||
margins: (0, 88, 0, 32),
|
||||
widget: Box::new(Widget::VerticalLayout(vec![
|
||||
Widget::Date { font_size: 64.0 },
|
||||
Widget::Clock { font_size: 256.0 },
|
||||
Widget::Date {
|
||||
font: None,
|
||||
font_size: 64.0,
|
||||
},
|
||||
Widget::Clock {
|
||||
font: None,
|
||||
font_size: 256.0,
|
||||
},
|
||||
])),
|
||||
},
|
||||
Widget::VerticalLayout(vec![
|
||||
Widget::Margin {
|
||||
margins: (0, 0, 0, 8),
|
||||
widget: Box::new(Widget::Battery {
|
||||
font: None,
|
||||
font_size: 24.0,
|
||||
length: 600,
|
||||
}),
|
||||
@@ -209,6 +286,7 @@ impl Default for Config {
|
||||
margins: (0, 0, 0, 8),
|
||||
widget: Box::new(Widget::Backlight {
|
||||
device: "intel_backlight".to_string(),
|
||||
font: None,
|
||||
font_size: 24.0,
|
||||
length: 600,
|
||||
}),
|
||||
@@ -217,6 +295,7 @@ impl Default for Config {
|
||||
Widget::Margin {
|
||||
margins: (0, 0, 0, 8),
|
||||
widget: Box::new(Widget::PulseAudio {
|
||||
font: None,
|
||||
font_size: 24.0,
|
||||
length: 600,
|
||||
}),
|
||||
@@ -224,10 +303,13 @@ impl Default for Config {
|
||||
]),
|
||||
]),
|
||||
Widget::Calendar {
|
||||
font_primary: None,
|
||||
font_secondary: None,
|
||||
font_size: 16.0,
|
||||
sections: 3,
|
||||
},
|
||||
Widget::Launcher {
|
||||
font: None,
|
||||
font_size: 32.0,
|
||||
length: 1200,
|
||||
app_opener: "".to_string(),
|
||||
@@ -239,6 +321,20 @@ impl Default for Config {
|
||||
output_mode: Default::default(),
|
||||
scale: 1,
|
||||
background: Color::new(0.0, 0.0, 0.0, 0.9),
|
||||
fonts: {
|
||||
let mut map = HashMap::with_capacity(2);
|
||||
map.insert("mono".to_string(), "mono".to_string());
|
||||
map.insert("sans".to_string(), "sans".to_string());
|
||||
map
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_font<'a>(name: &str, map: &'a FontMap) -> FontRef<'a> {
|
||||
match map.get(name) {
|
||||
Some(f) => f,
|
||||
None => panic!(format!("Font {} is missing from the config", name)),
|
||||
}
|
||||
}
|
||||
|
||||
+7
-21
@@ -1,23 +1,9 @@
|
||||
use crate::buffer::Buffer;
|
||||
use crate::color::Color;
|
||||
use crate::{color::Color, fonts::FontRef};
|
||||
|
||||
use rusttype::{point, Scale};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use rusttype::{point, Font as RustFont, Scale};
|
||||
|
||||
pub static DEJAVUSANS_MONO_FONT_DATA: &[u8] = include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf");
|
||||
pub static ROBOTO_REGULAR_FONT_DATA: &[u8] = include_bytes!("../fonts/Roboto-Regular.ttf");
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DEJAVUSANS_MONO: RustFont<'static> =
|
||||
RustFont::from_bytes(DEJAVUSANS_MONO_FONT_DATA as &[u8])
|
||||
.expect("error constructing DejaVuSansMono");
|
||||
pub static ref ROBOTO_REGULAR: RustFont<'static> =
|
||||
RustFont::from_bytes(ROBOTO_REGULAR_FONT_DATA as &[u8])
|
||||
.expect("error constructing Roboto-Regular");
|
||||
}
|
||||
|
||||
struct CachedGlyph {
|
||||
dimensions: (u32, u32),
|
||||
origin: (i32, i32),
|
||||
@@ -25,7 +11,7 @@ struct CachedGlyph {
|
||||
}
|
||||
|
||||
impl CachedGlyph {
|
||||
fn new(font: &RustFont, size: f32, ch: char) -> CachedGlyph {
|
||||
fn new(font: FontRef, size: f32, ch: char) -> CachedGlyph {
|
||||
let scale = Scale::uniform(size);
|
||||
let v_metrics = font.v_metrics(scale);
|
||||
let glyph = font
|
||||
@@ -81,14 +67,14 @@ impl CachedGlyph {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Font {
|
||||
pub struct Font<'a> {
|
||||
glyphs: HashMap<char, CachedGlyph>,
|
||||
font: &'static RustFont<'static>,
|
||||
font: FontRef<'a>,
|
||||
size: f32,
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn new(font: &'static RustFont, size: f32) -> Font {
|
||||
impl<'a> Font<'a> {
|
||||
pub fn new(font: FontRef<'a>, size: f32) -> Font {
|
||||
Font {
|
||||
glyphs: HashMap::new(),
|
||||
font,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//! Utility module for fonts
|
||||
|
||||
use fontconfig::Fontconfig as FontConfig;
|
||||
use rusttype::Font;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// FontMap is used to store different font configurations
|
||||
pub type FontMap<'a> = HashMap<String, rusttype::Font<'a>>;
|
||||
|
||||
/// FontRef is used to store Fonts on widgets.
|
||||
pub type FontRef<'a> = &'a rusttype::Font<'a>;
|
||||
|
||||
/// FontSeeker is a marker struct that is used to look up fonts
|
||||
pub(crate) struct FontSeeker;
|
||||
|
||||
impl FontSeeker {
|
||||
/// Acts like fc-match.
|
||||
/// Given a string, it matches it to a font file and returns its path.
|
||||
pub(crate) fn from_string(name: &str) -> PathBuf {
|
||||
let fc = FontConfig::new().unwrap();
|
||||
fc.find(name, None).unwrap().path
|
||||
}
|
||||
}
|
||||
|
||||
/// FontLoader is a marker struct that is used to load files
|
||||
pub(crate) struct FontLoader;
|
||||
|
||||
impl FontLoader {
|
||||
/// Given a path, loads it as a Font, which can be rendered to the screen.
|
||||
pub(crate) fn from_path<'a, P>(path: P) -> Option<Font<'a>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = File::open(path).expect("Font file not found");
|
||||
let mut data = match file.metadata() {
|
||||
Ok(metadata) => Vec::with_capacity(metadata.len() as usize),
|
||||
Err(_) => vec![],
|
||||
};
|
||||
file.read_to_end(&mut data).unwrap();
|
||||
Font::try_from_vec(data)
|
||||
}
|
||||
}
|
||||
+22
-13
@@ -3,7 +3,7 @@ use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::{collections::HashMap, sync::mpsc::channel};
|
||||
|
||||
use chrono::{Duration, Local};
|
||||
use nix::poll::{poll, PollFd, PollFlags};
|
||||
@@ -19,6 +19,7 @@ mod configfmt;
|
||||
mod desktop;
|
||||
mod doublemempool;
|
||||
mod draw;
|
||||
mod fonts;
|
||||
mod widget;
|
||||
mod widgets;
|
||||
|
||||
@@ -26,6 +27,7 @@ use app::{App, OutputMode};
|
||||
use cmd::Cmd;
|
||||
use config::Config;
|
||||
use configfmt::ConfigFmt;
|
||||
use fonts::{FontLoader, FontMap, FontSeeker};
|
||||
use widget::WaitContext;
|
||||
|
||||
enum Mode {
|
||||
@@ -79,6 +81,19 @@ fn main() {
|
||||
|
||||
let scale = config.scale;
|
||||
|
||||
let fonts: FontMap = {
|
||||
let load_font = |font_name| {
|
||||
let path = FontSeeker::from_string(font_name);
|
||||
FontLoader::from_path(&path).expect(&format!("Loading {} failed", path.display()))
|
||||
};
|
||||
|
||||
config
|
||||
.fonts
|
||||
.iter()
|
||||
.map(|(key, val)| (key.clone(), load_font(val)))
|
||||
.collect::<HashMap<_, _>>()
|
||||
};
|
||||
|
||||
let mut args = env::args();
|
||||
let _ = args.next();
|
||||
let mode = match args.next() {
|
||||
@@ -159,17 +174,12 @@ fn main() {
|
||||
|
||||
let (tx_draw, rx_draw) = channel();
|
||||
let tx_draw_mod = tx_draw.clone();
|
||||
let (mod_tx, mod_rx) = channel();
|
||||
std::thread::spawn(move || {
|
||||
// Print, write to a file, or send to an HTTP server.
|
||||
match config
|
||||
.widget
|
||||
.construct(Local::now().naive_local(), tx_draw_mod)
|
||||
{
|
||||
Some(w) => mod_tx.send(w).unwrap(),
|
||||
None => panic!("no widget configured"),
|
||||
}
|
||||
});
|
||||
|
||||
// Print, write to a file, or send to an HTTP server.
|
||||
let widget = config
|
||||
.widget
|
||||
.construct(Local::now().naive_local(), tx_draw_mod, &fonts)
|
||||
.expect("no widget configured");
|
||||
|
||||
let mut app = App::new(tx_draw, output_mode, background, scale);
|
||||
if daemon {
|
||||
@@ -177,7 +187,6 @@ fn main() {
|
||||
} else {
|
||||
app.show();
|
||||
}
|
||||
let widget = mod_rx.recv().unwrap();
|
||||
app.set_widget(widget).unwrap();
|
||||
|
||||
let (mut rx_pipe, mut tx_pipe) = pipe().unwrap();
|
||||
|
||||
+20
-20
@@ -69,18 +69,18 @@ pub trait Widget {
|
||||
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32));
|
||||
}
|
||||
|
||||
pub struct VerticalLayout {
|
||||
pub children: Vec<Box<dyn Widget + Send>>,
|
||||
pub struct VerticalLayout<'a> {
|
||||
pub children: Vec<Box<dyn Widget + Send + 'a>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl VerticalLayout {
|
||||
pub fn new(children: Vec<Box<dyn Widget + Send>>) -> Box<VerticalLayout> {
|
||||
impl<'a> VerticalLayout<'a> {
|
||||
pub fn new(children: Vec<Box<dyn Widget + Send + 'a>>) -> Box<VerticalLayout> {
|
||||
Box::new(VerticalLayout { children })
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for VerticalLayout {
|
||||
impl<'a> Widget for VerticalLayout<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
for child in &mut self.children {
|
||||
child.wait(ctx);
|
||||
@@ -180,18 +180,18 @@ impl Widget for VerticalLayout {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HorizontalLayout {
|
||||
pub children: Vec<Box<dyn Widget + Send>>,
|
||||
pub struct HorizontalLayout<'a> {
|
||||
pub children: Vec<Box<dyn Widget + Send + 'a>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl HorizontalLayout {
|
||||
pub fn new(children: Vec<Box<dyn Widget + Send>>) -> Box<HorizontalLayout> {
|
||||
impl<'a> HorizontalLayout<'a> {
|
||||
pub fn new(children: Vec<Box<dyn Widget + Send + 'a>>) -> Box<HorizontalLayout> {
|
||||
Box::new(HorizontalLayout { children })
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for HorizontalLayout {
|
||||
impl<'a> Widget for HorizontalLayout<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
for child in &mut self.children {
|
||||
child.wait(ctx);
|
||||
@@ -293,19 +293,19 @@ impl Widget for HorizontalLayout {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Margin {
|
||||
pub child: Box<dyn Widget + Send>,
|
||||
pub struct Margin<'a> {
|
||||
pub child: Box<dyn Widget + Send + 'a>,
|
||||
pub margins: (u32, u32, u32, u32), // left, right, top, bottom
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Margin {
|
||||
pub fn new(margins: (u32, u32, u32, u32), child: Box<dyn Widget + Send>) -> Box<Margin> {
|
||||
impl<'a> Margin<'a> {
|
||||
pub fn new(margins: (u32, u32, u32, u32), child: Box<dyn Widget + Send + 'a>) -> Box<Margin> {
|
||||
Box::new(Margin { child, margins })
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Margin {
|
||||
impl<'a> Widget for Margin<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
self.child.wait(ctx)
|
||||
}
|
||||
@@ -367,19 +367,19 @@ impl Widget for Margin {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Fixed {
|
||||
pub child: Box<dyn Widget + Send>,
|
||||
pub struct Fixed<'a> {
|
||||
pub child: Box<dyn Widget + Send + 'a>,
|
||||
pub size: (u32, u32),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Fixed {
|
||||
pub fn new(size: (u32, u32), child: Box<dyn Widget + Send>) -> Box<Fixed> {
|
||||
impl<'a> Fixed<'a> {
|
||||
pub fn new(size: (u32, u32), child: Box<dyn Widget + Send + 'a>) -> Box<Fixed> {
|
||||
Box::new(Fixed { child, size })
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Fixed {
|
||||
impl<'a> Widget for Fixed<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
self.child.wait(ctx)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::color::Color;
|
||||
use crate::widget::WaitContext;
|
||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widgets::bar_widget::{BarWidget, BarWidgetImpl},
|
||||
};
|
||||
|
||||
use alsa::mixer::{Mixer, Selem, SelemChannelId, SelemId};
|
||||
|
||||
@@ -34,15 +37,20 @@ pub struct Alsa {
|
||||
}
|
||||
|
||||
impl Alsa {
|
||||
pub fn new(font_size: f32, length: u32) -> ::std::io::Result<Box<BarWidget>> {
|
||||
pub fn new(font: FontRef, font_size: f32, length: u32) -> ::std::io::Result<Box<BarWidget>> {
|
||||
let mixer = Mixer::new(CARD_NAME, true)
|
||||
.map_err(|err| alsa_error_to_io_error("Failed to create ALSA mixer", &err))?;
|
||||
let mixer = Self { mixer };
|
||||
mixer.get_master_volume()?; // probing
|
||||
Ok(BarWidget::new_simple(font_size, length, Box::new(mixer)))
|
||||
Ok(BarWidget::new_simple(
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
Box::new(mixer),
|
||||
))
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_master<'a>(&'a self) -> ::std::io::Result<Selem<'a>> {
|
||||
pub fn get_master(&self) -> ::std::io::Result<Selem> {
|
||||
let master_id = SelemId::new(SELEM_NAME, SELEM_ID);
|
||||
match self.mixer.find_selem(&master_id) {
|
||||
Some(master) => Ok(master),
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::cmd::Cmd;
|
||||
use crate::color::Color;
|
||||
use crate::widget::WaitContext;
|
||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widgets::bar_widget::{BarWidget, BarWidgetImpl},
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -484,11 +487,12 @@ pub struct PulseAudio {
|
||||
|
||||
impl PulseAudio {
|
||||
pub fn new(
|
||||
font: FontRef,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
sender: Sender<Cmd>,
|
||||
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
||||
BarWidget::new(font_size, length, move |dirty| {
|
||||
BarWidget::new(font, font_size, length, move |dirty| {
|
||||
let device = PulseAudioSoundDevice::new(move || {
|
||||
*dirty.lock().unwrap() = true;
|
||||
sender.send(Cmd::Draw).unwrap();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::color::Color;
|
||||
use crate::widget::WaitContext;
|
||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widgets::bar_widget::{BarWidget, BarWidgetImpl},
|
||||
};
|
||||
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
@@ -70,7 +73,12 @@ impl Backlight {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new(path: &str, font_size: f32, length: u32) -> Result<Box<BarWidget>, Error> {
|
||||
pub fn new<'a>(
|
||||
path: &str,
|
||||
font: FontRef<'a>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
) -> Result<Box<BarWidget<'a>>, Error> {
|
||||
let mut dev = Backlight {
|
||||
device_path: Path::new("/sys/class/backlight").to_path_buf().join(path),
|
||||
cur_brightness: 0,
|
||||
@@ -79,7 +87,12 @@ impl Backlight {
|
||||
|
||||
dev.update()?;
|
||||
|
||||
Ok(BarWidget::new_simple(font_size, length, Box::new(dev)))
|
||||
Ok(BarWidget::new_simple(
|
||||
font,
|
||||
font_size,
|
||||
length,
|
||||
Box::new(dev),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-10
@@ -1,6 +1,9 @@
|
||||
use crate::color::Color;
|
||||
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||
use crate::draw::{draw_bar, draw_box, Font};
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget},
|
||||
};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -14,21 +17,22 @@ pub trait BarWidgetImpl {
|
||||
fn toggle(&mut self);
|
||||
}
|
||||
|
||||
pub struct BarWidget {
|
||||
pub struct BarWidget<'a> {
|
||||
bar_impl: Box<dyn BarWidgetImpl + Send>,
|
||||
font: Font,
|
||||
font: Font<'a>,
|
||||
font_size: u32,
|
||||
length: u32,
|
||||
dirty: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl BarWidget {
|
||||
impl<'a> BarWidget<'a> {
|
||||
pub fn new_simple(
|
||||
font: FontRef,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
w: Box<dyn BarWidgetImpl + Send>,
|
||||
) -> Box<BarWidget> {
|
||||
let mut font = Font::new(&ROBOTO_REGULAR, font_size);
|
||||
let mut font = Font::new(font, font_size);
|
||||
font.add_str_to_cache(w.name());
|
||||
|
||||
Box::new(BarWidget {
|
||||
@@ -40,15 +44,20 @@ impl BarWidget {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new<F>(font_size: f32, length: u32, f: F) -> Result<Box<BarWidget>, ::std::io::Error>
|
||||
pub fn new<F>(
|
||||
font: FontRef<'a>,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
f: F,
|
||||
) -> Result<Box<BarWidget>, ::std::io::Error>
|
||||
where
|
||||
F: FnOnce(Arc<Mutex<bool>>) -> Result<Box<dyn BarWidgetImpl + Send>, ::std::io::Error>,
|
||||
F: Send + 'static + Clone,
|
||||
F: Send + 'a + Clone,
|
||||
{
|
||||
let dirty = Arc::new(Mutex::new(true));
|
||||
let im = f(dirty.clone())?;
|
||||
|
||||
let mut font = Font::new(&ROBOTO_REGULAR, font_size);
|
||||
let mut font = Font::new(font, font_size);
|
||||
font.add_str_to_cache(im.name());
|
||||
|
||||
Ok(Box::new(BarWidget {
|
||||
@@ -61,7 +70,7 @@ impl BarWidget {
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for BarWidget {
|
||||
impl<'a> Widget for BarWidget<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
self.bar_impl.wait(ctx);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::cmd::Cmd;
|
||||
use crate::color::Color;
|
||||
use crate::widget::WaitContext;
|
||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widgets::bar_widget::{BarWidget, BarWidgetImpl},
|
||||
};
|
||||
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -161,11 +164,12 @@ impl UpowerBattery {
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
font: FontRef,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
sender: Sender<Cmd>,
|
||||
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
||||
BarWidget::new(font_size, length, move |dirty| {
|
||||
BarWidget::new(font, font_size, length, move |dirty| {
|
||||
let d = UpowerBattery::from_device(dirty, sender, "DisplayDevice")?;
|
||||
Ok(Box::new(d))
|
||||
})
|
||||
|
||||
+24
-15
@@ -1,23 +1,26 @@
|
||||
use crate::buffer::Buffer;
|
||||
use crate::color::Color;
|
||||
use crate::draw::{Font, DEJAVUSANS_MONO, ROBOTO_REGULAR};
|
||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||
use crate::draw::Font;
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget},
|
||||
};
|
||||
|
||||
use chrono::{Datelike, NaiveDate, NaiveDateTime};
|
||||
|
||||
pub struct Calendar {
|
||||
pub struct Calendar<'a> {
|
||||
cur_date: NaiveDate,
|
||||
dirty: bool,
|
||||
offset: f64,
|
||||
sections: u32,
|
||||
font_size: u32,
|
||||
calendar_cache: Font,
|
||||
month_cache: Font,
|
||||
year_cache: Font,
|
||||
day_cache: Font,
|
||||
calendar_cache: Font<'a>,
|
||||
month_cache: Font<'a>,
|
||||
year_cache: Font<'a>,
|
||||
day_cache: Font<'a>,
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
impl<'a> Calendar<'a> {
|
||||
fn draw_month(
|
||||
&self,
|
||||
buf: &mut Buffer,
|
||||
@@ -152,15 +155,21 @@ impl Calendar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
pub fn new(time: NaiveDateTime, font_size: f32, sections: u32) -> Box<Calendar> {
|
||||
let mut calendar_cache = Font::new(&DEJAVUSANS_MONO, font_size * 2.0);
|
||||
impl<'a> Calendar<'a> {
|
||||
pub fn new(
|
||||
time: NaiveDateTime,
|
||||
font_primary: FontRef<'a>,
|
||||
font_secondary: FontRef<'a>,
|
||||
font_size: f32,
|
||||
sections: u32,
|
||||
) -> Box<Calendar<'a>> {
|
||||
let mut calendar_cache = Font::new(font_secondary, font_size * 2.0);
|
||||
calendar_cache.add_str_to_cache("0123456789");
|
||||
let mut month_cache = Font::new(&ROBOTO_REGULAR, font_size * 4.0);
|
||||
let mut month_cache = Font::new(font_primary, font_size * 4.0);
|
||||
month_cache.add_str_to_cache("JanuryFebMchApilJgstSmOoNvD");
|
||||
let mut year_cache = Font::new(&DEJAVUSANS_MONO, font_size * 1.5);
|
||||
let mut year_cache = Font::new(font_primary, font_size * 1.5);
|
||||
year_cache.add_str_to_cache("-0123456789");
|
||||
let mut day_cache = Font::new(&DEJAVUSANS_MONO, font_size);
|
||||
let mut day_cache = Font::new(font_primary, font_size);
|
||||
day_cache.add_str_to_cache("MONTUEWDHFRISA");
|
||||
Box::new(Calendar {
|
||||
cur_date: time.date(),
|
||||
@@ -176,7 +185,7 @@ impl Calendar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Calendar {
|
||||
impl<'a> Widget for Calendar<'a> {
|
||||
fn wait(&mut self, _: &mut WaitContext) {}
|
||||
fn enter(&mut self) {}
|
||||
fn leave(&mut self) {}
|
||||
|
||||
+11
-8
@@ -1,18 +1,21 @@
|
||||
use crate::color::Color;
|
||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||
use crate::draw::Font;
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget},
|
||||
};
|
||||
|
||||
use chrono::{Duration, NaiveDateTime, Timelike};
|
||||
|
||||
pub struct Clock {
|
||||
pub struct Clock<'a> {
|
||||
cur_time: NaiveDateTime,
|
||||
clock_cache: Font,
|
||||
clock_cache: Font<'a>,
|
||||
size: f32,
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
pub fn new(time: NaiveDateTime, size: f32) -> Box<Clock> {
|
||||
let mut clock_cache = Font::new(&ROBOTO_REGULAR, size);
|
||||
impl<'a> Clock<'a> {
|
||||
pub fn new(time: NaiveDateTime, font: FontRef, size: f32) -> Box<Clock> {
|
||||
let mut clock_cache = Font::new(font, size);
|
||||
clock_cache.add_str_to_cache("0123456789:");
|
||||
|
||||
Box::new(Clock {
|
||||
@@ -23,7 +26,7 @@ impl Clock {
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Clock {
|
||||
impl<'a> Widget for Clock<'a> {
|
||||
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||
let target = (self.cur_time + Duration::seconds(60))
|
||||
.with_second(0)
|
||||
|
||||
+11
-8
@@ -1,18 +1,21 @@
|
||||
use crate::color::Color;
|
||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||
use crate::draw::Font;
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget},
|
||||
};
|
||||
|
||||
use chrono::{Datelike, NaiveDateTime};
|
||||
|
||||
pub struct Date {
|
||||
pub struct Date<'a> {
|
||||
cur_time: NaiveDateTime,
|
||||
date_cache: Font,
|
||||
date_cache: Font<'a>,
|
||||
size: f32,
|
||||
}
|
||||
|
||||
impl Date {
|
||||
pub fn new(time: NaiveDateTime, size: f32) -> Box<Date> {
|
||||
let mut date_cache = Font::new(&ROBOTO_REGULAR, size);
|
||||
impl<'a> Date<'a> {
|
||||
pub fn new(time: NaiveDateTime, font: FontRef, size: f32) -> Box<Date> {
|
||||
let mut date_cache = Font::new(font, size);
|
||||
date_cache
|
||||
.add_str_to_cache("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/ ");
|
||||
|
||||
@@ -24,7 +27,7 @@ impl Date {
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Date {
|
||||
impl<'a> Widget for Date<'a> {
|
||||
fn wait(&mut self, _: &mut WaitContext) {}
|
||||
fn enter(&mut self) {}
|
||||
fn leave(&mut self) {}
|
||||
|
||||
+11
-7
@@ -2,8 +2,11 @@ use crate::buffer::Buffer;
|
||||
use crate::cmd::Cmd;
|
||||
use crate::color::Color;
|
||||
use crate::desktop::{load_desktop_files, Desktop};
|
||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||
use crate::draw::Font;
|
||||
use crate::{
|
||||
fonts::FontRef,
|
||||
widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget},
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
@@ -14,7 +17,7 @@ use std::sync::mpsc::Sender;
|
||||
use fuzzy_matcher::skim::{fuzzy_indices, fuzzy_match};
|
||||
use smithay_client_toolkit::keyboard::keysyms;
|
||||
|
||||
pub struct Launcher {
|
||||
pub struct Launcher<'a> {
|
||||
options: Vec<Desktop>,
|
||||
term_opener: String,
|
||||
app_opener: String,
|
||||
@@ -23,15 +26,16 @@ pub struct Launcher {
|
||||
input: String,
|
||||
result: Option<String>,
|
||||
offset: usize,
|
||||
font: RefCell<Font>,
|
||||
font: RefCell<Font<'a>>,
|
||||
font_size: u32,
|
||||
length: u32,
|
||||
dirty: bool,
|
||||
tx: Sender<Cmd>,
|
||||
}
|
||||
|
||||
impl Launcher {
|
||||
impl<'a> Launcher<'a> {
|
||||
pub fn new(
|
||||
font: FontRef,
|
||||
font_size: f32,
|
||||
length: u32,
|
||||
listener: Sender<Cmd>,
|
||||
@@ -48,7 +52,7 @@ impl Launcher {
|
||||
input: "".to_string(),
|
||||
result: None,
|
||||
offset: 0,
|
||||
font: RefCell::new(Font::new(&ROBOTO_REGULAR, font_size)),
|
||||
font: RefCell::new(Font::new(font, font_size)),
|
||||
font_size: font_size as u32,
|
||||
length,
|
||||
dirty: true,
|
||||
@@ -244,7 +248,7 @@ impl Matcher {
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Launcher {
|
||||
impl<'a> Widget for Launcher<'a> {
|
||||
fn wait(&mut self, _: &mut WaitContext) {}
|
||||
fn enter(&mut self) {}
|
||||
fn leave(&mut self) {
|
||||
|
||||
Reference in New Issue
Block a user