Skip to content

Commit

Permalink
Merge pull request #423 from kas-gui/work
Browse files Browse the repository at this point in the history
Expand AdaptConfigCx, AdaptEventCx
  • Loading branch information
dhardy authored Dec 8, 2023
2 parents 806babd + d129a22 commit a979f04
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 193 deletions.
131 changes: 15 additions & 116 deletions crates/kas-widgets/src/adapt/adapt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,112 +5,11 @@

//! Adapt widget
use super::{AdaptConfigCx, AdaptEventCx};
use kas::prelude::*;
use linear_map::LinearMap;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::time::Duration;

/// An [`EventCx`] with embedded [`Id`]
///
/// NOTE: this is a temporary design: it may be expanded or integrated with
/// `EventCx` in the future.
#[autoimpl(Deref, DerefMut using self.cx)]
pub struct AdaptEventCx<'a: 'b, 'b> {
cx: &'b mut EventCx<'a>,
id: Id,
}

impl<'a: 'b, 'b> AdaptEventCx<'a, 'b> {
#[inline]
fn new(cx: &'b mut EventCx<'a>, id: Id) -> Self {
AdaptEventCx { cx, id }
}

/// Check whether this widget is disabled
#[inline]
pub fn is_disabled(&self) -> bool {
self.cx.is_disabled(&self.id)
}

/// Set/unset disabled status for this widget
#[inline]
pub fn set_disabled(&mut self, state: bool) {
self.cx.set_disabled(self.id.clone(), state);
}

/// Schedule a timed update
///
/// This widget will receive an update for timer `timer_id` 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 a timer
/// handler where it might cause an infinite loop.
///
/// Multiple timer requests with the same `timer_id` are merged
/// (choosing the earliest time).
#[inline]
pub fn request_timer(&mut self, timer_id: u64, delay: Duration) {
self.cx.request_timer(self.id.clone(), timer_id, delay);
}
}

/// A [`ConfigCx`] with embedded [`Id`]
///
/// NOTE: this is a temporary design: it may be expanded or integrated with
/// `ConfigCx` in the future.
#[autoimpl(Deref, DerefMut using self.cx)]
pub struct AdaptConfigCx<'a: 'b, 'b> {
cx: &'b mut ConfigCx<'a>,
id: Id,
}

impl<'a: 'b, 'b> AdaptConfigCx<'a, 'b> {
#[inline]
fn new(cx: &'b mut ConfigCx<'a>, id: Id) -> Self {
AdaptConfigCx { cx, id }
}

/// Check whether this widget is disabled
#[inline]
pub fn is_disabled(&self) -> bool {
self.cx.is_disabled(&self.id)
}

/// Set/unset disabled status for this widget
#[inline]
pub fn set_disabled(&mut self, state: bool) {
self.cx.set_disabled(self.id.clone(), state);
}

/// Enable `alt_bypass` for layer
///
/// This may be called by a child widget during configure to enable or
/// disable alt-bypass for the access-key layer containing its access keys.
/// This allows access keys to be used as shortcuts without the Alt
/// key held. See also [`EventState::new_access_layer`].
#[inline]
pub fn enable_alt_bypass(&mut self, alt_bypass: bool) {
self.cx.enable_alt_bypass(&self.id, alt_bypass);
}

/// Schedule a timed update
///
/// This widget will receive an update for timer `timer_id` 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 a timer
/// handler where it might cause an infinite loop.
///
/// Multiple timer requests with the same `timer_id` are merged
/// (choosing the earliest time).
#[inline]
pub fn request_timer(&mut self, timer_id: u64, delay: Duration) {
self.cx.request_timer(self.id.clone(), timer_id, delay);
}
}

impl_scope! {
/// Data adaption node
Expand All @@ -129,9 +28,9 @@ impl_scope! {
#[widget(&self.state)]
inner: W,
configure_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut S)>>,
update_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &A, &mut S)>>,
timer_handlers: LinearMap<u64, Box<dyn Fn(&mut AdaptEventCx, &A, &mut S) -> bool>>,
message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &A, &mut S) -> bool>>,
update_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut S, &A)>>,
timer_handlers: LinearMap<u64, Box<dyn Fn(&mut AdaptEventCx, &mut S, &A) -> bool>>,
message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &mut S, &A) -> bool>>,
}

impl Self {
Expand Down Expand Up @@ -164,7 +63,7 @@ impl_scope! {
/// Children will be updated after the handler is called.
pub fn on_update<F>(mut self, handler: F) -> Self
where
F: Fn(&mut AdaptConfigCx, &A, &mut S) + 'static,
F: Fn(&mut AdaptConfigCx, &mut S, &A) + 'static,
{
debug_assert!(self.update_handler.is_none());
self.update_handler = Some(Box::new(handler));
Expand All @@ -176,7 +75,7 @@ impl_scope! {
/// The closure should return `true` if state was updated.
pub fn on_timer<H>(mut self, timer_id: u64, handler: H) -> Self
where
H: Fn(&mut AdaptEventCx, &A, &mut S) -> bool + 'static,
H: Fn(&mut AdaptEventCx, &mut S, &A) -> bool + 'static,
{
debug_assert!(self.timer_handlers.get(&timer_id).is_none());
self.timer_handlers.insert(timer_id, Box::new(handler));
Expand All @@ -187,16 +86,16 @@ impl_scope! {
///
/// Children will be updated whenever this handler is invoked.
///
/// Where multiple message types must be handled or access to the
/// [`AdaptEventCx`] is required, use [`Self::on_messages`] instead.
/// Where access to input data (from parent widgets) is required,
/// use [`Self::on_messages`] instead.
pub fn on_message<M, H>(self, handler: H) -> Self
where
M: Debug + 'static,
H: Fn(&A, &mut S, M) + 'static,
H: Fn(&mut AdaptEventCx, &mut S, M) + 'static,
{
self.on_messages(move |cx, data, state| {
self.on_messages(move |cx, state, _data| {
if let Some(m) = cx.try_pop() {
handler(data, state, m);
handler(cx, state, m);
true
} else {
false
Expand All @@ -209,7 +108,7 @@ impl_scope! {
/// The closure should return `true` if state was updated.
pub fn on_messages<H>(mut self, handler: H) -> Self
where
H: Fn(&mut AdaptEventCx, &A, &mut S) -> bool + 'static,
H: Fn(&mut AdaptEventCx, &mut S, &A) -> bool + 'static,
{
self.message_handlers.push(Box::new(handler));
self
Expand All @@ -229,7 +128,7 @@ impl_scope! {
fn update(&mut self, cx: &mut ConfigCx, data: &A) {
if let Some(handler) = self.update_handler.as_ref() {
let mut cx = AdaptConfigCx::new(cx, self.id());
handler(&mut cx, data, &mut self.state);
handler(&mut cx, &mut self.state, data);
}
}

Expand All @@ -238,7 +137,7 @@ impl_scope! {
Event::Timer(timer_id) => {
if let Some(handler) = self.timer_handlers.get(&timer_id) {
let mut cx = AdaptEventCx::new(cx, self.id());
if handler(&mut cx, data, &mut self.state) {
if handler(&mut cx, &mut self.state, data) {
cx.update(self.as_node(data));
}
Used
Expand All @@ -254,7 +153,7 @@ impl_scope! {
let mut update = false;
let mut cx = AdaptEventCx::new(cx, self.id());
for handler in self.message_handlers.iter() {
update |= handler(&mut cx, data, &mut self.state);
update |= handler(&mut cx, &mut self.state, data);
}
if update {
cx.update(self.as_node(data));
Expand Down
112 changes: 112 additions & 0 deletions crates/kas-widgets/src/adapt/adapt_cx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0

//! Adapted contexts
use kas::prelude::*;
use std::time::Duration;

/// An [`EventCx`] with embedded [`Id`] and input data
///
/// NOTE: this is a temporary design: it may be expanded or integrated with
/// `EventCx` in the future.
#[autoimpl(Deref, DerefMut using self.cx)]
pub struct AdaptEventCx<'a: 'b, 'b> {
cx: &'b mut EventCx<'a>,
id: Id,
}

impl<'a: 'b, 'b> AdaptEventCx<'a, 'b> {
/// Construct
#[inline]
pub fn new(cx: &'b mut EventCx<'a>, id: Id) -> Self {
AdaptEventCx { cx, id }
}

/// Check whether this widget is disabled
#[inline]
pub fn is_disabled(&self) -> bool {
self.cx.is_disabled(&self.id)
}

/// Set/unset disabled status for this widget
#[inline]
pub fn set_disabled(&mut self, state: bool) {
self.cx.set_disabled(self.id.clone(), state);
}

/// Schedule a timed update
///
/// This widget will receive an update for timer `timer_id` 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 a timer
/// handler where it might cause an infinite loop.
///
/// Multiple timer requests with the same `timer_id` are merged
/// (choosing the earliest time).
#[inline]
pub fn request_timer(&mut self, timer_id: u64, delay: Duration) {
self.cx.request_timer(self.id.clone(), timer_id, delay);
}
}

/// A [`ConfigCx`] with embedded [`Id`]
///
/// NOTE: this is a temporary design: it may be expanded or integrated with
/// `ConfigCx` in the future.
#[autoimpl(Deref, DerefMut using self.cx)]
pub struct AdaptConfigCx<'a: 'b, 'b> {
cx: &'b mut ConfigCx<'a>,
id: Id,
}

impl<'a: 'b, 'b> AdaptConfigCx<'a, 'b> {
/// Construct
#[inline]
pub fn new(cx: &'b mut ConfigCx<'a>, id: Id) -> Self {
AdaptConfigCx { cx, id }
}

/// Check whether this widget is disabled
#[inline]
pub fn is_disabled(&self) -> bool {
self.cx.is_disabled(&self.id)
}

/// Set/unset disabled status for this widget
#[inline]
pub fn set_disabled(&mut self, state: bool) {
self.cx.set_disabled(self.id.clone(), state);
}

/// Enable `alt_bypass` for layer
///
/// This may be called by a child widget during configure to enable or
/// disable alt-bypass for the access-key layer containing its access keys.
/// This allows access keys to be used as shortcuts without the Alt
/// key held. See also [`EventState::new_access_layer`].
#[inline]
pub fn enable_alt_bypass(&mut self, alt_bypass: bool) {
self.cx.enable_alt_bypass(&self.id, alt_bypass);
}

/// Schedule a timed update
///
/// This widget will receive an update for timer `timer_id` 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 a timer
/// handler where it might cause an infinite loop.
///
/// Multiple timer requests with the same `timer_id` are merged
/// (choosing the earliest time).
#[inline]
pub fn request_timer(&mut self, timer_id: u64, delay: Duration) {
self.cx.request_timer(self.id.clone(), timer_id, delay);
}
}
Loading

0 comments on commit a979f04

Please sign in to comment.