Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make fonts configurable per text class #200

Merged
merged 5 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ features = ["serde"]
members = ["kas-macros", "kas-theme", "kas-wgpu"]

[patch.crates-io]
kas-text = { git = "https://github.com/kas-gui/kas-text.git", rev = "c594569d5d8a4640c2b76865114453b2701d0c35" }
kas-text = { git = "https://github.com/kas-gui/kas-text.git", rev = "05b50366a0a2fe7d1e611d93acbb2d9f83c52f0a" }
11 changes: 11 additions & 0 deletions example-config/theme.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,14 @@ font_aliases:
Calibri:
mode: Append
list: [Carlito]
fonts:
Edit:
families:
- serif
EditMulti:
families:
- serif
MenuLabel:
families:
- sans-serif
weight: 600
1 change: 1 addition & 0 deletions kas-theme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ stack_dst = ["kas/stack_dst", "stack_dst_"]
unsize = ["stack_dst_/unsize"]

[dependencies]
linear-map = "1.2.0"
log = "0.4"
serde = { version = "1.0.123", features = ["derive"], optional = true }
stack_dst_ = { version = "0.6", package = "stack_dst", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion kas-theme/src/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl ColorsLinear {
/// Get text colour from class
pub fn text_class(&self, class: TextClass) -> Rgba {
match class {
TextClass::Label | TextClass::LabelFixed | TextClass::LabelScroll => self.label_text,
TextClass::Label | TextClass::MenuLabel | TextClass::LabelScroll => self.label_text,
TextClass::Button => self.button_text,
TextClass::Edit | TextClass::EditMulti => self.text,
}
Expand Down
24 changes: 23 additions & 1 deletion kas-theme/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
//! Theme configuration

use crate::{ColorsLinear, ColorsSrgb, ThemeConfig};
use kas::text::fonts::{fonts, AddMode};
use kas::draw::TextClass;
use kas::text::fonts::{fonts, AddMode, FontSelector};
use kas::TkAction;
use std::collections::BTreeMap;

Expand Down Expand Up @@ -34,6 +35,10 @@ pub struct Config {
/// Font aliases, used when searching for a font family matching the key.
#[cfg_attr(feature = "config", serde(default))]
font_aliases: BTreeMap<String, FontAliases>,

/// Standard fonts
#[cfg_attr(feature = "config", serde(default))]
fonts: BTreeMap<TextClass, FontSelector<'static>>,
}

impl Default for Config {
Expand All @@ -44,6 +49,7 @@ impl Default for Config {
active_scheme: Default::default(),
color_schemes: defaults::color_schemes(),
font_aliases: Default::default(),
fonts: defaults::fonts(),
}
}
}
Expand Down Expand Up @@ -85,6 +91,12 @@ impl Config {
pub fn get_active_scheme(&self) -> Option<ColorsSrgb> {
self.color_schemes.get(&self.active_scheme).cloned()
}

/// Get an iterator over font mappings
#[inline]
pub fn iter_fonts(&self) -> impl Iterator<Item = (&TextClass, &FontSelector<'static>)> {
self.fonts.iter()
}
}

/// Setters
Expand Down Expand Up @@ -163,4 +175,14 @@ mod defaults {
schemes.insert("dark".to_string(), ColorsLinear::dark().into());
schemes
}

pub fn fonts() -> BTreeMap<TextClass, FontSelector<'static>> {
let mut selector = FontSelector::new();
selector.set_families(vec!["serif".into()]);
let list = [
(TextClass::Edit, selector.clone()),
(TextClass::EditMulti, selector),
];
list.iter().cloned().collect()
}
}
86 changes: 51 additions & 35 deletions kas-theme/src/dim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
//!
//! Widget size and appearance can be modified through themes.

use linear_map::LinearMap;
use std::any::Any;
use std::f32;
use std::path::Path;
use std::rc::Rc;

use kas::cast::{Cast, CastFloat, ConvFloat};
use kas::draw::{self, DrawShared, ImageId, TextClass};
use kas::geom::{Size, Vec2};
use kas::layout::{AxisInfo, FrameRules, Margins, SizeRules, Stretch};
use kas::text::{TextApi, TextApiExt};
use kas::text::{fonts::FontId, TextApi, TextApiExt};

