mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
parent
7a9ddaa926
commit
3762bc6dc1
12 changed files with 498 additions and 75 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -53,6 +53,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `ColorExt::into_source_and_lightness` has been renamed to
|
- `ColorExt::into_source_and_lightness` has been renamed to
|
||||||
`ColorExt::into_hsl`, and its return type is now `Hsl` instead of the
|
`ColorExt::into_hsl`, and its return type is now `Hsl` instead of the
|
||||||
individual components.
|
individual components.
|
||||||
|
- `Window::font_data_to_load` has been renamed to `fonts`, and now has the
|
||||||
|
`FontCollection` type.
|
||||||
|
- Several font-related functions have been moved from `GraphicsContext` to
|
||||||
|
`WidgetContext`:
|
||||||
|
|
||||||
|
- `GraphicsContext::set_font_family()`
|
||||||
|
- `GraphicsContext::find_available_font_family()`
|
||||||
|
- `GraphicsContext::set_available_font_family()`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
@ -218,6 +226,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `List` is a new widget that creates lists similar to HTML's `ol` and `ul`
|
- `List` is a new widget that creates lists similar to HTML's `ol` and `ul`
|
||||||
tags.
|
tags.
|
||||||
- `Dynamic::try_lock()` is a panic-free version of `Dynamic::lock()`.
|
- `Dynamic::try_lock()` is a panic-free version of `Dynamic::lock()`.
|
||||||
|
- `FontCollection` is a new type that can be used to load fonts at app/window
|
||||||
|
startup or at runtime.
|
||||||
|
- `Cushy::fonts()`returns a `FontCollection` that is loaded into all windows.
|
||||||
|
- `WidgetContext::loaded_font_faces()` returns a list of fonts that were loaded
|
||||||
|
for a given `LoadedFont`.
|
||||||
|
- `Graphics::font_system()` returns a reference to the underlying Cosmic Text
|
||||||
|
`FontSystem`.
|
||||||
|
|
||||||
[plotters]: https://github.com/plotters-rs/plotters
|
[plotters]: https://github.com/plotters-rs/plotters
|
||||||
|
|
||||||
|
|
|
||||||
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -321,9 +321,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.89"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -1123,7 +1123,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kludgine"
|
name = "kludgine"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "git+https://github.com/khonsulabs/kludgine#eff686f12918a52321a63c3cb2abbbd68be45068"
|
source = "git+https://github.com/khonsulabs/kludgine#f2bc0d6ea9ebe2709ab208723fc7a072cf87164f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"alot",
|
"alot",
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ nominals = "0.3.0"
|
||||||
|
|
||||||
|
|
||||||
# [patch.crates-io]
|
# [patch.crates-io]
|
||||||
# kludgine = { path = "../kludgine" }
|
# cosmic-text = { path = "../cosmic-text" }
|
||||||
# appit = { path = "../appit" }
|
# appit = { path = "../appit" }
|
||||||
# figures = { path = "../figures" }
|
# figures = { path = "../figures" }
|
||||||
# alot = { git = "https://github.com/khonsulabs/alot" }
|
# alot = { git = "https://github.com/khonsulabs/alot" }
|
||||||
|
|
|
||||||
56
examples/dynamic-fonts.rs
Normal file
56
examples/dynamic-fonts.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
use cushy::fonts::FontCollection;
|
||||||
|
use cushy::styles::components::{FontFamily, FontWeight, LineHeight, TextSize};
|
||||||
|
use cushy::styles::{Component, DynamicComponent, FamilyOwned, FontFamilyList};
|
||||||
|
use cushy::value::{Dynamic, Source};
|
||||||
|
use cushy::widget::MakeWidget;
|
||||||
|
use cushy::widgets::input::InputValue;
|
||||||
|
use cushy::Run;
|
||||||
|
use figures::units::Px;
|
||||||
|
|
||||||
|
fn main() -> cushy::Result<()> {
|
||||||
|
let file_path = Dynamic::<String>::default();
|
||||||
|
let fonts = FontCollection::default();
|
||||||
|
let font_data = file_path.map_each(|path| std::fs::read(path).map_err(|err| err.to_string()));
|
||||||
|
let loaded_font = font_data.map_each({
|
||||||
|
let fonts = fonts.clone();
|
||||||
|
move |result| {
|
||||||
|
result
|
||||||
|
.as_ref()
|
||||||
|
.ok()
|
||||||
|
.map(|data| fonts.push_unloadable(data.to_vec()))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let primary_family_name = DynamicComponent::new({
|
||||||
|
let loaded_font = loaded_font.clone();
|
||||||
|
move |context| {
|
||||||
|
let font = loaded_font.get_tracking_invalidate(context)?;
|
||||||
|
|
||||||
|
let face = context.loaded_font_faces(&font).first()?;
|
||||||
|
Some(Component::custom(FontFamilyList::from(vec![
|
||||||
|
FamilyOwned::Name(face.families[0].0.clone()),
|
||||||
|
])))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let family_weight = DynamicComponent::new(move |context| {
|
||||||
|
let font = loaded_font.get_tracking_invalidate(context)?;
|
||||||
|
|
||||||
|
let face = context.loaded_font_faces(&font).first()?;
|
||||||
|
|
||||||
|
Some(Component::FontWeight(face.weight))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut window = file_path
|
||||||
|
.into_input()
|
||||||
|
.validation(font_data.clone())
|
||||||
|
.and(
|
||||||
|
"The quick brown fox jumps over the lazy dog."
|
||||||
|
.with(&TextSize, Px::new(36))
|
||||||
|
.with(&LineHeight, Px::new(36))
|
||||||
|
.with_dynamic(&FontFamily, primary_family_name)
|
||||||
|
.with_dynamic(&FontWeight, family_weight),
|
||||||
|
)
|
||||||
|
.into_rows()
|
||||||
|
.into_window();
|
||||||
|
window.fonts = fonts;
|
||||||
|
window.run()
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use kludgine::app::{AppEvent, AsApplication};
|
use kludgine::app::{AppEvent, AsApplication};
|
||||||
|
|
||||||
|
use crate::fonts::FontCollection;
|
||||||
use crate::utils::IgnorePoison;
|
use crate::utils::IgnorePoison;
|
||||||
use crate::window::sealed::WindowCommand;
|
use crate::window::sealed::WindowCommand;
|
||||||
use crate::window::WindowHandle;
|
use crate::window::WindowHandle;
|
||||||
|
|
@ -46,6 +47,7 @@ impl AsApplication<AppEvent<WindowCommand>> for PendingApp {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Cushy {
|
pub struct Cushy {
|
||||||
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
|
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
|
||||||
|
pub(crate) fonts: FontCollection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cushy {
|
impl Cushy {
|
||||||
|
|
@ -54,6 +56,7 @@ impl Cushy {
|
||||||
clipboard: Clipboard::new()
|
clipboard: Clipboard::new()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
|
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
|
||||||
|
fonts: FontCollection::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +68,12 @@ impl Cushy {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|mutex| mutex.lock().ignore_poison())
|
.map(|mutex| mutex.lock().ignore_poison())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the font collection that will be loaded in all Cushy windows.
|
||||||
|
#[must_use]
|
||||||
|
pub fn fonts(&self) -> &FontCollection {
|
||||||
|
&self.fonts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that is a Cushy application.
|
/// A type that is a Cushy application.
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,18 @@ use figures::units::{Lp, Px, UPx};
|
||||||
use figures::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size, Zero};
|
use figures::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size, Zero};
|
||||||
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
|
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
|
||||||
use kludgine::app::winit::window::CursorIcon;
|
use kludgine::app::winit::window::CursorIcon;
|
||||||
|
use kludgine::cosmic_text::FamilyOwned;
|
||||||
use kludgine::shapes::{Shape, StrokeOptions};
|
use kludgine::shapes::{Shape, StrokeOptions};
|
||||||
use kludgine::{Color, Kludgine, KludgineId};
|
use kludgine::{Color, Kludgine, KludgineId};
|
||||||
|
|
||||||
use crate::animation::ZeroToOne;
|
use crate::animation::ZeroToOne;
|
||||||
use crate::graphics::Graphics;
|
use crate::fonts::{LoadedFont, LoadedFontFace};
|
||||||
|
use crate::graphics::{FontState, Graphics};
|
||||||
use crate::styles::components::{
|
use crate::styles::components::{
|
||||||
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
|
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
|
||||||
Opacity, TextSize, WidgetBackground,
|
Opacity, TextSize, WidgetBackground,
|
||||||
};
|
};
|
||||||
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
|
use crate::styles::{ComponentDefinition, FontFamilyList, Styles, Theme, ThemePair};
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::value::{IntoValue, Source, Value};
|
use crate::value::{IntoValue, Source, Value};
|
||||||
use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance};
|
use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance};
|
||||||
|
|
@ -563,6 +565,36 @@ impl<'context, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'clip, 'gfx, 'pass>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the current font family.
|
||||||
|
pub fn set_font_family(&mut self, family: FamilyOwned) {
|
||||||
|
self.font_state.current_font_family = None;
|
||||||
|
self.gfx.set_font_family(family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the currently set font family list.
|
||||||
|
pub fn current_family_list(&mut self) -> FontFamilyList {
|
||||||
|
self.font_state
|
||||||
|
.current_font_family
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| FontFamilyList::from(vec![FamilyOwned::new(self.gfx.font_family())]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the first font family in `list` that is currently in the font
|
||||||
|
/// system, or None if no font families match.
|
||||||
|
pub fn find_available_font_family(&mut self, list: &FontFamilyList) -> Option<FamilyOwned> {
|
||||||
|
self.font_state.find_available_font_family(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the font family to the first family in `list`.
|
||||||
|
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
|
||||||
|
if self.font_state.current_font_family.as_ref() != Some(list) {
|
||||||
|
if let Some(family) = self.find_available_font_family(list) {
|
||||||
|
self.set_font_family(family);
|
||||||
|
}
|
||||||
|
self.font_state.current_font_family = Some(list.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates `self` to have `opacity`.
|
/// Updates `self` to have `opacity`.
|
||||||
///
|
///
|
||||||
/// This setting will be mixed with the current opacity value.
|
/// This setting will be mixed with the current opacity value.
|
||||||
|
|
@ -636,8 +668,7 @@ impl<'context, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'clip, 'gfx, 'pass>
|
||||||
/// Applies the current style settings for font family, text size, font
|
/// Applies the current style settings for font family, text size, font
|
||||||
/// style, and font weight.
|
/// style, and font weight.
|
||||||
pub fn apply_current_font_settings(&mut self) {
|
pub fn apply_current_font_settings(&mut self) {
|
||||||
self.gfx
|
self.set_available_font_family(&self.widget.get(&FontFamily));
|
||||||
.set_available_font_family(&self.widget.get(&FontFamily));
|
|
||||||
self.gfx.set_font_size(self.widget.get(&TextSize));
|
self.gfx.set_font_size(self.widget.get(&TextSize));
|
||||||
self.gfx.set_line_height(self.widget.get(&LineHeight));
|
self.gfx.set_line_height(self.widget.get(&LineHeight));
|
||||||
self.gfx.set_font_style(self.widget.get(&FontStyle));
|
self.gfx.set_font_style(self.widget.get(&FontStyle));
|
||||||
|
|
@ -856,6 +887,7 @@ pub struct WidgetContext<'context> {
|
||||||
theme: Cow<'context, ThemePair>,
|
theme: Cow<'context, ThemePair>,
|
||||||
cursor: &'context mut CursorState,
|
cursor: &'context mut CursorState,
|
||||||
pending_state: PendingState<'context>,
|
pending_state: PendingState<'context>,
|
||||||
|
font_state: &'context mut FontState,
|
||||||
effective_styles: Styles,
|
effective_styles: Styles,
|
||||||
cache: WidgetCacheKey,
|
cache: WidgetCacheKey,
|
||||||
}
|
}
|
||||||
|
|
@ -865,6 +897,7 @@ impl<'context> WidgetContext<'context> {
|
||||||
current_node: MountedWidget,
|
current_node: MountedWidget,
|
||||||
theme: &'context ThemePair,
|
theme: &'context ThemePair,
|
||||||
window: &'context mut dyn PlatformWindow,
|
window: &'context mut dyn PlatformWindow,
|
||||||
|
font_state: &'context mut FontState,
|
||||||
theme_mode: ThemeMode,
|
theme_mode: ThemeMode,
|
||||||
cursor: &'context mut CursorState,
|
cursor: &'context mut CursorState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
@ -891,6 +924,7 @@ impl<'context> WidgetContext<'context> {
|
||||||
},
|
},
|
||||||
cursor,
|
cursor,
|
||||||
current_node,
|
current_node,
|
||||||
|
font_state,
|
||||||
theme: Cow::Borrowed(theme),
|
theme: Cow::Borrowed(theme),
|
||||||
window,
|
window,
|
||||||
}
|
}
|
||||||
|
|
@ -902,6 +936,7 @@ impl<'context> WidgetContext<'context> {
|
||||||
tree: self.tree.clone(),
|
tree: self.tree.clone(),
|
||||||
current_node: self.current_node.clone(),
|
current_node: self.current_node.clone(),
|
||||||
window: &mut *self.window,
|
window: &mut *self.window,
|
||||||
|
font_state: &mut *self.font_state,
|
||||||
theme: Cow::Borrowed(self.theme.as_ref()),
|
theme: Cow::Borrowed(self.theme.as_ref()),
|
||||||
pending_state: self.pending_state.borrowed(),
|
pending_state: self.pending_state.borrowed(),
|
||||||
cache: self.cache,
|
cache: self.cache,
|
||||||
|
|
@ -940,6 +975,7 @@ impl<'context> WidgetContext<'context> {
|
||||||
},
|
},
|
||||||
current_node,
|
current_node,
|
||||||
tree: self.tree.clone(),
|
tree: self.tree.clone(),
|
||||||
|
font_state: &mut *self.font_state,
|
||||||
window: &mut *self.window,
|
window: &mut *self.window,
|
||||||
theme,
|
theme,
|
||||||
pending_state: self.pending_state.borrowed(),
|
pending_state: self.pending_state.borrowed(),
|
||||||
|
|
@ -1197,6 +1233,16 @@ impl<'context> WidgetContext<'context> {
|
||||||
pub fn cache_key(&self) -> WidgetCacheKey {
|
pub fn cache_key(&self) -> WidgetCacheKey {
|
||||||
self.cache
|
self.cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of faces that were loaded from `font`, or an empty slice
|
||||||
|
/// if no faces were loaded.
|
||||||
|
#[must_use]
|
||||||
|
pub fn loaded_font_faces(&self, font: &LoadedFont) -> &[LoadedFontFace] {
|
||||||
|
self.font_state
|
||||||
|
.loaded_fonts
|
||||||
|
.get(&font.id())
|
||||||
|
.map_or(&[], |ids| &ids.faces)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EventContext<'_> {
|
impl Drop for EventContext<'_> {
|
||||||
|
|
|
||||||
129
src/fonts.rs
Normal file
129
src/fonts.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
//! Types for loading fonts to use in Cushy.
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use alot::{LotId, Lots};
|
||||||
|
use kludgine::cosmic_text::fontdb::{self, Language};
|
||||||
|
use kludgine::cosmic_text::{Stretch, Style, Weight};
|
||||||
|
|
||||||
|
use crate::value::Dynamic;
|
||||||
|
|
||||||
|
/// A collection of fonts that can be loaded into Cushy.
|
||||||
|
#[derive(Clone, Default, PartialEq)]
|
||||||
|
pub struct FontCollection(pub(crate) Dynamic<FontCollectionData>);
|
||||||
|
|
||||||
|
impl FontCollection {
|
||||||
|
/// Pushes a font that will be unloaded when the last clone of the
|
||||||
|
/// [`LoadedFont`] is dropped.
|
||||||
|
#[must_use]
|
||||||
|
pub fn push_unloadable(&self, font_data: Vec<u8>) -> LoadedFont {
|
||||||
|
LoadedFont(Arc::new(LoadedFontHandle {
|
||||||
|
collection: self.clone(),
|
||||||
|
id: self.push_inner(font_data),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds `font_data` to this collection and returns self.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with(self, font_data: Vec<u8>) -> Self {
|
||||||
|
self.push(font_data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes `font_data` into this collection.
|
||||||
|
pub fn push(&self, font_data: Vec<u8>) {
|
||||||
|
self.push_inner(font_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_inner(&self, font_data: Vec<u8>) -> LotId {
|
||||||
|
self.0.lock().fonts.push(Arc::new(font_data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FontIter<'a> {
|
||||||
|
collection: *const (),
|
||||||
|
iter: alot::unordered::EntryIter<'a, Arc<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for FontIter<'a> {
|
||||||
|
type Item = (LoadedFontId, &'a Arc<Vec<u8>>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|(id, data)| {
|
||||||
|
(
|
||||||
|
LoadedFontId {
|
||||||
|
collection: self.collection,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct FontCollectionData {
|
||||||
|
fonts: Lots<Arc<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontCollectionData {
|
||||||
|
pub(crate) fn fonts(&self, collection: &FontCollection) -> FontIter<'_> {
|
||||||
|
FontIter {
|
||||||
|
collection: collection.0.as_ptr(),
|
||||||
|
iter: self.fonts.entries(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct LoadedFontHandle {
|
||||||
|
collection: FontCollection,
|
||||||
|
id: LotId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LoadedFontHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut collection = self.collection.0.lock();
|
||||||
|
collection.fonts.remove(self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to font data that has been loaded.
|
||||||
|
///
|
||||||
|
/// This type can be cloned and uses reference counting to track when to release
|
||||||
|
/// the underlying font data.
|
||||||
|
///
|
||||||
|
/// Font data is not parsed until it is loaded into a running Cushy window. To
|
||||||
|
/// find information about this handle, use
|
||||||
|
/// [`WidgetContext::loaded_font_faces()`](crate::context::WidgetContext::loaded_font_faces).
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub struct LoadedFont(Arc<LoadedFontHandle>);
|
||||||
|
|
||||||
|
impl LoadedFont {
|
||||||
|
pub(crate) fn id(&self) -> LoadedFontId {
|
||||||
|
LoadedFontId {
|
||||||
|
collection: self.0.collection.0.as_ptr(),
|
||||||
|
id: self.0.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
|
||||||
|
pub(crate) struct LoadedFontId {
|
||||||
|
pub(crate) collection: *const (),
|
||||||
|
pub(crate) id: LotId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a [`LoadedFont`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoadedFontFace {
|
||||||
|
/// The font database ID for this face.
|
||||||
|
pub id: fontdb::ID,
|
||||||
|
/// The names of the families contained in this face, and the corresponding
|
||||||
|
/// language of the name.
|
||||||
|
pub families: Vec<(String, Language)>,
|
||||||
|
/// The weight of the font face.
|
||||||
|
pub weight: Weight,
|
||||||
|
/// The style of the font face.
|
||||||
|
pub style: Style,
|
||||||
|
/// The stretch of the font face.
|
||||||
|
pub stretch: Stretch,
|
||||||
|
}
|
||||||
195
src/graphics.rs
195
src/graphics.rs
|
|
@ -1,11 +1,11 @@
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use ahash::AHashSet;
|
|
||||||
use figures::units::{Px, UPx};
|
use figures::units::{Px, UPx};
|
||||||
use figures::{
|
use figures::{
|
||||||
self, Fraction, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, ScreenUnit, Size, Zero,
|
self, Fraction, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, ScreenUnit, Size, Zero,
|
||||||
};
|
};
|
||||||
use kludgine::cosmic_text::FamilyOwned;
|
use kempt::{map, Map};
|
||||||
|
use kludgine::cosmic_text::{fontdb, FamilyOwned, FontSystem};
|
||||||
use kludgine::drawing::Renderer;
|
use kludgine::drawing::Renderer;
|
||||||
use kludgine::shapes::Shape;
|
use kludgine::shapes::Shape;
|
||||||
use kludgine::text::{MeasuredText, Text, TextOrigin};
|
use kludgine::text::{MeasuredText, Text, TextOrigin};
|
||||||
|
|
@ -14,13 +14,14 @@ use kludgine::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::animation::ZeroToOne;
|
use crate::animation::ZeroToOne;
|
||||||
|
use crate::fonts::{FontCollection, LoadedFontFace, LoadedFontId};
|
||||||
use crate::styles::FontFamilyList;
|
use crate::styles::FontFamilyList;
|
||||||
|
use crate::value::{DynamicRead, Generation, Source};
|
||||||
|
|
||||||
/// A 2d graphics context
|
/// A 2d graphics context
|
||||||
pub struct Graphics<'clip, 'gfx, 'pass> {
|
pub struct Graphics<'clip, 'gfx, 'pass> {
|
||||||
renderer: RenderContext<'clip, 'gfx, 'pass>,
|
renderer: RenderContext<'clip, 'gfx, 'pass>,
|
||||||
region: Rect<Px>,
|
region: Rect<Px>,
|
||||||
font_state: &'clip mut FontState,
|
|
||||||
pub(crate) opacity: ZeroToOne,
|
pub(crate) opacity: ZeroToOne,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,11 +33,10 @@ enum RenderContext<'clip, 'gfx, 'pass> {
|
||||||
impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
||||||
/// Returns a new graphics context for the given [`Renderer`].
|
/// Returns a new graphics context for the given [`Renderer`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(renderer: Renderer<'gfx, 'pass>, font_state: &'clip mut FontState) -> Self {
|
pub fn new(renderer: Renderer<'gfx, 'pass>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
region: renderer.clip_rect().into_signed(),
|
region: renderer.clip_rect().into_signed(),
|
||||||
renderer: RenderContext::Renderer(renderer),
|
renderer: RenderContext::Renderer(renderer),
|
||||||
font_state,
|
|
||||||
opacity: ZeroToOne::ONE,
|
opacity: ZeroToOne::ONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,28 +66,6 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the current font family.
|
|
||||||
pub fn set_font_family(&mut self, family: cosmic_text::FamilyOwned) {
|
|
||||||
self.font_state.current_font_family = None;
|
|
||||||
self.renderer.set_font_family(family);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the first font family in `list` that is currently in the font
|
|
||||||
/// system, or None if no font families match.
|
|
||||||
pub fn find_available_font_family(&mut self, list: &FontFamilyList) -> Option<FamilyOwned> {
|
|
||||||
self.font_state.find_available_font_family(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the font family to the first family in `list`.
|
|
||||||
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
|
|
||||||
if self.font_state.current_font_family.as_ref() != Some(list) {
|
|
||||||
if let Some(family) = self.find_available_font_family(list) {
|
|
||||||
self.set_font_family(family);
|
|
||||||
}
|
|
||||||
self.font_state.current_font_family = Some(list.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the underlying renderer.
|
/// Returns the underlying renderer.
|
||||||
///
|
///
|
||||||
/// Note: Kludgine graphics contexts only support clipping. This type adds
|
/// Note: Kludgine graphics contexts only support clipping. This type adds
|
||||||
|
|
@ -120,7 +98,6 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
||||||
Graphics {
|
Graphics {
|
||||||
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
|
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
|
||||||
region,
|
region,
|
||||||
font_state: &mut *self.font_state,
|
|
||||||
opacity: self.opacity,
|
opacity: self.opacity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,6 +287,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
||||||
self.renderer.draw_measured_text(text, origin);
|
self.renderer.draw_measured_text(text, origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the font system used to render.
|
||||||
|
pub fn font_system(&mut self) -> &mut FontSystem {
|
||||||
|
self.renderer.font_system()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns this renderer as a
|
/// Returns this renderer as a
|
||||||
/// [`DrawingArea`](plotters::drawing::DrawingArea) compatible with the
|
/// [`DrawingArea`](plotters::drawing::DrawingArea) compatible with the
|
||||||
/// [plotters](https://github.com/plotters-rs/plotters) crate.
|
/// [plotters](https://github.com/plotters-rs/plotters) crate.
|
||||||
|
|
@ -358,26 +340,163 @@ impl<'gfx, 'pass> DerefMut for RenderContext<'_, 'gfx, 'pass> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct LoadedFontIds {
|
||||||
|
generation: usize,
|
||||||
|
pub(crate) faces: Vec<LoadedFontFace>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FontState {
|
pub struct FontState {
|
||||||
fonts: AHashSet<String>,
|
app_fonts: FontCollection,
|
||||||
current_font_family: Option<FontFamilyList>,
|
app_font_generation: Generation,
|
||||||
|
window_fonts: FontCollection,
|
||||||
|
window_font_generation: Generation,
|
||||||
|
pub(crate) loaded_fonts: Map<LoadedFontId, LoadedFontIds>,
|
||||||
|
font_generation: usize,
|
||||||
|
fonts: Map<String, usize>,
|
||||||
|
pub(crate) current_font_family: Option<FontFamilyList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontState {
|
impl FontState {
|
||||||
pub fn new(db: &cosmic_text::fontdb::Database) -> Self {
|
pub fn new(
|
||||||
let faces = db.faces();
|
db: &mut cosmic_text::fontdb::Database,
|
||||||
let mut fonts = AHashSet::with_capacity(faces.size_hint().0);
|
window_fonts: FontCollection,
|
||||||
for (family, _) in faces.filter_map(|f| f.families.first()) {
|
app_fonts: FontCollection,
|
||||||
fonts.insert(family.clone());
|
) -> Self {
|
||||||
}
|
let mut fonts = Map::new();
|
||||||
Self {
|
Self::gather_available_family_names(&mut fonts, 0, db);
|
||||||
|
let mut state = Self {
|
||||||
fonts,
|
fonts,
|
||||||
current_font_family: None,
|
current_font_family: None,
|
||||||
|
window_font_generation: window_fonts.0.generation(),
|
||||||
|
window_fonts,
|
||||||
|
app_font_generation: app_fonts.0.generation(),
|
||||||
|
app_fonts,
|
||||||
|
font_generation: 0,
|
||||||
|
loaded_fonts: Map::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
state.update_fonts(db);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gather_available_family_names(
|
||||||
|
families: &mut Map<String, usize>,
|
||||||
|
generation: usize,
|
||||||
|
db: &cosmic_text::fontdb::Database,
|
||||||
|
) {
|
||||||
|
for (family, _) in db.faces().filter_map(|f| f.families.first()) {
|
||||||
|
families
|
||||||
|
.entry(family)
|
||||||
|
.and_modify(|gen| *gen = generation)
|
||||||
|
.or_insert(generation);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < families.len() {
|
||||||
|
if families.field(i).expect("length checked").value == generation {
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
families.remove_by_index(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_frame(&mut self) {
|
pub fn update_fonts(&mut self, db: &mut cosmic_text::fontdb::Database) -> bool {
|
||||||
|
let new_app_generation = self.app_fonts.0.generation();
|
||||||
|
let app_fonts_changed = if self.app_font_generation == new_app_generation {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.app_font_generation = new_app_generation;
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let new_window_generation = self.window_fonts.0.generation();
|
||||||
|
let window_fonts_changed = if self.window_font_generation == new_window_generation {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.window_font_generation = new_window_generation;
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
let changed = app_fonts_changed || window_fonts_changed;
|
||||||
|
if changed {
|
||||||
|
self.font_generation += 1;
|
||||||
|
|
||||||
|
if app_fonts_changed {
|
||||||
|
Self::synchronize_font_list(
|
||||||
|
&mut self.loaded_fonts,
|
||||||
|
self.font_generation,
|
||||||
|
&self.app_fonts,
|
||||||
|
db,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if window_fonts_changed {
|
||||||
|
Self::synchronize_font_list(
|
||||||
|
&mut self.loaded_fonts,
|
||||||
|
self.font_generation,
|
||||||
|
&self.window_fonts,
|
||||||
|
db,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all fonts that didn't have their generation touched.
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.loaded_fonts.len() {
|
||||||
|
let field = self.loaded_fonts.field(i).expect("length checked");
|
||||||
|
let check_if_changed = (app_fonts_changed
|
||||||
|
&& self.app_fonts.0.as_ptr() == field.key().collection)
|
||||||
|
|| (window_fonts_changed
|
||||||
|
&& self.window_fonts.0.as_ptr() == field.key().collection);
|
||||||
|
if !check_if_changed || field.value.generation == self.font_generation {
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
for face in self.loaded_fonts.remove_by_index(i).value.faces {
|
||||||
|
db.remove_face(face.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::gather_available_family_names(&mut self.fonts, self.font_generation, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synchronize_font_list(
|
||||||
|
loaded_fonts: &mut Map<LoadedFontId, LoadedFontIds>,
|
||||||
|
generation: usize,
|
||||||
|
collection: &FontCollection,
|
||||||
|
db: &mut cosmic_text::fontdb::Database,
|
||||||
|
) {
|
||||||
|
for (font_id, data) in collection.0.read().fonts(collection) {
|
||||||
|
match loaded_fonts.entry(font_id) {
|
||||||
|
map::Entry::Occupied(mut entry) => {
|
||||||
|
entry.generation = generation;
|
||||||
|
}
|
||||||
|
map::Entry::Vacant(entry) => {
|
||||||
|
let faces = db
|
||||||
|
.load_font_source(fontdb::Source::Binary(data.clone()))
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|id| {
|
||||||
|
db.face(id).map(|face| LoadedFontFace {
|
||||||
|
id,
|
||||||
|
families: face.families.clone(),
|
||||||
|
weight: face.weight,
|
||||||
|
style: face.style,
|
||||||
|
stretch: face.stretch,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
entry.insert(LoadedFontIds { generation, faces });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn next_frame(&mut self, db: &mut cosmic_text::fontdb::Database) -> bool {
|
||||||
self.current_font_family = None;
|
self.current_font_family = None;
|
||||||
|
self.update_fonts(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_available_font_family(&self, list: &FontFamilyList) -> Option<FamilyOwned> {
|
pub fn find_available_font_family(&self, list: &FontFamilyList) -> Option<FamilyOwned> {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ mod names;
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
mod app;
|
mod app;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
pub mod fonts;
|
||||||
mod tick;
|
mod tick;
|
||||||
mod tree;
|
mod tree;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
|
||||||
|
|
@ -892,6 +892,10 @@ impl<T> Dynamic<T> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_ptr(&self) -> *const () {
|
||||||
|
Arc::as_ptr(&self.0).cast()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a weak reference to this dynamic.
|
/// Returns a weak reference to this dynamic.
|
||||||
///
|
///
|
||||||
/// This is powered by [`Arc`]/[`Weak`] and follows the same semantics for
|
/// This is powered by [`Arc`]/[`Weak`] and follows the same semantics for
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use kludgine::{CanRenderTo, Color, DrawableExt};
|
||||||
use super::input::CowString;
|
use super::input::CowString;
|
||||||
use crate::context::{GraphicsContext, LayoutContext, Trackable, WidgetContext};
|
use crate::context::{GraphicsContext, LayoutContext, Trackable, WidgetContext};
|
||||||
use crate::styles::components::TextColor;
|
use crate::styles::components::TextColor;
|
||||||
|
use crate::styles::FontFamilyList;
|
||||||
use crate::value::{Dynamic, Generation, IntoReadOnly, ReadOnly, Value};
|
use crate::value::{Dynamic, Generation, IntoReadOnly, ReadOnly, Value};
|
||||||
use crate::widget::{Widget, WidgetInstance};
|
use crate::widget::{Widget, WidgetInstance};
|
||||||
use crate::window::WindowLocal;
|
use crate::window::WindowLocal;
|
||||||
|
|
@ -21,7 +22,7 @@ pub struct Label<T> {
|
||||||
/// The contents of the label.
|
/// The contents of the label.
|
||||||
pub display: ReadOnly<T>,
|
pub display: ReadOnly<T>,
|
||||||
displayed: String,
|
displayed: String,
|
||||||
prepared_text: WindowLocal<(MeasuredText<Px>, Option<Generation>, Px, Color)>,
|
prepared_text: WindowLocal<LabelCacheKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Label<T>
|
impl<T> Label<T>
|
||||||
|
|
@ -44,14 +45,16 @@ where
|
||||||
width: Px,
|
width: Px,
|
||||||
) -> &MeasuredText<Px> {
|
) -> &MeasuredText<Px> {
|
||||||
let check_generation = self.display.generation();
|
let check_generation = self.display.generation();
|
||||||
|
context.apply_current_font_settings();
|
||||||
|
let current_families = context.current_family_list();
|
||||||
match self.prepared_text.get(context) {
|
match self.prepared_text.get(context) {
|
||||||
Some((prepared, prepared_generation, prepared_width, prepared_color))
|
Some(cache)
|
||||||
if prepared.can_render_to(&context.gfx)
|
if cache.text.can_render_to(&context.gfx)
|
||||||
&& *prepared_generation == check_generation
|
&& cache.generation == check_generation
|
||||||
&& *prepared_color == color
|
&& cache.color == color
|
||||||
&& *prepared_width == width => {}
|
&& cache.width == width
|
||||||
|
&& cache.families == current_families => {}
|
||||||
_ => {
|
_ => {
|
||||||
context.apply_current_font_settings();
|
|
||||||
let measured = self.display.map(|text| {
|
let measured = self.display.map(|text| {
|
||||||
self.displayed.clear();
|
self.displayed.clear();
|
||||||
if let Err(err) = write!(&mut self.displayed, "{}", text.as_display(context)) {
|
if let Err(err) = write!(&mut self.displayed, "{}", text.as_display(context)) {
|
||||||
|
|
@ -61,14 +64,22 @@ where
|
||||||
.gfx
|
.gfx
|
||||||
.measure_text(Text::new(&self.displayed, color).wrap_at(width))
|
.measure_text(Text::new(&self.displayed, color).wrap_at(width))
|
||||||
});
|
});
|
||||||
self.prepared_text
|
self.prepared_text.set(
|
||||||
.set(context, (measured, check_generation, width, color));
|
context,
|
||||||
|
LabelCacheKey {
|
||||||
|
text: measured,
|
||||||
|
generation: check_generation,
|
||||||
|
width,
|
||||||
|
color,
|
||||||
|
families: current_families,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.prepared_text
|
self.prepared_text
|
||||||
.get(context)
|
.get(context)
|
||||||
.map(|(prepared, _, _, _)| prepared)
|
.map(|cache| &cache.text)
|
||||||
.expect("always initialized")
|
.expect("always initialized")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +144,15 @@ impl_make_widget!(
|
||||||
ReadOnly<String> => String
|
ReadOnly<String> => String
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct LabelCacheKey {
|
||||||
|
text: MeasuredText<Px>,
|
||||||
|
generation: Option<Generation>,
|
||||||
|
width: Px,
|
||||||
|
color: Color,
|
||||||
|
families: FontFamilyList,
|
||||||
|
}
|
||||||
|
|
||||||
/// A context-aware [`Display`] implementation.
|
/// A context-aware [`Display`] implementation.
|
||||||
///
|
///
|
||||||
/// This trait is automatically implemented for all types that implement
|
/// This trait is automatically implemented for all types that implement
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ use crate::context::{
|
||||||
AsEventContext, EventContext, Exclusive, GraphicsContext, LayoutContext, Trackable,
|
AsEventContext, EventContext, Exclusive, GraphicsContext, LayoutContext, Trackable,
|
||||||
WidgetContext,
|
WidgetContext,
|
||||||
};
|
};
|
||||||
|
use crate::fonts::FontCollection;
|
||||||
use crate::graphics::{FontState, Graphics};
|
use crate::graphics::{FontState, Graphics};
|
||||||
use crate::styles::{Edges, FontFamilyList, ThemePair};
|
use crate::styles::{Edges, FontFamilyList, ThemePair};
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
|
|
@ -472,9 +473,8 @@ where
|
||||||
/// The list of font families to try to find when a [`FamilyOwned::Cursive`]
|
/// The list of font families to try to find when a [`FamilyOwned::Cursive`]
|
||||||
/// font is requested.
|
/// font is requested.
|
||||||
pub cursive_font_family: FontFamilyList,
|
pub cursive_font_family: FontFamilyList,
|
||||||
/// A list of data buffers that contain font data to ensure are loaded
|
/// A collection of fonts that this window will load.
|
||||||
/// during drawing operations.
|
pub fonts: FontCollection,
|
||||||
pub font_data_to_load: Vec<Vec<u8>>,
|
|
||||||
|
|
||||||
on_closed: Option<OnceCallback>,
|
on_closed: Option<OnceCallback>,
|
||||||
inner_size: Option<Dynamic<Size<UPx>>>,
|
inner_size: Option<Dynamic<Size<UPx>>>,
|
||||||
|
|
@ -572,8 +572,8 @@ impl Window<WidgetInstance> {
|
||||||
/// rendering.
|
/// rendering.
|
||||||
///
|
///
|
||||||
/// All font families contained in `font_data` will be loaded.
|
/// All font families contained in `font_data` will be loaded.
|
||||||
pub fn loading_font(mut self, font_data: Vec<u8>) -> Self {
|
pub fn loading_font(self, font_data: Vec<u8>) -> Self {
|
||||||
self.font_data_to_load.push(font_data);
|
self.fonts.push(font_data);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -636,10 +636,12 @@ where
|
||||||
fantasy_font_family: FontFamilyList::default(),
|
fantasy_font_family: FontFamilyList::default(),
|
||||||
monospace_font_family: FontFamilyList::default(),
|
monospace_font_family: FontFamilyList::default(),
|
||||||
cursive_font_family: FontFamilyList::default(),
|
cursive_font_family: FontFamilyList::default(),
|
||||||
font_data_to_load: vec![
|
fonts: {
|
||||||
|
let fonts = FontCollection::default();
|
||||||
#[cfg(feature = "roboto-flex")]
|
#[cfg(feature = "roboto-flex")]
|
||||||
include_bytes!("../assets/RobotoFlex.ttf").to_vec(),
|
fonts.push(include_bytes!("../assets/RobotoFlex.ttf").to_vec());
|
||||||
],
|
fonts
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -681,7 +683,7 @@ where
|
||||||
inner_size: self.inner_size.unwrap_or_default(),
|
inner_size: self.inner_size.unwrap_or_default(),
|
||||||
theme: Some(self.theme),
|
theme: Some(self.theme),
|
||||||
theme_mode: self.theme_mode,
|
theme_mode: self.theme_mode,
|
||||||
font_data_to_load: self.font_data_to_load,
|
font_data_to_load: self.fonts,
|
||||||
serif_font_family: self.serif_font_family,
|
serif_font_family: self.serif_font_family,
|
||||||
sans_serif_font_family: self.sans_serif_font_family,
|
sans_serif_font_family: self.sans_serif_font_family,
|
||||||
fantasy_font_family: self.fantasy_font_family,
|
fantasy_font_family: self.fantasy_font_family,
|
||||||
|
|
@ -802,6 +804,7 @@ where
|
||||||
previously_active,
|
previously_active,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -814,6 +817,7 @@ where
|
||||||
default.clone(),
|
default.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -832,6 +836,7 @@ where
|
||||||
keyboard_activated,
|
keyboard_activated,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -864,6 +869,7 @@ where
|
||||||
managed,
|
managed,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -938,13 +944,10 @@ where
|
||||||
|
|
||||||
fn load_fonts(
|
fn load_fonts(
|
||||||
settings: &mut sealed::WindowSettings,
|
settings: &mut sealed::WindowSettings,
|
||||||
|
app_fonts: FontCollection,
|
||||||
fontdb: &mut fontdb::Database,
|
fontdb: &mut fontdb::Database,
|
||||||
) -> FontState {
|
) -> FontState {
|
||||||
for font_to_load in settings.font_data_to_load.drain(..) {
|
let fonts = FontState::new(fontdb, settings.font_data_to_load.clone(), app_fonts);
|
||||||
fontdb.load_font_data(font_to_load);
|
|
||||||
}
|
|
||||||
|
|
||||||
let fonts = FontState::new(fontdb);
|
|
||||||
fonts.apply_font_family_list(
|
fonts.apply_font_family_list(
|
||||||
&settings.serif_font_family,
|
&settings.serif_font_family,
|
||||||
|| default_family(Family::Serif),
|
|| default_family(Family::Serif),
|
||||||
|
|
@ -1017,6 +1020,7 @@ where
|
||||||
target,
|
target,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1049,6 +1053,7 @@ where
|
||||||
target,
|
target,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1122,7 +1127,11 @@ where
|
||||||
|
|
||||||
inner_size.set(window.inner_size());
|
inner_size.set(window.inner_size());
|
||||||
|
|
||||||
let fonts = Self::load_fonts(&mut settings, graphics.font_system().db_mut());
|
let fonts = Self::load_fonts(
|
||||||
|
&mut settings,
|
||||||
|
cushy.fonts.clone(),
|
||||||
|
graphics.font_system().db_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
let theme_mode = match settings.theme_mode.take() {
|
let theme_mode = match settings.theme_mode.take() {
|
||||||
Some(Value::Dynamic(dynamic)) => {
|
Some(Value::Dynamic(dynamic)) => {
|
||||||
|
|
@ -1201,17 +1210,22 @@ where
|
||||||
);
|
);
|
||||||
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
|
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
|
||||||
|
|
||||||
self.fonts.next_frame();
|
let fonts_changed = self.fonts.next_frame(graphics.font_system().db_mut());
|
||||||
|
if fonts_changed {
|
||||||
|
println!("Rebuilding font system");
|
||||||
|
graphics.rebuild_font_system();
|
||||||
|
}
|
||||||
let graphics = self.contents.new_frame(graphics);
|
let graphics = self.contents.new_frame(graphics);
|
||||||
let mut context = GraphicsContext {
|
let mut context = GraphicsContext {
|
||||||
widget: WidgetContext::new(
|
widget: WidgetContext::new(
|
||||||
self.root.clone(),
|
self.root.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
gfx: Exclusive::Owned(Graphics::new(graphics, &mut self.fonts)),
|
gfx: Exclusive::Owned(Graphics::new(graphics)),
|
||||||
};
|
};
|
||||||
if self.initial_frame {
|
if self.initial_frame {
|
||||||
self.root
|
self.root
|
||||||
|
|
@ -1342,6 +1356,7 @@ where
|
||||||
target,
|
target,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1391,6 +1406,7 @@ where
|
||||||
widget,
|
widget,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1430,6 +1446,7 @@ where
|
||||||
widget,
|
widget,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1470,6 +1487,7 @@ where
|
||||||
self.root.clone(),
|
self.root.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1488,6 +1506,7 @@ where
|
||||||
handler.clone(),
|
handler.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1521,6 +1540,7 @@ where
|
||||||
self.root.clone(),
|
self.root.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1557,6 +1577,7 @@ where
|
||||||
self.root.clone(),
|
self.root.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1575,6 +1596,7 @@ where
|
||||||
hovered.clone(),
|
hovered.clone(),
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1616,6 +1638,7 @@ where
|
||||||
handler,
|
handler,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
&mut self.fonts,
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&mut self.cursor,
|
||||||
),
|
),
|
||||||
|
|
@ -1934,6 +1957,7 @@ pub(crate) mod sealed {
|
||||||
|
|
||||||
use crate::app::Cushy;
|
use crate::app::Cushy;
|
||||||
use crate::context::sealed::InvalidationStatus;
|
use crate::context::sealed::InvalidationStatus;
|
||||||
|
use crate::fonts::FontCollection;
|
||||||
use crate::styles::{FontFamilyList, ThemePair};
|
use crate::styles::{FontFamilyList, ThemePair};
|
||||||
use crate::value::{Dynamic, Value};
|
use crate::value::{Dynamic, Value};
|
||||||
use crate::widget::OnceCallback;
|
use crate::widget::OnceCallback;
|
||||||
|
|
@ -1960,7 +1984,7 @@ pub(crate) mod sealed {
|
||||||
pub fantasy_font_family: FontFamilyList,
|
pub fantasy_font_family: FontFamilyList,
|
||||||
pub monospace_font_family: FontFamilyList,
|
pub monospace_font_family: FontFamilyList,
|
||||||
pub cursive_font_family: FontFamilyList,
|
pub cursive_font_family: FontFamilyList,
|
||||||
pub font_data_to_load: Vec<Vec<u8>>,
|
pub font_data_to_load: FontCollection,
|
||||||
pub on_closed: Option<OnceCallback>,
|
pub on_closed: Option<OnceCallback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2488,7 +2512,7 @@ impl CushyWindowBuilder {
|
||||||
fantasy_font_family: FontFamilyList::default(),
|
fantasy_font_family: FontFamilyList::default(),
|
||||||
monospace_font_family: FontFamilyList::default(),
|
monospace_font_family: FontFamilyList::default(),
|
||||||
cursive_font_family: FontFamilyList::default(),
|
cursive_font_family: FontFamilyList::default(),
|
||||||
font_data_to_load: Vec::default(),
|
font_data_to_load: FontCollection::default(),
|
||||||
on_closed: None,
|
on_closed: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue