Skip to content

Commit

Permalink
Styles are now reactive
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Nov 9, 2023
1 parent 897880d commit 1714948
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 119 deletions.
6 changes: 3 additions & 3 deletions examples/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ fn main() -> gooey::Result<()> {
Canvas::new(move |context| {
angle += Angle::degrees(1);

let center = Point::from(context.graphics.size()).into_signed() / 2;
context.graphics.draw_text(
let center = Point::from(context.gfx.size()).into_signed() / 2;
context.gfx.draw_text(
Text::new("Canvas exposes the full power of Kludgine", Color::WHITE)
.origin(TextOrigin::Center),
center - Point::new(Px(0), Px(100)),
None,
None,
);
context.graphics.draw_shape(
context.gfx.draw_shape(
&Shape::filled_rect(
Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))),
Color::RED,
Expand Down
4 changes: 2 additions & 2 deletions examples/gameui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ fn main() -> gooey::Result {
Stack::columns(
Label::new(chat_log.clone()).vertical_scroll().expand().and(
Canvas::new(|context| {
let entire_canvas = Rect::from(context.graphics.size());
context.graphics.draw_shape(
let entire_canvas = Rect::from(context.gfx.size());
context.gfx.draw_shape(
&Shape::filled_rect(entire_canvas, Color::RED),
Point::default(),
None,
Expand Down
26 changes: 14 additions & 12 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,15 +401,15 @@ pub struct GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> {
/// The graphics context clipped and offset to the area of the widget being
/// rendered. Drawing at 0,0 will draw at the top-left pixel of the laid-out
/// widget region.
pub graphics: Exclusive<'context, Graphics<'clip, 'gfx, 'pass>>,
pub gfx: Exclusive<'context, Graphics<'clip, 'gfx, 'pass>>,
}

impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> {
/// Returns a new instance that borrows from `self`.
pub fn borrowed(&mut self) -> GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass> {
GraphicsContext {
widget: self.widget.borrowed(),
graphics: Exclusive::Borrowed(&mut self.graphics),
gfx: Exclusive::Borrowed(&mut self.gfx),
}
}

Expand All @@ -428,12 +428,12 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
widget.manage(self).map(|widget| {
let widget = self.widget.for_other(&widget);
let layout = widget.last_layout().map_or_else(
|| Rect::from(self.graphics.clip_rect().size).into_signed(),
|rect| rect - self.graphics.region().origin,
|| Rect::from(self.gfx.clip_rect().size).into_signed(),
|rect| rect - self.gfx.region().origin,
);
GraphicsContext {
widget,
graphics: Exclusive::Owned(self.graphics.clipped_to(layout)),
gfx: Exclusive::Owned(self.gfx.clipped_to(layout)),
}
})
}
Expand All @@ -442,7 +442,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
pub fn clipped_to(&mut self, clip: Rect<Px>) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> {
GraphicsContext {
widget: self.widget.borrowed(),
graphics: Exclusive::Owned(self.graphics.clipped_to(clip)),
gfx: Exclusive::Owned(self.gfx.clipped_to(clip)),
}
}

Expand All @@ -456,13 +456,13 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
return;
}

let visible_rect = Rect::from(self.graphics.region().size - (Px(1), Px(1)));
let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1)));
let focus_ring = Shape::stroked_rect(
visible_rect,
styles.get_or_default(&HighlightColor),
styles.get(&HighlightColor, self),
StrokeOptions::default(),
);
self.graphics
self.gfx
.draw_shape(&focus_ring, Point::default(), None, None);
}

Expand Down Expand Up @@ -519,7 +519,9 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut

/// A context to a function that is rendering a widget.
pub struct LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> {
graphics: GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
/// The graphics context that this layout operation is being performed
/// within.
pub graphics: GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
persist_layout: bool,
}

Expand Down Expand Up @@ -647,7 +649,7 @@ impl<'window> AsEventContext<'window> for EventContext<'_, 'window> {

impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> {
fn as_event_context(&mut self) -> EventContext<'_, 'window> {
EventContext::new(self.widget.borrowed(), &mut self.graphics)
EventContext::new(self.widget.borrowed(), &mut self.gfx)
}
}

Expand Down Expand Up @@ -886,7 +888,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
) -> Component::ComponentType {
self.current_node
.tree
.query_style(&self.current_node, query)
.query_style(&self.current_node, query, self)
}

pub(crate) fn handle(&self) -> WindowHandle {
Expand Down
61 changes: 49 additions & 12 deletions src/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ use std::ops::Add;
use std::sync::Arc;

use crate::animation::{EasingFunction, ZeroToOne};
use crate::context::WidgetContext;
use crate::names::Name;
use crate::styles::components::{FocusableWidgets, VisualOrder};
use crate::utils::Lazy;
use crate::value::{IntoValue, Value};
use crate::value::{Dynamic, IntoValue, Value};

pub mod components;

/// A collection of style components organized by their name.
#[derive(Clone, Debug, Default)]
pub struct Styles(Arc<HashMap<Group, HashMap<Name, Component>>>);
pub struct Styles(Arc<HashMap<Group, HashMap<Name, Value<Component>>>>);

impl Styles {
/// Returns an empty collection.
Expand All @@ -32,15 +33,15 @@ impl Styles {
}

/// Inserts a [`Component`] with a given name.
pub fn insert_named(&mut self, name: ComponentName, component: impl Into<Component>) {
pub fn insert_named(&mut self, name: ComponentName, component: impl IntoComponentValue) {
Arc::make_mut(&mut self.0)
.entry(name.group)
.or_default()
.insert(name.name, component.into());
.insert(name.name, component.into_component_value());
}

/// Inserts a [`Component`] using then name provided.
pub fn insert(&mut self, name: &impl NamedComponent, component: impl Into<Component>) {
pub fn insert(&mut self, name: &impl NamedComponent, component: impl IntoComponentValue) {
let name = name.name().into_owned();
self.insert_named(name, component);
}
Expand All @@ -54,7 +55,7 @@ impl Styles {

/// Returns the associated component for the given name, if found.
#[must_use]
pub fn get<Named>(&self, component: &Named) -> Option<&Component>
pub fn get_named<Named>(&self, component: &Named) -> Option<&Value<Component>>
where
Named: NamedComponent + ?Sized,
{
Expand All @@ -67,7 +68,11 @@ impl Styles {
/// Returns the component associated with the given name, or if not found,
/// returns the default value provided by the definition.
#[must_use]
pub fn get_or_default<Named>(&self, component: &Named) -> Named::ComponentType
pub fn get<Named>(
&self,
component: &Named,
context: &WidgetContext<'_, '_>,
) -> Named::ComponentType
where
Named: ComponentDefinition + ?Sized,
{
Expand All @@ -76,12 +81,44 @@ impl Styles {
.get(&name.group)
.and_then(|group| group.get(&name.name))
.and_then(|component| {
<Named::ComponentType>::try_from_component(component.clone()).ok()
component.redraw_when_changed(context);
<Named::ComponentType>::try_from_component(component.get()).ok()
})
.unwrap_or_else(|| component.default_value())
}
}

/// A value that can be converted into a `Value<Component>`.
pub trait IntoComponentValue {
/// Returns `self` stored in a component value.
fn into_component_value(self) -> Value<Component>;
}

impl<T> IntoComponentValue for T
where
T: Into<Component>,
{
fn into_component_value(self) -> Value<Component> {
Value::Constant(self.into())
}
}

impl IntoComponentValue for Value<Component> {
fn into_component_value(self) -> Value<Component> {
self
}
}

impl<T> IntoComponentValue for Dynamic<T>
where
T: Clone,
Component: From<T>,
{
fn into_component_value(self) -> Value<Component> {
Value::Dynamic(self.map_each_into())
}
}

impl FromIterator<(ComponentName, Component)> for Styles {
fn from_iter<T: IntoIterator<Item = (ComponentName, Component)>>(iter: T) -> Self {
let iter = iter.into_iter();
Expand All @@ -95,7 +132,7 @@ impl FromIterator<(ComponentName, Component)> for Styles {

impl IntoIterator for Styles {
type IntoIter = StylesIntoIter;
type Item = (ComponentName, Component);
type Item = (ComponentName, Value<Component>);

fn into_iter(self) -> Self::IntoIter {
StylesIntoIter {
Expand All @@ -109,12 +146,12 @@ impl IntoIterator for Styles {

/// An iterator over the owned contents of a [`Styles`] instance.
pub struct StylesIntoIter {
main: hash_map::IntoIter<Group, HashMap<Name, Component>>,
names: Option<(Group, hash_map::IntoIter<Name, Component>)>,
main: hash_map::IntoIter<Group, HashMap<Name, Value<Component>>>,
names: Option<(Group, hash_map::IntoIter<Name, Value<Component>>)>,
}

impl Iterator for StylesIntoIter {
type Item = (ComponentName, Component);
type Item = (ComponentName, Value<Component>);

fn next(&mut self) -> Option<Self::Item> {
loop {
Expand Down
15 changes: 9 additions & 6 deletions src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex, PoisonError};
use kludgine::figures::units::Px;
use kludgine::figures::{Point, Rect};

use crate::context::WidgetContext;
use crate::styles::components::VisualOrder;
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles};
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};
Expand Down Expand Up @@ -296,11 +297,12 @@ impl Tree {
&self,
perspective: &ManagedWidget,
component: &Component,
context: &WidgetContext<'_, '_>,
) -> Component::ComponentType {
self.data
.lock()
.map_or_else(PoisonError::into_inner, |g| g)
.query_style(perspective.id(), component)
.query_style(perspective.id(), component, context)
}
}

Expand Down Expand Up @@ -396,8 +398,8 @@ impl TreeData {
let node = &self.nodes[&perspective];
if let Some(styles) = &node.styles {
query.retain(|name| {
if let Some(component) = styles.get(dbg!(name)) {
resolved.insert(name, dbg!(component.clone()));
if let Some(component) = styles.get_named(name) {
resolved.insert(name, component.clone());
false
} else {
true
Expand All @@ -414,17 +416,18 @@ impl TreeData {
&self,
mut perspective: WidgetId,
query: &Component,
context: &WidgetContext<'_, '_>,
) -> Component::ComponentType {
let name = query.name();
loop {
let node = &self.nodes[&perspective];
if let Some(styles) = &node.styles {
if let Some(component) = styles.get(&name) {
let Ok(value) =
<Component::ComponentType>::try_from_component(component.clone())
if let Some(component) = styles.get_named(&name) {
let Ok(value) = <Component::ComponentType>::try_from_component(component.get())
else {
break;
};
component.redraw_when_changed(context);
return value;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ impl Align {
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> Layout {
let margin = self.edges.get();
let vertical = FrameInfo::new(context.graphics.scale(), margin.top, margin.bottom);
let horizontal = FrameInfo::new(context.graphics.scale(), margin.left, margin.right);
let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom);
let horizontal = FrameInfo::new(context.gfx.scale(), margin.left, margin.right);

let content_available = Size::new(
horizontal.child_constraint(available_space.width),
Expand Down
25 changes: 13 additions & 12 deletions src/widgets/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,23 @@ impl Button {
&Easing,
]);
let background_color = if !self.enabled.get() {
styles.get_or_default(&ButtonDisabledBackground)
styles.get(&ButtonDisabledBackground, context)
} else if context.active() {
styles.get_or_default(&ButtonActiveBackground)
styles.get(&ButtonActiveBackground, context)
} else if context.hovered() {
styles.get_or_default(&ButtonHoverBackground)
styles.get(&ButtonHoverBackground, context)
} else if context.is_default() {
styles.get_or_default(&PrimaryColor)
styles.get(&PrimaryColor, context)
} else {
styles.get_or_default(&ButtonBackground)
styles.get(&ButtonBackground, context)
};

match (immediate, &self.background_color) {
(false, Some(dynamic)) => {
self.background_color_animation = dynamic
.transition_to(background_color)
.over(Duration::from_millis(150))
.with_easing(styles.get_or_default(&Easing))
.with_easing(styles.get(&Easing, context))
.spawn();
}
(true, Some(dynamic)) => {
Expand Down Expand Up @@ -139,7 +139,7 @@ impl Widget for Button {
self.currently_enabled = enabled;
}

let size = context.graphics.region().size;
let size = context.gfx.region().size;
let center = Point::from(size) / 2;
self.label.redraw_when_changed(context);
self.enabled.redraw_when_changed(context);
Expand All @@ -157,16 +157,17 @@ impl Widget for Button {
let background = self.current_background_color(context);
let background = Shape::filled_rect(visible_rect, background);
context
.graphics
.gfx
.draw_shape(&background, Point::default(), None, None);

if context.focused() {
context.draw_focus_ring_using(&styles);
}

self.label.map(|label| {
context.graphics.draw_text(
Text::new(label, styles.get_or_default(&TextColor))
let text_color = styles.get(&TextColor, context);
context.gfx.draw_text(
Text::new(label, text_color)
.origin(kludgine::text::TextOrigin::Center)
.wrap_at(size.width),
center,
Expand Down Expand Up @@ -246,12 +247,12 @@ impl Widget for Button {
) -> Size<UPx> {
let padding = context
.query_style(&IntrinsicPadding)
.into_px(context.graphics.scale())
.into_px(context.gfx.scale())
.into_unsigned();
let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
self.label.map(|label| {
let measured = context
.graphics
.gfx
.measure_text::<Px>(Text::from(label).wrap_at(width));

let mut size = measured.size.into_unsigned();
Expand Down
Loading

0 comments on commit 1714948

Please sign in to comment.