Skip to content

Commit

Permalink
feat: Focus-based keyboard events (#877)
Browse files Browse the repository at this point in the history
* feat: Rename `keydown` to `globalkeydown`

* feat: Rename `keyup` to `globalkeyup`

* feat: Focus-based keydown and keyup events

* chore: Fix tests
  • Loading branch information
marc2332 authored Sep 12, 2024
1 parent eec6603 commit a92983a
Show file tree
Hide file tree
Showing 28 changed files with 157 additions and 97 deletions.
6 changes: 3 additions & 3 deletions crates/components/src/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ pub fn Button(
status.set(ButtonStatus::default());
};

let onkeydown = move |ev: KeyboardEvent| {
if focus.validate_keydown(&ev) {
let onglobalkeydown = move |ev: KeyboardEvent| {
if focus.validate_globalkeydown(&ev) {
if let Some(onpress) = &onpress {
onpress.call(PressEvent::Key(ev))
}
Expand All @@ -174,7 +174,7 @@ pub fn Button(
onpointerup,
onmouseenter,
onmouseleave,
onkeydown,
onglobalkeydown,
a11y_id,
width: "{width}",
height: "{height}",
Expand Down
10 changes: 5 additions & 5 deletions crates/components/src/dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ where
status.set(DropdownItemStatus::default());
};

let onkeydown = {
let onglobalkeydown = {
to_owned![onclick];
move |ev: KeyboardEvent| {
if ev.key == Key::Enter && is_focused {
Expand Down Expand Up @@ -124,7 +124,7 @@ where
onmouseenter,
onmouseleave,
onclick,
onkeydown,
onglobalkeydown,
{children}
}
)
Expand Down Expand Up @@ -217,7 +217,7 @@ where
opened.set(true)
};

let onkeydown = move |e: KeyboardEvent| {
let onglobalkeydown = move |e: KeyboardEvent| {
match e.key {
// Close when `Escape` key is pressed
Key::Escape => {
Expand Down Expand Up @@ -267,7 +267,7 @@ where
onmouseenter,
onmouseleave,
onclick,
onkeydown,
onglobalkeydown,
margin: "{margin}",
a11y_id,
background: "{button_background}",
Expand Down Expand Up @@ -298,7 +298,7 @@ where
width: "100v",
rect {
onglobalclick,
onkeydown,
onglobalkeydown,
layer: "-99",
margin: "{margin}",
border: "1 solid {border_fill}",
Expand Down
12 changes: 6 additions & 6 deletions crates/components/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,16 @@ pub fn Input(
});

let onkeydown = move |e: Event<KeyboardData>| {
if is_focused && e.data.key != Key::Enter {
if e.data.key != Key::Enter && e.data.key != Key::Tab {
e.stop_propagation();
editable.process_event(&EditableEvent::KeyDown(e.data));
onchange.call(editable.editor().peek().to_string());
}
};

let onkeyup = move |e: Event<KeyboardData>| {
if is_focused {
editable.process_event(&EditableEvent::KeyUp(e.data));
}
e.stop_propagation();
editable.process_event(&EditableEvent::KeyUp(e.data));
};

let onmousedown = move |e: MouseEvent| {
Expand Down Expand Up @@ -215,10 +215,10 @@ pub fn Input(
a11y_focusable: "true",
a11y_role:"textInput",
main_align: "center",
onkeydown,
onkeyup,
paragraph {
margin: "8 12",
onkeydown,
onkeyup,
onglobalclick,
onmouseenter,
onmouseleave,
Expand Down
5 changes: 3 additions & 2 deletions crates/components/src/native_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ pub fn NativeContainer(children: Element) -> Element {
let mut native_platform = use_init_native_platform();
let platform = use_platform();

let onkeydown = move |e: KeyboardEvent| {
let onglobalkeydown = move |e: KeyboardEvent| {
let allowed_to_navigate = native_platform.navigation_mark.peek().allowed();
println!(">> {allowed_to_navigate}");
if e.key == Key::Tab && allowed_to_navigate {
if e.modifiers.contains(Modifiers::SHIFT) {
platform
Expand All @@ -35,7 +36,7 @@ pub fn NativeContainer(children: Element) -> Element {
rsx!(rect {
width: "100%",
height: "100%",
onkeydown,
onglobalkeydown,
{children}
})
}
8 changes: 4 additions & 4 deletions crates/components/src/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub fn Popup(
}
};

let onkeydown = move |event: KeyboardEvent| {
let onglobalkeydown = move |event: KeyboardEvent| {
if close_on_escape_key && event.key == Key::Escape {
request_to_close()
}
Expand All @@ -118,7 +118,7 @@ pub fn Popup(
shadow: "0 4 5 0 rgb(0, 0, 0, 30)",
width: "{width}",
height: "{height}",
onkeydown,
onglobalkeydown,
if show_close_button {
rect {
height: "0",
Expand Down Expand Up @@ -229,7 +229,7 @@ mod test {
// Open the popup
utils.click_cursor((15., 15.)).await;

// Send a random keydown event
// Send a random globalkeydown event
utils.push_event(PlatformEvent::Keyboard {
name: EventName::KeyDown,
key: Key::ArrowDown,
Expand All @@ -240,7 +240,7 @@ mod test {
// Check the popup is still open
assert_eq!(utils.sdom().get().layout().size(), 10);

// Send a ESC keydown event
// Send a ESC globalkeydown event
utils.push_event(PlatformEvent::Keyboard {
name: EventName::KeyDown,
key: Key::Escape,
Expand Down
8 changes: 4 additions & 4 deletions crates/components/src/scroll_views/scroll_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ pub fn ScrollView(
}
};

let onkeydown = move |e: KeyboardEvent| {
let onglobalkeydown = move |e: KeyboardEvent| {
match &e.key {
Key::Shift => {
clicking_shift.set(true);
Expand Down Expand Up @@ -319,7 +319,7 @@ pub fn ScrollView(
};
};

let onkeyup = move |e: KeyboardEvent| {
let onglobalkeyup = move |e: KeyboardEvent| {
if e.key == Key::Shift {
clicking_shift.set(false);
} else if e.key == Key::Alt {
Expand Down Expand Up @@ -379,8 +379,8 @@ pub fn ScrollView(
height,
onglobalclick: onclick,
onglobalmousemove: onmousemove,
onkeydown,
onkeyup,
onglobalkeydown,
onglobalkeyup,
a11y_id,
rect {
direction: "vertical",
Expand Down
8 changes: 4 additions & 4 deletions crates/components/src/scroll_views/virtual_scroll_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ pub fn VirtualScrollView<
}
};

let onkeydown = move |e: KeyboardEvent| {
let onglobalkeydown = move |e: KeyboardEvent| {
match &e.key {
Key::Shift => {
clicking_shift.set(true);
Expand Down Expand Up @@ -365,7 +365,7 @@ pub fn VirtualScrollView<
};
};

let onkeyup = move |e: KeyboardEvent| {
let onglobalkeyup = move |e: KeyboardEvent| {
if e.key == Key::Shift {
clicking_shift.set(false);
} else if e.key == Key::Alt {
Expand Down Expand Up @@ -454,8 +454,8 @@ pub fn VirtualScrollView<
height: "{height}",
onglobalclick: onclick,
onglobalmousemove: onmousemove,
onkeydown,
onkeyup,
onglobalkeydown,
onglobalkeyup,
a11y_id,
rect {
direction: "vertical",
Expand Down
6 changes: 3 additions & 3 deletions crates/components/src/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ pub fn Switch(props: SwitchProps) -> Element {
props.ontoggled.call(());
};

let onkeydown = move |e: KeyboardEvent| {
if focus.validate_keydown(&e) {
let onglobalkeydown = move |e: KeyboardEvent| {
if focus.validate_globalkeydown(&e) {
props.ontoggled.call(());
}
};
Expand Down Expand Up @@ -169,7 +169,7 @@ pub fn Switch(props: SwitchProps) -> Element {
onmousedown,
onmouseenter,
onmouseleave,
onkeydown,
onglobalkeydown,
onclick,
a11y_id,
offset_x: "{offset_x}",
Expand Down
3 changes: 3 additions & 0 deletions crates/components/src/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub fn Tile(
onselect: Option<EventHandler<()>>,
/// Theme override.
theme: Option<TileThemeWith>,

a11y_name: Option<String>,
) -> Element {
let mut focus = use_focus();
let mut status = use_signal(TileStatus::default);
Expand Down Expand Up @@ -70,6 +72,7 @@ pub fn Tile(

rsx!(
rect {
a11y_name,
onclick,
onmouseenter,
onmouseleave,
Expand Down
4 changes: 4 additions & 0 deletions crates/core/src/accessibility/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ impl AccessibilityTree {
}
}

pub fn focused_node_id(&self) -> Option<NodeId> {
self.map.get(&self.focused_id).cloned()
}

/// Initialize the Accessibility Tree
pub fn init(
&self,
Expand Down
19 changes: 12 additions & 7 deletions crates/core/src/events/events_measurer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ pub fn process_events(
event_emitter: &EventEmitter,
nodes_state: &mut NodesState,
scale_factor: f64,

focus_id: Option<NodeId>,
) {
// 1. Get global events created from the incoming events
let global_events = measure_global_events(events);

// 2. Get potential events that could be emitted based on the elements layout and viewports
let potential_events = measure_potential_event_listeners(events, dom, scale_factor);
let potential_events = measure_potential_event_listeners(events, dom, scale_factor, focus_id);

// 3. Get what events can be actually emitted based on what elements are listening
let mut dom_events = measure_dom_events(&potential_events, dom, nodes_state, scale_factor);
Expand Down Expand Up @@ -102,6 +104,7 @@ pub fn measure_potential_event_listeners(
events: &EventsQueue,
fdom: &FreyaDOM,
scale_factor: f64,
focus_id: Option<NodeId>,
) -> PotentialEvents {
let mut potential_events = PotentialEvents::default();

Expand All @@ -116,12 +119,14 @@ pub fn measure_potential_event_listeners(
if let Some(layout_node) = layout_node {
'events: for event in events.iter() {
if let PlatformEvent::Keyboard { name, .. } = event {
let event_data = PotentialEvent {
node_id: *node_id,
layer: Some(*layer),
event: event.clone(),
};
potential_events.entry(*name).or_default().push(event_data);
if focus_id == Some(*node_id) {
let event_data = PotentialEvent {
node_id: *node_id,
layer: Some(*layer),
event: event.clone(),
};
potential_events.entry(*name).or_default().push(event_data);
}
} else {
let data = match event {
PlatformEvent::Mouse { name, cursor, .. } => Some((name, cursor)),
Expand Down
16 changes: 16 additions & 0 deletions crates/elements/src/_docs/events/globalkeydown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
The `globalkeydown` event fires when the user starts pressing any key.

Event Data: [`KeyboardData`](crate::events::KeyboardData)

### Example

```rust, no_run
# use freya::prelude::*;
fn app() -> Element {
rsx!(
rect {
onglobalkeydown: |e| println!("Event: {e:?}")
}
)
}
```
16 changes: 16 additions & 0 deletions crates/elements/src/_docs/events/globalkeyup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
The `globalkeyup` event fires when the user releases any key being pressed.

Event Data: [`KeyboardData`](crate::events::KeyboardData)

### Example

```rust, no_run
# use freya::prelude::*;
fn app() -> Element {
rsx!(
rect {
onglobalkeyup: |e| println!("Event: {e:?}")
}
)
}
```
2 changes: 1 addition & 1 deletion crates/elements/src/_docs/events/keydown.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The `keydown` event fires when the user starts pressing any key.
The `keydown` event fires when the user starts pressing any key in the currently focused element.

Event Data: [`KeyboardData`](crate::events::KeyboardData)

Expand Down
2 changes: 1 addition & 1 deletion crates/elements/src/_docs/events/keyup.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The `keyup` event fires when the user releases any key being pressed.
The `keyup` event fires when the user releases any key being pressed in the currently focused element.

Event Data: [`KeyboardData`](crate::events::KeyboardData)

Expand Down
8 changes: 6 additions & 2 deletions crates/elements/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,10 +611,14 @@ pub mod events {
impl_event! [
KeyboardData;

#[doc = include_str!("_docs/events/keydown.md")]
onkeydown
#[doc = include_str!("_docs/events/keyup.md")]

onkeyup

#[doc = include_str!("_docs/events/globalkeydown.md")]
onglobalkeydown
#[doc = include_str!("_docs/events/globalkeyup.md")]
onglobalkeyup
];

impl_event! [
Expand Down
6 changes: 3 additions & 3 deletions crates/hooks/src/use_focus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ impl UseFocus {
.ok();
}

/// Validate keydown event
pub fn validate_keydown(&self, e: &KeyboardEvent) -> bool {
/// Validate globalkeydown event
pub fn validate_globalkeydown(&self, e: &KeyboardEvent) -> bool {
e.data.code == Code::Enter && self.is_selected()
}

/// Prevent navigating the accessible nodes with the keyboard.
/// You must use this this inside of a `onkeydown` event handler.
/// You must use this this inside of a `onglobalkeydown` event handler.
pub fn prevent_navigation(&mut self) {
self.navigation_mark.write().set_allowed(false);
}
Expand Down
Loading

0 comments on commit a92983a

Please sign in to comment.