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

kas-text revisions: text_is_rtl, configure, wrap_width, remove Environment, fonts::library #445

Merged
merged 10 commits into from
Mar 10, 2024
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,7 @@ members = [
"crates/kas-view",
"examples/mandlebrot",
]

[patch.crates-io.kas-text]
git = "https://github.com/kas-gui/kas-text.git"
rev = "d9d2a99b7d94deeea48d67f3b7ba9237a12cea6d"
18 changes: 12 additions & 6 deletions crates/kas-core/src/decorations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! Note: due to definition in kas-core, some widgets must be duplicated.

use crate::event::{CursorIcon, ResizeDirection};
use crate::text::Text;
use crate::text::{NotReady, Text};
use crate::theme::TextClass;
use kas::prelude::*;
use kas::theme::MarkStyle;
Expand Down Expand Up @@ -125,22 +125,27 @@ impl_scope! {
}

impl Layout for Self {
#[inline]
fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
axis.set_default_align_hv(Align::Center, Align::Center);
sizer.text_rules(&mut self.label, Self::CLASS, axis)
sizer.text_rules(&mut self.label, axis)
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
self.core.rect = rect;
cx.text_set_size(&mut self.label, Self::CLASS, rect.size, None);
cx.text_set_size(&mut self.label, rect.size);
}

fn draw(&mut self, mut draw: DrawCx) {
draw.text(self.rect(), &self.label, Self::CLASS);
}
}

impl Events for Self {
fn configure(&mut self, cx: &mut ConfigCx) {
cx.text_configure(&mut self.label, Self::CLASS);
}
}

impl HasStr for Self {
fn get_str(&self) -> &str {
self.label.as_str()
Expand All @@ -150,9 +155,10 @@ impl_scope! {
impl HasString for Self {
fn set_string(&mut self, string: String) -> Action {
self.label.set_string(string);
match self.label.try_prepare() {
match self.label.prepare() {
Err(NotReady) => Action::empty(),
Ok(false) => Action::REDRAW,
Ok(true) => Action::RESIZE,
_ => Action::REDRAW,
}
}
}
Expand Down
29 changes: 17 additions & 12 deletions crates/kas-core/src/event/cx/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::messages::Erased;
use crate::text::TextApi;
use crate::theme::{Feature, SizeCx, TextClass, ThemeSize};
use crate::{Id, Node};
use cast::Cast;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};

Expand Down Expand Up @@ -122,22 +123,26 @@ impl<'a> ConfigCx<'a> {
self.sh.align_feature(feature, rect, align)
}

/// Configure a text object
///
/// This selects a font given the [`TextClass`],
/// [theme configuration][crate::theme::Config] and
/// the loaded [fonts][crate::text::fonts].
#[inline]
pub fn text_configure(&self, text: &mut dyn TextApi, class: TextClass) {
self.sh.text_configure(text, class);
}

/// Prepare a text object
///
/// This sets the text's font, font size, wrapping and optionally alignment,
/// then performs the text preparation necessary before display.
/// Wrap and align text for display at the given `size`.
///
/// Note: setting alignment here is not necessary when the default alignment
/// is desired or when [`SizeCx::text_rules`] is used.
/// Call [`text_configure`][Self::text_configure] before this method.
#[inline]
pub fn text_set_size(
&self,
text: &mut dyn TextApi,
class: TextClass,
size: Size,
align: Option<AlignPair>,
) {
self.sh.text_set_size(text, class, size, align)
pub fn text_set_size(&self, text: &mut dyn TextApi, size: Size) {
text.set_wrap_width(size.0.cast());
text.set_bounds(size.cast());
text.prepare().expect("not configured");
}
}

Expand Down
12 changes: 9 additions & 3 deletions crates/kas-core/src/hidden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::geom::{Coord, Offset, Rect};
use crate::layout::{Align, AxisInfo, SizeRules};
use crate::text::{Text, TextApi};
use crate::theme::{DrawCx, SizeCx, TextClass};
use crate::{Id, Layout, NavAdvance, Node, Widget};
use crate::{Events, Id, Layout, NavAdvance, Node, Widget};
use kas_macros::{autoimpl, impl_scope};

impl_scope! {
Expand Down Expand Up @@ -51,19 +51,25 @@ impl_scope! {
#[inline]
fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
axis.set_default_align_hv(Align::Default, Align::Center);
sizer.text_rules(&mut self.label, Self::CLASS, axis)
sizer.text_rules(&mut self.label, axis)
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
self.core.rect = rect;
cx.text_set_size(&mut self.label, Self::CLASS, rect.size, None);
cx.text_set_size(&mut self.label, rect.size);
}

fn draw(&mut self, mut draw: DrawCx) {
draw.text(self.rect(), &self.label, Self::CLASS);
}
}

impl Events for Self {
fn configure(&mut self, cx: &mut ConfigCx) {
cx.text_configure(&mut self.label, Self::CLASS);
}
}

impl HasStr for Self {
fn get_str(&self) -> &str {
self.label.as_str()
Expand Down
10 changes: 0 additions & 10 deletions crates/kas-core/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,6 @@ impl AxisInfo {
}
}

/// Size of other axis, if fixed and `vertical` matches this axis.
#[inline]
pub fn size_other_if_fixed(&self, vertical: bool) -> Option<i32> {
if vertical == self.vertical && self.has_fixed {
Some(self.other_axis)
} else {
None
}
}

/// Subtract `x` from size of other axis (if applicable)
#[inline]
pub fn sub_other(&mut self, x: i32) {
Expand Down
9 changes: 5 additions & 4 deletions crates/kas-core/src/text/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
//! Most of this module is simply a re-export of the [KAS Text] API, hence the
//! lower level of integration than other parts of the library.
//!
//! [`Text`] objects *must* be prepared before usage, otherwise they may appear
//! empty. Call [`ConfigCx::text_set_size`] from [`Layout::set_rect`] to set
//! text position and prepare. If text is adjusted, one may use e.g.
//! [`TextApi::prepare`] to update.
//! [`Text`] objects *must* be configured and prepared before usage, otherwise
//! they may appear empty. Call [`ConfigCx::text_config`] from
//! [`Events::configure`] and [`ConfigCx::text_set_size`] from
//! [`Layout::set_rect`] to set text position and prepare.
//! If text is adjusted, one may use e.g. [`TextApi::prepare`] to update.
//!
//! [KAS Text]: https://github.com/kas-gui/kas-text/

Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/theme/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! Theme configuration

use super::{ColorsSrgb, TextClass, ThemeConfig};
use crate::text::fonts::{fonts, AddMode, FontSelector};
use crate::text::fonts::{self, AddMode, FontSelector};
use crate::Action;
use std::collections::BTreeMap;
use std::time::Duration;
Expand Down Expand Up @@ -244,7 +244,7 @@ impl ThemeConfig for Config {
/// Apply config effects which only happen on startup
fn apply_startup(&self) {
if !self.font_aliases.is_empty() {
fonts().update_db(|db| {
fonts::library().update_db(|db| {
for (family, aliases) in self.font_aliases.iter() {
db.add_aliases(
family.to_string().into(),
Expand Down
72 changes: 28 additions & 44 deletions crates/kas-core/src/theme/dimensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::cast::traits::*;
use crate::dir::Directional;
use crate::geom::{Rect, Size, Vec2};
use crate::layout::{AlignPair, AxisInfo, FrameRules, Margins, SizeRules, Stretch};
use crate::text::{fonts::FontId, TextApi, TextApiExt};
use crate::text::{fonts::FontId, Direction, TextApi};

crate::impl_scope! {
/// Parameterisation of [`Dimensions`]
Expand Down Expand Up @@ -319,50 +319,50 @@ impl<D: 'static> ThemeSize for Window<D> {

fn line_height(&self, class: TextClass) -> i32 {
let font_id = self.fonts.get(&class).cloned().unwrap_or_default();
crate::text::fonts::fonts()
crate::text::fonts::library()
.get_first_face(font_id)
.expect("invalid font_id")
.height(self.dims.dpem)
.cast_ceil()
}

fn text_rules(&self, text: &mut dyn TextApi, class: TextClass, axis: AxisInfo) -> SizeRules {
fn text_configure(&self, text: &mut dyn TextApi, class: TextClass) {
let direction = Direction::Auto;
let font_id = self.fonts.get(&class).cloned().unwrap_or_default();
let dpem = self.dims.dpem;
let wrap = match class.multi_line() {
false => f32::INFINITY,
true => 0.0, // NOTE: finite value used as a flag
};
text.set_font_properties(direction, font_id, dpem, wrap);
text.configure().expect("invalid font_id");
}

fn text_rules(&self, text: &mut dyn TextApi, axis: AxisInfo) -> SizeRules {
let margin = match axis.is_horizontal() {
true => self.dims.m_text.0,
false => self.dims.m_text.1,
};
let margins = (margin, margin);

let mut env = text.env();

// TODO(opt): maybe font look-up should only happen during configure?
if let Some(font_id) = self.fonts.get(&class).cloned() {
env.font_id = font_id;
}
env.dpem = self.dims.dpem;
// TODO(opt): setting horizontal alignment now could avoid re-wrapping
// text. Unfortunately we don't know the desired alignment here.
let wrap = class.multi_line();
env.wrap = wrap;
let mut align_pair = text.get_align();
let align = axis.align_or_default();
if axis.is_horizontal() {
env.align.0 = align;
align_pair.0 = align;
} else {
env.align.1 = align;
}
if let Some(size) = axis.size_other_if_fixed(true) {
env.bounds.0 = size.cast();
align_pair.1 = align;
}
text.set_align(align_pair);

text.set_env(env);
let wrap = text.get_wrap_width();

if axis.is_horizontal() {
if wrap {
if wrap.is_finite() {
let min = self.dims.min_line_length;
let limit = 2 * min;
let bound: i32 = text
.measure_width(limit.cast())
.expect("invalid font_id")
.expect("not configured")
.cast_ceil();

// NOTE: using different variable-width stretch policies here can
Expand All @@ -372,36 +372,20 @@ impl<D: 'static> ThemeSize for Window<D> {
} else {
let bound: i32 = text
.measure_width(f32::INFINITY)
.expect("invalid font_id")
.expect("not configured")
.cast_ceil();
SizeRules::new(bound, bound, margins, Stretch::Filler)
}
} else {
let bound: i32 = text.measure_height().expect("invalid font_id").cast_ceil();
if wrap.is_finite() {
text.set_wrap_width(axis.other().map(|w| w.cast()).unwrap_or(f32::INFINITY));
}

let bound: i32 = text.measure_height().expect("not configured").cast_ceil();

let line_height = self.dims.dpem.cast_ceil();
let min = bound.max(line_height);
SizeRules::new(min, min, margins, Stretch::Filler)
}
}

fn text_set_size(
&self,
text: &mut dyn TextApi,
class: TextClass,
size: Size,
align: Option<AlignPair>,
) {
let mut env = text.env();
if let Some(font_id) = self.fonts.get(&class).cloned() {
env.font_id = font_id;
}
env.dpem = self.dims.dpem;
env.wrap = class.multi_line();
if let Some(align) = align {
env.align = align.into();
}
env.bounds = size.cast();
text.update_env(env).expect("invalid font_id");
}
}
Loading