mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
Added font size + heading helpers
This commit is contained in:
parent
3f2aace55e
commit
8e19a89bca
7 changed files with 583 additions and 30 deletions
|
|
@ -131,7 +131,6 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn game_end(winner: Option<Player>, app: &Dynamic<AppState>) -> impl MakeWidget {
|
fn game_end(winner: Option<Player>, app: &Dynamic<AppState>) -> impl MakeWidget {
|
||||||
// TODO we need typography styles
|
|
||||||
let app = app.clone();
|
let app = app.clone();
|
||||||
let label = if let Some(winner) = winner {
|
let label = if let Some(winner) = winner {
|
||||||
format!("{winner:?} wins!")
|
format!("{winner:?} wins!")
|
||||||
|
|
@ -140,6 +139,7 @@ fn game_end(winner: Option<Player>, app: &Dynamic<AppState>) -> impl MakeWidget
|
||||||
};
|
};
|
||||||
|
|
||||||
label
|
label
|
||||||
|
.h1()
|
||||||
.and(
|
.and(
|
||||||
"Play Again"
|
"Play Again"
|
||||||
.into_button()
|
.into_button()
|
||||||
|
|
|
||||||
18
examples/typography.rs
Normal file
18
examples/typography.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use gooey::widget::MakeWidget;
|
||||||
|
use gooey::Run;
|
||||||
|
|
||||||
|
fn main() -> gooey::Result {
|
||||||
|
"Heading 1"
|
||||||
|
.h1()
|
||||||
|
.and("Heading 2".h2())
|
||||||
|
.and("Heading 3".h3())
|
||||||
|
.and("Heading 4".h4())
|
||||||
|
.and("Heading 5".h5())
|
||||||
|
.and("Heading 6".h6())
|
||||||
|
.and("Regular Text")
|
||||||
|
.and("Small Text".small())
|
||||||
|
.and("X-Small Text".x_small())
|
||||||
|
.into_rows()
|
||||||
|
.centered()
|
||||||
|
.run()
|
||||||
|
}
|
||||||
202
src/styles.rs
202
src/styles.rs
|
|
@ -30,7 +30,7 @@ pub mod components;
|
||||||
|
|
||||||
/// A collection of style components organized by their name.
|
/// A collection of style components organized by their name.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Styles(Arc<AHashMap<ComponentName, Value<Component>>>);
|
pub struct Styles(Arc<StyleData>);
|
||||||
|
|
||||||
impl Styles {
|
impl Styles {
|
||||||
/// Returns an empty collection.
|
/// Returns an empty collection.
|
||||||
|
|
@ -43,12 +43,16 @@ impl Styles {
|
||||||
/// without reallocating.
|
/// without reallocating.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self(Arc::new(AHashMap::with_capacity(capacity)))
|
Self(Arc::new(StyleData {
|
||||||
|
components: AHashMap::with_capacity(capacity),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a [`Component`] with a given name.
|
/// Inserts a [`Component`] with a given name.
|
||||||
pub fn insert_named(&mut self, name: ComponentName, component: impl IntoComponentValue) {
|
pub fn insert_named(&mut self, name: ComponentName, component: impl IntoComponentValue) {
|
||||||
Arc::make_mut(&mut self.0).insert(name, component.into_component_value());
|
Arc::make_mut(&mut self.0)
|
||||||
|
.components
|
||||||
|
.insert(name, component.into_component_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a [`Component`] using then name provided.
|
/// Inserts a [`Component`] using then name provided.
|
||||||
|
|
@ -57,6 +61,16 @@ impl Styles {
|
||||||
self.insert_named(name, component);
|
self.insert_named(name, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts a [`Component`] using then name provided, resolving the value
|
||||||
|
/// through `dynamic`.
|
||||||
|
pub fn insert_dynamic(
|
||||||
|
&mut self,
|
||||||
|
name: &impl NamedComponent,
|
||||||
|
dynamic: impl Into<DynamicComponent>,
|
||||||
|
) {
|
||||||
|
self.insert(name, Component::Dynamic(dynamic.into()));
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a [`Component`] for the name provided and returns self.
|
/// Adds a [`Component`] for the name provided and returns self.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with<C: ComponentDefinition>(
|
pub fn with<C: ComponentDefinition>(
|
||||||
|
|
@ -71,14 +85,64 @@ impl Styles {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a [`Component`] using then name provided, resolving the value
|
||||||
|
/// through `dynamic`. This function returns self.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_dynamic<C: ComponentDefinition>(
|
||||||
|
mut self,
|
||||||
|
name: &C,
|
||||||
|
dynamic: impl Into<DynamicComponent>,
|
||||||
|
) -> Self {
|
||||||
|
self.insert_dynamic(name, dynamic);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the associated component for the given name, if found.
|
/// Returns the associated component for the given name, if found.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_named<Named>(&self, component: &Named) -> Option<&Value<Component>>
|
pub fn get_with_fallback<Fallback>(
|
||||||
|
&self,
|
||||||
|
component: &impl NamedComponent,
|
||||||
|
fallback: &Fallback,
|
||||||
|
context: &WidgetContext<'_, '_>,
|
||||||
|
) -> Fallback::ComponentType
|
||||||
where
|
where
|
||||||
Named: NamedComponent + ?Sized,
|
Fallback: ComponentDefinition + ?Sized,
|
||||||
{
|
{
|
||||||
let name = component.name();
|
self.0
|
||||||
self.0.get(&name)
|
.components
|
||||||
|
.get(&component.name())
|
||||||
|
.or_else(|| self.0.components.get(&fallback.name()))
|
||||||
|
.and_then(|component| Self::resolve_component(component, context))
|
||||||
|
.unwrap_or_else(|| fallback.default_value(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_component<T>(
|
||||||
|
component: &Value<Component>,
|
||||||
|
context: &WidgetContext<'_, '_>,
|
||||||
|
) -> Option<T>
|
||||||
|
where
|
||||||
|
T: ComponentType,
|
||||||
|
{
|
||||||
|
let mut resolved = component.get();
|
||||||
|
loop {
|
||||||
|
match T::try_from_component(resolved) {
|
||||||
|
Ok(value) => {
|
||||||
|
if value.requires_invalidation() {
|
||||||
|
component.invalidate_when_changed(context);
|
||||||
|
} else {
|
||||||
|
component.redraw_when_changed(context);
|
||||||
|
}
|
||||||
|
break Some(value);
|
||||||
|
}
|
||||||
|
Err(Component::Dynamic(dynamic)) => {
|
||||||
|
let Some(new_component) = dynamic.resolve(context) else {
|
||||||
|
break None;
|
||||||
|
};
|
||||||
|
resolved = new_component;
|
||||||
|
}
|
||||||
|
Err(_) => break None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the component associated with the given name, or if not found,
|
/// Returns the component associated with the given name, or if not found,
|
||||||
|
|
@ -92,22 +156,10 @@ impl Styles {
|
||||||
where
|
where
|
||||||
Named: ComponentDefinition + ?Sized,
|
Named: ComponentDefinition + ?Sized,
|
||||||
{
|
{
|
||||||
let name = component.name();
|
|
||||||
self.0
|
self.0
|
||||||
.get(&name)
|
.components
|
||||||
.and_then(|component| {
|
.get(&component.name())
|
||||||
match <Named::ComponentType>::try_from_component(component.get()) {
|
.and_then(|component| Self::resolve_component(component, context))
|
||||||
Ok(value) => {
|
|
||||||
if value.requires_invalidation() {
|
|
||||||
component.invalidate_when_changed(context);
|
|
||||||
} else {
|
|
||||||
component.redraw_when_changed(context);
|
|
||||||
}
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
Err(_) => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| component.default_value(context))
|
.unwrap_or_else(|| component.default_value(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +172,11 @@ impl Styles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct StyleData {
|
||||||
|
components: AHashMap<ComponentName, Value<Component>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A value that can be converted into a `Value<Component>`.
|
/// A value that can be converted into a `Value<Component>`.
|
||||||
pub trait IntoComponentValue {
|
pub trait IntoComponentValue {
|
||||||
/// Returns `self` stored in a component value.
|
/// Returns `self` stored in a component value.
|
||||||
|
|
@ -173,6 +230,7 @@ impl IntoIterator for Styles {
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Arc::try_unwrap(self.0)
|
Arc::try_unwrap(self.0)
|
||||||
.unwrap_or_else(|err| err.as_ref().clone())
|
.unwrap_or_else(|err| err.as_ref().clone())
|
||||||
|
.components
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +287,9 @@ pub enum Component {
|
||||||
|
|
||||||
/// A custom component type.
|
/// A custom component type.
|
||||||
Custom(CustomComponent),
|
Custom(CustomComponent),
|
||||||
|
|
||||||
|
/// This component should use the associated value in the named class.
|
||||||
|
Dynamic(DynamicComponent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component {
|
impl Component {
|
||||||
|
|
@ -241,6 +302,23 @@ impl Component {
|
||||||
{
|
{
|
||||||
Self::Custom(CustomComponent::new(component))
|
Self::Custom(CustomComponent::new(component))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new [`DynamicComponent`] which allows resolving a component at
|
||||||
|
/// runtime.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dynamic<T, Func>(resolve: Func) -> Self
|
||||||
|
where
|
||||||
|
Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<T>
|
||||||
|
+ RefUnwindSafe
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
T: ComponentType,
|
||||||
|
{
|
||||||
|
Self::Dynamic(DynamicComponent::new(move |context| {
|
||||||
|
resolve(context).map(T::into_component)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FamilyOwned> for Component {
|
impl From<FamilyOwned> for Component {
|
||||||
|
|
@ -854,7 +932,7 @@ impl From<&'static Lazy<ComponentName>> for ComponentName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that represents a named style component.
|
/// A type that represents a named style component.
|
||||||
pub trait NamedComponent {
|
pub trait NamedComponent: Sized {
|
||||||
/// Returns the name of the style component.
|
/// Returns the name of the style component.
|
||||||
fn name(&self) -> Cow<'_, ComponentName>;
|
fn name(&self) -> Cow<'_, ComponentName>;
|
||||||
}
|
}
|
||||||
|
|
@ -2253,3 +2331,81 @@ impl TryFrom<Component> for FontFamilyList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Component`] that resolves its value at runtime.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DynamicComponent(Arc<dyn DynamicComponentResolver>);
|
||||||
|
|
||||||
|
impl Debug for DynamicComponent {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("DynamicComponent").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for DynamicComponent {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Arc::ptr_eq(&self.0, &other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that resolves to a [`Component`] at runtime.
|
||||||
|
pub trait DynamicComponentResolver: RefUnwindSafe + Send + Sync + 'static {
|
||||||
|
/// Returns the effective component, if one should be applied.
|
||||||
|
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DynamicFunctionWrapper<F>(F);
|
||||||
|
|
||||||
|
impl<T> DynamicComponentResolver for DynamicFunctionWrapper<T>
|
||||||
|
where
|
||||||
|
T: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<Component>
|
||||||
|
+ RefUnwindSafe
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component> {
|
||||||
|
self.0(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DynamicComponentResolver for T
|
||||||
|
where
|
||||||
|
T: ComponentDefinition + Clone + RefUnwindSafe + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component> {
|
||||||
|
Some(context.get(self).into_component())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for DynamicComponent
|
||||||
|
where
|
||||||
|
T: DynamicComponentResolver,
|
||||||
|
{
|
||||||
|
fn from(resolve: T) -> Self {
|
||||||
|
Self(Arc::new(resolve))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicComponent {
|
||||||
|
/// Returns a new dynamic component that invokes `resolve` each time it is
|
||||||
|
/// used by widgets.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new<Func>(resolve: Func) -> Self
|
||||||
|
where
|
||||||
|
Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<Component>
|
||||||
|
+ RefUnwindSafe
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
Self::from(DynamicFunctionWrapper(resolve))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invokes the resolver function, optionally returning a resolved
|
||||||
|
/// component.
|
||||||
|
#[must_use]
|
||||||
|
pub fn resolve(&self, context: &WidgetContext<'_, '_>) -> Option<Component> {
|
||||||
|
self.0.resolve_component(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,52 @@ macro_rules! define_components {
|
||||||
define_components! {
|
define_components! {
|
||||||
Global {
|
Global {
|
||||||
/// The [`Dimension`] to use as the size to render text.
|
/// The [`Dimension`] to use as the size to render text.
|
||||||
TextSize(Dimension, "text_size", Dimension::Lp(Lp::points(12)))
|
TextSize(Dimension, "text_size", @BaseTextSize)
|
||||||
/// The [`Dimension`] to use to space multiple lines of text.
|
/// The [`Dimension`] to use to space multiple lines of text.
|
||||||
LineHeight(Dimension,"line_height",Dimension::Lp(Lp::points(16)))
|
LineHeight(Dimension,"line_height", @BaseLineHeight)
|
||||||
|
|
||||||
|
/// The base [`Dimension`] to use as the normal text size. Unless
|
||||||
|
/// overridden, all other sizes for built-in widgets will be based on
|
||||||
|
/// this dimension.
|
||||||
|
BaseTextSize(Dimension, "base_text_size", Dimension::Lp(Lp::points(12)))
|
||||||
|
/// The base [`Dimension`] to use to space multiple lines of text.
|
||||||
|
/// Unless overridden, all other sizes for built-in widgets will be
|
||||||
|
/// based on this dimension.
|
||||||
|
BaseLineHeight(Dimension,"base_line_height", Dimension::Lp(Lp::points(16)))
|
||||||
|
/// The largest text size on a series of 8 steps.
|
||||||
|
TextSize8(Dimension, "text_size_8", |context| context.get(&BaseTextSize) * 2.5)
|
||||||
|
/// The second-largest text size on a series of 8 steps.
|
||||||
|
TextSize7(Dimension, "text_size_7", |context| context.get(&BaseTextSize) * 2.25)
|
||||||
|
/// The third-largest text size on a series of 8 steps.
|
||||||
|
TextSize6(Dimension, "text_size_6", |context| context.get(&BaseTextSize) * 2.0)
|
||||||
|
/// The fourth-largest text size on a series of 8 steps.
|
||||||
|
TextSize5(Dimension, "text_size_5", |context| context.get(&BaseTextSize) * 1.5)
|
||||||
|
/// The fifth-largest text size on a series of 8 steps.
|
||||||
|
TextSize4(Dimension, "text_size_4", |context| context.get(&BaseTextSize) * 1.25)
|
||||||
|
/// The base text size on a series of 8 steps.
|
||||||
|
TextSize3(Dimension, "text_size_3", @BaseTextSize)
|
||||||
|
/// The second-smallest text size on a series of 8 steps.
|
||||||
|
TextSize2(Dimension, "text_size_2", |context| context.get(&BaseTextSize) * 0.75)
|
||||||
|
/// The smallest text size on a series of 8 steps.
|
||||||
|
TextSize1(Dimension, "text_size_1", |context| context.get(&BaseTextSize) * 0.5)
|
||||||
|
|
||||||
|
/// The largest line height on a series of 8 steps.
|
||||||
|
LineHeight8(Dimension, "line_height_8", |context| context.get(&BaseLineHeight) * 2.5)
|
||||||
|
/// The second-largest line height on a series of 8 steps.
|
||||||
|
LineHeight7(Dimension, "line_height_7", |context| context.get(&BaseLineHeight) * 2.25)
|
||||||
|
/// The third-largest line height on a series of 8 steps.
|
||||||
|
LineHeight6(Dimension, "line_height_6", |context| context.get(&BaseLineHeight) * 2.0)
|
||||||
|
/// The fourth-largest line height on a series of 8 steps.
|
||||||
|
LineHeight5(Dimension, "line_height_5", |context| context.get(&BaseLineHeight) * 1.5)
|
||||||
|
/// The fifth-largest line height on a series of 8 steps.
|
||||||
|
LineHeight4(Dimension, "line_height_4", |context| context.get(&BaseLineHeight) * 1.25)
|
||||||
|
/// The base line height on a series of 8 steps.
|
||||||
|
LineHeight3(Dimension, "line_height_4", @BaseLineHeight)
|
||||||
|
/// The second-smallest line height on a series of 8 steps.
|
||||||
|
LineHeight2(Dimension, "line_height_2", |context| context.get(&BaseLineHeight) * 0.75)
|
||||||
|
/// The smallest line height on a series of 8 steps.
|
||||||
|
LineHeight1(Dimension, "line_height_1", |context| context.get(&BaseLineHeight) * 0.675)
|
||||||
|
|
||||||
/// The [`Color`] of the surface for the user interface to draw upon.
|
/// The [`Color`] of the surface for the user interface to draw upon.
|
||||||
SurfaceColor(Color, "surface_color", .surface.color)
|
SurfaceColor(Color, "surface_color", .surface.color)
|
||||||
/// The [`Color`] to use when rendering text.
|
/// The [`Color`] to use when rendering text.
|
||||||
|
|
@ -147,5 +190,50 @@ define_components! {
|
||||||
FontWeight(Weight, "font_weight", Weight::NORMAL)
|
FontWeight(Weight, "font_weight", Weight::NORMAL)
|
||||||
/// The font style to apply to text rendering.
|
/// The font style to apply to text rendering.
|
||||||
FontStyle(Style, "font_style", Style::Normal)
|
FontStyle(Style, "font_style", Style::Normal)
|
||||||
|
|
||||||
|
/// The default [`Weight`] to apply to headings.
|
||||||
|
HeadingWeight(Weight, "heading_weight", Weight::BOLD)
|
||||||
|
/// The [`Weight`] to apply to h1 headings.
|
||||||
|
Heading1Weight(Weight, "heading_weight_1", @HeadingWeight)
|
||||||
|
/// The [`Weight`] to apply to h2 headings.
|
||||||
|
Heading2Weight(Weight, "heading_weight_2", @HeadingWeight)
|
||||||
|
/// The [`Weight`] to apply to h3 headings.
|
||||||
|
Heading3Weight(Weight, "heading_weight_3", @HeadingWeight)
|
||||||
|
/// The [`Weight`] to apply to h4 headings.
|
||||||
|
Heading4Weight(Weight, "heading_weight_4", @HeadingWeight)
|
||||||
|
/// The [`Weight`] to apply to h5 headings.
|
||||||
|
Heading5Weight(Weight, "heading_weight_5", @HeadingWeight)
|
||||||
|
/// The [`Weight`] to apply to h6 headings.
|
||||||
|
Heading6Weight(Weight, "heading_weight_6", @HeadingWeight)
|
||||||
|
|
||||||
|
/// The default [`Style`] to apply to headings.
|
||||||
|
HeadingStyle(Style, "heading_style", Style::Normal)
|
||||||
|
/// The [`Style`] to apply to h1 headings.
|
||||||
|
Heading1Style(Style, "heading_style_1", @HeadingStyle)
|
||||||
|
/// The [`Style`] to apply to h2 headings.
|
||||||
|
Heading2Style(Style, "heading_style_2", @HeadingStyle)
|
||||||
|
/// The [`Style`] to apply to h3 headings.
|
||||||
|
Heading3Style(Style, "heading_style_3", @HeadingStyle)
|
||||||
|
/// The [`Style`] to apply to h4 headings.
|
||||||
|
Heading4Style(Style, "heading_style_4", @HeadingStyle)
|
||||||
|
/// The [`Style`] to apply to h5 headings.
|
||||||
|
Heading5Style(Style, "heading_style_5", @HeadingStyle)
|
||||||
|
/// The [`Style`] to apply to h6 headings.
|
||||||
|
Heading6Style(Style, "heading_style_6", @HeadingStyle)
|
||||||
|
|
||||||
|
/// The default [`FontFamilyList`] to apply to headings.
|
||||||
|
HeadingFontFamily(FontFamilyList, "heading_font_family", FontFamilyList::from(FamilyOwned::SansSerif))
|
||||||
|
/// The [`FontFamilyList`] to apply to h1 headings.
|
||||||
|
Heading1FontFamily(FontFamilyList, "heading_font_family_1", @HeadingFontFamily)
|
||||||
|
/// The [`FontFamilyList`] to apply to h2 headings.
|
||||||
|
Heading2FontFamily(FontFamilyList, "heading_font_family_2", @HeadingFontFamily)
|
||||||
|
/// The [`FontFamilyList`] to apply to h3 headings.
|
||||||
|
Heading3FontFamily(FontFamilyList, "heading_font_family_3", @HeadingFontFamily)
|
||||||
|
/// The [`FontFamilyList`] to apply to h4 headings.
|
||||||
|
Heading4FontFamily(FontFamilyList, "heading_font_family_4", @HeadingFontFamily)
|
||||||
|
/// The [`FontFamilyList`] to apply to h5 headings.
|
||||||
|
Heading5FontFamily(FontFamilyList, "heading_font_family_5", @HeadingFontFamily)
|
||||||
|
/// The [`FontFamilyList`] to apply to h6 headings.
|
||||||
|
Heading6FontFamily(FontFamilyList, "heading_font_family_6", @HeadingFontFamily)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/widget.rs
130
src/widget.rs
|
|
@ -19,9 +19,18 @@ use kludgine::Color;
|
||||||
|
|
||||||
use crate::context::sealed::WindowHandle;
|
use crate::context::sealed::WindowHandle;
|
||||||
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
|
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
|
||||||
|
use crate::styles::components::{
|
||||||
|
FontFamily, FontStyle, FontWeight, Heading1FontFamily, Heading1Style, Heading1Weight,
|
||||||
|
Heading2FontFamily, Heading2Style, Heading2Weight, Heading3FontFamily, Heading3Style,
|
||||||
|
Heading3Weight, Heading4FontFamily, Heading4Style, Heading4Weight, Heading5FontFamily,
|
||||||
|
Heading5Style, Heading5Weight, Heading6FontFamily, Heading6Style, Heading6Weight, LineHeight,
|
||||||
|
LineHeight1, LineHeight2, LineHeight3, LineHeight4, LineHeight5, LineHeight6, LineHeight7,
|
||||||
|
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
|
||||||
|
TextSize7, TextSize8,
|
||||||
|
};
|
||||||
use crate::styles::{
|
use crate::styles::{
|
||||||
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue,
|
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, DynamicComponent, Edges,
|
||||||
Styles, ThemePair, VisualOrder,
|
IntoComponentValue, Styles, ThemePair, VisualOrder,
|
||||||
};
|
};
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::utils::IgnorePoison;
|
use crate::utils::IgnorePoison;
|
||||||
|
|
@ -675,6 +684,123 @@ pub trait MakeWidget: Sized {
|
||||||
Style::new(Styles::new().with(name, component), self)
|
Style::new(Styles::new().with(name, component), self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Associates a style component with `self`, resolving its value using
|
||||||
|
/// `dynamic` at runtime.
|
||||||
|
fn with_dynamic<C: ComponentDefinition>(
|
||||||
|
self,
|
||||||
|
name: &C,
|
||||||
|
dynamic: impl Into<DynamicComponent>,
|
||||||
|
) -> Style
|
||||||
|
where
|
||||||
|
Value<C::ComponentType>: IntoComponentValue,
|
||||||
|
{
|
||||||
|
Style::new(Styles::new().with_dynamic(name, dynamic), self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the largest of 6 heading styles.
|
||||||
|
fn h1(self) -> Style {
|
||||||
|
self.xxxx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading1Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading1FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading1Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second largest of 6 heading styles.
|
||||||
|
fn h2(self) -> Style {
|
||||||
|
self.xxx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading2Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading2FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading2Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third largest of 6 heading styles.
|
||||||
|
fn h3(self) -> Style {
|
||||||
|
self.xx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading3Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading3FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading3Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third smallest of 6 heading styles.
|
||||||
|
fn h4(self) -> Style {
|
||||||
|
self.x_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading4Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading4FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading4Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second smallest of 6 heading styles.
|
||||||
|
fn h5(self) -> Style {
|
||||||
|
self.large()
|
||||||
|
.with_dynamic(&FontStyle, Heading5Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading5FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading5Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the smallest of 6 heading styles.
|
||||||
|
fn h6(self) -> Style {
|
||||||
|
self.default_size()
|
||||||
|
.with_dynamic(&FontStyle, Heading6Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading6FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading6Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the largest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn xxxx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize8)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight8)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second largest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn xxx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize7)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight7)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third largest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn xx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize6)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight6)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the fourth largest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn x_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize5)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight5)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the fifth largest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize4)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn default_size(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize3)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn small(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize2)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
fn x_small(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize1)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight1)
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the widget that should be focused next.
|
/// Sets the widget that should be focused next.
|
||||||
///
|
///
|
||||||
/// Gooey automatically determines reverse tab order by using this same
|
/// Gooey automatically determines reverse tab order by using this same
|
||||||
|
|
|
||||||
|
|
@ -98,4 +98,10 @@ macro_rules! impl_make_widget {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_make_widget!(&'_ str, String, Value<String>, Dynamic<String>);
|
impl_make_widget!(
|
||||||
|
&'_ str,
|
||||||
|
String,
|
||||||
|
Value<String>,
|
||||||
|
Dynamic<String>,
|
||||||
|
Dynamic<&'static str>
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
use crate::context::EventContext;
|
use crate::context::EventContext;
|
||||||
use crate::styles::Styles;
|
use crate::styles::components::{
|
||||||
|
FontFamily, FontStyle, FontWeight, Heading1FontFamily, Heading1Style, Heading1Weight,
|
||||||
|
Heading2FontFamily, Heading2Style, Heading2Weight, Heading3FontFamily, Heading3Style,
|
||||||
|
Heading3Weight, Heading4FontFamily, Heading4Style, Heading4Weight, Heading5FontFamily,
|
||||||
|
Heading5Style, Heading5Weight, Heading6FontFamily, Heading6Style, Heading6Weight, LineHeight,
|
||||||
|
LineHeight1, LineHeight2, LineHeight3, LineHeight4, LineHeight5, LineHeight6, LineHeight7,
|
||||||
|
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
|
||||||
|
TextSize7, TextSize8,
|
||||||
|
};
|
||||||
|
use crate::styles::{ComponentDefinition, DynamicComponent, IntoComponentValue, Styles};
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
|
||||||
|
|
@ -19,6 +28,156 @@ impl Style {
|
||||||
child: WidgetRef::new(child),
|
child: WidgetRef::new(child),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_styles_mut<R>(&mut self, map: impl FnOnce(&mut Styles) -> R) -> R {
|
||||||
|
match &mut self.styles {
|
||||||
|
Value::Constant(styles) => map(styles),
|
||||||
|
Value::Dynamic(dynamic) => dynamic.map_mut(map),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates a style component with `self`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with<C: ComponentDefinition>(
|
||||||
|
mut self,
|
||||||
|
name: &C,
|
||||||
|
component: impl IntoValue<C::ComponentType>,
|
||||||
|
) -> Style
|
||||||
|
where
|
||||||
|
Value<C::ComponentType>: IntoComponentValue,
|
||||||
|
{
|
||||||
|
self.map_styles_mut(|styles| {
|
||||||
|
styles.insert(name, component.into_value());
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates a style component with `self`, resolving its value using
|
||||||
|
/// `dynamic` at runtime.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_dynamic<C: ComponentDefinition>(
|
||||||
|
mut self,
|
||||||
|
name: &C,
|
||||||
|
dynamic: impl Into<DynamicComponent>,
|
||||||
|
) -> Style
|
||||||
|
where
|
||||||
|
Value<C::ComponentType>: IntoComponentValue,
|
||||||
|
{
|
||||||
|
self.map_styles_mut(|styles| {
|
||||||
|
styles.insert_dynamic(name, dynamic);
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the largest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h1(self) -> Style {
|
||||||
|
self.xxxx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading1Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading1FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading1Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second largest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h2(self) -> Style {
|
||||||
|
self.xxx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading2Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading2FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading2Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third largest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h3(self) -> Style {
|
||||||
|
self.xx_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading3Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading3FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading3Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third smallest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h4(self) -> Style {
|
||||||
|
self.x_large()
|
||||||
|
.with_dynamic(&FontStyle, Heading4Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading4FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading4Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second smallest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h5(self) -> Style {
|
||||||
|
self.large()
|
||||||
|
.with_dynamic(&FontStyle, Heading5Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading5FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading5Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the smallest of 6 heading styles.
|
||||||
|
#[must_use]
|
||||||
|
pub fn h6(self) -> Style {
|
||||||
|
self.default_size()
|
||||||
|
.with_dynamic(&FontStyle, Heading6Style)
|
||||||
|
.with_dynamic(&FontFamily, Heading6FontFamily)
|
||||||
|
.with_dynamic(&FontWeight, Heading6Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the largest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn xxxx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize8)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight8)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second largest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn xxx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize7)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight7)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third largest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn xx_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize6)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight6)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the fourth largest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn x_large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize5)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight5)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the fifth largest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn large(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize4)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the third smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn default_size(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize3)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the second smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn small(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize2)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles `self` with the smallest text size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn x_small(self) -> Style {
|
||||||
|
self.with_dynamic(&TextSize, TextSize1)
|
||||||
|
.with_dynamic(&LineHeight, LineHeight1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WrapperWidget for Style {
|
impl WrapperWidget for Style {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue