Styles are now reactive

This commit is contained in:
Jonathan Johnson 2023-11-09 10:42:56 -08:00
parent 897880de25
commit 1714948174
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
15 changed files with 168 additions and 119 deletions

View file

@ -11,15 +11,15 @@ fn main() -> gooey::Result<()> {
Canvas::new(move |context| { Canvas::new(move |context| {
angle += Angle::degrees(1); angle += Angle::degrees(1);
let center = Point::from(context.graphics.size()).into_signed() / 2; let center = Point::from(context.gfx.size()).into_signed() / 2;
context.graphics.draw_text( context.gfx.draw_text(
Text::new("Canvas exposes the full power of Kludgine", Color::WHITE) Text::new("Canvas exposes the full power of Kludgine", Color::WHITE)
.origin(TextOrigin::Center), .origin(TextOrigin::Center),
center - Point::new(Px(0), Px(100)), center - Point::new(Px(0), Px(100)),
None, None,
None, None,
); );
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))), Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))),
Color::RED, Color::RED,

View file

@ -16,8 +16,8 @@ fn main() -> gooey::Result {
Stack::columns( Stack::columns(
Label::new(chat_log.clone()).vertical_scroll().expand().and( Label::new(chat_log.clone()).vertical_scroll().expand().and(
Canvas::new(|context| { Canvas::new(|context| {
let entire_canvas = Rect::from(context.graphics.size()); let entire_canvas = Rect::from(context.gfx.size());
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect(entire_canvas, Color::RED), &Shape::filled_rect(entire_canvas, Color::RED),
Point::default(), Point::default(),
None, None,

View file

@ -401,7 +401,7 @@ pub struct GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> {
/// The graphics context clipped and offset to the area of the widget being /// 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 /// rendered. Drawing at 0,0 will draw at the top-left pixel of the laid-out
/// widget region. /// 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> { impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> {
@ -409,7 +409,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
pub fn borrowed(&mut self) -> GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass> { pub fn borrowed(&mut self) -> GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass> {
GraphicsContext { GraphicsContext {
widget: self.widget.borrowed(), widget: self.widget.borrowed(),
graphics: Exclusive::Borrowed(&mut self.graphics), gfx: Exclusive::Borrowed(&mut self.gfx),
} }
} }
@ -428,12 +428,12 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
widget.manage(self).map(|widget| { widget.manage(self).map(|widget| {
let widget = self.widget.for_other(&widget); let widget = self.widget.for_other(&widget);
let layout = widget.last_layout().map_or_else( let layout = widget.last_layout().map_or_else(
|| Rect::from(self.graphics.clip_rect().size).into_signed(), || Rect::from(self.gfx.clip_rect().size).into_signed(),
|rect| rect - self.graphics.region().origin, |rect| rect - self.gfx.region().origin,
); );
GraphicsContext { GraphicsContext {
widget, widget,
graphics: Exclusive::Owned(self.graphics.clipped_to(layout)), gfx: Exclusive::Owned(self.gfx.clipped_to(layout)),
} }
}) })
} }
@ -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> { pub fn clipped_to(&mut self, clip: Rect<Px>) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> {
GraphicsContext { GraphicsContext {
widget: self.widget.borrowed(), widget: self.widget.borrowed(),
graphics: Exclusive::Owned(self.graphics.clipped_to(clip)), gfx: Exclusive::Owned(self.gfx.clipped_to(clip)),
} }
} }
@ -456,13 +456,13 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
return; 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( let focus_ring = Shape::stroked_rect(
visible_rect, visible_rect,
styles.get_or_default(&HighlightColor), styles.get(&HighlightColor, self),
StrokeOptions::default(), StrokeOptions::default(),
); );
self.graphics self.gfx
.draw_shape(&focus_ring, Point::default(), None, None); .draw_shape(&focus_ring, Point::default(), None, None);
} }
@ -519,7 +519,9 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut
/// A context to a function that is rendering a widget. /// A context to a function that is rendering a widget.
pub struct LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> { 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, persist_layout: bool,
} }
@ -647,7 +649,7 @@ impl<'window> AsEventContext<'window> for EventContext<'_, 'window> {
impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> { impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> {
fn as_event_context(&mut self) -> EventContext<'_, '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)
} }
} }
@ -886,7 +888,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
) -> Component::ComponentType { ) -> Component::ComponentType {
self.current_node self.current_node
.tree .tree
.query_style(&self.current_node, query) .query_style(&self.current_node, query, self)
} }
pub(crate) fn handle(&self) -> WindowHandle { pub(crate) fn handle(&self) -> WindowHandle {

View file

@ -6,16 +6,17 @@ use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use crate::animation::{EasingFunction, ZeroToOne}; use crate::animation::{EasingFunction, ZeroToOne};
use crate::context::WidgetContext;
use crate::names::Name; use crate::names::Name;
use crate::styles::components::{FocusableWidgets, VisualOrder}; use crate::styles::components::{FocusableWidgets, VisualOrder};
use crate::utils::Lazy; use crate::utils::Lazy;
use crate::value::{IntoValue, Value}; use crate::value::{Dynamic, IntoValue, Value};
pub mod components; 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<HashMap<Group, HashMap<Name, Component>>>); pub struct Styles(Arc<HashMap<Group, HashMap<Name, Value<Component>>>>);
impl Styles { impl Styles {
/// Returns an empty collection. /// Returns an empty collection.
@ -32,15 +33,15 @@ impl Styles {
} }
/// Inserts a [`Component`] with a given name. /// 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) Arc::make_mut(&mut self.0)
.entry(name.group) .entry(name.group)
.or_default() .or_default()
.insert(name.name, component.into()); .insert(name.name, component.into_component_value());
} }
/// Inserts a [`Component`] using then name provided. /// 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(); let name = name.name().into_owned();
self.insert_named(name, component); self.insert_named(name, component);
} }
@ -54,7 +55,7 @@ impl Styles {
/// 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>(&self, component: &Named) -> Option<&Component> pub fn get_named<Named>(&self, component: &Named) -> Option<&Value<Component>>
where where
Named: NamedComponent + ?Sized, Named: NamedComponent + ?Sized,
{ {
@ -67,7 +68,11 @@ impl Styles {
/// 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]
pub fn get_or_default<Named>(&self, component: &Named) -> Named::ComponentType pub fn get<Named>(
&self,
component: &Named,
context: &WidgetContext<'_, '_>,
) -> Named::ComponentType
where where
Named: ComponentDefinition + ?Sized, Named: ComponentDefinition + ?Sized,
{ {
@ -76,12 +81,44 @@ impl Styles {
.get(&name.group) .get(&name.group)
.and_then(|group| group.get(&name.name)) .and_then(|group| group.get(&name.name))
.and_then(|component| { .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()) .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 { impl FromIterator<(ComponentName, Component)> for Styles {
fn from_iter<T: IntoIterator<Item = (ComponentName, Component)>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = (ComponentName, Component)>>(iter: T) -> Self {
let iter = iter.into_iter(); let iter = iter.into_iter();
@ -95,7 +132,7 @@ impl FromIterator<(ComponentName, Component)> for Styles {
impl IntoIterator for Styles { impl IntoIterator for Styles {
type IntoIter = StylesIntoIter; type IntoIter = StylesIntoIter;
type Item = (ComponentName, Component); type Item = (ComponentName, Value<Component>);
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
StylesIntoIter { StylesIntoIter {
@ -109,12 +146,12 @@ impl IntoIterator for Styles {
/// An iterator over the owned contents of a [`Styles`] instance. /// An iterator over the owned contents of a [`Styles`] instance.
pub struct StylesIntoIter { pub struct StylesIntoIter {
main: hash_map::IntoIter<Group, HashMap<Name, Component>>, main: hash_map::IntoIter<Group, HashMap<Name, Value<Component>>>,
names: Option<(Group, hash_map::IntoIter<Name, Component>)>, names: Option<(Group, hash_map::IntoIter<Name, Value<Component>>)>,
} }
impl Iterator for StylesIntoIter { impl Iterator for StylesIntoIter {
type Item = (ComponentName, Component); type Item = (ComponentName, Value<Component>);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { loop {

View file

@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex, PoisonError};
use kludgine::figures::units::Px; use kludgine::figures::units::Px;
use kludgine::figures::{Point, Rect}; use kludgine::figures::{Point, Rect};
use crate::context::WidgetContext;
use crate::styles::components::VisualOrder; use crate::styles::components::VisualOrder;
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles}; use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles};
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance}; use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};
@ -296,11 +297,12 @@ impl Tree {
&self, &self,
perspective: &ManagedWidget, perspective: &ManagedWidget,
component: &Component, component: &Component,
context: &WidgetContext<'_, '_>,
) -> Component::ComponentType { ) -> Component::ComponentType {
self.data self.data
.lock() .lock()
.map_or_else(PoisonError::into_inner, |g| g) .map_or_else(PoisonError::into_inner, |g| g)
.query_style(perspective.id(), component) .query_style(perspective.id(), component, context)
} }
} }
@ -396,8 +398,8 @@ impl TreeData {
let node = &self.nodes[&perspective]; let node = &self.nodes[&perspective];
if let Some(styles) = &node.styles { if let Some(styles) = &node.styles {
query.retain(|name| { query.retain(|name| {
if let Some(component) = styles.get(dbg!(name)) { if let Some(component) = styles.get_named(name) {
resolved.insert(name, dbg!(component.clone())); resolved.insert(name, component.clone());
false false
} else { } else {
true true
@ -414,17 +416,18 @@ impl TreeData {
&self, &self,
mut perspective: WidgetId, mut perspective: WidgetId,
query: &Component, query: &Component,
context: &WidgetContext<'_, '_>,
) -> Component::ComponentType { ) -> Component::ComponentType {
let name = query.name(); let name = query.name();
loop { loop {
let node = &self.nodes[&perspective]; let node = &self.nodes[&perspective];
if let Some(styles) = &node.styles { if let Some(styles) = &node.styles {
if let Some(component) = styles.get(&name) { if let Some(component) = styles.get_named(&name) {
let Ok(value) = let Ok(value) = <Component::ComponentType>::try_from_component(component.get())
<Component::ComponentType>::try_from_component(component.clone())
else { else {
break; break;
}; };
component.redraw_when_changed(context);
return value; return value;
} }
} }

View file

@ -90,8 +90,8 @@ impl Align {
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> Layout { ) -> Layout {
let margin = self.edges.get(); let margin = self.edges.get();
let vertical = FrameInfo::new(context.graphics.scale(), margin.top, margin.bottom); let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom);
let horizontal = FrameInfo::new(context.graphics.scale(), margin.left, margin.right); let horizontal = FrameInfo::new(context.gfx.scale(), margin.left, margin.right);
let content_available = Size::new( let content_available = Size::new(
horizontal.child_constraint(available_space.width), horizontal.child_constraint(available_space.width),

View file

@ -88,15 +88,15 @@ impl Button {
&Easing, &Easing,
]); ]);
let background_color = if !self.enabled.get() { let background_color = if !self.enabled.get() {
styles.get_or_default(&ButtonDisabledBackground) styles.get(&ButtonDisabledBackground, context)
} else if context.active() { } else if context.active() {
styles.get_or_default(&ButtonActiveBackground) styles.get(&ButtonActiveBackground, context)
} else if context.hovered() { } else if context.hovered() {
styles.get_or_default(&ButtonHoverBackground) styles.get(&ButtonHoverBackground, context)
} else if context.is_default() { } else if context.is_default() {
styles.get_or_default(&PrimaryColor) styles.get(&PrimaryColor, context)
} else { } else {
styles.get_or_default(&ButtonBackground) styles.get(&ButtonBackground, context)
}; };
match (immediate, &self.background_color) { match (immediate, &self.background_color) {
@ -104,7 +104,7 @@ impl Button {
self.background_color_animation = dynamic self.background_color_animation = dynamic
.transition_to(background_color) .transition_to(background_color)
.over(Duration::from_millis(150)) .over(Duration::from_millis(150))
.with_easing(styles.get_or_default(&Easing)) .with_easing(styles.get(&Easing, context))
.spawn(); .spawn();
} }
(true, Some(dynamic)) => { (true, Some(dynamic)) => {
@ -139,7 +139,7 @@ impl Widget for Button {
self.currently_enabled = enabled; self.currently_enabled = enabled;
} }
let size = context.graphics.region().size; let size = context.gfx.region().size;
let center = Point::from(size) / 2; let center = Point::from(size) / 2;
self.label.redraw_when_changed(context); self.label.redraw_when_changed(context);
self.enabled.redraw_when_changed(context); self.enabled.redraw_when_changed(context);
@ -157,7 +157,7 @@ impl Widget for Button {
let background = self.current_background_color(context); let background = self.current_background_color(context);
let background = Shape::filled_rect(visible_rect, background); let background = Shape::filled_rect(visible_rect, background);
context context
.graphics .gfx
.draw_shape(&background, Point::default(), None, None); .draw_shape(&background, Point::default(), None, None);
if context.focused() { if context.focused() {
@ -165,8 +165,9 @@ impl Widget for Button {
} }
self.label.map(|label| { self.label.map(|label| {
context.graphics.draw_text( let text_color = styles.get(&TextColor, context);
Text::new(label, styles.get_or_default(&TextColor)) context.gfx.draw_text(
Text::new(label, text_color)
.origin(kludgine::text::TextOrigin::Center) .origin(kludgine::text::TextOrigin::Center)
.wrap_at(size.width), .wrap_at(size.width),
center, center,
@ -246,12 +247,12 @@ impl Widget for Button {
) -> Size<UPx> { ) -> Size<UPx> {
let padding = context let padding = context
.query_style(&IntrinsicPadding) .query_style(&IntrinsicPadding)
.into_px(context.graphics.scale()) .into_px(context.gfx.scale())
.into_unsigned(); .into_unsigned();
let width = available_space.width.max().try_into().unwrap_or(Px::MAX); let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
self.label.map(|label| { self.label.map(|label| {
let measured = context let measured = context
.graphics .gfx
.measure_text::<Px>(Text::from(label).wrap_at(width)); .measure_text::<Px>(Text::from(label).wrap_at(width));
let mut size = measured.size.into_unsigned(); let mut size = measured.size.into_unsigned();

View file

@ -82,10 +82,10 @@ impl WrapperWidget for Expand {
Size::<UPx>::new( Size::<UPx>::new(
available_space available_space
.width .width
.fit_measured(size.width, context.graphics.scale()), .fit_measured(size.width, context.gfx.scale()),
available_space available_space
.height .height
.fit_measured(size.height, context.graphics.scale()), .fit_measured(size.height, context.gfx.scale()),
) )
.into_signed() .into_signed()
.into() .into()

View file

@ -62,7 +62,12 @@ impl Input {
self self
} }
fn editor_mut(&mut self, kludgine: &mut Kludgine, styles: &Styles) -> &mut Editor { fn editor_mut(
&mut self,
kludgine: &mut Kludgine,
styles: &Styles,
context: &WidgetContext<'_, '_>,
) -> &mut Editor {
match (&self.editor, self.text.generation()) { match (&self.editor, self.text.generation()) {
(Some(editor), generation) if editor.generation == generation => {} (Some(editor), generation) if editor.generation == generation => {}
(_, generation) => { (_, generation) => {
@ -70,11 +75,8 @@ impl Input {
let mut buffer = Buffer::new( let mut buffer = Buffer::new(
kludgine.font_system(), kludgine.font_system(),
Metrics::new( Metrics::new(
styles.get_or_default(&TextSize).into_px(scale).into_float(), styles.get(&TextSize, context).into_px(scale).into_float(),
styles styles.get(&LineHeight, context).into_px(scale).into_float(),
.get_or_default(&LineHeight)
.into_px(scale)
.into_float(),
), ),
); );
self.text.map(|text| { self.text.map(|text| {
@ -132,13 +134,14 @@ impl Widget for Input {
) -> EventHandling { ) -> EventHandling {
context.focus(); context.focus();
let styles = context.query_styles(&[&TextColor]); let styles = context.query_styles(&[&TextColor]);
self.editor_mut(context.kludgine, &styles).action( self.editor_mut(context.kludgine, &styles, &context.widget)
context.kludgine.font_system(), .action(
Action::Click { context.kludgine.font_system(),
x: location.x.0, Action::Click {
y: location.y.0, x: location.x.0,
}, y: location.y.0,
); },
);
context.set_needs_redraw(); context.set_needs_redraw();
HANDLED HANDLED
} }
@ -151,13 +154,14 @@ impl Widget for Input {
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_, '_>,
) { ) {
let styles = context.query_styles(&[&TextColor]); let styles = context.query_styles(&[&TextColor]);
self.editor_mut(context.kludgine, &styles).action( self.editor_mut(context.kludgine, &styles, &context.widget)
context.kludgine.font_system(), .action(
Action::Drag { context.kludgine.font_system(),
x: location.x.0, Action::Drag {
y: location.y.0, x: location.x.0,
}, y: location.y.0,
); },
);
self.cursor_state.force_on(); self.cursor_state.force_on();
context.set_needs_redraw(); context.set_needs_redraw();
} }
@ -166,19 +170,19 @@ impl Widget for Input {
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) {
self.cursor_state.update(context.elapsed()); self.cursor_state.update(context.elapsed());
let cursor_state = self.cursor_state; let cursor_state = self.cursor_state;
let size = context.graphics.size(); let size = context.gfx.size();
let styles = context.query_styles(&[&TextColor, &HighlightColor]); let styles = context.query_styles(&[&TextColor, &HighlightColor]);
let highlight = styles.get_or_default(&HighlightColor); let highlight = styles.get(&HighlightColor, context);
let editor = self.editor_mut(&mut context.graphics, &styles); let editor = self.editor_mut(&mut context.gfx, &styles, &context.widget);
let cursor = editor.cursor(); let cursor = editor.cursor();
let selection = editor.select_opt(); let selection = editor.select_opt();
let buffer = editor.buffer_mut(); let buffer = editor.buffer_mut();
buffer.set_size( buffer.set_size(
context.graphics.font_system(), context.gfx.font_system(),
size.width.into_float(), size.width.into_float(),
size.height.into_float(), size.height.into_float(),
); );
buffer.shape_until_scroll(context.graphics.font_system()); buffer.shape_until_scroll(context.gfx.font_system());
if context.focused() { if context.focused() {
context.draw_focus_ring_using(&styles); context.draw_focus_ring_using(&styles);
@ -196,7 +200,7 @@ impl Widget for Input {
if start_position.y == end_position.y { if start_position.y == end_position.y {
// Single line selection // Single line selection
let width = end_position.x - start_position.x + end_width; let width = end_position.x - start_position.x + end_width;
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new(start_position, Size::new(width, line_height)), Rect::new(start_position, Size::new(width, line_height)),
highlight, highlight,
@ -208,7 +212,7 @@ impl Widget for Input {
} else { } else {
// Draw from start to end of line, // Draw from start to end of line,
let width = size.width.into_signed() - start_position.x; let width = size.width.into_signed() - start_position.x;
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new(start_position, Size::new(width, line_height)), Rect::new(start_position, Size::new(width, line_height)),
highlight, highlight,
@ -221,7 +225,7 @@ impl Widget for Input {
let bottom_of_first_line = start_position.y + line_height; let bottom_of_first_line = start_position.y + line_height;
let distance_between = end_position.y - bottom_of_first_line; let distance_between = end_position.y - bottom_of_first_line;
if distance_between > 0 { if distance_between > 0 {
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
Point::new(Px(0), bottom_of_first_line), Point::new(Px(0), bottom_of_first_line),
@ -235,7 +239,7 @@ impl Widget for Input {
); );
} }
// Draw from 0 to end + width // Draw from 0 to end + width
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
Point::new(Px(0), end_position.y), Point::new(Px(0), end_position.y),
@ -251,7 +255,7 @@ impl Widget for Input {
} }
(Ok((start_position, _)), Err(_)) => { (Ok((start_position, _)), Err(_)) => {
let width = size.width.into_signed() - start_position.x; let width = size.width.into_signed() - start_position.x;
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new(start_position, Size::new(width, line_height)), Rect::new(start_position, Size::new(width, line_height)),
highlight, highlight,
@ -265,7 +269,7 @@ impl Widget for Input {
if end_position.y > 0 { if end_position.y > 0 {
todo!("fill above start"); todo!("fill above start");
} }
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
Point::new(Px(0), end_position.y), Point::new(Px(0), end_position.y),
@ -288,7 +292,7 @@ impl Widget for Input {
} else if let Ok((location, _)) = cursor_glyph(buffer, &cursor) { } else if let Ok((location, _)) = cursor_glyph(buffer, &cursor) {
let window_focused = context.window().focused().get(); let window_focused = context.window().focused().get();
if window_focused && cursor_state.visible { if window_focused && cursor_state.visible {
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
location, location,
@ -309,9 +313,10 @@ impl Widget for Input {
} }
} }
context.graphics.draw_text_buffer( let text_color = styles.get(&TextColor, context);
context.gfx.draw_text_buffer(
buffer, buffer,
styles.get_or_default(&TextColor), text_color,
TextOrigin::TopLeft, TextOrigin::TopLeft,
Point::<Px>::default(), Point::<Px>::default(),
None, None,
@ -325,15 +330,15 @@ impl Widget for Input {
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let styles = context.query_styles(&[&TextColor]); let styles = context.query_styles(&[&TextColor]);
let editor = self.editor_mut(&mut context.graphics, &styles); let editor = self.editor_mut(&mut context.graphics.gfx, &styles, &context.graphics.widget);
let buffer = editor.buffer_mut(); let buffer = editor.buffer_mut();
buffer.set_size( buffer.set_size(
context.graphics.font_system(), context.gfx.font_system(),
available_space.width.max().into_float(), available_space.width.max().into_float(),
available_space.height.max().into_float(), available_space.height.max().into_float(),
); );
context context
.graphics .gfx
.measure_text_buffer::<Px>(buffer, Color::WHITE) .measure_text_buffer::<Px>(buffer, Color::WHITE)
.size .size
.into_unsigned() .into_unsigned()
@ -351,7 +356,7 @@ impl Widget for Input {
} }
let styles = context.query_styles(&[&TextColor]); let styles = context.query_styles(&[&TextColor]);
let editor = self.editor_mut(context.kludgine, &styles); let editor = self.editor_mut(context.kludgine, &styles, &context.widget);
// println!( // println!(
// "Keyboard input: {:?}. {:?}, {:?}", // "Keyboard input: {:?}. {:?}, {:?}",
@ -434,11 +439,15 @@ impl Widget for Input {
match ime { match ime {
Ime::Enabled | Ime::Disabled => {} Ime::Enabled | Ime::Disabled => {}
Ime::Preedit(text, cursor) => { Ime::Preedit(text, cursor) => {
println!("TODO: preview IME input {text}, cursor: {cursor:?}"); tracing::warn!("TODO: preview IME input {text}, cursor: {cursor:?}");
} }
Ime::Commit(text) => { Ime::Commit(text) => {
self.editor_mut(context.kludgine, &Self::styles(&context.widget)) self.editor_mut(
.insert_string(&text, None); context.kludgine,
&Self::styles(&context.widget),
&context.widget,
)
.insert_string(&text, None);
context.set_needs_redraw(); context.set_needs_redraw();
} }
} }

View file

@ -30,18 +30,19 @@ impl Widget for Label {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
self.text.redraw_when_changed(context); self.text.redraw_when_changed(context);
let size = context.graphics.region().size; let size = context.gfx.region().size;
let center = Point::from(size) / 2; let center = Point::from(size) / 2;
let styles = context.query_styles(&[&TextColor]); let styles = context.query_styles(&[&TextColor]);
if let Some(measured) = &self.prepared_text { if let Some(measured) = &self.prepared_text {
context context
.graphics .gfx
.draw_measured_text(measured, TextOrigin::Center, center, None, None); .draw_measured_text(measured, TextOrigin::Center, center, None, None);
} else { } else {
let text_color = styles.get(&TextColor, context);
self.text.map(|contents| { self.text.map(|contents| {
context.graphics.draw_text( context.gfx.draw_text(
Text::new(contents, styles.get_or_default(&TextColor)) Text::new(contents, text_color)
.wrap_at(size.width) .wrap_at(size.width)
.origin(TextOrigin::Center), .origin(TextOrigin::Center),
center, center,
@ -59,12 +60,12 @@ impl Widget for Label {
) -> Size<UPx> { ) -> Size<UPx> {
let padding = context let padding = context
.query_style(&IntrinsicPadding) .query_style(&IntrinsicPadding)
.into_px(context.graphics.scale()) .into_px(context.gfx.scale())
.into_unsigned(); .into_unsigned();
let width = available_space.width.max().try_into().unwrap_or(Px::MAX); let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
self.text.map(|contents| { self.text.map(|contents| {
let measured = context let measured = context
.graphics .gfx
.measure_text(Text::from(contents).wrap_at(width)); .measure_text(Text::from(contents).wrap_at(width));
let mut size = measured.size.try_cast().unwrap_or_default(); let mut size = measured.size.try_cast().unwrap_or_default();
size += padding * 2; size += padding * 2;

View file

@ -69,17 +69,13 @@ impl WrapperWidget for Resize {
let child = self.child.mounted(&mut context.as_event_context()); let child = self.child.mounted(&mut context.as_event_context());
let size = if let (Some(width), Some(height)) = (self.width, self.height) { let size = if let (Some(width), Some(height)) = (self.width, self.height) {
Size::new( Size::new(
width.into_px(context.graphics.scale()).into_unsigned(), width.into_px(context.gfx.scale()).into_unsigned(),
height.into_px(context.graphics.scale()).into_unsigned(), height.into_px(context.gfx.scale()).into_unsigned(),
) )
} else { } else {
let available_space = Size::new( let available_space = Size::new(
override_constraint(available_space.width, self.width, context.graphics.scale()), override_constraint(available_space.width, self.width, context.gfx.scale()),
override_constraint( override_constraint(available_space.height, self.height, context.gfx.scale()),
available_space.height,
self.height,
context.graphics.scale(),
),
); );
context.for_other(&child).layout(available_space) context.for_other(&child).layout(available_space)
}; };

View file

@ -94,13 +94,13 @@ impl Scroll {
.scrollbar_opacity .scrollbar_opacity
.transition_to(ZeroToOne::ONE) .transition_to(ZeroToOne::ONE)
.over(Duration::from_millis(300)) .over(Duration::from_millis(300))
.with_easing(styles.get_or_default(&EasingIn)) .with_easing(styles.get(&EasingIn, context))
.and_then(Duration::from_secs(1)) .and_then(Duration::from_secs(1))
.and_then( .and_then(
self.scrollbar_opacity self.scrollbar_opacity
.transition_to(ZeroToOne::ZERO) .transition_to(ZeroToOne::ZERO)
.over(Duration::from_millis(300)) .over(Duration::from_millis(300))
.with_easing(styles.get_or_default(&EasingOut)), .with_easing(styles.get(&EasingOut, context)),
) )
.spawn(); .spawn();
} }
@ -126,7 +126,7 @@ impl Widget for Scroll {
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) {
context.redraw_when_changed(&self.scrollbar_opacity); context.redraw_when_changed(&self.scrollbar_opacity);
let Some(visible_rect) = context.graphics.visible_rect() else { let Some(visible_rect) = context.gfx.visible_rect() else {
return; return;
}; };
let visible_bottom_right = visible_rect.into_signed().extent(); let visible_bottom_right = visible_rect.into_signed().extent();
@ -135,7 +135,7 @@ impl Widget for Scroll {
context.for_other(&managed).redraw(); context.for_other(&managed).redraw();
if self.horizontal_bar.amount_hidden > 0 { if self.horizontal_bar.amount_hidden > 0 {
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
Point::new( Point::new(
@ -153,7 +153,7 @@ impl Widget for Scroll {
} }
if self.vertical_bar.amount_hidden > 0 { if self.vertical_bar.amount_hidden > 0 {
context.graphics.draw_shape( context.gfx.draw_shape(
&Shape::filled_rect( &Shape::filled_rect(
Rect::new( Rect::new(
Point::new( Point::new(
@ -178,11 +178,11 @@ impl Widget for Scroll {
) -> Size<UPx> { ) -> Size<UPx> {
let styles = context.query_styles(&[&ScrollBarThickness, &LineHeight]); let styles = context.query_styles(&[&ScrollBarThickness, &LineHeight]);
self.bar_width = styles self.bar_width = styles
.get_or_default(&ScrollBarThickness) .get(&ScrollBarThickness, context)
.into_px(context.graphics.scale()); .into_px(context.gfx.scale());
self.line_height = styles self.line_height = styles
.get_or_default(&LineHeight) .get(&LineHeight, context)
.into_px(context.graphics.scale()); .into_px(context.gfx.scale());
let (mut scroll, current_max_scroll) = self.constrain_scroll(); let (mut scroll, current_max_scroll) = self.constrain_scroll();

View file

@ -145,7 +145,7 @@ impl Widget for Stack {
let content_size = self.layout.update( let content_size = self.layout.update(
available_space, available_space,
context.graphics.scale(), context.gfx.scale(),
|child_index, constraints, persist| { |child_index, constraints, persist| {
let mut context = context.for_other(&self.synced_children[child_index]); let mut context = context.for_other(&self.synced_children[child_index]);
if !persist { if !persist {

View file

@ -67,7 +67,7 @@ where
let focus = self.focus.get(); let focus = self.focus.get();
// TODO this needs to be updated to support being placed in side of a scroll view. // TODO this needs to be updated to support being placed in side of a scroll view.
self.layers.map(|layers| { self.layers.map(|layers| {
tilemap::draw(layers, focus, self.zoom, context.graphics.inner_graphics()); tilemap::draw(layers, focus, self.zoom, context.gfx.inner_graphics());
}); });
context.draw_focus_ring(); context.draw_focus_ring();

View file

@ -352,10 +352,10 @@ where
let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut window = RunningWindow::new(window, &self.focused, &self.occluded);
let mut context = GraphicsContext { let mut context = GraphicsContext {
widget: WidgetContext::new(self.root.clone(), &self.redraw_status, &mut window), widget: WidgetContext::new(self.root.clone(), &self.redraw_status, &mut window),
graphics: Exclusive::Owned(Graphics::new(graphics)), gfx: Exclusive::Owned(Graphics::new(graphics)),
}; };
let mut layout_context = LayoutContext::new(&mut context); let mut layout_context = LayoutContext::new(&mut context);
let window_size = layout_context.graphics.size(); let window_size = layout_context.gfx.size();
let actual_size = layout_context.layout(Size::new( let actual_size = layout_context.layout(Size::new(
ConstraintLimit::ClippedAfter(window_size.width), ConstraintLimit::ClippedAfter(window_size.width),
ConstraintLimit::ClippedAfter(window_size.height), ConstraintLimit::ClippedAfter(window_size.height),