Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push &EventState into DrawHandle; remove DrawCtx #292

Merged
merged 16 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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