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

Rename timer_update → timer, add AdaptEventCx, pass only ConfigCx to Widget::_nav_next #421

Merged
merged 11 commits into from
Dec 4, 2023
6 changes: 3 additions & 3 deletions crates/kas-core/src/core/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

//! Widget method implementations

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

Expand Down Expand Up @@ -132,7 +132,7 @@ pub fn _replay<W: Events>(
/// Generic implementation of [`Widget::_nav_next`]
pub fn _nav_next<W: Events>(
widget: &mut W,
cx: &mut EventCx,
cx: &mut ConfigCx,
data: &<W as Widget>::Data,
focus: Option<&Id>,
advance: NavAdvance,
Expand All @@ -143,7 +143,7 @@ pub fn _nav_next<W: Events>(

fn nav_next(
mut widget: Node<'_>,
cx: &mut EventCx,
cx: &mut ConfigCx,
focus: Option<&Id>,
advance: NavAdvance,
navigable: bool,
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ trait NodeT {
fn _replay(&mut self, cx: &mut EventCx, id: Id, msg: Erased);
fn _nav_next(
&mut self,
cx: &mut EventCx,
cx: &mut ConfigCx,
focus: Option<&Id>,
advance: NavAdvance,
) -> Option<Id>;
Expand Down Expand Up @@ -102,7 +102,7 @@ impl<'a, T> NodeT for (&'a mut dyn Widget<Data = T>, &'a T) {
}
fn _nav_next(
&mut self,
cx: &mut EventCx,
cx: &mut ConfigCx,
focus: Option<&Id>,
advance: NavAdvance,
) -> Option<Id> {
Expand Down Expand Up @@ -369,7 +369,7 @@ impl<'a> Node<'a> {
// NOTE: public on account of ListView
pub fn _nav_next(
&mut self,
cx: &mut EventCx,
cx: &mut ConfigCx,
focus: Option<&Id>,
advance: NavAdvance,
) -> Option<Id> {
Expand Down
12 changes: 11 additions & 1 deletion crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,15 @@ pub trait Widget: Layout {
}

/// Internal method: configure recursively
///
/// Do not implement this method directly!
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn _configure(&mut self, cx: &mut ConfigCx, data: &Self::Data, id: Id);

/// Internal method: update recursively
///
/// Do not implement this method directly!
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn _update(&mut self, cx: &mut ConfigCx, data: &Self::Data);
Expand All @@ -397,6 +401,8 @@ pub trait Widget: Layout {
/// If `disabled`, widget `id` does not receive the `event`. Widget `id` is
/// the first disabled widget (may be an ancestor of the original target);
/// ancestors of `id` are not disabled.
///
/// Do not implement this method directly!
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn _send(
Expand All @@ -412,18 +418,22 @@ pub trait Widget: Layout {
///
/// Behaves as if an event had been sent to `id`, then the widget had pushed
/// `msg` to the message stack. Widget `id` or any ancestor may handle.
///
/// Do not implement this method directly!
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn _replay(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id, msg: Erased);

/// Internal method: search for the previous/next navigation target
///
/// `focus`: the current focus or starting point.
///
/// Do not implement this method directly!
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
fn _nav_next(
&mut self,
cx: &mut EventCx,
cx: &mut ConfigCx,
data: &Self::Data,
focus: Option<&Id>,
advance: NavAdvance,
Expand Down
59 changes: 32 additions & 27 deletions crates/kas-core/src/event/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::{Action, Id};
use kas_macros::impl_default;
use std::time::{Duration, Instant};

const PAYLOAD_SELECT: u64 = 1 << 60;
const PAYLOAD_GLIDE: u64 = (1 << 60) + 1;
const TIMER_SELECT: u64 = 1 << 60;
const TIMER_GLIDE: u64 = (1 << 60) + 1;
const GLIDE_POLL_MS: u64 = 3;
const GLIDE_MAX_SAMPLES: usize = 8;

Expand Down Expand Up @@ -198,13 +198,23 @@ impl ScrollComponent {
///
/// Returns [`Action::REGION_MOVED`] when the scroll offset changes.
pub fn focus_rect(&mut self, cx: &mut EventCx, rect: Rect, window_rect: Rect) -> Action {
let action = self.self_focus_rect(rect, window_rect);
cx.set_scroll(Scroll::Rect(rect - self.offset));
action
}

/// Scroll self to make the given `rect` visible
///
/// This is identical to [`Self::focus_rect`] except that it does not call
/// [`EventCx::set_scroll`], thus will not affect ancestors.
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn self_focus_rect(&mut self, rect: Rect, window_rect: Rect) -> Action {
self.glide.stop();
let v = rect.pos - window_rect.pos;
let off = Offset::conv(rect.size) - Offset::conv(window_rect.size);
let offset = self.offset.max(v + off).min(v);
let action = self.set_offset(offset);
cx.set_scroll(Scroll::Rect(rect - self.offset));
action
self.set_offset(offset)
}

/// Handle a [`Scroll`] action
Expand Down Expand Up @@ -249,7 +259,7 @@ impl ScrollComponent {
/// Use an event to scroll, if possible
///
/// Consumes the following events: `Command`, `Scroll`, `PressStart`,
/// `PressMove`, `PressEnd`, `TimerUpdate(pl)` where `pl == (1<<60) + 1`.
/// `PressMove`, `PressEnd`, `Timer(pl)` where `pl == (1<<60) + 1`.
/// May request timer updates.
///
/// Implements scroll by Home/End, Page Up/Down and arrow keys, by mouse
Expand Down Expand Up @@ -326,10 +336,10 @@ impl ScrollComponent {
let timeout = cx.config().scroll_flick_timeout();
let pan_dist_thresh = cx.config().pan_dist_thresh();
if self.glide.press_end(timeout, pan_dist_thresh) {
cx.request_timer_update(id.clone(), PAYLOAD_GLIDE, Duration::new(0, 0), true);
cx.request_timer(id.clone(), TIMER_GLIDE, Duration::new(0, 0));
}
}
Event::TimerUpdate(pl) if pl == PAYLOAD_GLIDE => {
Event::Timer(pl) if pl == TIMER_GLIDE => {
// Momentum/glide scrolling: update per arbitrary step time until movment stops.
let timeout = cx.config().scroll_flick_timeout();
let decay = cx.config().scroll_flick_decay();
Expand All @@ -338,7 +348,7 @@ impl ScrollComponent {

if self.glide.vel != Vec2::ZERO {
let dur = Duration::from_millis(GLIDE_POLL_MS);
cx.request_timer_update(id.clone(), PAYLOAD_GLIDE, dur, true);
cx.request_timer(id.clone(), TIMER_GLIDE, dur);
cx.set_scroll(Scroll::Scrolled);
}
}
Expand Down Expand Up @@ -399,7 +409,7 @@ impl TextInput {
/// Handle input events
///
/// Consumes the following events: `PressStart`, `PressMove`, `PressEnd`,
/// `TimerUpdate(pl)` where `pl == 1<<60 || pl == (1<<60)+1`.
/// `Timer(pl)` where `pl == 1<<60 || pl == (1<<60)+1`.
/// May request press grabs and timer updates.
///
/// Implements scrolling and text selection behaviour, excluding handling of
Expand All @@ -416,7 +426,7 @@ impl TextInput {
PressSource::Touch(touch_id) => {
self.touch_phase = TouchPhase::Start(touch_id, press.coord);
let delay = cx.config().touch_select_delay();
cx.request_timer_update(w_id.clone(), PAYLOAD_SELECT, delay, false);
cx.request_timer(w_id.clone(), TIMER_SELECT, delay);
None
}
PressSource::Mouse(..) if cx.config_enable_mouse_text_pan() => {
Expand Down Expand Up @@ -474,32 +484,27 @@ impl TextInput {
|| matches!(press.source, PressSource::Mouse(..) if cx.config_enable_mouse_text_pan()))
{
self.touch_phase = TouchPhase::None;
cx.request_timer_update(w_id, PAYLOAD_GLIDE, Duration::new(0, 0), true);
cx.request_timer(w_id, TIMER_GLIDE, Duration::new(0, 0));
}
Action::None
}
Event::TimerUpdate(pl) if pl == PAYLOAD_SELECT => {
match self.touch_phase {
TouchPhase::Start(touch_id, coord) => {
self.touch_phase = TouchPhase::Cursor(touch_id);
Action::Focus {
coord: Some(coord),
action: SelectionAction::new(true, !cx.modifiers().shift_key(), 1),
}
Event::Timer(pl) if pl == TIMER_SELECT => match self.touch_phase {
TouchPhase::Start(touch_id, coord) => {
self.touch_phase = TouchPhase::Cursor(touch_id);
Action::Focus {
coord: Some(coord),
action: SelectionAction::new(true, !cx.modifiers().shift_key(), 1),
}
// Note: if the TimerUpdate were from another requester it
// should technically be Unused, but it doesn't matter
// so long as other consumers match this first.
_ => Action::None,
}
}
Event::TimerUpdate(pl) if pl == PAYLOAD_GLIDE => {
_ => Action::None,
},
Event::Timer(pl) if pl == TIMER_GLIDE => {
// Momentum/glide scrolling: update per arbitrary step time until movment stops.
let timeout = cx.config().scroll_flick_timeout();
let decay = cx.config().scroll_flick_decay();
if let Some(delta) = self.glide.step(timeout, decay) {
let dur = Duration::from_millis(GLIDE_POLL_MS);
cx.request_timer_update(w_id, PAYLOAD_GLIDE, dur, true);
cx.request_timer(w_id, TIMER_GLIDE, dur);
Action::Pan(delta)
} else {
Action::None
Expand Down
22 changes: 11 additions & 11 deletions crates/kas-core/src/event/cx/cx_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,35 +188,35 @@ impl EventState {
}
}

/// Schedule an update
/// Schedule a timed update
///
/// Widget updates may be used for animation and timed responses. See also
/// [`Draw::animate`](crate::draw::Draw::animate) for animation.
///
/// Widget `id` will receive [`Event::TimerUpdate`] with this `payload` at
/// Widget `id` will receive [`Event::Timer`] with this `payload` at
/// approximately `time = now + delay` (or possibly a little later due to
/// frame-rate limiters and processing time).
///
/// Requesting an update with `delay == 0` is valid, except from an
/// [`Event::TimerUpdate`] handler (where it may cause an infinite loop).
/// [`Event::Timer`] handler (where it may cause an infinite loop).
///
/// If multiple updates with the same `id` and `payload` are requested,
/// these are merged (using the earliest time if `first` is true).
pub fn request_timer_update(&mut self, id: Id, payload: u64, delay: Duration, first: bool) {
/// Multiple timer requests with the same `id` and `payload` are merged
/// (choosing the earliest time).
pub fn request_timer(&mut self, id: Id, payload: u64, delay: Duration) {
let time = Instant::now() + delay;
if let Some(row) = self
.time_updates
.iter_mut()
.find(|row| row.1 == id && row.2 == payload)
{
if (first && row.0 <= time) || (!first && row.0 >= time) {
if row.0 <= time {
return;
}

row.0 = time;
log::trace!(
target: "kas_core::event",
"request_timer_update: update {id} at now+{}ms",
"request_timer: update {id} at now+{}ms",
delay.as_millis()
);
} else {
Expand Down Expand Up @@ -399,7 +399,7 @@ impl EventState {
#[inline]
pub fn request_key_focus(&mut self, target: Id, source: FocusSource) {
self.pending_sel_focus = Some(PendingSelFocus {
target,
target: Some(target),
key_focus: true,
source,
});
Expand All @@ -421,13 +421,13 @@ impl EventState {
#[inline]
pub fn request_sel_focus(&mut self, target: Id, source: FocusSource) {
if let Some(ref pending) = self.pending_sel_focus {
if pending.target == target {
if pending.target.as_ref() == Some(&target) {
return;
}
}

self.pending_sel_focus = Some(PendingSelFocus {
target,
target: Some(target),
key_focus: false,
source,
});
Expand Down
Loading