Skip to content

Commit

Permalink
Checkpoint
Browse files Browse the repository at this point in the history
Progress on tab focus
  • Loading branch information
ecton committed Nov 7, 2023
1 parent 8a2dae3 commit 5e5d826
Show file tree
Hide file tree
Showing 12 changed files with 383 additions and 105 deletions.
213 changes: 147 additions & 66 deletions src/context.rs

Large diffs are not rendered by default.

94 changes: 93 additions & 1 deletion src/styles/components.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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::<Global>("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>) -> 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),
}
}
}
65 changes: 56 additions & 9 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::styles::components::VisualOrder;
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles};
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};

Expand Down Expand Up @@ -86,17 +87,63 @@ impl Tree {
}
}

pub(crate) fn child_layouts(&self, parent: WidgetId) -> Vec<(ManagedWidget, Rect<Px>)> {
pub(crate) fn visually_ordered_children(
&self,
parent: WidgetId,
order: VisualOrder,
) -> Vec<ManagedWidget> {
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::<ManagedWidget>::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 {
Expand Down
10 changes: 10 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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<T> {
Expand Down
53 changes: 41 additions & 12 deletions src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<T> 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)
}
}
Expand All @@ -211,7 +212,7 @@ where
T: MakeWidgetWithId,
{
fn make_widget(self) -> WidgetInstance {
self.make_with_id(PendingWidgetId::unique())
self.make_with_id(WidgetTag::unique())
}
}

Expand Down Expand Up @@ -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<W>(widget: W, id: PendingWidgetId) -> Self
pub fn with_id<W>(widget: W, id: WidgetTag) -> Self
where
W: Widget,
{
Expand All @@ -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.
Expand Down Expand Up @@ -330,6 +331,11 @@ impl WidgetInstance {
self.next_focus.get()
}
}
impl AsRef<WidgetId> for WidgetInstance {
fn as_ref(&self) -> &WidgetId {
&self.id
}
}

impl Eq for WidgetInstance {}

Expand Down Expand Up @@ -484,8 +490,14 @@ impl ManagedWidget {
self.tree.reset_child_layouts(self.id());
}

pub(crate) fn child_layouts(&self) -> Vec<(ManagedWidget, Rect<Px>)> {
self.tree.child_layouts(self.id())
pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<ManagedWidget> {
self.tree.visually_ordered_children(self.id(), order)
}
}

impl AsRef<WidgetId> for ManagedWidget {
fn as_ref(&self) -> &WidgetId {
self.widget.as_ref()
}
}

Expand Down Expand Up @@ -631,6 +643,15 @@ impl WidgetRef {
}
}

impl AsRef<WidgetId> 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
Expand All @@ -654,9 +675,17 @@ impl WidgetId {
/// assigned a given [`WidgetId`]. The contained [`WidgetId`] can be accessed
/// via [`id()`](Self::id), `Into<WidgetId>`, 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]
Expand All @@ -671,13 +700,13 @@ impl PendingWidgetId {
}
}

impl From<PendingWidgetId> for WidgetId {
fn from(value: PendingWidgetId) -> Self {
impl From<WidgetTag> 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 {
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
}
Expand Down
Loading

0 comments on commit 5e5d826

Please sign in to comment.