From 5e5d826267bbb3737cbf860d8913893571653499 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Tue, 7 Nov 2023 15:16:29 -0800 Subject: [PATCH] Checkpoint Progress on tab focus --- src/context.rs | 213 +++++++++++++++++++++++++++------------ src/styles/components.rs | 94 ++++++++++++++++- src/tree.rs | 65 ++++++++++-- src/utils.rs | 10 ++ src/widget.rs | 53 +++++++--- src/widgets/align.rs | 4 +- src/widgets/expand.rs | 4 +- src/widgets/resize.rs | 5 +- src/widgets/scroll.rs | 10 +- src/widgets/stack.rs | 4 +- src/widgets/style.rs | 4 +- src/window.rs | 22 +++- 12 files changed, 383 insertions(+), 105 deletions(-) diff --git a/src/context.rs b/src/context.rs index 937e71d..f0d8644 100644 --- a/src/context.rs +++ b/src/context.rs @@ -12,11 +12,12 @@ use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::Kludgine; use crate::graphics::Graphics; -use crate::styles::components::HighlightColor; +use crate::styles::components::{HighlightColor, VisualOrder}; use crate::styles::{ComponentDefaultvalue, ComponentDefinition, Styles}; use crate::value::Dynamic; -use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance}; -use crate::window::{sealed, RunningWindow}; +use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef}; +use crate::window::sealed::WindowCommand; +use crate::window::RunningWindow; use crate::ConstraintLimit; /// A context to an event function. @@ -48,11 +49,17 @@ impl<'context, 'window> EventContext<'context, 'window> { /// parent widget needs to invoke events on a child widget. This is done by /// creating an `EventContext` pointing to the child and calling the /// appropriate function to invoke the event. - pub fn for_other<'child>( + pub fn for_other<'child, Widget>( &'child mut self, - widget: ManagedWidget, - ) -> EventContext<'child, 'window> { - EventContext::new(self.widget.for_other(widget), self.kludgine) + widget: &Widget, + ) -> >>::Result + where + Widget: ManageWidget, + Widget::Managed: MapManagedWidget>, + { + widget + .manage(self) + .map(|managed| EventContext::new(self.widget.for_other(&managed), self.kludgine)) } /// Invokes [`Widget::hit_test()`](crate::widget::Widget::hit_test) on this @@ -145,11 +152,11 @@ impl<'context, 'window> EventContext<'context, 'window> { pub(crate) fn hover(&mut self, location: Point) { let changes = self.current_node.tree.hover(Some(&self.current_node)); for unhovered in changes.unhovered { - let mut context = self.for_other(unhovered.clone()); + let mut context = self.for_other(&unhovered); unhovered.lock().as_widget().unhover(&mut context); } for hover in changes.hovered { - let mut context = self.for_other(hover.clone()); + let mut context = self.for_other(&hover); hover.lock().as_widget().hover(location, &mut context); } } @@ -159,7 +166,7 @@ impl<'context, 'window> EventContext<'context, 'window> { assert!(changes.hovered.is_empty()); for old_hover in changes.unhovered { - let mut old_hover_context = self.for_other(old_hover.clone()); + let mut old_hover_context = self.for_other(&old_hover); old_hover.lock().as_widget().unhover(&mut old_hover_context); } } @@ -170,7 +177,7 @@ impl<'context, 'window> EventContext<'context, 'window> { let new = match self.current_node.tree.activate(active.as_ref()) { Ok(old) => { if let Some(old) = old { - let mut old_context = self.for_other(old.clone()); + let mut old_context = self.for_other(&old); old.lock().as_widget().deactivate(&mut old_context); } true @@ -182,7 +189,7 @@ impl<'context, 'window> EventContext<'context, 'window> { active .lock() .as_widget() - .activate(&mut self.for_other(active.clone())); + .activate(&mut self.for_other(active)); } self.pending_state.active = active; } @@ -194,19 +201,19 @@ impl<'context, 'window> EventContext<'context, 'window> { if focus .lock() .as_widget() - .accept_focus(&mut self.for_other(focus.clone())) + .accept_focus(&mut self.for_other(&focus)) { break Some(focus); } else if let Some(next_focus) = focus.next_focus() { focus = next_focus; } else { - break self.next_focus_after(focus); + break self.next_focus_after(focus, VisualOrder::left_to_right()); } }); let new = match self.current_node.tree.focus(focus.as_ref()) { Ok(old) => { if let Some(old) = old { - let mut old_context = self.for_other(old.clone()); + let mut old_context = self.for_other(&old); old.lock().as_widget().blur(&mut old_context); } true @@ -215,26 +222,27 @@ impl<'context, 'window> EventContext<'context, 'window> { }; if new { if let Some(focus) = &focus { - focus - .lock() - .as_widget() - .focus(&mut self.for_other(focus.clone())); + focus.lock().as_widget().focus(&mut self.for_other(focus)); } self.pending_state.focus = focus; } } } - fn next_focus_after(&mut self, mut focus: ManagedWidget) -> Option { + fn next_focus_after( + &mut self, + mut focus: ManagedWidget, + order: VisualOrder, + ) -> Option { // First, look within the current focus for any focusable children. let stop_at = focus.id(); - if let Some(focus) = self.next_focus_within(&focus, None, stop_at) { + if let Some(focus) = self.next_focus_within(&focus, None, stop_at, order) { return Some(focus); } // Now, look for the next widget in each hierarchy let root = loop { - if let Some(focus) = self.next_focus_sibling(&focus, stop_at) { + if let Some(focus) = self.next_focus_sibling(&focus, stop_at, order) { return Some(focus); } let Some(parent) = focus.parent() else { @@ -245,15 +253,16 @@ impl<'context, 'window> EventContext<'context, 'window> { // We've exhausted a forward scan, we can now start searching the final // parent, which is the root. - self.next_focus_within(&root, None, stop_at) + self.next_focus_within(&root, None, stop_at, order) } fn next_focus_sibling( &mut self, focus: &ManagedWidget, stop_at: WidgetId, + order: VisualOrder, ) -> Option { - self.next_focus_within(&focus.parent()?, Some(focus.id()), stop_at) + self.next_focus_within(&focus.parent()?, Some(focus.id()), stop_at, order) } /// Searches for the next focus inside of `focus`, returning `None` if @@ -264,21 +273,22 @@ impl<'context, 'window> EventContext<'context, 'window> { focus: &ManagedWidget, start_at: Option, stop_at: WidgetId, + order: VisualOrder, ) -> Option { - let child_layouts = focus.child_layouts(); - // TODO visually sort the layouts - - let mut child_layouts = child_layouts.into_iter().peekable(); + let mut children = focus + .visually_ordered_children(order) + .into_iter() + .peekable(); if let Some(start_at) = start_at { // Skip all children up to `start_at` - while child_layouts.peek()?.0.id() != start_at { - child_layouts.next(); + while children.peek()?.id() != start_at { + children.next(); } // Skip `start_at` - child_layouts.next(); + children.next(); } - for (child, _layout) in child_layouts { + for child in children { // Ensure we haven't cycled completely. if stop_at == child.id() { break; @@ -287,16 +297,20 @@ impl<'context, 'window> EventContext<'context, 'window> { if child .lock() .as_widget() - .accept_focus(&mut self.for_other(child.clone())) + .accept_focus(&mut self.for_other(&child)) { return Some(child); - } else if let Some(focus) = self.next_focus_within(&child, None, stop_at) { + } else if let Some(focus) = self.next_focus_within(&child, None, stop_at, order) { return Some(focus); } } None } + + pub fn advance_focus(&mut self, direction: VisualOrder) { + self.pending_state.focus = self.next_focus_after(self.current_node.clone(), direction); + } } impl<'context, 'window> Deref for EventContext<'context, 'window> { @@ -362,19 +376,27 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, ' /// Returns a new `GraphicsContext` that allows invoking graphics functions /// for `widget`. - pub fn for_other<'child>( + pub fn for_other<'child, Widget>( &'child mut self, - widget: ManagedWidget, - ) -> GraphicsContext<'child, 'window, 'child, 'gfx, 'pass> { - 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, - ); - GraphicsContext { - widget, - graphics: Exclusive::Owned(self.graphics.clipped_to(layout)), - } + widget: &Widget, + ) -> , + >>::Result + where + Widget: ManageWidget, + Widget::Managed: MapManagedWidget>, + { + 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, + ); + GraphicsContext { + widget, + graphics: Exclusive::Owned(self.graphics.clipped_to(layout)), + } + }) } /// Returns a new graphics context that renders to the `clip` rectangle. @@ -468,17 +490,18 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl /// Returns a new `LayoutContext` that allows invoking layout functions for /// `widget`. - pub fn for_other<'child, 'widget>( + pub fn for_other<'child, Widget>( &'child mut self, - widget: ManagedWidget, - ) -> LayoutContext<'child, 'window, 'child, 'gfx, 'pass> + widget: &Widget, + ) -> >>::Result where - 'widget: 'child, + Widget: ManageWidget, + Widget::Managed: MapManagedWidget>, { - LayoutContext { - graphics: self.graphics.for_other(widget), + widget.manage(self).map(|widget| LayoutContext { + graphics: self.graphics.for_other(&widget), persist_layout: self.persist_layout, - } + }) } /// Invokes [`Widget::layout()`](crate::widget::Widget::layout) on this @@ -541,21 +564,21 @@ pub trait AsEventContext<'window> { pushed_widget .lock() .as_widget() - .mounted(&mut context.for_other(pushed_widget.clone())); + .mounted(&mut context.for_other(&pushed_widget)); pushed_widget } /// Removes a widget from the hierarchy. fn remove_child(&mut self, child: &ManagedWidget) { let mut context = self.as_event_context(); + child + .lock() + .as_widget() + .unmounted(&mut context.for_other(child)); context .current_node .tree .remove_child(child, &context.current_node); - child - .lock() - .as_widget() - .unmounted(&mut context.for_other(child.clone())); } } @@ -616,16 +639,20 @@ impl<'context, 'window> WidgetContext<'context, 'window> { } /// Returns a new context representing `widget`. - pub fn for_other<'child>( + pub fn for_other<'child, Widget>( &'child mut self, - widget: ManagedWidget, - ) -> WidgetContext<'child, 'window> { - WidgetContext { - current_node: widget, + widget: &Widget, + ) -> >>::Result + where + Widget: ManageWidget, + Widget::Managed: MapManagedWidget>, + { + widget.manage(self).map(|current_node| WidgetContext { + current_node, redraw_status: self.redraw_status, window: &mut *self.window, pending_state: self.pending_state.borrowed(), - } + }) } pub(crate) fn parent(&self) -> Option { @@ -792,14 +819,14 @@ impl<'context, 'window> WidgetContext<'context, 'window> { } pub(crate) struct WindowHandle { - kludgine: kludgine::app::WindowHandle, + kludgine: kludgine::app::WindowHandle, redraw_status: RedrawStatus, } impl WindowHandle { pub fn redraw(&self) { if self.redraw_status.should_send_refresh() { - let _result = self.kludgine.send(sealed::WindowCommand::Redraw); + let _result = self.kludgine.send(WindowCommand::Redraw); } } } @@ -880,3 +907,57 @@ impl RedrawStatus { self.refresh_sent.store(false, Ordering::Release); } } + +pub trait ManageWidget { + type Managed: MapManagedWidget; + fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed; +} + +impl ManageWidget for WidgetInstance { + type Managed = Option; + + fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + context.current_node.tree.widget(self.id()) + } +} + +impl ManageWidget for WidgetRef { + type Managed = Option; + + fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + match self { + WidgetRef::Unmounted(instance) => context.current_node.tree.widget(instance.id()), + WidgetRef::Mounted(instance) => Some(instance.clone()), + } + } +} + +impl ManageWidget for ManagedWidget { + type Managed = Self; + + fn manage(&self, _context: &WidgetContext<'_, '_>) -> Self::Managed { + self.clone() + } +} + +pub trait MapManagedWidget { + type Result; + + fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result; +} + +impl MapManagedWidget for Option { + type Result = Option; + + fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result { + self.map(map) + } +} + +impl MapManagedWidget for ManagedWidget { + type Result = T; + + fn map(self, map: impl FnOnce(ManagedWidget) -> T) -> Self::Result { + map(self) + } +} diff --git a/src/styles/components.rs b/src/styles/components.rs index d978d09..fdcca4b 100644 --- a/src/styles/components.rs +++ b/src/styles/components.rs @@ -1,7 +1,8 @@ //! All style components supported by the built-in widgets. use std::borrow::Cow; -use kludgine::figures::units::Lp; +use kludgine::figures::units::{Lp, Px}; +use kludgine::figures::Rect; use kludgine::Color; use crate::animation::easings::{EaseInQuadradic, EaseOutQuadradic}; @@ -160,3 +161,94 @@ impl ComponentDefinition for EasingOut { EasingFunction::from(EaseOutQuadradic) } } + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct VisualOrder { + pub horizontal: HorizontalOrder, + pub vertical: VerticalOrder, +} + +impl VisualOrder { + #[must_use] + pub const fn right_to_left() -> Self { + Self { + horizontal: HorizontalOrder::RightToLeft, + vertical: VerticalOrder::TopToBottom, + } + } + + #[must_use] + pub const fn left_to_right() -> Self { + Self { + horizontal: HorizontalOrder::LeftToRight, + vertical: VerticalOrder::TopToBottom, + } + } + + #[must_use] + pub fn rev(self) -> Self { + Self { + horizontal: self.horizontal.rev(), + vertical: self.vertical.rev(), + } + } +} + +impl NamedComponent for VisualOrder { + fn name(&self) -> Cow<'_, ComponentName> { + Cow::Owned(ComponentName::named::("visual_order")) + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum HorizontalOrder { + LeftToRight, + RightToLeft, +} + +impl HorizontalOrder { + #[must_use] + pub fn rev(self) -> Self { + match self { + Self::LeftToRight => Self::RightToLeft, + Self::RightToLeft => Self::LeftToRight, + } + } + + pub fn sort_key(self, rect: &Rect) -> Px { + match self { + HorizontalOrder::LeftToRight => rect.origin.x, + HorizontalOrder::RightToLeft => -(rect.origin.x + rect.size.width), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum VerticalOrder { + TopToBottom, + BottomToTop, +} + +impl VerticalOrder { + #[must_use] + pub fn rev(self) -> Self { + match self { + Self::TopToBottom => VerticalOrder::BottomToTop, + Self::BottomToTop => VerticalOrder::TopToBottom, + } + } + + pub fn max_px(self) -> Px { + match self { + VerticalOrder::TopToBottom => Px::MAX, + VerticalOrder::BottomToTop => Px::MIN, + } + } + + pub fn smallest_px(self, a: Px, b: Px) -> Px { + match self { + VerticalOrder::TopToBottom => a.min(b), + VerticalOrder::BottomToTop => b.max(a), + } + } +} diff --git a/src/tree.rs b/src/tree.rs index cb8fc92..699f2f5 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex, PoisonError}; use kludgine::figures::units::Px; use kludgine::figures::{Point, Rect}; +use crate::styles::components::VisualOrder; use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles}; use crate::widget::{ManagedWidget, WidgetId, WidgetInstance}; @@ -86,17 +87,63 @@ impl Tree { } } - pub(crate) fn child_layouts(&self, parent: WidgetId) -> Vec<(ManagedWidget, Rect)> { + pub(crate) fn visually_ordered_children( + &self, + parent: WidgetId, + order: VisualOrder, + ) -> Vec { let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g); - data.nodes[&parent] - .children - .iter() - .filter_map(|id| { - data.nodes[id] + let node = &data.nodes[&parent]; + let mut unordered = node.children.clone(); + let mut ordered = Vec::::with_capacity(unordered.len()); + loop { + // Identify the next "row" of widgets by finding the top of a widget that is the closest to the origin of + let mut min_vertical = order.vertical.max_px(); + let mut max_vertical = order.vertical.max_px(); + + let mut index = 0; + while index < unordered.len() { + let Some(layout) = &data.nodes[&unordered[index]].layout else { + unordered.remove(index); + continue; + }; + let top = layout.origin.y; + let bottom = top + layout.size.height; + min_vertical = order.vertical.smallest_px(min_vertical, top); + max_vertical = order.vertical.smallest_px(min_vertical, bottom); + + index += 1; + } + + if unordered.is_empty() { + break; + } + + // Find all widgets whose top is within the range found. + index = 0; + let row_base = ordered.len(); + while index < unordered.len() { + let top_left = data.nodes[&unordered[index]] .layout - .map(|layout| (data.widget(*id, self).expect("child still owned"), layout)) - }) - .collect() + .expect("all have layouts") + .origin; + if min_vertical <= top_left.y && top_left.y <= max_vertical { + ordered.push( + data.widget(unordered.remove(index), self) + .expect("widget is owned"), + ); + } else { + index += 1; + } + } + + ordered[row_base..].sort_unstable_by_key(|managed| { + order + .horizontal + .sort_key(&data.nodes[&managed.id()].layout.expect("all have layouts")) + }); + } + ordered } pub(crate) fn hover(&self, new_hover: Option<&ManagedWidget>) -> HoverResults { diff --git a/src/utils.rs b/src/utils.rs index 1e82ab6..ed74845 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -57,6 +57,8 @@ impl_all_tuples!(impl_with_clone); pub trait ModifiersExt { fn primary(&self) -> bool; fn word_select(&self) -> bool; + + fn possible_shortcut(&self) -> bool; } impl ModifiersExt for ModifiersState { @@ -79,6 +81,10 @@ impl ModifiersExt for ModifiersState { fn word_select(&self) -> bool { self.control_key() } + + fn possible_shortcut(&self) -> bool { + self.control_key() || self.alt_key() || self.super_key() + } } impl ModifiersExt for Modifiers { @@ -89,6 +95,10 @@ impl ModifiersExt for Modifiers { fn word_select(&self) -> bool { self.state().word_select() } + + fn possible_shortcut(&self) -> bool { + self.state().word_select() + } } pub struct Lazy { diff --git a/src/widget.rs b/src/widget.rs index e63325c..3744fc0 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -15,6 +15,7 @@ use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{Point, Rect, Size}; use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext}; +use crate::styles::components::VisualOrder; use crate::styles::Styles; use crate::tree::Tree; use crate::value::{IntoValue, Value}; @@ -194,14 +195,14 @@ pub trait MakeWidget: Sized { /// [`WidgetId`]. pub trait MakeWidgetWithId: Sized { /// Returns a new [`WidgetInstance`] whose [`WidgetId`] is `id`. - fn make_with_id(self, id: PendingWidgetId) -> WidgetInstance; + fn make_with_id(self, id: WidgetTag) -> WidgetInstance; } impl MakeWidgetWithId for T where T: Widget, { - fn make_with_id(self, id: PendingWidgetId) -> WidgetInstance { + fn make_with_id(self, id: WidgetTag) -> WidgetInstance { WidgetInstance::with_id(self, id) } } @@ -211,7 +212,7 @@ where T: MakeWidgetWithId, { fn make_widget(self) -> WidgetInstance { - self.make_with_id(PendingWidgetId::unique()) + self.make_with_id(WidgetTag::unique()) } } @@ -267,7 +268,7 @@ pub struct WidgetInstance { impl WidgetInstance { /// Returns a new instance containing `widget` that is assigned the unique /// `id` provided. - pub fn with_id(widget: W, id: PendingWidgetId) -> Self + pub fn with_id(widget: W, id: WidgetTag) -> Self where W: Widget, { @@ -283,7 +284,7 @@ impl WidgetInstance { where W: Widget, { - Self::with_id(widget, PendingWidgetId::unique()) + Self::with_id(widget, WidgetTag::unique()) } /// Returns the unique id of this widget instance. @@ -330,6 +331,11 @@ impl WidgetInstance { self.next_focus.get() } } +impl AsRef for WidgetInstance { + fn as_ref(&self) -> &WidgetId { + &self.id + } +} impl Eq for WidgetInstance {} @@ -484,8 +490,14 @@ impl ManagedWidget { self.tree.reset_child_layouts(self.id()); } - pub(crate) fn child_layouts(&self) -> Vec<(ManagedWidget, Rect)> { - self.tree.child_layouts(self.id()) + pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec { + self.tree.visually_ordered_children(self.id(), order) + } +} + +impl AsRef for ManagedWidget { + fn as_ref(&self) -> &WidgetId { + self.widget.as_ref() } } @@ -631,6 +643,15 @@ impl WidgetRef { } } +impl AsRef for WidgetRef { + fn as_ref(&self) -> &WidgetId { + match self { + WidgetRef::Unmounted(widget) => widget.as_ref(), + WidgetRef::Mounted(widget) => widget.as_ref(), + } + } +} + /// The unique id of a [`WidgetInstance`]. /// /// Each [`WidgetInstance`] is guaranteed to have a unique [`WidgetId`] across @@ -654,9 +675,17 @@ impl WidgetId { /// assigned a given [`WidgetId`]. The contained [`WidgetId`] can be accessed /// via [`id()`](Self::id), `Into`, or `Deref`. #[derive(Eq, PartialEq, Debug)] -pub struct PendingWidgetId(WidgetId); +pub struct WidgetTag(WidgetId); + +impl WidgetTag { + /// Returns a unique tag and its contained id. + #[must_use] + pub fn new() -> (Self, WidgetId) { + let tag = Self::unique(); + let id = *tag; + (tag, id) + } -impl PendingWidgetId { /// Returns a newly allocated [`WidgetId`] that is guaranteed to be unique /// for the lifetime of the application. #[must_use] @@ -671,13 +700,13 @@ impl PendingWidgetId { } } -impl From for WidgetId { - fn from(value: PendingWidgetId) -> Self { +impl From for WidgetId { + fn from(value: WidgetTag) -> Self { value.0 } } -impl Deref for PendingWidgetId { +impl Deref for WidgetTag { type Target = WidgetId; fn deref(&self) -> &Self::Target { diff --git a/src/widgets/align.rs b/src/widgets/align.rs index 2e7dfa3..22200b9 100644 --- a/src/widgets/align.rs +++ b/src/widgets/align.rs @@ -47,7 +47,7 @@ impl Align { ); let child = self.child.mounted(&mut context.as_event_context()); - let content_size = context.for_other(child).layout(content_available); + let content_size = context.for_other(&child).layout(content_available); let (left, right, width) = horizontal.measure(available_space.width, content_size.width); let (top, bottom, height) = vertical.measure(available_space.height, content_size.height); @@ -128,7 +128,7 @@ impl FrameInfo { impl Widget for Align { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).redraw(); + context.for_other(&child).redraw(); } fn layout( diff --git a/src/widgets/expand.rs b/src/widgets/expand.rs index eddb348..6eef246 100644 --- a/src/widgets/expand.rs +++ b/src/widgets/expand.rs @@ -49,7 +49,7 @@ impl Expand { impl Widget for Expand { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).redraw(); + context.for_other(&child).redraw(); } fn layout( @@ -62,7 +62,7 @@ impl Widget for Expand { ConstraintLimit::Known(available_space.height.max()), ); let child = self.child.mounted(&mut context.as_event_context()); - let size = context.for_other(child.clone()).layout(available_space); + let size = context.for_other(&child).layout(available_space); context.set_child_layout(&child, Rect::from(size.into_signed())); size } diff --git a/src/widgets/resize.rs b/src/widgets/resize.rs index 39e5925..c3bcf62 100644 --- a/src/widgets/resize.rs +++ b/src/widgets/resize.rs @@ -60,7 +60,7 @@ impl Resize { impl Widget for Resize { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).redraw(); + context.for_other(&child).redraw(); } fn layout( @@ -83,7 +83,8 @@ impl Widget for Resize { ), ); let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).layout(available_space) + // TODO set_child_layout + context.for_other(&child).layout(available_space) } } } diff --git a/src/widgets/scroll.rs b/src/widgets/scroll.rs index feba6dd..008d39e 100644 --- a/src/widgets/scroll.rs +++ b/src/widgets/scroll.rs @@ -12,7 +12,7 @@ use kludgine::shapes::Shape; use kludgine::Color; use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne}; -use crate::context::{AsEventContext, EventContext}; +use crate::context::{AsEventContext, EventContext, LayoutContext}; use crate::styles::components::{EasingIn, EasingOut}; use crate::styles::{ ComponentDefinition, ComponentGroup, ComponentName, Dimension, NamedComponent, @@ -126,7 +126,7 @@ impl Widget for Scroll { let visible_bottom_right = visible_rect.into_signed().extent(); let managed = self.contents.mounted(&mut context.as_event_context()); - context.for_other(managed).redraw(); + context.for_other(&managed).redraw(); if self.horizontal_bar.amount_hidden > 0 { context.graphics.draw_shape( @@ -167,8 +167,8 @@ impl Widget for Scroll { fn layout( &mut self, - available_space: Size, - context: &mut crate::context::LayoutContext<'_, '_, '_, '_, '_>, + available_space: Size, + context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> Size { let styles = context.query_styles(&[&ScrollBarThickness]); self.bar_width = styles @@ -192,7 +192,7 @@ impl Widget for Scroll { ); let managed = self.contents.mounted(&mut context.as_event_context()); let new_content_size = context - .for_other(managed.clone()) + .for_other(&managed) .layout(max_extents) .into_signed(); diff --git a/src/widgets/stack.rs b/src/widgets/stack.rs index 44dc5ad..ec46243 100644 --- a/src/widgets/stack.rs +++ b/src/widgets/stack.rs @@ -132,7 +132,7 @@ impl Stack { impl Widget for Stack { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { for child in &self.synced_children { - context.for_other(child.clone()).redraw(); + context.for_other(child).redraw(); } } @@ -147,7 +147,7 @@ impl Widget for Stack { available_space, context.graphics.scale(), |child_index, constraints, persist| { - let mut context = context.for_other(self.synced_children[child_index].clone()); + let mut context = context.for_other(&self.synced_children[child_index]); if !persist { context = context.as_temporary(); } diff --git a/src/widgets/style.rs b/src/widgets/style.rs index b51c146..4b1d257 100644 --- a/src/widgets/style.rs +++ b/src/widgets/style.rs @@ -31,7 +31,7 @@ impl Widget for Style { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).redraw(); + context.for_other(&child).redraw(); } fn layout( @@ -40,6 +40,6 @@ impl Widget for Style { context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> Size { let child = self.child.mounted(&mut context.as_event_context()); - context.for_other(child).layout(available_space) + context.for_other(&child).layout(available_space) } } diff --git a/src/window.rs b/src/window.rs index 78dea16..0311f10 100644 --- a/src/window.rs +++ b/src/window.rs @@ -25,6 +25,7 @@ use crate::context::{ WidgetContext, }; use crate::graphics::Graphics; +use crate::styles::components::VisualOrder; use crate::tree::Tree; use crate::utils::ModifiersExt; use crate::widget::{EventHandling, ManagedWidget, Widget, WidgetInstance, HANDLED, IGNORED}; @@ -316,6 +317,23 @@ where window.set_needs_redraw(); } } + KeyCode::Tab + if !window.modifiers().state().possible_shortcut() + && input.state.is_pressed() => + { + let direction = if window.modifiers().state().shift_key() { + VisualOrder::left_to_right().rev() + } else { + VisualOrder::left_to_right() + }; + let target = self.root.tree.focused_widget().unwrap_or(self.root.id()); + let target = self.root.tree.widget(target).expect("missing widget"); + let mut target = EventContext::new( + WidgetContext::new(target, &self.redraw_status, &mut window), + kludgine, + ); + target.advance_focus(direction); + } _ => {} } } @@ -401,7 +419,7 @@ where ); self.mouse_state.widget = None; for widget in self.root.tree.widgets_at_point(location) { - let mut widget_context = context.for_other(widget.clone()); + let mut widget_context = context.for_other(&widget); let relative = location - widget_context .last_layout() @@ -527,7 +545,7 @@ fn recursively_handle_event( match each_widget(context) { HANDLED => Some(context.widget().clone()), IGNORED => context.parent().and_then(|parent| { - recursively_handle_event(&mut context.for_other(parent), each_widget) + recursively_handle_event(&mut context.for_other(&parent), each_widget) }), } }