use teksilo_canvas::Point;
use crate::event::{ButtonMask, EventResponse, WidgetEvent};
use crate::event_handlers::EventHandlers;
use crate::gesture::{DragPhase, PinchPhase, SwipeDirection, TapEvent};
use crate::signal::Prop;
use crate::widget::{CursorIcon, EventContext, Widget};
use crate::widget_id::WidgetId;
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
pub enum AccessSubtreeMode {
#[default]
Inherit,
Exclude,
Merge,
}
#[derive(Default)]
pub struct AccessibilityOverrides {
pub label: Option<Prop<String>>,
pub description: Option<Prop<String>>,
pub value: Option<Prop<String>>,
pub role: Option<accesskit::Role>,
pub hidden: Option<Prop<bool>>,
pub disabled: Option<bool>,
pub identifier: Option<String>,
pub controls: Vec<WidgetId>,
pub described_by: Vec<WidgetId>,
pub labelled_by: Vec<WidgetId>,
pub live: Option<accesskit::Live>,
pub aria_current: Option<accesskit::AriaCurrent>,
pub shortcut: Option<String>,
pub shortcut_id: Option<String>,
pub has_popup: Option<accesskit::HasPopup>,
pub orientation: Option<accesskit::Orientation>,
pub numeric_value: Option<f64>,
pub min_numeric_value: Option<f64>,
pub max_numeric_value: Option<f64>,
pub numeric_step: Option<f64>,
pub actions: Vec<(accesskit::Action, Box<dyn FnMut(&mut EventContext)>)>,
pub removed_actions: Vec<accesskit::Action>,
pub custom_actions: Vec<(Prop<String>, Box<dyn FnMut(&mut EventContext)>)>,
pub customize: Option<Box<dyn Fn(&mut crate::accessibility::AccessNodeBuilder)>>,
}
impl std::fmt::Debug for AccessibilityOverrides {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AccessibilityOverrides")
.field("label", &self.label)
.field("description", &self.description)
.field("value", &self.value)
.field("role", &self.role)
.field("hidden", &self.hidden)
.field("disabled", &self.disabled)
.field("identifier", &self.identifier)
.field("shortcut", &self.shortcut)
.field("shortcut_id", &self.shortcut_id)
.field("controls_len", &self.controls.len())
.field("described_by_len", &self.described_by.len())
.field("labelled_by_len", &self.labelled_by.len())
.field("actions_len", &self.actions.len())
.field("removed_actions", &self.removed_actions)
.field("custom_actions_len", &self.custom_actions.len())
.finish()
}
}
impl AccessibilityOverrides {
pub(crate) fn apply(&self, b: &mut crate::accessibility::AccessNodeBuilder) {
use crate::accessibility::widget_id_to_node_id;
if let Some(ref p) = self.label {
b.set_name(p.get());
}
if let Some(ref p) = self.description {
b.set_description(p.get());
}
if let Some(ref p) = self.value {
b.set_value(p.get());
}
if let Some(role) = self.role {
b.set_role(role);
}
match self.hidden.as_ref().map(|p| p.get()) {
Some(true) => b.set_hidden(),
Some(false) => b.clear_hidden(),
None => {}
}
match self.disabled {
Some(true) => b.set_disabled(),
Some(false) => b.clear_disabled(),
None => {}
}
if let Some(ref s) = self.identifier {
b.set_author_id(s.clone());
}
for &id in &self.controls {
b.push_controlled(widget_id_to_node_id(id));
}
for &id in &self.described_by {
b.push_described_by(widget_id_to_node_id(id));
}
for &id in &self.labelled_by {
b.push_labelled_by(widget_id_to_node_id(id));
}
if let Some(live) = self.live {
b.set_live(live);
}
if let Some(c) = self.aria_current {
b.set_aria_current(c);
}
if let Some(ref s) = self.shortcut {
b.set_keyboard_shortcut(s.clone());
}
if let Some(p) = self.has_popup {
b.set_has_popup(p);
}
if let Some(o) = self.orientation {
b.set_orientation(o);
}
if let Some(v) = self.numeric_value {
b.set_numeric_value(v);
}
if let Some(v) = self.min_numeric_value {
b.set_min_numeric_value(v);
}
if let Some(v) = self.max_numeric_value {
b.set_max_numeric_value(v);
}
if let Some(v) = self.numeric_step {
b.set_numeric_value_step(v);
}
for &a in &self.removed_actions {
b.remove_action(a);
}
for (action, _) in &self.actions {
b.add_action(*action);
}
if !self.custom_actions.is_empty() {
let custom: Vec<accesskit::CustomAction> = self
.custom_actions
.iter()
.enumerate()
.map(|(i, (label, _))| accesskit::CustomAction {
id: i as i32,
description: label.get(),
})
.collect();
b.set_custom_actions(custom);
}
if let Some(ref f) = self.customize {
f(b);
}
}
}
pub type ContextMenuFactory = Box<dyn Fn(Point, &mut EventContext) -> Option<Box<dyn Widget>>>;
pub struct HandlerSet {
pub(crate) handlers: EventHandlers,
pub(crate) focusable: Option<bool>,
pub(crate) tab_index: Option<i32>,
pub(crate) cursor: Option<CursorIcon>,
pub(crate) clips_children: Option<bool>,
pub(crate) ime: Option<crate::ime::ImeContext>,
pub(crate) event_pass_through: Option<bool>,
pub(crate) gesture_dead_zone: Option<bool>,
pub(crate) keyboard_capture: Option<bool>,
pub(crate) hit_transparent: Option<bool>,
pub(crate) context_menu_factory: Option<ContextMenuFactory>,
pub(crate) focus_within: Option<crate::signal::Signal<bool>>,
pub(crate) hover_within: Option<crate::signal::Signal<bool>>,
pub(crate) visible_when: Option<Prop<bool>>,
pub(crate) access: Option<Box<AccessibilityOverrides>>,
pub(crate) access_subtree: Option<AccessSubtreeMode>,
}
impl HandlerSet {
pub fn new() -> Self {
Self {
handlers: EventHandlers::new(),
focusable: None,
tab_index: None,
cursor: None,
clips_children: None,
ime: None,
event_pass_through: None,
gesture_dead_zone: None,
keyboard_capture: None,
hit_transparent: None,
context_menu_factory: None,
focus_within: None,
hover_within: None,
visible_when: None,
access: None,
access_subtree: None,
}
}
pub(crate) fn access_mut(&mut self) -> &mut AccessibilityOverrides {
self.access
.get_or_insert_with(|| Box::new(AccessibilityOverrides::default()))
}
pub fn on_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handlers.on_tap = Some(Box::new(f));
self
}
pub fn on_double_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handlers.on_double_tap = Some(Box::new(f));
self
}
pub fn on_triple_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handlers.on_triple_tap = Some(Box::new(f));
self
}
pub fn on_long_press(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handlers.on_long_press = Some(Box::new(f));
self
}
pub fn accept_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handlers.tap_buttons = Some(mask.into());
self
}
pub fn accept_double_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handlers.double_tap_buttons = Some(mask.into());
self
}
pub fn accept_triple_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handlers.triple_tap_buttons = Some(mask.into());
self
}
pub fn accept_long_press_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handlers.long_press_buttons = Some(mask.into());
self
}
pub fn on_hover(mut self, f: impl FnMut(bool, &mut EventContext) + 'static) -> Self {
self.handlers.on_hover = Some(Box::new(f));
self
}
pub fn on_key(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handlers.on_key = Some(Box::new(f));
self
}
pub fn on_key_preview(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handlers.on_key_preview = Some(Box::new(f));
self
}
pub fn on_drag(mut self, f: impl FnMut(DragPhase, &mut EventContext) + 'static) -> Self {
self.handlers.on_drag = Some(Box::new(f));
self
}
pub fn on_swipe(
mut self,
f: impl FnMut(SwipeDirection, f32, &mut EventContext) + 'static,
) -> Self {
self.handlers.on_swipe = Some(Box::new(f));
self
}
pub fn on_pinch(mut self, f: impl FnMut(PinchPhase, &mut EventContext) + 'static) -> Self {
self.handlers.on_pinch = Some(Box::new(f));
self
}
pub fn on_focus(mut self, f: impl FnMut(bool, &mut EventContext) + 'static) -> Self {
self.handlers.on_focus = Some(Box::new(f));
self
}
pub fn on_pointer_event(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handlers.on_pointer_event = Some(Box::new(f));
self
}
pub fn on_scroll(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handlers.on_scroll = Some(Box::new(f));
self
}
pub fn on_access_action(
mut self,
f: impl FnMut(accesskit::Action, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handlers.on_access_action = Some(Box::new(f));
self
}
pub fn on_access_action_request(
mut self,
f: impl FnMut(
accesskit::Action,
accesskit::NodeId,
Option<accesskit::ActionData>,
&mut EventContext,
) -> EventResponse
+ 'static,
) -> Self {
self.handlers.on_access_action_request = Some(Box::new(f));
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = Some(focusable);
self
}
pub fn cursor(mut self, cursor: CursorIcon) -> Self {
self.cursor = Some(cursor);
self
}
pub fn clips_children(mut self, clips: bool) -> Self {
self.clips_children = Some(clips);
self
}
pub fn ime_input(mut self, ctx: crate::ime::ImeContext) -> Self {
self.ime = Some(ctx);
self
}
pub fn event_pass_through(mut self, pass_through: bool) -> Self {
self.event_pass_through = Some(pass_through);
self
}
pub fn gesture_dead_zone(mut self, dead_zone: bool) -> Self {
self.gesture_dead_zone = Some(dead_zone);
self
}
pub fn keyboard_capture(mut self, capture: bool) -> Self {
self.keyboard_capture = Some(capture);
self
}
pub fn hit_transparent(mut self, transparent: bool) -> Self {
self.hit_transparent = Some(transparent);
self
}
pub fn focus_within(mut self, signal: crate::signal::Signal<bool>) -> Self {
self.focus_within = Some(signal);
self
}
pub fn hover_within(mut self, signal: crate::signal::Signal<bool>) -> Self {
self.hover_within = Some(signal);
self
}
pub fn visible_when(mut self, state: impl Into<Prop<bool>>) -> Self {
self.visible_when = Some(state.into());
self
}
pub fn context_menu(
mut self,
factory: impl Fn(Point, &mut EventContext) -> Option<Box<dyn Widget>> + 'static,
) -> Self {
self.context_menu_factory = Some(Box::new(factory));
self
}
pub fn on_drag_hover(
mut self,
f: impl FnMut(
&crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> crate::drag_state::DropFeedback
+ 'static,
) -> Self {
self.handlers.on_drag_hover = Some(Box::new(f));
self
}
pub fn on_drag_leave(mut self, f: impl FnMut(&mut EventContext) + 'static) -> Self {
self.handlers.on_drag_leave = Some(Box::new(f));
self
}
pub fn on_drag_tick(
mut self,
f: impl FnMut(teksilo_canvas::Point, &mut EventContext) + 'static,
) -> Self {
self.handlers.on_drag_tick = Some(Box::new(f));
self
}
pub fn on_drop(
mut self,
f: impl FnMut(
crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> bool
+ 'static,
) -> Self {
self.handlers.on_drop = Some(Box::new(f));
self
}
pub fn on_drag_ended(
mut self,
f: impl FnMut(crate::drag_payload::DropOutcome, &mut EventContext) + 'static,
) -> Self {
self.handlers.on_drag_ended = Some(Box::new(f));
self
}
}
impl Default for HandlerSet {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for HandlerSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HandlerSet")
.field("handlers", &self.handlers)
.field("focusable", &self.focusable)
.field("tab_index", &self.tab_index)
.field("cursor", &self.cursor)
.finish()
}
}
pub struct WidgetWithHandlers<W: Widget> {
pub(crate) widget: W,
pub(crate) handler_set: HandlerSet,
}
impl<W: Widget> WidgetWithHandlers<W> {
fn new(widget: W) -> Self {
Self {
widget,
handler_set: HandlerSet::new(),
}
}
pub(crate) fn take_handler_set(&mut self) -> HandlerSet {
std::mem::take(&mut self.handler_set)
}
pub fn on_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_tap = Some(Box::new(f));
self
}
pub fn on_double_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_double_tap = Some(Box::new(f));
self
}
pub fn on_triple_tap(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_triple_tap = Some(Box::new(f));
self
}
pub fn on_long_press(mut self, f: impl FnMut(&TapEvent, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_long_press = Some(Box::new(f));
self
}
pub fn accept_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handler_set.handlers.tap_buttons = Some(mask.into());
self
}
pub fn accept_double_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handler_set.handlers.double_tap_buttons = Some(mask.into());
self
}
pub fn accept_triple_tap_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handler_set.handlers.triple_tap_buttons = Some(mask.into());
self
}
pub fn accept_long_press_buttons(mut self, mask: impl Into<ButtonMask>) -> Self {
self.handler_set.handlers.long_press_buttons = Some(mask.into());
self
}
pub fn on_drag(mut self, f: impl FnMut(DragPhase, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_drag = Some(Box::new(f));
self
}
pub fn on_swipe(
mut self,
f: impl FnMut(SwipeDirection, f32, &mut EventContext) + 'static,
) -> Self {
self.handler_set.handlers.on_swipe = Some(Box::new(f));
self
}
pub fn on_pinch(mut self, f: impl FnMut(PinchPhase, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_pinch = Some(Box::new(f));
self
}
pub fn on_focus(mut self, f: impl FnMut(bool, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_focus = Some(Box::new(f));
self
}
pub fn on_key(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handler_set.handlers.on_key = Some(Box::new(f));
self
}
pub fn on_key_preview(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handler_set.handlers.on_key_preview = Some(Box::new(f));
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.handler_set.focusable = Some(focusable);
self
}
pub fn tab_index(mut self, index: i32) -> Self {
self.handler_set.tab_index = Some(index);
self
}
pub fn on_pointer_event(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handler_set.handlers.on_pointer_event = Some(Box::new(f));
self
}
pub fn on_hover(mut self, f: impl FnMut(bool, &mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_hover = Some(Box::new(f));
self
}
pub fn cursor(mut self, cursor: CursorIcon) -> Self {
self.handler_set.cursor = Some(cursor);
self
}
pub fn on_scroll(
mut self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handler_set.handlers.on_scroll = Some(Box::new(f));
self
}
pub fn on_access_action(
mut self,
f: impl FnMut(accesskit::Action, &mut EventContext) -> EventResponse + 'static,
) -> Self {
self.handler_set.handlers.on_access_action = Some(Box::new(f));
self
}
pub fn on_access_action_request(
mut self,
f: impl FnMut(
accesskit::Action,
accesskit::NodeId,
Option<accesskit::ActionData>,
&mut EventContext,
) -> EventResponse
+ 'static,
) -> Self {
self.handler_set.handlers.on_access_action_request = Some(Box::new(f));
self
}
pub fn clips_children(mut self, clips: bool) -> Self {
self.handler_set.clips_children = Some(clips);
self
}
pub fn ime_input(mut self, ctx: crate::ime::ImeContext) -> Self {
self.handler_set.ime = Some(ctx);
self
}
pub fn event_pass_through(mut self, pass_through: bool) -> Self {
self.handler_set.event_pass_through = Some(pass_through);
self
}
pub fn gesture_dead_zone(mut self, dead_zone: bool) -> Self {
self.handler_set.gesture_dead_zone = Some(dead_zone);
self
}
pub fn keyboard_capture(mut self, capture: bool) -> Self {
self.handler_set.keyboard_capture = Some(capture);
self
}
pub fn hit_transparent(mut self, transparent: bool) -> Self {
self.handler_set.hit_transparent = Some(transparent);
self
}
pub fn context_menu(
mut self,
factory: impl Fn(Point, &mut EventContext) -> Option<Box<dyn Widget>> + 'static,
) -> Self {
self.handler_set.context_menu_factory = Some(Box::new(factory));
self
}
pub fn focus_within(mut self, signal: crate::signal::Signal<bool>) -> Self {
self.handler_set.focus_within = Some(signal);
self
}
pub fn hover_within(mut self, signal: crate::signal::Signal<bool>) -> Self {
self.handler_set.hover_within = Some(signal);
self
}
pub fn visible_when(mut self, state: impl Into<Prop<bool>>) -> Self {
self.handler_set.visible_when = Some(state.into());
self
}
pub fn on_drag_hover(
mut self,
f: impl FnMut(
&crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> crate::drag_state::DropFeedback
+ 'static,
) -> Self {
self.handler_set.handlers.on_drag_hover = Some(Box::new(f));
self
}
pub fn on_drag_leave(mut self, f: impl FnMut(&mut EventContext) + 'static) -> Self {
self.handler_set.handlers.on_drag_leave = Some(Box::new(f));
self
}
pub fn on_drag_tick(
mut self,
f: impl FnMut(teksilo_canvas::Point, &mut EventContext) + 'static,
) -> Self {
self.handler_set.handlers.on_drag_tick = Some(Box::new(f));
self
}
pub fn on_drop(
mut self,
f: impl FnMut(
crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> bool
+ 'static,
) -> Self {
self.handler_set.handlers.on_drop = Some(Box::new(f));
self
}
pub fn on_drag_ended(
mut self,
f: impl FnMut(crate::drag_payload::DropOutcome, &mut EventContext) + 'static,
) -> Self {
self.handler_set.handlers.on_drag_ended = Some(Box::new(f));
self
}
pub fn access_label(mut self, label: impl Into<Prop<String>>) -> Self {
self.handler_set.access_mut().label = Some(label.into());
self
}
#[doc(hidden)]
pub fn access_label_literal(self, label: impl Into<String>) -> Self {
self.access_label(Prop::Static(label.into()))
}
pub fn access_description(mut self, description: impl Into<Prop<String>>) -> Self {
self.handler_set.access_mut().description = Some(description.into());
self
}
#[doc(hidden)]
pub fn access_description_literal(self, description: impl Into<String>) -> Self {
self.access_description(Prop::Static(description.into()))
}
pub fn access_hint(self, hint: impl Into<Prop<String>>) -> Self {
self.access_description(hint)
}
#[doc(hidden)]
pub fn access_hint_literal(self, hint: impl Into<String>) -> Self {
self.access_description(Prop::Static(hint.into()))
}
pub fn access_value(mut self, value: impl Into<Prop<String>>) -> Self {
self.handler_set.access_mut().value = Some(value.into());
self
}
#[doc(hidden)]
pub fn access_value_literal(self, value: impl Into<String>) -> Self {
self.access_value(Prop::Static(value.into()))
}
pub fn access_role(mut self, role: accesskit::Role) -> Self {
self.handler_set.access_mut().role = Some(role);
self
}
pub fn access_hidden(mut self, hidden: impl Into<Prop<bool>>) -> Self {
self.handler_set.access_mut().hidden = Some(hidden.into());
self
}
pub fn access_disabled(mut self, disabled: bool) -> Self {
self.handler_set.access_mut().disabled = Some(disabled);
self
}
pub fn access_identifier(mut self, id: impl Into<String>) -> Self {
self.handler_set.access_mut().identifier = Some(id.into());
self
}
pub fn access_controls(mut self, target: WidgetId) -> Self {
self.handler_set.access_mut().controls.push(target);
self
}
pub fn access_described_by(mut self, target: WidgetId) -> Self {
self.handler_set.access_mut().described_by.push(target);
self
}
pub fn access_labelled_by(mut self, target: WidgetId) -> Self {
self.handler_set.access_mut().labelled_by.push(target);
self
}
pub fn access_live(mut self, mode: accesskit::Live) -> Self {
self.handler_set.access_mut().live = Some(mode);
self
}
pub fn access_current(mut self, current: accesskit::AriaCurrent) -> Self {
self.handler_set.access_mut().aria_current = Some(current);
self
}
pub fn access_shortcut_literal(mut self, shortcut: impl Into<String>) -> Self {
self.handler_set.access_mut().shortcut = Some(shortcut.into());
self
}
pub fn access_shortcut_id(mut self, id: impl Into<String>) -> Self {
self.handler_set.access_mut().shortcut_id = Some(id.into());
self
}
pub fn access_has_popup(mut self, kind: accesskit::HasPopup) -> Self {
self.handler_set.access_mut().has_popup = Some(kind);
self
}
pub fn access_orientation(mut self, orientation: accesskit::Orientation) -> Self {
self.handler_set.access_mut().orientation = Some(orientation);
self
}
pub fn access_exclude_subtree(mut self) -> Self {
self.handler_set.access_subtree = Some(AccessSubtreeMode::Exclude);
self
}
pub fn access_merge_subtree(mut self) -> Self {
self.handler_set.access_subtree = Some(AccessSubtreeMode::Merge);
self
}
pub fn access_subtree(mut self, mode: AccessSubtreeMode) -> Self {
self.handler_set.access_subtree = Some(mode);
self
}
pub fn access_numeric_value(mut self, value: f64) -> Self {
self.handler_set.access_mut().numeric_value = Some(value);
self
}
pub fn access_numeric_range(mut self, min: f64, max: f64) -> Self {
let access = self.handler_set.access_mut();
access.min_numeric_value = Some(min);
access.max_numeric_value = Some(max);
self
}
pub fn access_numeric_step(mut self, step: f64) -> Self {
self.handler_set.access_mut().numeric_step = Some(step);
self
}
pub fn access_action<F>(mut self, action: accesskit::Action, handler: F) -> Self
where
F: FnMut(&mut EventContext) + 'static,
{
self.handler_set
.access_mut()
.actions
.push((action, Box::new(handler)));
self
}
pub fn access_remove_action(mut self, action: accesskit::Action) -> Self {
self.handler_set.access_mut().removed_actions.push(action);
self
}
pub fn access_custom_action<F>(mut self, label: impl Into<Prop<String>>, handler: F) -> Self
where
F: FnMut(&mut EventContext) + 'static,
{
self.handler_set
.access_mut()
.custom_actions
.push((label.into(), Box::new(handler)));
self
}
#[doc(hidden)]
pub fn access_custom_action_literal<F>(self, label: impl Into<String>, handler: F) -> Self
where
F: FnMut(&mut EventContext) + 'static,
{
self.access_custom_action(Prop::Static(label.into()), handler)
}
pub fn access_customize<F>(mut self, f: F) -> Self
where
F: Fn(&mut crate::accessibility::AccessNodeBuilder) + 'static,
{
self.handler_set.access_mut().customize = Some(Box::new(f));
self
}
}
impl<W: Widget> std::fmt::Debug for WidgetWithHandlers<W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WidgetWithHandlers")
.field("widget", &self.widget)
.field("handler_set", &self.handler_set)
.finish()
}
}
impl<W: Widget + 'static> Widget for WidgetWithHandlers<W> {
fn build(
&mut self,
ctx: &mut crate::build_context::BuildContext,
) -> Vec<crate::widget_id::WidgetId> {
self.widget.build(ctx)
}
fn layout_response(
&self,
proposal: teksilo_canvas::SizeProposal,
ctx: &crate::widget::LayoutContext,
) -> crate::widget::LayoutResponse {
self.widget.layout_response(proposal, ctx)
}
fn place_children(
&self,
bounds: teksilo_canvas::Rect,
proposal: teksilo_canvas::SizeProposal,
children: &mut [crate::widget::WidgetPlacement],
ctx: &crate::widget::LayoutContext,
) {
self.widget.place_children(bounds, proposal, children, ctx)
}
fn paint(
&self,
bounds: teksilo_canvas::Rect,
canvas: &mut teksilo_canvas::Canvas,
ctx: &crate::widget::PaintContext,
) {
self.widget.paint(bounds, canvas, ctx)
}
fn accessibility(&self, builder: &mut crate::accessibility::AccessNodeBuilder) {
self.widget.accessibility(builder)
}
fn children(&self) -> Vec<crate::widget_id::WidgetId> {
self.widget.children()
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
self.widget.as_any()
}
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
self.widget.as_any_mut()
}
fn clips_children(&self) -> bool {
self.handler_set
.clips_children
.unwrap_or_else(|| self.widget.clips_children())
}
fn take_handler_set(&mut self) -> Option<HandlerSet> {
Some(self.take_handler_set())
}
}
pub trait WidgetBuilder: Widget + Sized + 'static {
fn on_tap(
self,
f: impl FnMut(&TapEvent, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_tap(f)
}
fn on_double_tap(
self,
f: impl FnMut(&TapEvent, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_double_tap(f)
}
fn on_triple_tap(
self,
f: impl FnMut(&TapEvent, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_triple_tap(f)
}
fn on_long_press(
self,
f: impl FnMut(&TapEvent, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_long_press(f)
}
fn accept_tap_buttons(self, mask: impl Into<ButtonMask>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).accept_tap_buttons(mask)
}
fn accept_double_tap_buttons(self, mask: impl Into<ButtonMask>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).accept_double_tap_buttons(mask)
}
fn accept_triple_tap_buttons(self, mask: impl Into<ButtonMask>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).accept_triple_tap_buttons(mask)
}
fn accept_long_press_buttons(self, mask: impl Into<ButtonMask>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).accept_long_press_buttons(mask)
}
fn dim_when_inactive(self, factor: f32) -> crate::dim_when_inactive::DimWhenInactive {
crate::dim_when_inactive::DimWhenInactive::new()
.child(self)
.factor(factor)
}
fn dim_when_inactive_default(self) -> crate::dim_when_inactive::DimWhenInactive {
crate::dim_when_inactive::DimWhenInactive::new().child(self)
}
fn on_drag(
self,
f: impl FnMut(DragPhase, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drag(f)
}
fn on_swipe(
self,
f: impl FnMut(SwipeDirection, f32, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_swipe(f)
}
fn on_pinch(
self,
f: impl FnMut(PinchPhase, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_pinch(f)
}
fn on_focus(
self,
f: impl FnMut(bool, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_focus(f)
}
fn on_key(
self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_key(f)
}
fn on_key_preview(
self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_key_preview(f)
}
fn on_pointer_event(
self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_pointer_event(f)
}
fn on_hover(
self,
f: impl FnMut(bool, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_hover(f)
}
fn on_scroll(
self,
f: impl FnMut(&WidgetEvent, &mut EventContext) -> EventResponse + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_scroll(f)
}
fn on_access_action(
self,
f: impl FnMut(accesskit::Action, &mut EventContext) -> EventResponse + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_access_action(f)
}
fn focusable(self, focusable: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).focusable(focusable)
}
fn tab_index(self, index: i32) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).tab_index(index)
}
fn cursor(self, cursor: CursorIcon) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).cursor(cursor)
}
fn clips_children_on(self, clips: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).clips_children(clips)
}
fn ime_input(self, ctx: crate::ime::ImeContext) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).ime_input(ctx)
}
fn event_pass_through(self, pass_through: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).event_pass_through(pass_through)
}
fn gesture_dead_zone(self, dead_zone: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).gesture_dead_zone(dead_zone)
}
fn keyboard_capture(self, capture: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).keyboard_capture(capture)
}
fn hit_transparent(self, transparent: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).hit_transparent(transparent)
}
fn context_menu(
self,
factory: impl Fn(Point, &mut EventContext) -> Option<Box<dyn Widget>> + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).context_menu(factory)
}
fn focus_within(self, signal: crate::signal::Signal<bool>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).focus_within(signal)
}
fn hover_within(self, signal: crate::signal::Signal<bool>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).hover_within(signal)
}
fn visible_when(self, state: impl Into<Prop<bool>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).visible_when(state)
}
fn on_drag_hover(
self,
f: impl FnMut(
&crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> crate::drag_state::DropFeedback
+ 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drag_hover(f)
}
fn on_drag_leave(self, f: impl FnMut(&mut EventContext) + 'static) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drag_leave(f)
}
fn on_drag_tick(
self,
f: impl FnMut(teksilo_canvas::Point, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drag_tick(f)
}
fn on_drop(
self,
f: impl FnMut(
crate::drag_payload::DragPayload,
teksilo_canvas::Point,
&mut EventContext,
) -> bool
+ 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drop(f)
}
fn on_drag_ended(
self,
f: impl FnMut(crate::drag_payload::DropOutcome, &mut EventContext) + 'static,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).on_drag_ended(f)
}
fn access_label(self, label: impl Into<Prop<String>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_label(label)
}
#[doc(hidden)]
fn access_label_literal(self, label: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_label_literal(label)
}
fn access_description(self, description: impl Into<Prop<String>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_description(description)
}
#[doc(hidden)]
fn access_description_literal(
self,
description: impl Into<String>,
) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_description_literal(description)
}
fn access_hint(self, hint: impl Into<Prop<String>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_hint(hint)
}
#[doc(hidden)]
fn access_hint_literal(self, hint: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_hint_literal(hint)
}
fn access_value(self, value: impl Into<Prop<String>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_value(value)
}
#[doc(hidden)]
fn access_value_literal(self, value: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_value_literal(value)
}
fn access_role(self, role: accesskit::Role) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_role(role)
}
fn access_hidden(self, hidden: impl Into<Prop<bool>>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_hidden(hidden)
}
fn access_disabled(self, disabled: bool) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_disabled(disabled)
}
fn access_identifier(self, id: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_identifier(id)
}
fn access_controls(self, target: WidgetId) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_controls(target)
}
fn access_described_by(self, target: WidgetId) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_described_by(target)
}
fn access_labelled_by(self, target: WidgetId) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_labelled_by(target)
}
fn access_live(self, mode: accesskit::Live) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_live(mode)
}
fn access_current(self, current: accesskit::AriaCurrent) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_current(current)
}
fn access_shortcut_literal(self, shortcut: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_shortcut_literal(shortcut)
}
fn access_shortcut_id(self, id: impl Into<String>) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_shortcut_id(id)
}
fn access_has_popup(self, kind: accesskit::HasPopup) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_has_popup(kind)
}
fn access_orientation(self, orientation: accesskit::Orientation) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_orientation(orientation)
}
fn access_exclude_subtree(self) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_exclude_subtree()
}
fn access_merge_subtree(self) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_merge_subtree()
}
fn access_subtree(self, mode: AccessSubtreeMode) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_subtree(mode)
}
fn access_numeric_value(self, value: f64) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_numeric_value(value)
}
fn access_numeric_range(self, min: f64, max: f64) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_numeric_range(min, max)
}
fn access_numeric_step(self, step: f64) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_numeric_step(step)
}
fn access_action<F>(self, action: accesskit::Action, handler: F) -> WidgetWithHandlers<Self>
where
F: FnMut(&mut EventContext) + 'static,
{
WidgetWithHandlers::new(self).access_action(action, handler)
}
fn access_remove_action(self, action: accesskit::Action) -> WidgetWithHandlers<Self> {
WidgetWithHandlers::new(self).access_remove_action(action)
}
fn access_custom_action<F>(
self,
label: impl Into<Prop<String>>,
handler: F,
) -> WidgetWithHandlers<Self>
where
F: FnMut(&mut EventContext) + 'static,
{
WidgetWithHandlers::new(self).access_custom_action(label, handler)
}
#[doc(hidden)]
fn access_custom_action_literal<F>(
self,
label: impl Into<String>,
handler: F,
) -> WidgetWithHandlers<Self>
where
F: FnMut(&mut EventContext) + 'static,
{
WidgetWithHandlers::new(self).access_custom_action_literal(label, handler)
}
fn access_customize<F>(self, f: F) -> WidgetWithHandlers<Self>
where
F: Fn(&mut crate::accessibility::AccessNodeBuilder) + 'static,
{
WidgetWithHandlers::new(self).access_customize(f)
}
}
impl<W: Widget + Sized + 'static> WidgetBuilder for W {}
#[cfg(test)]
mod tests {
use super::*;
use crate::widget::WidgetPlacement;
use crate::widget_id::WidgetId;
use crate::widget_tree::WidgetTree;
#[derive(Debug)]
struct CompositeLeaf {
child_id: Option<WidgetId>,
}
impl CompositeLeaf {
fn new() -> Self {
Self { child_id: None }
}
}
impl Widget for CompositeLeaf {
fn build(&mut self, ctx: &mut crate::build_context::BuildContext) -> Vec<WidgetId> {
let child = ctx.add(crate::test_widgets::FillWidget::new());
self.child_id = Some(child);
vec![child]
}
fn layout_response(
&self,
proposal: teksilo_canvas::SizeProposal,
_ctx: &crate::widget::LayoutContext,
) -> crate::widget::LayoutResponse {
proposal.resolve(120.0, 40.0).into()
}
fn place_children(
&self,
bounds: teksilo_canvas::Rect,
_proposal: teksilo_canvas::SizeProposal,
children: &mut [WidgetPlacement],
_ctx: &crate::widget::LayoutContext,
) {
for child in children.iter_mut() {
child.origin = bounds.origin();
child.size = bounds.size();
}
}
fn children(&self) -> Vec<WidgetId> {
self.child_id.into_iter().collect()
}
}
#[test]
fn external_handlers_survive_rebuild() {
use std::cell::Cell;
use std::rc::Rc;
let tap_count = Rc::new(Cell::new(0_u32));
let tc = tap_count.clone();
let mut tree = WidgetTree::new();
let id = tree.add(CompositeLeaf::new().on_tap(move |_pos, _ctx| {
tc.set(tc.get() + 1);
}));
tree.layout(teksilo_canvas::SizeProposal::exact(200.0, 100.0));
tree.arena_mark_needs_rebuild_for_testing(id);
tree.layout(teksilo_canvas::SizeProposal::exact(200.0, 100.0));
tree.click(id);
assert_eq!(
tap_count.get(),
1,
"externally-attached on_tap must survive a rebuild"
);
}
#[test]
fn wrapped_composite_widget_still_builds_children() {
let mut tree = WidgetTree::new();
let root = tree.add(CompositeLeaf::new().on_tap(|_pos, _ctx| {}));
tree.layout(teksilo_canvas::SizeProposal::exact(200.0, 100.0));
assert_eq!(tree.children(root).len(), 1);
}
#[derive(Debug)]
struct Reflective {
marker: u32,
}
impl Widget for Reflective {
fn layout_response(
&self,
proposal: teksilo_canvas::SizeProposal,
_ctx: &crate::widget::LayoutContext,
) -> crate::widget::LayoutResponse {
proposal.resolve(0.0, 0.0).into()
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
Some(self)
}
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
Some(self)
}
}
#[test]
fn both_downcast_hooks_see_through_the_handler_wrapper() {
let mut wrapped = Reflective { marker: 7 }.focusable(true);
let seen = wrapped
.as_any()
.and_then(|a| a.downcast_ref::<Reflective>())
.map(|r| r.marker);
assert_eq!(seen, Some(7), "as_any must forward through the wrapper");
let seen_mut = wrapped
.as_any_mut()
.and_then(|a| a.downcast_mut::<Reflective>())
.map(|r| r.marker);
assert_eq!(
seen_mut,
Some(7),
"as_any_mut must forward through the wrapper too"
);
}
}