mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
Various fixes/improvements
- On Linux, `fm-match` is used to query for the default fonts. - DynamicComponents now have their own trait and can now be specified with a constant or dynamic. - Roboto Flex is now always loaded when the feature is enabled. Overriding the default sans serif font prefers the overridden value, then roboto, then the result of fc-match/fontdb's default. - Button now supports background colors being set on a transparent button.
This commit is contained in:
parent
55eea5fad3
commit
17847d6947
7 changed files with 169 additions and 77 deletions
|
|
@ -1,14 +1,19 @@
|
||||||
|
use gooey::styles::components::FontFamily;
|
||||||
|
use gooey::styles::FontFamilyList;
|
||||||
use gooey::widget::MakeWidget;
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::Run;
|
use gooey::Run;
|
||||||
|
use kludgine::cosmic_text::FamilyOwned;
|
||||||
use kludgine::figures::units::Lp;
|
use kludgine::figures::units::Lp;
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
include_str!("./nested-scroll.rs")
|
include_str!("./nested-scroll.rs")
|
||||||
.vertical_scroll()
|
.vertical_scroll()
|
||||||
|
.with(&FontFamily, FontFamilyList::from(FamilyOwned::Monospace))
|
||||||
.height(Lp::inches(3))
|
.height(Lp::inches(3))
|
||||||
.and(
|
.and(
|
||||||
include_str!("./canvas.rs")
|
include_str!("./canvas.rs")
|
||||||
.vertical_scroll()
|
.vertical_scroll()
|
||||||
|
.with(&FontFamily, FontFamilyList::from(FamilyOwned::Monospace))
|
||||||
.height(Lp::inches(3)),
|
.height(Lp::inches(3)),
|
||||||
)
|
)
|
||||||
.into_rows()
|
.into_rows()
|
||||||
|
|
|
||||||
|
|
@ -1111,6 +1111,19 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
self.effective_styles.get(query, self)
|
self.effective_styles.get(query, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queries the widget hierarchy for a single style component.
|
||||||
|
///
|
||||||
|
/// This function traverses up the widget hierarchy looking for the
|
||||||
|
/// component being requested. If a matching component is found, it will be
|
||||||
|
/// returned.
|
||||||
|
#[must_use]
|
||||||
|
pub fn try_get<Component: ComponentDefinition>(
|
||||||
|
&self,
|
||||||
|
query: &Component,
|
||||||
|
) -> Option<Component::ComponentType> {
|
||||||
|
self.effective_styles.try_get(query, self)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn handle(&self) -> WindowHandle {
|
pub(crate) fn handle(&self) -> WindowHandle {
|
||||||
WindowHandle {
|
WindowHandle {
|
||||||
kludgine: self.window.handle(),
|
kludgine: self.window.handle(),
|
||||||
|
|
|
||||||
132
src/styles.rs
132
src/styles.rs
|
|
@ -66,9 +66,13 @@ impl Styles {
|
||||||
pub fn insert_dynamic(
|
pub fn insert_dynamic(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &impl NamedComponent,
|
name: &impl NamedComponent,
|
||||||
dynamic: impl Into<DynamicComponent>,
|
dynamic: impl IntoDynamicComponentValue,
|
||||||
) {
|
) {
|
||||||
self.insert(name, Component::Dynamic(dynamic.into()));
|
let component = match dynamic.into_dynamic_component() {
|
||||||
|
Value::Constant(dynamic) => Value::Constant(Component::Dynamic(dynamic)),
|
||||||
|
Value::Dynamic(dynamic) => Value::Dynamic(dynamic.map_each_cloned(Component::Dynamic)),
|
||||||
|
};
|
||||||
|
self.insert(name, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a [`Component`] for the name provided and returns self.
|
/// Adds a [`Component`] for the name provided and returns self.
|
||||||
|
|
@ -91,7 +95,7 @@ impl Styles {
|
||||||
pub fn with_dynamic<C: ComponentDefinition>(
|
pub fn with_dynamic<C: ComponentDefinition>(
|
||||||
mut self,
|
mut self,
|
||||||
name: &C,
|
name: &C,
|
||||||
dynamic: impl Into<DynamicComponent>,
|
dynamic: impl IntoDynamicComponentValue,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.insert_dynamic(name, dynamic);
|
self.insert_dynamic(name, dynamic);
|
||||||
self
|
self
|
||||||
|
|
@ -145,6 +149,23 @@ impl Styles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the component associated with the given name, if a value is
|
||||||
|
/// specified.
|
||||||
|
#[must_use]
|
||||||
|
pub fn try_get<Named>(
|
||||||
|
&self,
|
||||||
|
component: &Named,
|
||||||
|
context: &WidgetContext<'_, '_>,
|
||||||
|
) -> Option<Named::ComponentType>
|
||||||
|
where
|
||||||
|
Named: ComponentDefinition + ?Sized,
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
.components
|
||||||
|
.get(&component.name())
|
||||||
|
.and_then(|component| Self::resolve_component(component, context))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the component associated with the given name, or if not found,
|
/// Returns the component associated with the given name, or if not found,
|
||||||
/// returns the default value provided by the definition.
|
/// returns the default value provided by the definition.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -156,10 +177,7 @@ impl Styles {
|
||||||
where
|
where
|
||||||
Named: ComponentDefinition + ?Sized,
|
Named: ComponentDefinition + ?Sized,
|
||||||
{
|
{
|
||||||
self.0
|
self.try_get(component, context)
|
||||||
.components
|
|
||||||
.get(&component.name())
|
|
||||||
.and_then(|component| Self::resolve_component(component, context))
|
|
||||||
.unwrap_or_else(|| component.default_value(context))
|
.unwrap_or_else(|| component.default_value(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +204,29 @@ impl Debug for Styles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromIterator<(ComponentName, Component)> for Styles {
|
||||||
|
fn from_iter<T: IntoIterator<Item = (ComponentName, Component)>>(iter: T) -> Self {
|
||||||
|
let iter = iter.into_iter();
|
||||||
|
let mut styles = Self::with_capacity(iter.size_hint().0);
|
||||||
|
for (name, component) in iter {
|
||||||
|
styles.insert_named(name, component);
|
||||||
|
}
|
||||||
|
styles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Styles {
|
||||||
|
type IntoIter = hash_map::IntoIter<ComponentName, Value<Component>>;
|
||||||
|
type Item = (ComponentName, Value<Component>);
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
Arc::try_unwrap(self.0)
|
||||||
|
.unwrap_or_else(|err| err.as_ref().clone())
|
||||||
|
.components
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct StyleData {
|
struct StyleData {
|
||||||
components: AHashMap<ComponentName, Value<Component>>,
|
components: AHashMap<ComponentName, Value<Component>>,
|
||||||
|
|
@ -226,51 +267,35 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<(ComponentName, Component)> for Styles {
|
/// A type that can convert into a [`Value`] containing a [`DynamicComponent`].
|
||||||
fn from_iter<T: IntoIterator<Item = (ComponentName, Component)>>(iter: T) -> Self {
|
pub trait IntoDynamicComponentValue {
|
||||||
let iter = iter.into_iter();
|
/// Returns this type converted into a dynamic component value.
|
||||||
let mut styles = Self::with_capacity(iter.size_hint().0);
|
fn into_dynamic_component(self) -> Value<DynamicComponent>;
|
||||||
for (name, component) in iter {
|
}
|
||||||
styles.insert_named(name, component);
|
|
||||||
}
|
impl IntoDynamicComponentValue for DynamicComponent {
|
||||||
styles
|
fn into_dynamic_component(self) -> Value<DynamicComponent> {
|
||||||
|
Value::Constant(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Styles {
|
impl<T> IntoDynamicComponentValue for T
|
||||||
type IntoIter = hash_map::IntoIter<ComponentName, Value<Component>>;
|
where
|
||||||
type Item = (ComponentName, Value<Component>);
|
T: ComponentDefinition + Clone + RefUnwindSafe + Send + Sync + 'static,
|
||||||
|
{
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_dynamic_component(self) -> Value<DynamicComponent> {
|
||||||
Arc::try_unwrap(self.0)
|
Value::Constant(DynamicComponent::from(self))
|
||||||
.unwrap_or_else(|err| err.as_ref().clone())
|
|
||||||
.components
|
|
||||||
.into_iter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// An iterator over the owned contents of a [`Styles`] instance.
|
impl<T> IntoDynamicComponentValue for Dynamic<T>
|
||||||
// pub struct StylesIntoIter {
|
where
|
||||||
// main: hash_map::IntoIter<ComponentName, Value<Component>>,
|
T: ComponentDefinition + Clone + RefUnwindSafe + Send + Sync + 'static,
|
||||||
// }
|
{
|
||||||
|
fn into_dynamic_component(self) -> Value<DynamicComponent> {
|
||||||
// impl Iterator for StylesIntoIter {
|
Value::Dynamic(self.map_each_into())
|
||||||
// type Item = (ComponentName, Value<Component>);
|
}
|
||||||
|
}
|
||||||
// fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
// loop {
|
|
||||||
// if let Some((group, names)) = &mut self.names {
|
|
||||||
// if let Some((name, component)) = names.next() {
|
|
||||||
// return Some((ComponentName::new(group.clone(), name), component));
|
|
||||||
// }
|
|
||||||
// self.names = None;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let (group, names) = self.main.next()?;
|
|
||||||
// self.names = Some((group, names.into_iter()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// A value of a style component.
|
/// A value of a style component.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
@ -1019,21 +1044,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that represents a named component with a default value.
|
|
||||||
pub trait ComponentDefaultvalue: NamedComponent {
|
|
||||||
/// Returns the default value for this component.
|
|
||||||
fn default_component_value(&self, context: &WidgetContext<'_, '_>) -> Component;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ComponentDefaultvalue for T
|
|
||||||
where
|
|
||||||
T: ComponentDefinition,
|
|
||||||
{
|
|
||||||
fn default_component_value(&self, context: &WidgetContext<'_, '_>) -> Component {
|
|
||||||
self.default_value(context).into_component()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NamedComponent for ComponentName {
|
impl NamedComponent for ComponentName {
|
||||||
fn name(&self) -> Cow<'_, ComponentName> {
|
fn name(&self) -> Cow<'_, ComponentName> {
|
||||||
Cow::Borrowed(self)
|
Cow::Borrowed(self)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ use crate::styles::components::{
|
||||||
TextSize7, TextSize8,
|
TextSize7, TextSize8,
|
||||||
};
|
};
|
||||||
use crate::styles::{
|
use crate::styles::{
|
||||||
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, DynamicComponent, Edges,
|
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue,
|
||||||
IntoComponentValue, Styles, ThemePair, VisualOrder,
|
IntoDynamicComponentValue, Styles, ThemePair, VisualOrder,
|
||||||
};
|
};
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::utils::IgnorePoison;
|
use crate::utils::IgnorePoison;
|
||||||
|
|
@ -712,7 +712,7 @@ pub trait MakeWidget: Sized {
|
||||||
fn with_dynamic<C: ComponentDefinition>(
|
fn with_dynamic<C: ComponentDefinition>(
|
||||||
self,
|
self,
|
||||||
name: &C,
|
name: &C,
|
||||||
dynamic: impl Into<DynamicComponent>,
|
dynamic: impl IntoDynamicComponentValue,
|
||||||
) -> Style
|
) -> Style
|
||||||
where
|
where
|
||||||
Value<C::ComponentType>: IntoComponentValue,
|
Value<C::ComponentType>: IntoComponentValue,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ pub struct Button {
|
||||||
pub on_click: Option<Callback<()>>,
|
pub on_click: Option<Callback<()>>,
|
||||||
/// The kind of button to draw.
|
/// The kind of button to draw.
|
||||||
pub kind: Value<ButtonKind>,
|
pub kind: Value<ButtonKind>,
|
||||||
|
focusable: bool,
|
||||||
buttons_pressed: usize,
|
buttons_pressed: usize,
|
||||||
cached_state: CacheState,
|
cached_state: CacheState,
|
||||||
active_colors: Option<Dynamic<ButtonColors>>,
|
active_colors: Option<Dynamic<ButtonColors>>,
|
||||||
|
|
@ -140,6 +141,7 @@ impl Button {
|
||||||
active_colors: None,
|
active_colors: None,
|
||||||
kind: Value::Constant(ButtonKind::default()),
|
kind: Value::Constant(ButtonKind::default()),
|
||||||
color_animation: AnimationHandle::default(),
|
color_animation: AnimationHandle::default(),
|
||||||
|
focusable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,6 +164,13 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prevents focus being given to this button.
|
||||||
|
#[must_use]
|
||||||
|
pub fn prevent_focus(mut self) -> Self {
|
||||||
|
self.focusable = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn invoke_on_click(&mut self, context: &WidgetContext<'_, '_>) {
|
fn invoke_on_click(&mut self, context: &WidgetContext<'_, '_>) {
|
||||||
if context.enabled() {
|
if context.enabled() {
|
||||||
if let Some(on_click) = self.on_click.as_mut() {
|
if let Some(on_click) = self.on_click.as_mut() {
|
||||||
|
|
@ -190,7 +199,9 @@ impl Button {
|
||||||
) -> ButtonColors {
|
) -> ButtonColors {
|
||||||
match visual_state {
|
match visual_state {
|
||||||
VisualState::Normal => ButtonColors {
|
VisualState::Normal => ButtonColors {
|
||||||
background: Color::CLEAR_BLACK,
|
background: context
|
||||||
|
.try_get(&ButtonBackground)
|
||||||
|
.unwrap_or(Color::CLEAR_BLACK),
|
||||||
foreground: context.get(&TextColor),
|
foreground: context.get(&TextColor),
|
||||||
outline: context.get(&ButtonOutline),
|
outline: context.get(&ButtonOutline),
|
||||||
},
|
},
|
||||||
|
|
@ -205,7 +216,9 @@ impl Button {
|
||||||
outline: context.get(&ButtonActiveOutline),
|
outline: context.get(&ButtonActiveOutline),
|
||||||
},
|
},
|
||||||
VisualState::Disabled => ButtonColors {
|
VisualState::Disabled => ButtonColors {
|
||||||
background: Color::CLEAR_BLACK,
|
background: context
|
||||||
|
.try_get(&ButtonDisabledBackground)
|
||||||
|
.unwrap_or(Color::CLEAR_BLACK),
|
||||||
foreground: context.theme().surface.on_color_variant,
|
foreground: context.theme().surface.on_color_variant,
|
||||||
outline: context.get(&ButtonDisabledOutline),
|
outline: context.get(&ButtonDisabledOutline),
|
||||||
},
|
},
|
||||||
|
|
@ -391,7 +404,7 @@ impl Widget for Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool {
|
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool {
|
||||||
context.enabled() && context.get(&AutoFocusableControls).is_all()
|
self.focusable && context.enabled() && context.get(&AutoFocusableControls).is_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_down(
|
fn mouse_down(
|
||||||
|
|
@ -437,7 +450,7 @@ impl Widget for Button {
|
||||||
if self.buttons_pressed == 0 {
|
if self.buttons_pressed == 0 {
|
||||||
context.deactivate();
|
context.deactivate();
|
||||||
|
|
||||||
if let Some(location) = location {
|
if let (true, Some(location)) = (self.focusable, location) {
|
||||||
if Rect::from(context.last_layout().expect("must have been rendered").size)
|
if Rect::from(context.last_layout().expect("must have been rendered").size)
|
||||||
.contains(location)
|
.contains(location)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::styles::components::{
|
||||||
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
|
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
|
||||||
TextSize7, TextSize8,
|
TextSize7, TextSize8,
|
||||||
};
|
};
|
||||||
use crate::styles::{ComponentDefinition, DynamicComponent, IntoComponentValue, Styles};
|
use crate::styles::{ComponentDefinition, IntoComponentValue, IntoDynamicComponentValue, Styles};
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ impl Style {
|
||||||
pub fn with_dynamic<C: ComponentDefinition>(
|
pub fn with_dynamic<C: ComponentDefinition>(
|
||||||
mut self,
|
mut self,
|
||||||
name: &C,
|
name: &C,
|
||||||
dynamic: impl Into<DynamicComponent>,
|
dynamic: impl IntoDynamicComponentValue,
|
||||||
) -> Style
|
) -> Style
|
||||||
where
|
where
|
||||||
Value<C::ComponentType>: IntoComponentValue,
|
Value<C::ComponentType>: IntoComponentValue,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::ffi::OsStr;
|
||||||
use std::ops::{Deref, DerefMut, Not};
|
use std::ops::{Deref, DerefMut, Not};
|
||||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
use std::sync::{MutexGuard, OnceLock};
|
use std::sync::{MutexGuard, OnceLock};
|
||||||
|
|
||||||
|
|
@ -18,7 +19,7 @@ use kludgine::app::winit::event::{
|
||||||
use kludgine::app::winit::keyboard::{Key, NamedKey};
|
use kludgine::app::winit::keyboard::{Key, NamedKey};
|
||||||
use kludgine::app::winit::window;
|
use kludgine::app::winit::window;
|
||||||
use kludgine::app::WindowBehavior as _;
|
use kludgine::app::WindowBehavior as _;
|
||||||
use kludgine::cosmic_text::FamilyOwned;
|
use kludgine::cosmic_text::{Family, FamilyOwned};
|
||||||
use kludgine::figures::units::{Px, UPx};
|
use kludgine::figures::units::{Px, UPx};
|
||||||
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, ScreenScale, Size};
|
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, ScreenScale, Size};
|
||||||
use kludgine::render::Drawing;
|
use kludgine::render::Drawing;
|
||||||
|
|
@ -534,34 +535,53 @@ where
|
||||||
let theme = settings.theme.take().expect("theme always present");
|
let theme = settings.theme.take().expect("theme always present");
|
||||||
|
|
||||||
let fontdb = graphics.font_system().db_mut();
|
let fontdb = graphics.font_system().db_mut();
|
||||||
|
|
||||||
if let Some(FamilyOwned::Name(name)) =
|
if let Some(FamilyOwned::Name(name)) =
|
||||||
Graphics::inner_find_available_font_family(fontdb, &settings.serif_font_family)
|
Graphics::inner_find_available_font_family(fontdb, &settings.serif_font_family)
|
||||||
|
.or_else(|| default_family(Family::Serif))
|
||||||
{
|
{
|
||||||
fontdb.set_serif_family(name);
|
fontdb.set_serif_family(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bundled_font_name;
|
||||||
|
#[cfg(feature = "roboto-flex")]
|
||||||
|
{
|
||||||
|
fontdb.load_font_data(include_bytes!("../assets/RobotoFlex.ttf").to_vec());
|
||||||
|
bundled_font_name = Some(String::from("Roboto Flex"));
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "roboto-flex"))]
|
||||||
|
{
|
||||||
|
bundled_font_name = None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(FamilyOwned::Name(name)) =
|
if let Some(FamilyOwned::Name(name)) =
|
||||||
Graphics::inner_find_available_font_family(fontdb, &settings.sans_serif_font_family)
|
Graphics::inner_find_available_font_family(fontdb, &settings.sans_serif_font_family)
|
||||||
|
.or_else(|| {
|
||||||
|
if let Some(name) = bundled_font_name {
|
||||||
|
Some(FamilyOwned::Name(name))
|
||||||
|
} else {
|
||||||
|
default_family(Family::SansSerif)
|
||||||
|
}
|
||||||
|
})
|
||||||
{
|
{
|
||||||
fontdb.set_sans_serif_family(name);
|
fontdb.set_sans_serif_family(name);
|
||||||
} else {
|
|
||||||
#[cfg(feature = "roboto-flex")]
|
|
||||||
{
|
|
||||||
fontdb.load_font_data(include_bytes!("../assets/RobotoFlex.ttf").to_vec());
|
|
||||||
fontdb.set_sans_serif_family("Roboto Flex");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(FamilyOwned::Name(name)) =
|
if let Some(FamilyOwned::Name(name)) =
|
||||||
Graphics::inner_find_available_font_family(fontdb, &settings.fantasy_font_family)
|
Graphics::inner_find_available_font_family(fontdb, &settings.fantasy_font_family)
|
||||||
|
.or_else(|| default_family(Family::Fantasy))
|
||||||
{
|
{
|
||||||
fontdb.set_fantasy_family(name);
|
fontdb.set_fantasy_family(name);
|
||||||
}
|
}
|
||||||
if let Some(FamilyOwned::Name(name)) =
|
if let Some(FamilyOwned::Name(name)) =
|
||||||
Graphics::inner_find_available_font_family(fontdb, &settings.monospace_font_family)
|
Graphics::inner_find_available_font_family(fontdb, &settings.monospace_font_family)
|
||||||
|
.or_else(|| default_family(Family::Monospace))
|
||||||
{
|
{
|
||||||
fontdb.set_monospace_family(name);
|
fontdb.set_monospace_family(name);
|
||||||
}
|
}
|
||||||
if let Some(FamilyOwned::Name(name)) =
|
if let Some(FamilyOwned::Name(name)) =
|
||||||
Graphics::inner_find_available_font_family(fontdb, &settings.cursive_font_family)
|
Graphics::inner_find_available_font_family(fontdb, &settings.cursive_font_family)
|
||||||
|
.or_else(|| default_family(Family::Cursive))
|
||||||
{
|
{
|
||||||
fontdb.set_cursive_family(name);
|
fontdb.set_cursive_family(name);
|
||||||
}
|
}
|
||||||
|
|
@ -1292,3 +1312,34 @@ impl Ranged for ThemeMode {
|
||||||
const MAX: Self = Self::Dark;
|
const MAX: Self = Self::Dark;
|
||||||
const MIN: Self = Self::Light;
|
const MIN: Self = Self::Light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "windows"))]
|
||||||
|
fn default_family(query: Family<'_>) -> Option<FamilyOwned> {
|
||||||
|
// fontdb uses system APIs to determine these defaults.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
|
||||||
|
fn default_family(query: Family<'_>) -> Option<FamilyOwned> {
|
||||||
|
// fontdb does not yet support configuring itself automatically. We will try
|
||||||
|
// to use `fc-match` to query font config. Once this is supported, we can
|
||||||
|
// remove this functionality.
|
||||||
|
// <https://github.com/RazrFalcon/fontdb/issues/24>
|
||||||
|
let query = match query {
|
||||||
|
Family::Serif => "serif",
|
||||||
|
Family::SansSerif => "sans",
|
||||||
|
Family::Cursive => "cursive",
|
||||||
|
Family::Fantasy => "fantasy",
|
||||||
|
Family::Monospace => "monospace",
|
||||||
|
Family::Name(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Command::new("fc-match")
|
||||||
|
.arg("-f")
|
||||||
|
.arg("%{family}")
|
||||||
|
.arg(query)
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.map(FamilyOwned::Name)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue