feat: improve font handling, add wrapper error type

This commit is contained in:
Rowan 2025-01-26 14:05:37 -06:00
parent 27b7b25814
commit 3e656671d9
2 changed files with 91 additions and 37 deletions

View file

@ -1,13 +1,57 @@
use pango::{prelude::FontMapExt, Font, FontDescription};
use std::fmt::Display;
use pango::{
prelude::{FontExt, FontMapExt},
Font as PangoFont, FontDescription, Language, SCALE,
};
use pangocairo::FontMap;
pub trait FontExt {
fn load_from_string(value: &str) -> Option<Font> {
let fontmap = FontMap::default();
let context = fontmap.create_context();
let desc = FontDescription::from_string(value);
fontmap.load_font(&context, &desc)
pub struct Font {
font: PangoFont,
language: Language,
}
impl Font {
pub fn new(font: PangoFont) -> Self {
Self {
font,
language: Language::default(),
}
}
impl FontExt for pango::Font {}
pub fn with_language(self, language: Language) -> Self {
Self { language, ..self }
}
pub fn family(&self) -> String {
self.font.describe().family().unwrap().to_string()
}
pub fn size(&self) -> i32 {
self.font.describe().size() / SCALE
}
pub fn height(&self) -> i32 {
self.font.metrics(Some(&self.language)).height() / SCALE
}
pub fn load_from_string(family: &str, size: usize) -> Option<Font> {
let font_str = format!("{family} {size}");
let fontmap = FontMap::default();
let context = fontmap.create_context();
let desc = FontDescription::from_string(&font_str);
fontmap.load_font(&context, &desc).map(Font::new)
}
}
impl Default for Font {
fn default() -> Self {
Font::load_from_string("monospace", 10).unwrap()
}
}
impl Display for Font {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}\" {}", self.family(), self.size())
}
}

View file

@ -1,48 +1,58 @@
pub mod color;
pub mod font;
use std::fmt::Display;
use color::ColorPair;
use font::FontExt;
use pango::{prelude::FontExt as _, Font, Language, SCALE};
use font::Font;
use wmenu_rs::{Menu as WMenu, MenuError};
#[derive(Debug)]
pub enum Error {
WMenu(MenuError),
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::WMenu(menu_error) => write!(f, "{menu_error}"),
}
}
}
impl std::error::Error for Error {}
impl From<MenuError> for Error {
fn from(value: MenuError) -> Self {
Self::WMenu(value)
}
}
pub struct Config {
pub bottom: bool,
pub padding: usize,
pub lines: i32,
pub font: String,
pub font_size: u8,
pub font_family: String,
pub font_size: usize,
pub language: Option<String>,
pub normal_color: ColorPair,
pub prompt_color: ColorPair,
pub selection_color: ColorPair,
}
impl Config {
// TODO: add font struct to handle these methods
fn font_string(&self) -> String {
format!("{} {}", self.font, self.font_size)
}
fn font_height(&self, font: &str) -> i32 {
let font = Font::load_from_string(font).unwrap();
// TODO: let user pick language
let metrics = font.metrics(Some(&Language::default()));
metrics.height() / SCALE
}
// TODO: add ability to override height/line_height
fn apply(self, wmenu: &mut WMenu) {
let font_string = self.font_string();
let font_height = self.font_height(&font_string);
let font =
Font::load_from_string(self.font_family.as_str(), self.font_size).unwrap_or_default();
let font_height = font.height();
let line_height = font_height + 2;
let mut height = line_height;
// FIXME: ugly
if self.lines > 0 {
height = height + height * self.lines;
}
let height = if self.lines > 0 {
line_height + line_height * self.lines
} else {
line_height
};
wmenu
.height(height)
@ -50,7 +60,7 @@ impl Config {
.padding(self.padding.try_into().unwrap())
.lines(self.lines.try_into().unwrap())
.line_height(line_height)
.font(&font_string)
.font(&font.to_string())
.normal_bg(self.normal_color.bg.into())
.normal_fg(self.normal_color.fg.into())
.prompt_bg(self.prompt_color.bg.into())
@ -66,8 +76,9 @@ impl Default for Config {
bottom: false,
lines: 0,
padding: 16,
font: "monospace".to_string(),
font_family: "monospace".to_string(),
font_size: 12,
language: None,
normal_color: (0x222222ff, 0xbbbbbbffu32).into(),
prompt_color: (0x005577ff, 0xeeeeeeffu32).into(),
selection_color: (0x005577ff, 0x227799ffu32).into(),
@ -104,9 +115,8 @@ impl Menu {
self
}
// TODO: wrap MenuError with new error type
pub fn run(&self) -> Result<String, MenuError> {
self.inner().run()
pub fn run(&self) -> Result<String, Error> {
Ok(self.inner().run()?)
}
}