mirror of
https://github.com/danbulant/cushy
synced 2026-05-19 04:08:38 +00:00
Component type safety, some font support
This commit is contained in:
parent
92249047ce
commit
4af82ae188
13 changed files with 467 additions and 72 deletions
|
|
@ -6,8 +6,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["tracing-output"]
|
||||
default = ["tracing-output", "roboto-flex"]
|
||||
tracing-output = ["dep:tracing-subscriber"]
|
||||
roboto-flex = []
|
||||
|
||||
[dependencies]
|
||||
kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
|
||||
|
|
|
|||
93
assets/OFL.txt
Normal file
93
assets/OFL.txt
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
Copyright 2017 The Roboto Flex Project Authors (https://github.com/TypeNetwork/Roboto-Flex)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
assets/RobotoFlex.ttf
Normal file
BIN
assets/RobotoFlex.ttf
Normal file
Binary file not shown.
|
|
@ -22,10 +22,7 @@ fn main() -> gooey::Result {
|
|||
|
||||
let password_field = "Password"
|
||||
.align_left()
|
||||
.and(
|
||||
// TODO secure input
|
||||
password.clone().into_input(),
|
||||
)
|
||||
.and(password.clone().into_input())
|
||||
.into_rows();
|
||||
|
||||
let buttons = "Cancel"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub mod easings;
|
|||
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::ops::{ControlFlow, Deref, Div, Mul};
|
||||
use std::ops::{ControlFlow, Deref, Div, Mul, Sub};
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
|
||||
|
|
@ -52,7 +52,8 @@ use alot::{LotId, Lots};
|
|||
use derive_more::From;
|
||||
use intentional::Cast;
|
||||
use kempt::Set;
|
||||
use kludgine::figures::Ranged;
|
||||
use kludgine::figures::units::{Lp, Px, UPx};
|
||||
use kludgine::figures::{Ranged, UnscaledUnit};
|
||||
use kludgine::Color;
|
||||
|
||||
use crate::animation::easings::Linear;
|
||||
|
|
@ -733,6 +734,27 @@ impl PercentBetween for bool {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_unscaled_lerp {
|
||||
($wrapper:ident) => {
|
||||
impl LinearInterpolate for $wrapper {
|
||||
fn lerp(&self, target: &Self, percent: f32) -> Self {
|
||||
Self::from_unscaled(self.into_unscaled().lerp(&target.into_unscaled(), percent))
|
||||
}
|
||||
}
|
||||
|
||||
impl PercentBetween for $wrapper {
|
||||
fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
|
||||
self.into_unscaled()
|
||||
.percent_between(&min.into_unscaled(), &max.into_unscaled())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_unscaled_lerp!(Px);
|
||||
impl_unscaled_lerp!(Lp);
|
||||
impl_unscaled_lerp!(UPx);
|
||||
|
||||
#[test]
|
||||
fn integer_lerps() {
|
||||
#[track_caller]
|
||||
|
|
@ -815,7 +837,7 @@ pub trait PercentBetween {
|
|||
}
|
||||
|
||||
macro_rules! impl_percent_between {
|
||||
($type:ident, $float:ident) => {
|
||||
($type:ident, $float:ident, $sub:ident) => {
|
||||
impl PercentBetween for $type {
|
||||
fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
|
||||
assert!(min <= max, "percent_between requires min <= max");
|
||||
|
|
@ -824,27 +846,27 @@ macro_rules! impl_percent_between {
|
|||
"self must satisfy min <= self <= max"
|
||||
);
|
||||
|
||||
let range = *max - *min;
|
||||
ZeroToOne::from((*self - *min) as $float / range as $float)
|
||||
let range = max.$sub(*min);
|
||||
ZeroToOne::from(self.$sub(*min) as $float / range as $float)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_percent_between!(u8, f32);
|
||||
impl_percent_between!(u16, f32);
|
||||
impl_percent_between!(u32, f32);
|
||||
impl_percent_between!(u64, f32);
|
||||
impl_percent_between!(u128, f64);
|
||||
impl_percent_between!(usize, f64);
|
||||
impl_percent_between!(i8, f32);
|
||||
impl_percent_between!(i16, f32);
|
||||
impl_percent_between!(i32, f32);
|
||||
impl_percent_between!(i64, f32);
|
||||
impl_percent_between!(i128, f64);
|
||||
impl_percent_between!(isize, f64);
|
||||
impl_percent_between!(f32, f32);
|
||||
impl_percent_between!(f64, f64);
|
||||
impl_percent_between!(u8, f32, saturating_sub);
|
||||
impl_percent_between!(u16, f32, saturating_sub);
|
||||
impl_percent_between!(u32, f32, saturating_sub);
|
||||
impl_percent_between!(u64, f32, saturating_sub);
|
||||
impl_percent_between!(u128, f64, saturating_sub);
|
||||
impl_percent_between!(usize, f64, saturating_sub);
|
||||
impl_percent_between!(i8, f32, saturating_sub);
|
||||
impl_percent_between!(i16, f32, saturating_sub);
|
||||
impl_percent_between!(i32, f32, saturating_sub);
|
||||
impl_percent_between!(i64, f32, saturating_sub);
|
||||
impl_percent_between!(i128, f64, saturating_sub);
|
||||
impl_percent_between!(isize, f64, saturating_sub);
|
||||
impl_percent_between!(f32, f32, sub);
|
||||
impl_percent_between!(f64, f64, sub);
|
||||
|
||||
impl PercentBetween for Color {
|
||||
fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ use kludgine::shapes::{Shape, StrokeOptions};
|
|||
use kludgine::{Color, Kludgine};
|
||||
|
||||
use crate::graphics::Graphics;
|
||||
use crate::styles::components::{CornerRadius, HighlightColor, LayoutOrder, WidgetBackground};
|
||||
use crate::styles::components::{
|
||||
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, TextSize,
|
||||
WidgetBackground,
|
||||
};
|
||||
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
|
||||
use crate::utils::IgnorePoison;
|
||||
use crate::value::{Dynamic, IntoValue, Value};
|
||||
|
|
@ -574,6 +577,25 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
|
|||
self.stroke_outline::<Lp>(color, StrokeOptions::lp_wide(Lp::points(2)));
|
||||
}
|
||||
|
||||
/// Applies the current style settings for font family, text size, font
|
||||
/// style, and font weight.
|
||||
pub fn apply_current_font_settings(&mut self) {
|
||||
self.gfx
|
||||
.set_available_font_family(&self.widget.get(&FontFamily));
|
||||
self.gfx.set_font_size(self.widget.get(&TextSize));
|
||||
self.gfx.set_font_style(self.widget.get(&FontStyle));
|
||||
self.gfx.set_font_weight(self.widget.get(&FontWeight));
|
||||
}
|
||||
|
||||
// /// Applies the current style settings for font family, text size, font
|
||||
// /// style, and font weight.
|
||||
// pub fn apply_current_font_settings_to<'a>(&self, attrs: Attrs<'a>) -> Attrs<'a> {
|
||||
// attrs.set_available_font_family(&self.widget.get(&FontFamily));
|
||||
// attrs.set_font_size(self.widget.get(&TextSize));
|
||||
// attrs.set_font_style(self.widget.get(&FontStyle));
|
||||
// attrs.set_font_weight(self.widget.get(&FontWeight));
|
||||
// }
|
||||
|
||||
/// Invokes [`Widget::redraw()`](crate::widget::Widget::redraw) on this
|
||||
/// context's widget.
|
||||
///
|
||||
|
|
@ -590,6 +612,8 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
|
|||
let background = self.get(&WidgetBackground);
|
||||
self.gfx.fill(background);
|
||||
|
||||
self.apply_current_font_settings();
|
||||
|
||||
self.current_node
|
||||
.tree
|
||||
.note_widget_rendered(self.current_node.node_id);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use ahash::{HashSet, HashSetExt};
|
||||
use kludgine::cosmic_text::FamilyOwned;
|
||||
use kludgine::figures::units::{Px, UPx};
|
||||
use kludgine::figures::{
|
||||
self, Fraction, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, ScreenUnit, Size, Zero,
|
||||
|
|
@ -11,6 +13,8 @@ use kludgine::{
|
|||
cosmic_text, ClipGuard, Color, Drawable, Kludgine, ShaderScalable, ShapeSource, TextureSource,
|
||||
};
|
||||
|
||||
use crate::styles::FontFamilyList;
|
||||
|
||||
/// A 2d graphics context
|
||||
pub struct Graphics<'clip, 'gfx, 'pass> {
|
||||
renderer: RenderContext<'clip, 'gfx, 'pass>,
|
||||
|
|
@ -57,6 +61,36 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn inner_find_available_font_family(
|
||||
db: &cosmic_text::fontdb::Database,
|
||||
list: &FontFamilyList,
|
||||
) -> Option<FamilyOwned> {
|
||||
let mut fonts = HashSet::new();
|
||||
for (family, _) in db.faces().filter_map(|f| f.families.first()) {
|
||||
fonts.insert(family.clone());
|
||||
}
|
||||
|
||||
list.iter()
|
||||
.find(|family| match family {
|
||||
FamilyOwned::Name(name) => fonts.contains(name),
|
||||
_ => true,
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// 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::inner_find_available_font_family(self.font_system().db(), list)
|
||||
}
|
||||
|
||||
/// Sets the font family to the first family in `list`.
|
||||
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
|
||||
if let Some(family) = self.find_available_font_family(list) {
|
||||
self.set_font_family(family);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying renderer.
|
||||
///
|
||||
/// Note: Kludgine graphics contexts only support clipping. This type adds
|
||||
|
|
|
|||
174
src/styles.rs
174
src/styles.rs
|
|
@ -5,12 +5,14 @@ use std::borrow::Cow;
|
|||
use std::collections::hash_map;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{
|
||||
Add, Bound, Div, Mul, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||
Add, Bound, Deref, Div, Mul, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
|
||||
RangeToInclusive,
|
||||
};
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ahash::AHashMap;
|
||||
use kludgine::cosmic_text::{FamilyOwned, Style, Weight};
|
||||
use kludgine::figures::units::{Lp, Px, UPx};
|
||||
use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size, Zero};
|
||||
use kludgine::shapes::CornerRadii;
|
||||
|
|
@ -57,8 +59,15 @@ impl Styles {
|
|||
|
||||
/// Adds a [`Component`] for the name provided and returns self.
|
||||
#[must_use]
|
||||
pub fn with(mut self, name: &impl NamedComponent, component: impl IntoComponentValue) -> Self {
|
||||
self.insert(name, component);
|
||||
pub fn with<C: ComponentDefinition>(
|
||||
mut self,
|
||||
name: &C,
|
||||
component: impl IntoValue<C::ComponentType>,
|
||||
) -> Self
|
||||
where
|
||||
Value<C::ComponentType>: IntoComponentValue,
|
||||
{
|
||||
self.insert(name, component.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -126,9 +135,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoComponentValue for Value<Component> {
|
||||
impl<T> IntoComponentValue for Value<T>
|
||||
where
|
||||
T: Clone,
|
||||
Component: From<T>,
|
||||
{
|
||||
fn into_component_value(self) -> Value<Component> {
|
||||
self
|
||||
self.map_each(|v| Component::from(v.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +220,12 @@ pub enum Component {
|
|||
/// A description of the depth of a
|
||||
/// [`Container`](crate::widgets::Container).
|
||||
ContainerLevel(ContainerLevel),
|
||||
/// A font family.
|
||||
FontFamily(FamilyOwned),
|
||||
/// The weight (boldness) of a font.
|
||||
FontWeight(Weight),
|
||||
/// The style of a font.
|
||||
FontStyle(Style),
|
||||
|
||||
/// A custom component type.
|
||||
Custom(CustomComponent),
|
||||
|
|
@ -224,6 +243,75 @@ impl Component {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<FamilyOwned> for Component {
|
||||
fn from(value: FamilyOwned) -> Self {
|
||||
Self::FontFamily(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Component> for FamilyOwned {
|
||||
type Error = Component;
|
||||
|
||||
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Component::FontFamily(family) => Ok(family),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireInvalidation for FamilyOwned {
|
||||
fn requires_invalidation(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Weight> for Component {
|
||||
fn from(value: Weight) -> Self {
|
||||
Self::FontWeight(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Component> for Weight {
|
||||
type Error = Component;
|
||||
|
||||
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Component::FontWeight(weight) => Ok(weight),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireInvalidation for Weight {
|
||||
fn requires_invalidation(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for Component {
|
||||
fn from(value: Style) -> Self {
|
||||
Self::FontStyle(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Component> for Style {
|
||||
type Error = Component;
|
||||
|
||||
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Component::FontStyle(style) => Ok(style),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireInvalidation for Style {
|
||||
fn requires_invalidation(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Component {
|
||||
fn from(value: Color) -> Self {
|
||||
Self::Color(value)
|
||||
|
|
@ -1235,7 +1323,7 @@ impl ColorTheme {
|
|||
pub fn light_from_source(source: ColorSource) -> Self {
|
||||
Self {
|
||||
color: source.color(40),
|
||||
color_dim: source.color(30),
|
||||
color_dim: source.color(20),
|
||||
color_bright: source.color(45),
|
||||
on_color: source.color(100),
|
||||
container: source.color(90),
|
||||
|
|
@ -1247,9 +1335,9 @@ impl ColorTheme {
|
|||
#[must_use]
|
||||
pub fn dark_from_source(source: ColorSource) -> Self {
|
||||
Self {
|
||||
color: source.color(70),
|
||||
color: source.color(80),
|
||||
color_dim: source.color(60),
|
||||
color_bright: source.color(75),
|
||||
color_bright: source.color(85),
|
||||
on_color: source.color(10),
|
||||
container: source.color(30),
|
||||
on_container: source.color(90),
|
||||
|
|
@ -1967,3 +2055,73 @@ impl From<ColorSource> for ColorScheme {
|
|||
ColorScheme::from_primary(primary)
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of font families.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FontFamilyList(Arc<Vec<FamilyOwned>>);
|
||||
|
||||
impl Default for FontFamilyList {
|
||||
fn default() -> Self {
|
||||
static DEFAULT: Lazy<FontFamilyList> = Lazy::new(|| FontFamilyList::from(vec![]));
|
||||
DEFAULT.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FontFamilyList {
|
||||
/// Pushes `family` on the end of this list.
|
||||
pub fn push(&mut self, family: FamilyOwned) {
|
||||
Arc::make_mut(&mut self.0).push(family);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FontFamilyList {
|
||||
type Target = [FamilyOwned];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<FamilyOwned> for FontFamilyList {
|
||||
fn from_iter<T: IntoIterator<Item = FamilyOwned>>(iter: T) -> Self {
|
||||
Self(Arc::new(iter.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FamilyOwned> for FontFamilyList {
|
||||
fn from(value: FamilyOwned) -> Self {
|
||||
Self::from(vec![value])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<FamilyOwned>> for FontFamilyList {
|
||||
fn from(value: Vec<FamilyOwned>) -> Self {
|
||||
Self(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FontFamilyList> for Component {
|
||||
fn from(list: FontFamilyList) -> Self {
|
||||
Component::custom(list)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireInvalidation for FontFamilyList {
|
||||
fn requires_invalidation(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Component> for FontFamilyList {
|
||||
type Error = Component;
|
||||
|
||||
fn try_from(value: Component) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Component::Custom(custom) => custom
|
||||
.downcast()
|
||||
.cloned()
|
||||
.ok_or_else(|| Component::Custom(custom)),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
//! All style components supported by the built-in widgets.
|
||||
|
||||
use kludgine::cosmic_text::{FamilyOwned, Style, Weight};
|
||||
use kludgine::figures::units::Lp;
|
||||
use kludgine::shapes::CornerRadii;
|
||||
use kludgine::Color;
|
||||
|
||||
use crate::animation::easings::{EaseInOutQuadradic, EaseInQuadradic, EaseOutQuadradic};
|
||||
use crate::animation::EasingFunction;
|
||||
use crate::styles::{Dimension, FocusableWidgets, VisualOrder};
|
||||
use crate::styles::{Dimension, FocusableWidgets, FontFamilyList, VisualOrder};
|
||||
|
||||
/// Defines a set of style components for Gooey.
|
||||
///
|
||||
|
|
@ -104,7 +105,7 @@ define_components! {
|
|||
/// Intrinsic, uniform padding for a widget.
|
||||
///
|
||||
/// This component is opt-in and does not automatically work for all widgets.
|
||||
IntrinsicPadding(Dimension, "padding", Dimension::Lp(Lp::points(5)))
|
||||
IntrinsicPadding(Dimension, "padding", Dimension::Lp(Lp::points(6)))
|
||||
/// The [`EasingFunction`] to apply to animations that have no inherent
|
||||
/// directionality.
|
||||
Easing(EasingFunction, "Easing", EasingFunction::from(EaseInOutQuadradic))
|
||||
|
|
@ -136,6 +137,12 @@ define_components! {
|
|||
OpaqueWidgetColor(Color, "opaque_color", .surface.opaque_widget)
|
||||
/// A set of radius descriptions for how much roundness to apply to the
|
||||
/// shapes of widgets.
|
||||
CornerRadius(CornerRadii<Dimension>, "corner_radius", CornerRadii::from(Dimension::Lp(Lp::points(7))))
|
||||
CornerRadius(CornerRadii<Dimension>, "corner_radius", CornerRadii::from(Dimension::Lp(Lp::points(10))))
|
||||
/// The font family to render text using.
|
||||
FontFamily(FontFamilyList, "font_family", FontFamilyList::from(FamilyOwned::SansSerif))
|
||||
/// The font (boldness) weight to apply to text rendering.
|
||||
FontWeight(Weight, "font_weight", Weight::NORMAL)
|
||||
/// The font style to apply to text rendering.
|
||||
FontStyle(Style, "font_style", Style::Normal)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ use crate::context::{
|
|||
AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext, WindowHandle,
|
||||
};
|
||||
use crate::styles::{
|
||||
ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue, NamedComponent, Styles,
|
||||
ThemePair, VisualOrder,
|
||||
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue,
|
||||
Styles, ThemePair, VisualOrder,
|
||||
};
|
||||
use crate::tree::Tree;
|
||||
use crate::utils::IgnorePoison;
|
||||
|
|
@ -568,10 +568,15 @@ pub trait MakeWidget: Sized {
|
|||
}
|
||||
|
||||
/// Associates a style component with `self`.
|
||||
fn with(self, name: &impl NamedComponent, component: impl IntoComponentValue) -> Style {
|
||||
let mut styles = Styles::new();
|
||||
styles.insert(name, component);
|
||||
Style::new(styles, self)
|
||||
fn with<C: ComponentDefinition>(
|
||||
self,
|
||||
name: &C,
|
||||
component: impl IntoValue<C::ComponentType>,
|
||||
) -> Style
|
||||
where
|
||||
Value<C::ComponentType>: IntoComponentValue,
|
||||
{
|
||||
Style::new(Styles::new().with(name, component), self)
|
||||
}
|
||||
|
||||
/// Sets the widget that should be focused next.
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ where
|
|||
|
||||
fn selected_range(&mut self) -> (Cursor, Option<Cursor>) {
|
||||
match self.selection.start {
|
||||
Some(start) => match start.cmp(&self.selection.cursor) {
|
||||
Some(start) => match start.offset.cmp(&self.selection.cursor.offset) {
|
||||
Ordering::Less => (start, Some(self.selection.cursor)),
|
||||
Ordering::Equal => {
|
||||
if self.mouse_buttons_down == 0 {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
//! A read-only text widget.
|
||||
|
||||
use kludgine::figures::units::{Px, UPx};
|
||||
use kludgine::figures::{Point, ScreenScale, Size};
|
||||
use kludgine::figures::{Point, Size};
|
||||
use kludgine::text::{MeasuredText, Text, TextOrigin};
|
||||
use kludgine::{Color, DrawableExt};
|
||||
|
||||
use crate::context::{GraphicsContext, LayoutContext};
|
||||
use crate::styles::components::{IntrinsicPadding, TextColor};
|
||||
use crate::styles::components::TextColor;
|
||||
use crate::value::{Dynamic, Generation, IntoValue, Value};
|
||||
use crate::widget::{MakeWidget, Widget, WidgetInstance};
|
||||
use crate::ConstraintLimit;
|
||||
|
|
@ -79,14 +79,11 @@ impl Widget for Label {
|
|||
available_space: Size<ConstraintLimit>,
|
||||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Size<UPx> {
|
||||
let padding = context.get(&IntrinsicPadding).into_upx(context.gfx.scale());
|
||||
let color = context.get(&TextColor);
|
||||
let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
|
||||
let prepared = self.prepared_text(context, color, width);
|
||||
|
||||
let mut size = prepared.size.try_cast().unwrap_or_default();
|
||||
size += padding * 2;
|
||||
size
|
||||
prepared.size.try_cast().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
103
src/window.rs
103
src/window.rs
|
|
@ -18,6 +18,7 @@ use kludgine::app::winit::event::{
|
|||
use kludgine::app::winit::keyboard::Key;
|
||||
use kludgine::app::winit::window;
|
||||
use kludgine::app::WindowBehavior as _;
|
||||
use kludgine::cosmic_text::FamilyOwned;
|
||||
use kludgine::figures::units::{Px, UPx};
|
||||
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, ScreenScale, Size};
|
||||
use kludgine::render::Drawing;
|
||||
|
|
@ -31,7 +32,7 @@ use crate::context::{
|
|||
WidgetContext,
|
||||
};
|
||||
use crate::graphics::Graphics;
|
||||
use crate::styles::ThemePair;
|
||||
use crate::styles::{FontFamilyList, ThemePair};
|
||||
use crate::tree::Tree;
|
||||
use crate::utils::{IgnorePoison, ModifiersExt};
|
||||
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
|
||||
|
|
@ -117,6 +118,25 @@ where
|
|||
pub attributes: WindowAttributes,
|
||||
/// The colors to use to theme the user interface.
|
||||
pub theme: Value<ThemePair>,
|
||||
/// When true, the system fonts will be loaded into the font database. This
|
||||
/// is on by default.
|
||||
pub load_system_fonts: bool,
|
||||
/// The list of font families to try to find when a [`FamilyOwned::Serif`]
|
||||
/// font is requested.
|
||||
pub serif_font_family: FontFamilyList,
|
||||
/// The list of font families to try to find when a
|
||||
/// [`FamilyOwned::SansSerif`] font is requested.
|
||||
pub sans_serif_font_family: FontFamilyList,
|
||||
/// The list of font families to try to find when a [`FamilyOwned::Fantasy`]
|
||||
/// font is requested.
|
||||
pub fantasy_font_family: FontFamilyList,
|
||||
/// The list of font families to try to find when a
|
||||
/// [`FamilyOwned::Monospace`] font is requested.
|
||||
pub monospace_font_family: FontFamilyList,
|
||||
/// The list of font families to try to find when a [`FamilyOwned::Cursive`]
|
||||
/// font is requested.
|
||||
pub cursive_font_family: FontFamilyList,
|
||||
|
||||
occluded: Option<Dynamic<bool>>,
|
||||
focused: Option<Dynamic<bool>>,
|
||||
theme_mode: Option<Value<ThemeMode>>,
|
||||
|
|
@ -225,10 +245,16 @@ where
|
|||
..WindowAttributes::default()
|
||||
},
|
||||
context,
|
||||
load_system_fonts: true,
|
||||
theme: Value::default(),
|
||||
occluded: None,
|
||||
focused: None,
|
||||
theme_mode: None,
|
||||
serif_font_family: FontFamilyList::default(),
|
||||
sans_serif_font_family: FontFamilyList::default(),
|
||||
fantasy_font_family: FontFamilyList::default(),
|
||||
monospace_font_family: FontFamilyList::default(),
|
||||
cursive_font_family: FontFamilyList::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -248,6 +274,12 @@ where
|
|||
focused: self.focused,
|
||||
theme: Some(self.theme),
|
||||
theme_mode: self.theme_mode,
|
||||
load_system_fonts: self.load_system_fonts,
|
||||
serif_font_family: self.serif_font_family,
|
||||
sans_serif_font_family: self.sans_serif_font_family,
|
||||
fantasy_font_family: self.fantasy_font_family,
|
||||
monospace_font_family: self.monospace_font_family,
|
||||
cursive_font_family: self.cursive_font_family,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
|
@ -449,33 +481,52 @@ where
|
|||
|
||||
fn initialize(
|
||||
window: kludgine::app::Window<'_, WindowCommand>,
|
||||
_graphics: &mut kludgine::Graphics<'_>,
|
||||
graphics: &mut kludgine::Graphics<'_>,
|
||||
AssertUnwindSafe(context): Self::Context,
|
||||
) -> Self {
|
||||
let occluded = context
|
||||
.settings
|
||||
.borrow_mut()
|
||||
.occluded
|
||||
.take()
|
||||
.unwrap_or_default();
|
||||
let focused = context
|
||||
.settings
|
||||
.borrow_mut()
|
||||
.focused
|
||||
.take()
|
||||
.unwrap_or_default();
|
||||
let theme = context
|
||||
.settings
|
||||
.borrow_mut()
|
||||
.theme
|
||||
.take()
|
||||
.expect("theme always present");
|
||||
let mut settings = context.settings.borrow_mut();
|
||||
let occluded = settings.occluded.take().unwrap_or_default();
|
||||
let focused = settings.focused.take().unwrap_or_default();
|
||||
let theme = settings.theme.take().expect("theme always present");
|
||||
|
||||
let fontdb = graphics.font_system().db_mut();
|
||||
if let Some(FamilyOwned::Name(name)) =
|
||||
Graphics::inner_find_available_font_family(fontdb, &settings.serif_font_family)
|
||||
{
|
||||
fontdb.set_serif_family(name);
|
||||
}
|
||||
if let Some(FamilyOwned::Name(name)) =
|
||||
Graphics::inner_find_available_font_family(fontdb, &settings.sans_serif_font_family)
|
||||
{
|
||||
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)) =
|
||||
Graphics::inner_find_available_font_family(fontdb, &settings.fantasy_font_family)
|
||||
{
|
||||
fontdb.set_fantasy_family(name);
|
||||
}
|
||||
if let Some(FamilyOwned::Name(name)) =
|
||||
Graphics::inner_find_available_font_family(fontdb, &settings.monospace_font_family)
|
||||
{
|
||||
fontdb.set_monospace_family(name);
|
||||
}
|
||||
if let Some(FamilyOwned::Name(name)) =
|
||||
Graphics::inner_find_available_font_family(fontdb, &settings.cursive_font_family)
|
||||
{
|
||||
fontdb.set_cursive_family(name);
|
||||
}
|
||||
|
||||
let clipboard = Clipboard::new()
|
||||
.ok()
|
||||
.map(|clipboard| Arc::new(Mutex::new(clipboard)));
|
||||
|
||||
let theme_mode = match context.settings.borrow_mut().theme_mode.take() {
|
||||
let theme_mode = match settings.theme_mode.take() {
|
||||
Some(Value::Dynamic(dynamic)) => {
|
||||
dynamic.update(window.theme().into());
|
||||
Value::Dynamic(dynamic)
|
||||
|
|
@ -483,7 +534,7 @@ where
|
|||
Some(Value::Constant(mode)) => Value::Constant(mode),
|
||||
None => Value::dynamic(window.theme().into()),
|
||||
};
|
||||
let transparent = context.settings.borrow().transparent;
|
||||
let transparent = settings.transparent;
|
||||
let mut behavior = T::initialize(
|
||||
&mut RunningWindow::new(window, &clipboard, &focused, &occluded),
|
||||
context.user,
|
||||
|
|
@ -1093,7 +1144,7 @@ pub(crate) struct CursorState {
|
|||
pub(crate) mod sealed {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::styles::ThemePair;
|
||||
use crate::styles::{FontFamilyList, ThemePair};
|
||||
use crate::value::{Dynamic, Value};
|
||||
use crate::window::{ThemeMode, WindowAttributes};
|
||||
|
||||
|
|
@ -1109,6 +1160,12 @@ pub(crate) mod sealed {
|
|||
pub theme: Option<Value<ThemePair>>,
|
||||
pub theme_mode: Option<Value<ThemeMode>>,
|
||||
pub transparent: bool,
|
||||
pub load_system_fonts: bool,
|
||||
pub serif_font_family: FontFamilyList,
|
||||
pub sans_serif_font_family: FontFamilyList,
|
||||
pub fantasy_font_family: FontFamilyList,
|
||||
pub monospace_font_family: FontFamilyList,
|
||||
pub cursive_font_family: FontFamilyList,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue