Skip to content

Commit

Permalink
Merge pull request #292 from kas-gui/work2
Browse files Browse the repository at this point in the history
Push &EventState into DrawHandle; remove DrawCtx
  • Loading branch information
dhardy committed Mar 8, 2022
2 parents 48d9181 + 04f8f48 commit 00128f3
Show file tree
Hide file tree
Showing 56 changed files with 912 additions and 821 deletions.
2 changes: 1 addition & 1 deletion crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ easy-cast = { git = "https://github.com/kas-gui/easy-cast.git", rev = "6bf6084bb
log = "0.4"
smallvec = "1.6.1"
stack_dst = { version = "0.6", optional = true }
bitflags = "1.3.1" # only used without winit
bitflags = "1.3.1"
unicode-segmentation = "1.7"
linear-map = "1.2.0"
thiserror = "1.0.23"
Expand Down
2 changes: 0 additions & 2 deletions crates/kas-core/src/core/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ pub struct CoreData {
pub layout: StorageChain,
pub rect: Rect,
pub id: WidgetId,
pub disabled: bool,
}

/// Note: the clone has default-initialised layout storage and identifier.
Expand All @@ -55,7 +54,6 @@ impl Clone for CoreData {
layout: StorageChain::default(),
rect: self.rect,
id: WidgetId::default(),
disabled: self.disabled,
}
}
}
Expand Down
66 changes: 11 additions & 55 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ use crate::event;
use crate::event::{EventMgr, EventState};
use crate::geom::{Coord, Offset, Rect};
use crate::layout::{self, AlignHints, AxisInfo, SetRectMgr, SizeRules};
#[allow(unused)]
use crate::theme::DrawCtx;
use crate::theme::{DrawMgr, SizeMgr};
use crate::util::IdentifyWidget;
use crate::{CoreData, TkAction, WidgetId};
use crate::{CoreData, WidgetId};

impl dyn WidgetCore {
/// Forwards to the method defined on the type `Any`.
Expand Down Expand Up @@ -65,24 +63,6 @@ pub trait WidgetCore: Any + fmt::Debug {
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn core_data_mut(&mut self) -> &mut CoreData;

/// Set disabled state (chaining)
///
/// This is identical to [`WidgetExt::set_disabled`], but can be called in
/// chaining fashion. Example:
/// ```ignore
/// use kas::{WidgetCore, widget::MenuEntry};
/// let entry = MenuEntry::new("Disabled Item", ()).with_disabled(true);
/// ```
#[inline]
#[must_use]
fn with_disabled(mut self, disabled: bool) -> Self
where
Self: Sized,
{
self.core_data_mut().disabled = disabled;
self
}

/// Get the name of the widget struct
fn widget_name(&self) -> &'static str;

Expand All @@ -99,8 +79,8 @@ pub trait WidgetCore: Any + fmt::Debug {
///
/// Dynamic widgets must implement this trait manually, since [`derive(Widget)`]
/// cannot currently handle fields like `Vec<SomeWidget>`. Additionally, any
/// parent adding child widgets must ensure they get configured, either via
/// [`TkAction::RECONFIGURE`] or via [`SetRectMgr::configure`].
/// parent adding child widgets must ensure they get configured by calling
/// [`SetRectMgr::configure`].
///
/// [`derive(Widget)`]: https://docs.rs/kas/latest/kas/macros/index.html#the-derivewidget-macro
pub trait WidgetChildren: WidgetCore {
Expand Down Expand Up @@ -170,9 +150,8 @@ pub trait WidgetChildren: WidgetCore {
pub trait WidgetConfig: Layout {
/// Pre-configure widget
///
/// Widgets are *configured* on window creation (before sizing) and when
/// [`TkAction::RECONFIGURE`] is sent. Child-widgets may alternatively be
/// configured locally by calling [`SetRectMgr::configure`].
/// Widgets are *configured* on window creation (before sizing) or
/// dynamically via the parent calling [`SetRectMgr::configure`].
///
/// Configuration happens at least once
/// before sizing and drawing, and may be repeated at a later time.
Expand All @@ -189,9 +168,8 @@ pub trait WidgetConfig: Layout {

/// Configure widget
///
/// Widgets are *configured* on window creation (before sizing) and when
/// [`TkAction::RECONFIGURE`] is sent. Child-widgets may alternatively be
/// configured locally by calling [`SetRectMgr::configure`].
/// Widgets are *configured* on window creation (before sizing) or
/// dynamically via the parent calling [`SetRectMgr::configure`].
///
/// Configuration happens at least once
/// before sizing and drawing, and may be repeated at a later time.
Expand Down Expand Up @@ -320,7 +298,7 @@ pub trait Layout: WidgetChildren {
///
/// Affects event handling via [`Self::find_id`] and affects the positioning
/// of pop-up menus. [`Self::draw`] must be implemented directly using
/// [`DrawCtx::with_clip_region`] to offset contents.
/// [`DrawMgr::with_clip_region`] to offset contents.
#[inline]
fn translation(&self) -> Offset {
Offset::ZERO
Expand Down Expand Up @@ -401,13 +379,10 @@ pub trait Layout: WidgetChildren {
/// This method is invoked each frame to draw visible widgets. It should
/// draw itself and recurse into all visible children.
///
/// One should use `let draw = draw.with_core(self.core_data());` to obtain
/// a [`DrawCtx`], enabling further drawing.
///
/// The default impl draws elements as defined by [`Self::layout`].
fn draw(&mut self, mut draw: DrawMgr) {
let draw = draw.with_core(self.core_data());
self.layout().draw(draw);
fn draw(&mut self, draw: DrawMgr) {
let id = self.id(); // clone to avoid borrow conflict
self.layout().draw(draw, &id);
}
}

Expand Down Expand Up @@ -531,25 +506,6 @@ pub trait WidgetExt: WidgetChildren {
}
}

/// Get whether the widget is disabled
#[inline]
fn is_disabled(&self) -> bool {
self.core_data().disabled
}

/// Set the disabled state of a widget
///
/// If disabled, a widget should not respond to input and should appear
/// greyed out.
///
/// The disabled status is inherited by children: events should not be
/// passed to them, and they should also be drawn greyed out.
#[inline]
fn set_disabled(&mut self, disabled: bool) -> TkAction {
self.core_data_mut().disabled = disabled;
TkAction::REDRAW
}

/// Get the widget's region, relative to its parent.
#[inline]
fn rect(&self) -> Rect {
Expand Down
63 changes: 63 additions & 0 deletions crates/kas-core/src/core/widget_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,42 @@ impl WidgetId {
}
}

/// Get the parent widget's identifier, if not root
///
/// Note: there is no guarantee that [`Self::as_u64`] on the result will
/// match that of the original parent identifier.
pub fn parent(&self) -> Option<WidgetId> {
match self.0.get() {
Variant::Invalid => None,
Variant::Int(x) => {
let mut bit_len = 4 * block_len(x);
while bit_len > 0 {
bit_len -= 4;
if bit_len > 0 && (x >> (64 - bit_len)) & 8 != 0 {
continue;
}

let len = ((bit_len / 4) as u64) << SHIFT_LEN;
let mask = MASK_BITS << (56 - bit_len);
let id = (x & mask) | len | USE_BITS;
return Some(WidgetId(IntOrPtr::new_int(id)));
}
None
}
Variant::Slice(path) => {
let len = path.len();
if len > 1 {
// TODO(opt): in some cases we could make Variant::Int
Some(WidgetId(IntOrPtr::new_iter(
path[0..len - 1].iter().cloned(),
)))
} else {
None
}
}
}
}

/// Make an identifier for the child with the given `key`
///
/// Note: this is not a getter method. Calling multiple times with the same
Expand Down Expand Up @@ -705,6 +741,33 @@ mod test {
assert_eq!(as_vec(0x1A93_007F_0000_0071), vec![1, 139, 0, 0, 7]);
}

#[test]
fn test_parent() {
fn test(seq: &[usize]) {
println!("seq: {:?}", seq);
let mut id = WidgetId::ROOT;
let len = seq.len();
for key in &seq[..len - 1] {
id = id.make_child(*key);
}

if len == 0 {
assert_eq!(id.parent(), None);
} else {
let id2 = id.make_child(seq[len - 1]);
assert_eq!(id2.parent(), Some(id));
}
}

test(&[4]);
test(&[4, 0]);
test(&[0, 0, 0]);
test(&[0, 1, 0]);
test(&[9, 0, 1, 300]);
test(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]);
test(&[313553, 13513, 13511631]);
}

#[test]
fn test_make_child() {
fn test(seq: &[usize], x: u64) {
Expand Down
12 changes: 7 additions & 5 deletions crates/kas-core/src/event/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,10 @@ pub trait SendEvent: Handler {
///
/// This method is responsible for routing events toward descendents.
/// [`WidgetId`] values are assigned via depth-first search with parents
/// ordered after all children. Disabling a widget is recursive, hence
/// disabled widgets should not forward any events.
/// ordered after all children.
///
/// The following logic is recommended for routing events:
/// ```no_test
/// if self.is_disabled() {
/// return Response::Unused;
/// }
/// match self.find_child_index(&id) {
/// Some(widget_index![self.child0]) => self.child0.send(mgr, id, event).into(),
/// Some(widget_index![self.child1]) => self.child1.send(mgr, id, event).into(),
Expand All @@ -112,6 +108,12 @@ pub trait SendEvent: Handler {
/// The example above uses [`EventMgr::handle_generic`], which is an optional
/// tool able to perform some simplifications on events. It is also valid to
/// call [`Handler::handle`] directly or simply to embed handling logic here.
///
/// When a child widget returns [`Response::Unused`], the widget may call
/// its own event handler. This is useful e.g. to capture a click+drag on a
/// child which does not handle that event. Note further that in case the
/// child is disabled, events targetting the child may be sent directly to
/// self.
fn send(&mut self, mgr: &mut EventMgr, id: WidgetId, event: Event) -> Response<Self::Msg>;
}

Expand Down
35 changes: 28 additions & 7 deletions crates/kas-core/src/event/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,11 @@ type AccelLayer = (bool, HashMap<VirtualKeyCode, WidgetId>);
#[derive(Debug)]
pub struct EventState {
config: WindowConfig,
disabled: Vec<WidgetId>,
scale_factor: f32,
configure_active: bool,
configure_count: usize,
window_has_focus: bool,
modifiers: ModifiersState,
/// char focus is on same widget as sel_focus; otherwise its value is ignored
char_focus: bool,
Expand Down Expand Up @@ -401,11 +403,13 @@ impl<'a> EventMgr<'a> {

if let Some(id) = w_id {
let mut icon = Default::default();
if let Some(w) = widget.find_widget(&id) {
if w.hover_highlight() {
self.redraw(id);
if !self.is_disabled(&id) {
if let Some(w) = widget.find_widget(&id) {
if w.hover_highlight() {
self.redraw(id);
}
icon = w.cursor_icon();
}
icon = w.cursor_icon();
}
if icon != self.state.hover_icon {
self.state.hover_icon = icon;
Expand Down Expand Up @@ -543,9 +547,26 @@ impl<'a> EventMgr<'a> {
}
}

fn send_impl<W>(&mut self, widget: &mut W, mut id: WidgetId, event: Event) -> Response<W::Msg>
where
W: Widget + ?Sized,
{
// TODO(opt): we should be able to use binary search here
for d in &self.disabled {
if d.is_ancestor_of(&id) {
if let Some(p) = d.parent() {
id = p;
} else {
return Response::Unused;
}
}
}
widget.send(self, id, event)
}

fn send_event<W: Widget + ?Sized>(&mut self, widget: &mut W, id: WidgetId, event: Event) {
trace!("Send to {}: {:?}", id, event);
let _ = widget.send(self, id, event);
let _ = self.send_impl(widget, id, event);
}

// Similar to send_event, but return true only if response != Response::Unused
Expand All @@ -556,7 +577,7 @@ impl<'a> EventMgr<'a> {
event: Event,
) -> bool {
trace!("Send to {}: {:?}", id, event);
let r = widget.send(self, id, event);
let r = self.send_impl(widget, id, event);
!matches!(r, Response::Unused)
}

Expand All @@ -571,7 +592,7 @@ impl<'a> EventMgr<'a> {
.map(|(wid, p, _)| (*wid, p.parent.clone()))
{
trace!("Send to popup parent: {}: {:?}", parent, event);
match widget.send(self, parent, event.clone()) {
match self.send_impl(widget, parent, event.clone()) {
Response::Unused => (),
_ => return,
}
Expand Down
Loading

0 comments on commit 00128f3

Please sign in to comment.