Skip to content

Commit

Permalink
Merge pull request #413 from kas-gui/work2
Browse files Browse the repository at this point in the history
WidgetId → Id, revise handling of Action and pending ops
  • Loading branch information
dhardy authored Oct 11, 2023
2 parents 08db62d + 6362c4d commit 26fe69d
Show file tree
Hide file tree
Showing 73 changed files with 1,223 additions and 1,113 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Usage of `unsafe` is allowed, but not preferred. Current use cases:
- To get around lifetime restrictions on the theme API's `Theme::draw` and `Window::size`
methods; this will no longer require `unsafe` once the
`generic_associated_types` feature is stabilised.
- `WidgetId` uses `unsafe` code to support both inline and heap-allocated
- `Id` uses `unsafe` code to support both inline and heap-allocated
variants.
- Implementing `bytemuck::Pod` and `Zeroable`, as required to assert that
values may be copied to the GPU.
Expand Down
26 changes: 13 additions & 13 deletions crates/kas-core/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
bitflags! {
/// Action required after processing
///
/// This type is returned by many widgets on modification to self and is tracked
/// internally by [`event::EventCx`] to determine which updates are needed to
/// the UI.
/// Some methods operate directly on a context ([`ConfigCx`] or [`EventCx`])
/// while others don't reqiure a context but do require that some *action*
/// is performed afterwards. This enum is used to convey that action.
///
/// Two `Action` values may be combined via bit-or (`a | b`). Bit-or
/// assignments are supported by both `Action` and [`event::EventCx`].
/// An `Action` should be passed to a context: `cx.action(self.id(), action)`
/// (assuming `self` is a widget).
///
/// Users receiving a value of this type from a widget update method should
/// usually handle with `*cx |= action;`. Before the event loop starts
/// (`toolkit.run()`) or if the widget in question is not part of a UI these
/// values can be ignored.
/// Two `Action` values may be combined via bit-or (`a | b`).
#[must_use]
#[derive(Copy, Clone, Debug, Default)]
pub struct Action: u32 {
Expand All @@ -35,6 +32,11 @@ bitflags! {
///
/// Implies window redraw.
const REGION_MOVED = 1 << 4;
/// A widget was scrolled
///
/// This is used for inter-widget communication (see `EditBox`). If not
/// handled locally, it is handled identially to [`Self::SET_RECT`].
const SCROLLED = 1 << 6;
/// Reset size of all widgets without recalculating requirements
const SET_RECT = 1 << 8;
/// Resize all widgets in the window
Expand All @@ -49,10 +51,8 @@ bitflags! {
const EVENT_CONFIG = 1 << 11;
/// Reconfigure all widgets of the window
///
/// *Configuring* widgets assigns [`WidgetId`] identifiers and calls
/// [`crate::Events::configure`].
///
/// [`WidgetId`]: crate::WidgetId
/// *Configuring* widgets assigns [`Id`](crate::Id) identifiers and calls
/// [`Events::configure`](crate::Events::configure).
const RECONFIGURE = 1 << 16;
/// Update all widgets
///
Expand Down
16 changes: 8 additions & 8 deletions crates/kas-core/src/core/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

//! Widget data types

use super::Id;
#[allow(unused)] use super::Widget;
use super::WidgetId;
use crate::geom::Rect;

#[cfg(feature = "winit")] pub use winit::window::Icon;
Expand Down Expand Up @@ -37,7 +37,7 @@ impl Icon {
#[derive(Default, Debug)]
pub struct CoreData {
pub rect: Rect,
pub id: WidgetId,
pub id: Id,
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -77,20 +77,20 @@ pub enum WidgetStatus {

#[cfg(debug_assertions)]
impl WidgetStatus {
fn require(&self, id: &WidgetId, expected: Self) {
fn require(&self, id: &Id, expected: Self) {
if *self < expected {
panic!("WidgetStatus of {id}: require {expected:?}, found {self:?}");
}
}

/// Configure
pub fn configure(&mut self, _id: &WidgetId) {
pub fn configure(&mut self, _id: &Id) {
// re-configure does not require repeating other actions
*self = (*self).max(WidgetStatus::Configured);
}

/// Update
pub fn update(&self, id: &WidgetId) {
pub fn update(&self, id: &Id) {
self.require(id, WidgetStatus::Configured);

// Update-after-configure is already guaranteed (see impls module).
Expand All @@ -100,7 +100,7 @@ impl WidgetStatus {
}

/// Size rules
pub fn size_rules(&mut self, id: &WidgetId, axis: crate::layout::AxisInfo) {
pub fn size_rules(&mut self, id: &Id, axis: crate::layout::AxisInfo) {
// NOTE: Possibly this is too strict and we should not require
// re-running size_rules(vert) or set_rect afterwards?
if axis.is_horizontal() {
Expand All @@ -113,12 +113,12 @@ impl WidgetStatus {
}

/// Set rect
pub fn set_rect(&mut self, id: &WidgetId) {
pub fn set_rect(&mut self, id: &Id) {
self.require(id, WidgetStatus::SizeRulesY);
*self = WidgetStatus::SetRect;
}

pub fn require_rect(&self, id: &WidgetId) {
pub fn require_rect(&self, id: &Id) {
self.require(id, WidgetStatus::SetRect);
}
}
14 changes: 7 additions & 7 deletions crates/kas-core/src/core/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

use crate::event::{Event, EventCx, FocusSource, IsUsed, Scroll, Unused, Used};
#[cfg(debug_assertions)] use crate::util::IdentifyWidget;
use crate::{Erased, Events, Layout, NavAdvance, Node, Widget, WidgetId};
use crate::{Erased, Events, Id, Layout, NavAdvance, Node, Widget};

/// Generic implementation of [`Widget::_send`]
pub fn _send<W: Events>(
widget: &mut W,
cx: &mut EventCx,
data: &<W as Widget>::Data,
id: WidgetId,
id: Id,
disabled: bool,
event: Event,
) -> IsUsed {
Expand Down Expand Up @@ -88,7 +88,7 @@ pub fn _replay<W: Events>(
widget: &mut W,
cx: &mut EventCx,
data: &<W as Widget>::Data,
id: WidgetId,
id: Id,
msg: Erased,
) {
if let Some(index) = widget.find_child_index(&id) {
Expand Down Expand Up @@ -134,20 +134,20 @@ pub fn _nav_next<W: Events>(
widget: &mut W,
cx: &mut EventCx,
data: &<W as Widget>::Data,
focus: Option<&WidgetId>,
focus: Option<&Id>,
advance: NavAdvance,
) -> Option<WidgetId> {
) -> Option<Id> {
let navigable = widget.navigable();
nav_next(widget.as_node(data), cx, focus, advance, navigable)
}

fn nav_next(
mut widget: Node<'_>,
cx: &mut EventCx,
focus: Option<&WidgetId>,
focus: Option<&Id>,
advance: NavAdvance,
navigable: bool,
) -> Option<WidgetId> {
) -> Option<Id> {
let id = widget.id_ref();
if !id.is_valid() {
log::warn!("nav_next: encountered unconfigured node!");
Expand Down
54 changes: 34 additions & 20 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::geom::{Coord, Offset, Rect};
use crate::layout::{AxisInfo, SizeRules};
use crate::theme::{DrawCx, SizeCx};
use crate::util::IdentifyWidget;
use crate::WidgetId;
use crate::{HasId, Id};
use kas_macros::autoimpl;

#[allow(unused)] use super::{Events, Widget};
Expand Down Expand Up @@ -70,13 +70,13 @@ pub trait Layout {

/// Get the widget's identifier
///
/// Note that the default-constructed [`WidgetId`] is *invalid*: any
/// Note that the default-constructed [`Id`] is *invalid*: any
/// operations on this value will cause a panic. A valid identifier is
/// assigned when the widget is configured (immediately before calling
/// [`Events::configure`]).
///
/// This method is implemented by the `#[widget]` macro.
fn id_ref(&self) -> &WidgetId {
fn id_ref(&self) -> &Id {
unimplemented!() // make rustdoc show that this is a provided method
}

Expand Down Expand Up @@ -121,11 +121,11 @@ pub trait Layout {
/// If `Some(index)` is returned, this is *probably* but not guaranteed
/// to be a valid child index.
///
/// The default implementation simply uses [`WidgetId::next_key_after`].
/// The default implementation simply uses [`Id::next_key_after`].
/// Widgets may choose to assign children custom keys by overriding this
/// method and [`Events::make_child_id`].
#[inline]
fn find_child_index(&self, id: &WidgetId) -> Option<usize> {
fn find_child_index(&self, id: &Id) -> Option<usize> {
id.next_key_after(self.id_ref())
}

Expand Down Expand Up @@ -228,7 +228,7 @@ pub trait Layout {
Offset::ZERO
}

/// Translate a coordinate to a [`WidgetId`]
/// Translate a coordinate to an [`Id`]
///
/// This method is used to determine which widget reacts to the mouse cursor
/// or a touch event. The result affects mouse-hover highlighting, event
Expand Down Expand Up @@ -258,7 +258,7 @@ pub trait Layout {
///
/// - Widgets should test `self.rect().contains(coord)`, returning `None`
/// if this test is `false`; otherwise, they should always return *some*
/// [`WidgetId`], either a childs or their own.
/// [`Id`], either a childs or their own.
/// - If the Widget uses a translated coordinate space (i.e.
/// `self.translation() != Offset::ZERO`) then pass
/// `coord + self.translation()` to children.
Expand All @@ -276,7 +276,7 @@ pub trait Layout {
/// }
/// Some(self.id())
/// ```
fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
fn find_id(&mut self, coord: Coord) -> Option<Id> {
let _ = coord;
unimplemented!() // make rustdoc show that this is a provided method
}
Expand All @@ -290,37 +290,51 @@ pub trait Layout {
/// but failure to do so should not cause a fatal error.
///
/// The `draw` parameter is pre-parameterized with this widget's
/// [`WidgetId`], allowing drawn components to react to input state. This
/// [`Id`], allowing drawn components to react to input state. This
/// implies that when calling `draw` on children, the child's `id` must be
/// supplied via [`DrawCx::re_id`] or [`DrawCx::recurse`].
fn draw(&mut self, draw: DrawCx);
}

impl<W: Layout + ?Sized> HasId for &W {
#[inline]
fn has_id(self) -> Id {
self.id_ref().clone()
}
}

impl<W: Layout + ?Sized> HasId for &mut W {
#[inline]
fn has_id(self) -> Id {
self.id_ref().clone()
}
}

/// Extension trait over widgets
pub trait LayoutExt: Layout {
/// Get the widget's identifier
///
/// Note that the default-constructed [`WidgetId`] is *invalid*: any
/// Note that the default-constructed [`Id`] is *invalid*: any
/// operations on this value will cause a panic. Valid identifiers are
/// assigned during configure.
#[inline]
fn id(&self) -> WidgetId {
fn id(&self) -> Id {
self.id_ref().clone()
}

/// Test widget identifier for equality
///
/// This method may be used to test against `WidgetId`, `Option<WidgetId>`
/// and `Option<&WidgetId>`.
/// This method may be used to test against `Id`, `Option<Id>`
/// and `Option<&Id>`.
#[inline]
fn eq_id<T>(&self, rhs: T) -> bool
where
WidgetId: PartialEq<T>,
Id: PartialEq<T>,
{
*self.id_ref() == rhs
}

/// Display as "StructName#WidgetId"
/// Display as "StructName#Id"
#[inline]
fn identify(&self) -> IdentifyWidget {
IdentifyWidget(self.widget_name(), self.id_ref())
Expand All @@ -330,16 +344,16 @@ pub trait LayoutExt: Layout {
///
/// This function assumes that `id` is a valid widget.
#[inline]
fn is_ancestor_of(&self, id: &WidgetId) -> bool {
self.id().is_ancestor_of(id)
fn is_ancestor_of(&self, id: &Id) -> bool {
self.id_ref().is_ancestor_of(id)
}

/// Check whether `id` is not self and is a descendant
///
/// This function assumes that `id` is a valid widget.
#[inline]
fn is_strict_ancestor_of(&self, id: &WidgetId) -> bool {
!self.eq_id(id) && self.id().is_ancestor_of(id)
fn is_strict_ancestor_of(&self, id: &Id) -> bool {
!self.eq_id(id) && self.id_ref().is_ancestor_of(id)
}

/// Run a closure on all children
Expand Down Expand Up @@ -374,7 +388,7 @@ pub trait LayoutExt: Layout {
///
/// Since `id` represents a path, this operation is normally `O(d)` where
/// `d` is the depth of the path (depending on widget implementations).
fn find_widget(&self, id: &WidgetId) -> Option<&dyn Layout> {
fn find_widget(&self, id: &Id) -> Option<&dyn Layout> {
if let Some(child) = self.find_child_index(id).and_then(|i| self.get_child(i)) {
child.find_widget(id)
} else if self.eq_id(id) {
Expand Down
Loading

0 comments on commit 26fe69d

Please sign in to comment.