/// Parameterisation of [`Dimensions`]
///
Expand Down Expand Up @@ -99,14 +101,25 @@ impl Dimensions {
/// A convenient implementation of [`crate::Window`]
pub struct DimensionsWindow {
pub dims: Dimensions,
pub fonts: Rc<LinearMap<TextClass, FontId>>,
}

impl DimensionsWindow {
pub fn new(dims: DimensionsParams, pt_size: f32, scale_factor: f32) -> Self {
pub fn new(
dims: DimensionsParams,
pt_size: f32,
scale_factor: f32,
fonts: Rc<LinearMap<TextClass, FontId>>,
) -> Self {
DimensionsWindow {
dims: Dimensions::new(dims, pt_size, scale_factor),
fonts,
}
}

pub fn update(&mut self, dims: DimensionsParams, pt_size: f32, scale_factor: f32) {
self.dims = Dimensions::new(dims, pt_size, scale_factor);
}
}

impl<D: DrawShared> crate::Window<D> for DimensionsWindow {
Expand All @@ -118,12 +131,12 @@ impl<D: DrawShared> crate::Window<D> for DimensionsWindow {
#[cfg(not(feature = "gat"))]
unsafe fn size_handle<'a>(&'a mut self, draw: &'a mut D) -> Self::SizeHandle {
// We extend lifetimes (unsafe) due to the lack of associated type generics.
let h: SizeHandle<'a, D> = SizeHandle::new(&self.dims, draw);
let h: SizeHandle<'a, D> = SizeHandle::new(self, draw);
std::mem::transmute(h)
}
#[cfg(feature = "gat")]
fn size_handle<'a>(&'a mut self, draw: &'a mut D) -> Self::SizeHandle<'a> {
SizeHandle::new(&self.dims, draw)
SizeHandle::new(self, draw)
}

fn as_any_mut(&mut self) -> &mut dyn Any {
Expand All @@ -132,57 +145,57 @@ impl<D: DrawShared> crate::Window<D> for DimensionsWindow {
}

pub struct SizeHandle<'a, D: DrawShared> {
dims: &'a Dimensions,
w: &'a DimensionsWindow,
draw: &'a mut D,
}

impl<'a, D: DrawShared> SizeHandle<'a, D> {
pub fn new(dims: &'a Dimensions, draw: &'a mut D) -> Self {
SizeHandle { dims, draw }
pub fn new(w: &'a DimensionsWindow, draw: &'a mut D) -> Self {
SizeHandle { w, draw }
}
}

impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
fn scale_factor(&self) -> f32 {
self.dims.scale_factor
self.w.dims.scale_factor
}

fn pixels_from_points(&self, pt: f32) -> f32 {
self.dims.dpp * pt
self.w.dims.dpp * pt
}

fn pixels_from_em(&self, em: f32) -> f32 {
self.dims.dpp * self.dims.pt_size * em
self.w.dims.dpp * self.w.dims.pt_size * em
}

fn frame(&self, _vert: bool) -> FrameRules {
FrameRules::new_sym(self.dims.frame, 0, 0)
FrameRules::new_sym(self.w.dims.frame, 0, 0)
}
fn menu_frame(&self, vert: bool) -> FrameRules {
let mut size = self.dims.frame;
let mut size = self.w.dims.frame;
if vert {
size /= 2;
}
FrameRules::new_sym(size, 0, 0)
}
fn separator(&self) -> Size {
Size::splat(self.dims.frame)
Size::splat(self.w.dims.frame)
}

fn nav_frame(&self, _vert: bool) -> FrameRules {
FrameRules::new_sym(self.dims.inner_margin.into(), 0, 0)
FrameRules::new_sym(self.w.dims.inner_margin.into(), 0, 0)
}

fn inner_margin(&self) -> Size {
Size::splat(self.dims.inner_margin.into())
Size::splat(self.w.dims.inner_margin.into())
}

fn outer_margins(&self) -> Margins {
Margins::splat(self.dims.outer_margin)
Margins::splat(self.w.dims.outer_margin)
}

fn line_height(&self, _: TextClass) -> i32 {
self.dims.line_height
self.w.dims.line_height
}

fn text_bound(
Expand All @@ -192,8 +205,11 @@ impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
axis: AxisInfo,
) -> SizeRules {
let required = text.update_env(|env| {
env.set_dpp(self.dims.dpp);
env.set_pt_size(self.dims.pt_size);
if let Some(font_id) = self.w.fonts.get(&class).cloned() {
env.set_font_id(font_id);
}
env.set_dpp(self.w.dims.dpp);
env.set_pt_size(self.w.dims.pt_size);

let mut bounds = kas::text::Vec2::INFINITY;
if let Some(size) = axis.size_other_if_fixed(false) {
Expand All @@ -209,11 +225,11 @@ impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
});
});

let margin = self.dims.text_margin;
let margin = self.w.dims.text_margin;
let margins = (margin, margin);
if axis.is_horizontal() {
let bound = i32::conv_ceil(required.0);
let min = self.dims.min_line_length;
let min = self.w.dims.min_line_length;
let (min, ideal) = match class {
TextClass::Edit => (min, 2 * min),
TextClass::EditMulti => (min, 3 * min),
Expand All @@ -223,18 +239,18 @@ impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
// cause problems (e.g. edit boxes greedily consuming too much
// space). This is a hard layout problem; for now don't do this.
let stretch = match class {
TextClass::LabelFixed => Stretch::None,
TextClass::MenuLabel => Stretch::None,
TextClass::Button => Stretch::Filler,
_ => Stretch::Low,
};
SizeRules::new(min, ideal, margins, stretch)
} else {
let min = match class {
TextClass::Label => i32::conv_ceil(required.1),
TextClass::LabelFixed | TextClass::Button | TextClass::Edit => {
self.dims.line_height
TextClass::MenuLabel | TextClass::Button | TextClass::Edit => {
self.w.dims.line_height
}
TextClass::EditMulti | TextClass::LabelScroll => self.dims.line_height * 3,
TextClass::EditMulti | TextClass::LabelScroll => self.w.dims.line_height * 3,
};
let ideal = i32::conv_ceil(required.1).max(min);
let stretch = match class {
Expand All @@ -246,23 +262,23 @@ impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
}

fn edit_marker_width(&self) -> f32 {
self.dims.font_marker_width
self.w.dims.font_marker_width
}

fn button_surround(&self, _vert: bool) -> FrameRules {
let inner = self.dims.inner_margin.into();
let outer = self.dims.outer_margin;
FrameRules::new_sym(self.dims.frame, inner, outer)
let inner = self.w.dims.inner_margin.into();
let outer = self.w.dims.outer_margin;
FrameRules::new_sym(self.w.dims.frame, inner, outer)
}

fn edit_surround(&self, _vert: bool) -> FrameRules {
let inner = self.dims.inner_margin.into();
let inner = self.w.dims.inner_margin.into();
let outer = 0;
FrameRules::new_sym(self.dims.frame, inner, outer)
FrameRules::new_sym(self.w.dims.frame, inner, outer)
}

fn checkbox(&self) -> Size {
Size::splat(self.dims.checkbox)
Size::splat(self.w.dims.checkbox)
}

#[inline]
Expand All @@ -271,17 +287,17 @@ impl<'a, D: DrawShared> draw::SizeHandle for SizeHandle<'a, D> {
}

fn scrollbar(&self) -> (Size, i32) {
let size = self.dims.scrollbar;
let size = self.w.dims.scrollbar;
(size, 3 * size.0)
}

fn slider(&self) -> (Size, i32) {
let size = self.dims.slider;
let size = self.w.dims.slider;
(size, 5 * size.0)
}

fn progress_bar(&self) -> Size {
self.dims.progress_bar
self.w.dims.progress_bar
}

fn load_image(&mut self, path: &Path) -> Result<ImageId, Box<dyn std::error::Error + 'static>> {
Expand Down
26 changes: 19 additions & 7 deletions kas-theme/src/flat_theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
//!
//! Widget size and appearance can be modified through themes.

use linear_map::LinearMap;
use std::f32;
use std::ops::Range;
use std::rc::Rc;

use crate::{ColorsLinear, Config, Dimensions, DimensionsParams, DimensionsWindow, Theme, Window};
use crate::{ColorsLinear, Config, DimensionsParams, DimensionsWindow, Theme, Window};
use kas::cast::Cast;
use kas::dir::{Direction, Directional};
use kas::draw::{
Expand All @@ -19,7 +21,7 @@ use kas::draw::{
};
use kas::geom::*;
use kas::text::format::FormattableText;
use kas::text::{AccelString, Effect, Text, TextApi, TextDisplay};
use kas::text::{fonts, AccelString, Effect, Text, TextApi, TextDisplay};
use kas::TkAction;

// Used to ensure a rectangular background is inside a circular corner.
Expand All @@ -29,8 +31,9 @@ const BG_SHRINK_FACTOR: f32 = 1.0 - std::f32::consts::FRAC_1_SQRT_2;
/// A theme with flat (unshaded) rendering
#[derive(Clone, Debug)]
pub struct FlatTheme {
config: Config,
cols: ColorsLinear,
pub(crate) config: Config,
pub(crate) cols: ColorsLinear,
pub(crate) fonts: Option<Rc<LinearMap<TextClass, fonts::FontId>>>,
}

impl FlatTheme {
Expand All @@ -40,6 +43,7 @@ impl FlatTheme {
FlatTheme {
config: Default::default(),
cols: ColorsLinear::default(),
fonts: None,
}
}

Expand Down Expand Up @@ -113,14 +117,22 @@ where
if let Err(e) = kas::text::fonts::fonts().select_default() {
panic!("Error loading font: {}", e);
}
let fonts = fonts::fonts();
self.fonts = Some(Rc::new(
self.config
.iter_fonts()
.filter_map(|(c, s)| fonts.select_font(&s).ok().map(|id| (*c, id)))
.collect(),
));
}

fn new_window(&self, _draw: &mut D::Draw, dpi_factor: f32) -> Self::Window {
DimensionsWindow::new(DIMS, self.config.font_size(), dpi_factor)
fn new_window(&self, dpi_factor: f32) -> Self::Window {
let fonts = self.fonts.as_ref().clone().unwrap().clone();
DimensionsWindow::new(DIMS, self.config.font_size(), dpi_factor, fonts)
}

fn update_window(&self, window: &mut Self::Window, dpi_factor: f32) {
window.dims = Dimensions::new(DIMS, self.config.font_size(), dpi_factor);
window.update(DIMS, self.config.font_size(), dpi_factor);
}

#[cfg(not(feature = "gat"))]
Expand Down
Loading