From 6b07a4290e3c534e80b83fb486284110cc1d004d Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 26 Jan 2024 15:23:19 +0100 Subject: [PATCH 01/29] chore: Fix docs typo --- book/src/guides/style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/guides/style.md b/book/src/guides/style.md index a9184d8e3..2053e54de 100644 --- a/book/src/guides/style.md +++ b/book/src/guides/style.md @@ -126,7 +126,7 @@ Compatible elements: [`rect`](/guides/elements.html#rect) The attributes that have colors as values can use the following syntax: #### Static colors -- `rect` +- `red` - `blue` - `green` - `yellow` From 143e77b173e94c83a8a5cbb3f95bcb059f43094f Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sat, 27 Jan 2024 18:03:10 +0100 Subject: [PATCH 02/29] clean up --- crates/hooks/src/use_animation.rs | 130 ------------------ crates/hooks/src/use_animation_transition.rs | 60 -------- crates/hooks/tests/use_animation.rs | 126 +++++++++++++++++ .../hooks/tests/use_animation_transition.rs | 56 ++++++++ 4 files changed, 182 insertions(+), 190 deletions(-) create mode 100644 crates/hooks/tests/use_animation.rs create mode 100644 crates/hooks/tests/use_animation_transition.rs diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index b4d0345a1..f9455e9a1 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -118,133 +118,3 @@ pub fn use_animation(init_value: impl FnOnce() -> f64) -> AnimationManager { } }) } - -#[cfg(test)] -mod test { - use std::time::Duration; - - use crate::{use_animation, Animation}; - use dioxus_core::use_hook; - use dioxus_hooks::to_owned; - use freya::prelude::*; - use freya_testing::{events::pointer::MouseButton, launch_test, FreyaEvent}; - use tokio::time::sleep; - - #[tokio::test] - pub async fn track_progress() { - fn use_animation_app() -> Element { - let mut animation = use_animation(|| 0.0); - - let progress = animation.value(); - - let _ = use_memo(move || { - animation.start(Animation::new_linear(0.0..=100.0, 50)); - }); - - rsx!(rect { - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); - - // State somewhere in the middle - sleep(Duration::from_millis(15)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 0.0); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State in the end - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 100.0); - } - - #[tokio::test] - pub async fn restart_progress() { - fn use_animation_app() -> Element { - let mut animation = use_animation(|| 10.0); - - let progress = animation.value(); - - let mut restart = { - to_owned![animation]; - move || { - animation.clear(); - } - }; - - use_hook(|| { - animation.start(Animation::new_linear(10.0..=100.0, 50)); - }); - - rsx!(rect { - background: "white", - height: "100%", - onclick: move |_| restart(), - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 10.0); - - // State somewhere in the middle - sleep(Duration::from_millis(32)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 10.0); - - // Trigger the click event to restart the animation - utils.push_event(FreyaEvent::Mouse { - name: "click".to_string(), - cursor: (5.0, 5.0).into(), - button: Some(MouseButton::Left), - }); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State has been restarted - utils.wait_for_update().await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 10.0); - } - - #[test] - pub fn animation_mode_settings() { - let anim = Animation::new_sine_in_out(7.0..=99.0, 500); - assert_eq!(anim.duration(), 500); - assert_eq!(anim.initial_value(), 7.0); - assert_eq!(anim.final_value(), 99.0); - } -} diff --git a/crates/hooks/src/use_animation_transition.rs b/crates/hooks/src/use_animation_transition.rs index 1ca3e9b43..0af3f9c42 100644 --- a/crates/hooks/src/use_animation_transition.rs +++ b/crates/hooks/src/use_animation_transition.rs @@ -299,63 +299,3 @@ fn animations_map(animations: &[Transition]) -> Vec { .map(TransitionState::from) .collect::>() } - -#[cfg(test)] -mod test { - use std::time::Duration; - - use crate::{use_animation_transition, Transition, TransitionAnimation}; - use dioxus_core::use_hook; - use freya::prelude::*; - use freya_testing::launch_test; - use tokio::time::sleep; - - #[tokio::test] - pub async fn track_progress() { - fn use_animation_transition_app() -> Element { - let mut animation = - use_animation_transition(TransitionAnimation::new_linear(50), (), |_| { - vec![Transition::new_size(0.0, 100.0)] - }); - - let progress = animation.get(0).unwrap().as_size(); - - use_hook(move || { - animation.start(); - }); - - rsx!(rect { - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_transition_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); - - // State somewhere in the middle - sleep(Duration::from_millis(15)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 0.0); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State in the end - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 100.0); - } -} diff --git a/crates/hooks/tests/use_animation.rs b/crates/hooks/tests/use_animation.rs new file mode 100644 index 000000000..438946aab --- /dev/null +++ b/crates/hooks/tests/use_animation.rs @@ -0,0 +1,126 @@ +use std::time::Duration; + +use crate::{use_animation, Animation}; +use dioxus_core::use_hook; +use dioxus_hooks::to_owned; +use freya::prelude::*; +use freya_testing::{events::pointer::MouseButton, launch_test, FreyaEvent}; +use tokio::time::sleep; + +#[tokio::test] +pub async fn track_progress() { + fn use_animation_app() -> Element { + let mut animation = use_animation(|| 0.0); + + let progress = animation.value(); + + let _ = use_memo(move || { + animation.start(Animation::new_linear(0.0..=100.0, 50)); + }); + + rsx!(rect { + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); + + // State somewhere in the middle + sleep(Duration::from_millis(15)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 0.0); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State in the end + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 100.0); +} + +#[tokio::test] +pub async fn restart_progress() { + fn use_animation_app() -> Element { + let mut animation = use_animation(|| 10.0); + + let progress = animation.value(); + + let mut restart = { + to_owned![animation]; + move || { + animation.clear(); + } + }; + + use_hook(|| { + animation.start(Animation::new_linear(10.0..=100.0, 50)); + }); + + rsx!(rect { + background: "white", + height: "100%", + onclick: move |_| restart(), + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 10.0); + + // State somewhere in the middle + sleep(Duration::from_millis(32)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 10.0); + + // Trigger the click event to restart the animation + utils.push_event(FreyaEvent::Mouse { + name: "click".to_string(), + cursor: (5.0, 5.0).into(), + button: Some(MouseButton::Left), + }); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State has been restarted + utils.wait_for_update().await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 10.0); +} + +#[test] +pub fn animation_mode_settings() { + let anim = Animation::new_sine_in_out(7.0..=99.0, 500); + assert_eq!(anim.duration(), 500); + assert_eq!(anim.initial_value(), 7.0); + assert_eq!(anim.final_value(), 99.0); +} \ No newline at end of file diff --git a/crates/hooks/tests/use_animation_transition.rs b/crates/hooks/tests/use_animation_transition.rs new file mode 100644 index 000000000..6074d76c8 --- /dev/null +++ b/crates/hooks/tests/use_animation_transition.rs @@ -0,0 +1,56 @@ +use std::time::Duration; + + use crate::{use_animation_transition, Transition, TransitionAnimation}; + use dioxus_core::use_hook; + use freya::prelude::*; + use freya_testing::launch_test; + use tokio::time::sleep; + + #[tokio::test] + pub async fn track_progress() { + fn use_animation_transition_app() -> Element { + let mut animation = + use_animation_transition(TransitionAnimation::new_linear(50), (), |_| { + vec![Transition::new_size(0.0, 100.0)] + }); + + let progress = animation.get(0).unwrap().as_size(); + + use_hook(move || { + animation.start(); + }); + + rsx!(rect { + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_transition_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); + + // State somewhere in the middle + sleep(Duration::from_millis(15)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 0.0); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State in the end + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 100.0); + } \ No newline at end of file From 6842e8c1b16c31bdda84efe8ebbd93440a31ac47 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sat, 27 Jan 2024 18:04:03 +0100 Subject: [PATCH 03/29] docs: Fix typo --- book/src/guides/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/guides/testing.md b/book/src/guides/testing.md index aa2d05d9e..0cf151773 100644 --- a/book/src/guides/testing.md +++ b/book/src/guides/testing.md @@ -139,7 +139,7 @@ async fn test() { let mut utils = launch_test_with_config( our_component, - TestingConfig::default().with_size((500.0, 800.0).into()), + *TestingConfig::default().with_size((500.0, 800.0).into()), ); let root = utils.root(); From 684abd62e598c347cf9a3cd5f20be6e2476931f9 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sat, 27 Jan 2024 18:14:09 +0100 Subject: [PATCH 04/29] clean up --- crates/core/src/events/events_measurer.rs | 6 +- crates/core/src/layout/viewports.rs | 1 + crates/core/src/lib.rs | 2 +- crates/core/src/navigation_mode.rs | 1 + crates/core/src/plugins.rs | 1 + crates/core/src/types.rs | 16 +- crates/hooks/tests/use_animation.rs | 252 +++++++++--------- .../hooks/tests/use_animation_transition.rs | 112 ++++---- crates/renderer/src/app.rs | 15 +- crates/renderer/src/window.rs | 4 +- crates/testing/src/test_handler.rs | 6 - 11 files changed, 205 insertions(+), 211 deletions(-) diff --git a/crates/core/src/events/events_measurer.rs b/crates/core/src/events/events_measurer.rs index 2361d3600..edcf40bbb 100644 --- a/crates/core/src/events/events_measurer.rs +++ b/crates/core/src/events/events_measurer.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashMap; pub use crate::events::{DomEvent, ElementsState, FreyaEvent}; -use crate::types::{EventEmitter, EventsQueue, NodesEvents}; +use crate::types::{EventEmitter, EventsQueue, PotentialEvents}; /// Process the events and emit them to the VirtualDOM pub fn process_events( @@ -80,7 +80,7 @@ pub fn measure_potential_event_listeners( events: &EventsQueue, viewports: &Viewports, fdom: &FreyaDOM, -) -> NodesEvents { +) -> PotentialEvents { let mut potential_events = FxHashMap::default(); let layout = fdom.layout(); @@ -183,7 +183,7 @@ const LAST_CAPTURED_EVENTS: [&str; 3] = ["click", "touchstart", "touchend"]; /// Measure what DOM events could be emited fn measure_dom_events( - potential_events: &mut NodesEvents, + potential_events: &mut PotentialEvents, fdom: &FreyaDOM, scale_factor: f64, ) -> Vec { diff --git a/crates/core/src/layout/viewports.rs b/crates/core/src/layout/viewports.rs index d22a3a74f..f34129208 100644 --- a/crates/core/src/layout/viewports.rs +++ b/crates/core/src/layout/viewports.rs @@ -12,6 +12,7 @@ use freya_dom::prelude::FreyaDOM; use freya_node_state::{OverflowMode, Style}; use rustc_hash::FxHashMap; +/// Viewports of all elegible DOM elements. #[derive(Default)] pub struct Viewports { viewports: FxHashMap, Vec)>, diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 4b56080d3..12058352a 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -23,5 +23,5 @@ pub mod prelude { pub use crate::types::EventsQueue; pub use crate::types::FocusReceiver; pub use crate::types::FocusSender; - pub use crate::types::NodesEvents; + pub use crate::types::PotentialEvents; } diff --git a/crates/core/src/navigation_mode.rs b/crates/core/src/navigation_mode.rs index 75c80aa3e..8d97f832a 100644 --- a/crates/core/src/navigation_mode.rs +++ b/crates/core/src/navigation_mode.rs @@ -8,6 +8,7 @@ pub enum NavigationMode { NotKeyboard, } +/// Manages the navigation mode. #[derive(Clone)] pub struct NavigatorState { _setter: Arc>, diff --git a/crates/core/src/plugins.rs b/crates/core/src/plugins.rs index 573945cb9..556d6bb38 100644 --- a/crates/core/src/plugins.rs +++ b/crates/core/src/plugins.rs @@ -6,6 +6,7 @@ use winit::window::Window; use crate::layout::Viewports; +/// Manages all loaded plugins. #[derive(Default)] pub struct PluginsManager { plugins: Vec>, diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index b3369e605..e2a997e7b 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -1,15 +1,25 @@ use crate::events::{DomEvent, FreyaEvent}; -use dioxus_native_core::NodeId; - use accesskit::NodeId as AccessibilityId; +use dioxus_native_core::NodeId; use rustc_hash::FxHashMap; use smallvec::SmallVec; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::sync::watch; +/// Send focus updates to the Accessibility provider. pub type FocusSender = watch::Sender>; + +/// Receive updates by the platform of the focused elements pub type FocusReceiver = watch::Receiver>; + +/// Emit events to the VirtualDOM pub type EventEmitter = UnboundedSender; + +/// Receive events to be emitted to the VirtualDOM pub type EventReceiver = UnboundedReceiver; + +/// Queued list of events to be processed by Freya. pub type EventsQueue = SmallVec<[FreyaEvent; 2]>; -pub type NodesEvents = FxHashMap>; + +/// Potential events that might be emitted. +pub type PotentialEvents = FxHashMap>; diff --git a/crates/hooks/tests/use_animation.rs b/crates/hooks/tests/use_animation.rs index 438946aab..bba64b8d1 100644 --- a/crates/hooks/tests/use_animation.rs +++ b/crates/hooks/tests/use_animation.rs @@ -1,126 +1,126 @@ -use std::time::Duration; - -use crate::{use_animation, Animation}; -use dioxus_core::use_hook; -use dioxus_hooks::to_owned; -use freya::prelude::*; -use freya_testing::{events::pointer::MouseButton, launch_test, FreyaEvent}; -use tokio::time::sleep; - -#[tokio::test] -pub async fn track_progress() { - fn use_animation_app() -> Element { - let mut animation = use_animation(|| 0.0); - - let progress = animation.value(); - - let _ = use_memo(move || { - animation.start(Animation::new_linear(0.0..=100.0, 50)); - }); - - rsx!(rect { - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); - - // State somewhere in the middle - sleep(Duration::from_millis(15)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 0.0); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State in the end - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 100.0); -} - -#[tokio::test] -pub async fn restart_progress() { - fn use_animation_app() -> Element { - let mut animation = use_animation(|| 10.0); - - let progress = animation.value(); - - let mut restart = { - to_owned![animation]; - move || { - animation.clear(); - } - }; - - use_hook(|| { - animation.start(Animation::new_linear(10.0..=100.0, 50)); - }); - - rsx!(rect { - background: "white", - height: "100%", - onclick: move |_| restart(), - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 10.0); - - // State somewhere in the middle - sleep(Duration::from_millis(32)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 10.0); - - // Trigger the click event to restart the animation - utils.push_event(FreyaEvent::Mouse { - name: "click".to_string(), - cursor: (5.0, 5.0).into(), - button: Some(MouseButton::Left), - }); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State has been restarted - utils.wait_for_update().await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 10.0); -} - -#[test] -pub fn animation_mode_settings() { - let anim = Animation::new_sine_in_out(7.0..=99.0, 500); - assert_eq!(anim.duration(), 500); - assert_eq!(anim.initial_value(), 7.0); - assert_eq!(anim.final_value(), 99.0); -} \ No newline at end of file +use std::time::Duration; + +use crate::{use_animation, Animation}; +use dioxus_core::use_hook; +use dioxus_hooks::to_owned; +use freya::prelude::*; +use freya_testing::{events::pointer::MouseButton, launch_test, FreyaEvent}; +use tokio::time::sleep; + +#[tokio::test] +pub async fn track_progress() { + fn use_animation_app() -> Element { + let mut animation = use_animation(|| 0.0); + + let progress = animation.value(); + + let _ = use_memo(move || { + animation.start(Animation::new_linear(0.0..=100.0, 50)); + }); + + rsx!(rect { + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); + + // State somewhere in the middle + sleep(Duration::from_millis(15)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 0.0); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State in the end + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 100.0); +} + +#[tokio::test] +pub async fn restart_progress() { + fn use_animation_app() -> Element { + let mut animation = use_animation(|| 10.0); + + let progress = animation.value(); + + let mut restart = { + to_owned![animation]; + move || { + animation.clear(); + } + }; + + use_hook(|| { + animation.start(Animation::new_linear(10.0..=100.0, 50)); + }); + + rsx!(rect { + background: "white", + height: "100%", + onclick: move |_| restart(), + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 10.0); + + // State somewhere in the middle + sleep(Duration::from_millis(32)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 10.0); + + // Trigger the click event to restart the animation + utils.push_event(FreyaEvent::Mouse { + name: "click".to_string(), + cursor: (5.0, 5.0).into(), + button: Some(MouseButton::Left), + }); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State has been restarted + utils.wait_for_update().await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 10.0); +} + +#[test] +pub fn animation_mode_settings() { + let anim = Animation::new_sine_in_out(7.0..=99.0, 500); + assert_eq!(anim.duration(), 500); + assert_eq!(anim.initial_value(), 7.0); + assert_eq!(anim.final_value(), 99.0); +} diff --git a/crates/hooks/tests/use_animation_transition.rs b/crates/hooks/tests/use_animation_transition.rs index 6074d76c8..e9f6713e4 100644 --- a/crates/hooks/tests/use_animation_transition.rs +++ b/crates/hooks/tests/use_animation_transition.rs @@ -1,56 +1,56 @@ -use std::time::Duration; - - use crate::{use_animation_transition, Transition, TransitionAnimation}; - use dioxus_core::use_hook; - use freya::prelude::*; - use freya_testing::launch_test; - use tokio::time::sleep; - - #[tokio::test] - pub async fn track_progress() { - fn use_animation_transition_app() -> Element { - let mut animation = - use_animation_transition(TransitionAnimation::new_linear(50), (), |_| { - vec![Transition::new_size(0.0, 100.0)] - }); - - let progress = animation.get(0).unwrap().as_size(); - - use_hook(move || { - animation.start(); - }); - - rsx!(rect { - width: "{progress}", - }) - } - - let mut utils = launch_test(use_animation_transition_app); - - // Disable event loop ticker - utils.config().enable_ticker(false); - - // Initial state - utils.wait_for_update().await; - - assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); - - // State somewhere in the middle - sleep(Duration::from_millis(15)).await; - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert!(width > 0.0); - - // Enable event loop ticker - utils.config().enable_ticker(true); - - // Already finished - sleep(Duration::from_millis(50)).await; - - // State in the end - utils.wait_for_update().await; - - let width = utils.root().get(0).layout().unwrap().width(); - assert_eq!(width, 100.0); - } \ No newline at end of file +use std::time::Duration; + +use crate::{use_animation_transition, Transition, TransitionAnimation}; +use dioxus_core::use_hook; +use freya::prelude::*; +use freya_testing::launch_test; +use tokio::time::sleep; + +#[tokio::test] +pub async fn track_progress() { + fn use_animation_transition_app() -> Element { + let mut animation = + use_animation_transition(TransitionAnimation::new_linear(50), (), |_| { + vec![Transition::new_size(0.0, 100.0)] + }); + + let progress = animation.get(0).unwrap().as_size(); + + use_hook(move || { + animation.start(); + }); + + rsx!(rect { + width: "{progress}", + }) + } + + let mut utils = launch_test(use_animation_transition_app); + + // Disable event loop ticker + utils.config().enable_ticker(false); + + // Initial state + utils.wait_for_update().await; + + assert_eq!(utils.root().get(0).layout().unwrap().width(), 0.0); + + // State somewhere in the middle + sleep(Duration::from_millis(15)).await; + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert!(width > 0.0); + + // Enable event loop ticker + utils.config().enable_ticker(true); + + // Already finished + sleep(Duration::from_millis(50)).await; + + // State in the end + utils.wait_for_update().await; + + let width = utils.root().get(0).layout().unwrap().width(); + assert_eq!(width, 100.0); +} diff --git a/crates/renderer/src/app.rs b/crates/renderer/src/app.rs index 97968bb91..2dbbd1a27 100644 --- a/crates/renderer/src/app.rs +++ b/crates/renderer/src/app.rs @@ -42,33 +42,22 @@ fn winit_waker(proxy: &EventLoopProxy) -> std::task::Waker { pub struct App { sdom: SafeDOM, vdom: VirtualDom, - events: EventsQueue, - vdom_waker: Waker, proxy: EventLoopProxy, mutations_notifier: Option>, - event_emitter: EventEmitter, event_receiver: EventReceiver, - window_env: WindowEnv, - layers: Layers, elements_state: ElementsState, viewports: Viewports, - focus_sender: FocusSender, focus_receiver: FocusReceiver, - accessibility: NativeAccessibility, - font_collection: FontCollection, - ticker_sender: broadcast::Sender<()>, - plugins: PluginsManager, - navigator_state: NavigatorState, } @@ -105,8 +94,6 @@ impl App { plugins.send(PluginEvent::WindowCreated(window_env.window_mut())); - let navigator_state = NavigatorState::new(NavigationMode::NotKeyboard); - Self { sdom, vdom, @@ -126,7 +113,7 @@ impl App { font_collection, ticker_sender: broadcast::channel(5).0, plugins, - navigator_state, + navigator_state: NavigatorState::new(NavigationMode::NotKeyboard), } } diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index 3383dab73..f9e63f1b5 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -34,7 +34,7 @@ use crate::renderer::render_skia; use crate::HoveredNode; /// Manager for a Window -pub struct WindowEnv { +pub struct WindowEnv { gr_context: DirectContext, surface: Surface, gl_surface: GlutinSurface, @@ -43,7 +43,7 @@ pub struct WindowEnv { fb_info: FramebufferInfo, num_samples: usize, stencil_size: usize, - pub(crate) window_config: WindowConfig, + pub(crate) window_config: WindowConfig, } impl Drop for WindowEnv { diff --git a/crates/testing/src/test_handler.rs b/crates/testing/src/test_handler.rs index f6d661097..f4ec9850d 100644 --- a/crates/testing/src/test_handler.rs +++ b/crates/testing/src/test_handler.rs @@ -19,23 +19,17 @@ use crate::{TestingConfig, SCALE_FACTOR}; pub struct TestingHandler { pub(crate) vdom: VirtualDom, pub(crate) utils: TestUtils, - pub(crate) event_emitter: EventEmitter, pub(crate) event_receiver: EventReceiver, - pub(crate) platform_event_emitter: UnboundedSender, pub(crate) platform_event_receiver: UnboundedReceiver, - pub(crate) events_queue: EventsQueue, pub(crate) elements_state: ElementsState, pub(crate) font_collection: FontCollection, pub(crate) viewports: Viewports, pub(crate) accessibility_state: SharedAccessibilityState, - pub(crate) config: TestingConfig, - pub(crate) ticker_sender: broadcast::Sender<()>, - pub(crate) navigation_state: NavigatorState, } From 93c0909bf467e591101e655d780d59e64d25e82e Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 10:59:25 +0100 Subject: [PATCH 05/29] chore: Update winit and accesskit (#458) * chore: Update winit and accesskit * refinements * improvements and fixes --- Cargo.toml | 12 +- crates/components/src/accordion.rs | 2 +- crates/components/src/button.rs | 2 +- crates/components/src/dropdown.rs | 4 +- crates/components/src/input.rs | 1 - crates/components/src/slider.rs | 2 +- crates/components/src/switch.rs | 2 +- .../accessibility/accessibility_provider.rs | 125 ++-- .../src/accessibility/accessibility_state.rs | 26 +- crates/core/src/types.rs | 6 +- crates/devtools/src/lib.rs | 3 +- crates/dom/src/dom.rs | 4 +- crates/elements/src/events/keyboard.rs | 628 ++++++++---------- crates/freya/src/launch.rs | 3 +- crates/hooks/src/use_accessibility.rs | 33 +- crates/hooks/src/use_focus.rs | 37 +- crates/renderer/src/accessibility.rs | 31 +- crates/renderer/src/app.rs | 6 +- crates/renderer/src/event_loop.rs | 107 +-- crates/renderer/src/lib.rs | 4 +- crates/renderer/src/window.rs | 8 +- crates/state/src/custom_attributes.rs | 4 +- crates/state/src/values/accessibility.rs | 15 +- crates/testing/src/launch.rs | 5 +- crates/testing/src/test_handler.rs | 7 +- 25 files changed, 487 insertions(+), 590 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8b1777bc7..c5b9c6fa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,13 +39,13 @@ dioxus-router = { version = "0.4", default-features = false } skia-safe = { version = "0.67.0", features = ["gl", "textlayout", "svg"] } gl = "0.14.0" -glutin = "0.30.10" -glutin-winit = "0.3.0" -raw-window-handle = "0.5.1" -winit = "0.28.7" +glutin = "0.31.2" +glutin-winit = "0.4.2" +raw-window-handle = "0.5.2" +winit = "0.29.9" tokio = { version = "1.33.0", features = ["sync", "rt-multi-thread", "time", "macros"] } -accesskit = { version = "0.11.0", features = ["serde"]} -accesskit_winit = "0.14.1" +accesskit = { version = "0.12.2", features = ["serde"]} +accesskit_winit = "0.18.0" zbus = "3.14.1" euclid = "0.22.9" diff --git a/crates/components/src/accordion.rs b/crates/components/src/accordion.rs index 17e3bb781..2dca9d9f6 100644 --- a/crates/components/src/accordion.rs +++ b/crates/components/src/accordion.rs @@ -94,7 +94,7 @@ pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> { let onmouseenter = { to_owned![status, platform]; move |_| { - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); status.set(AccordionStatus::Hovering); } }; diff --git a/crates/components/src/button.rs b/crates/components/src/button.rs index bf9420943..e557640cf 100644 --- a/crates/components/src/button.rs +++ b/crates/components/src/button.rs @@ -92,7 +92,7 @@ pub fn Button<'a>(cx: Scope<'a, ButtonProps<'a>>) -> Element { let onmouseenter = { to_owned![status, platform]; move |_| { - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); status.set(ButtonStatus::Hovering); } }; diff --git a/crates/components/src/dropdown.rs b/crates/components/src/dropdown.rs index eed7956df..c0d85a252 100644 --- a/crates/components/src/dropdown.rs +++ b/crates/components/src/dropdown.rs @@ -84,7 +84,7 @@ where let onmouseenter = { to_owned![platform]; move |_| { - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); status.set(DropdownItemStatus::Hovering); } }; @@ -231,7 +231,7 @@ where let onmouseenter = { to_owned![status, platform]; move |_| { - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); status.set(DropdownStatus::Hovering); } }; diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs index dcf825985..20ac7f65f 100644 --- a/crates/components/src/input.rs +++ b/crates/components/src/input.rs @@ -64,7 +64,6 @@ pub struct InputProps<'a> { /// ```rust,no_run /// # use freya::prelude::*; /// fn app(cx: Scope) -> Element { -/// use_init_focus(cx); /// let value = use_state(cx, String::new); /// /// render!( diff --git a/crates/components/src/slider.rs b/crates/components/src/slider.rs index d5743d7a4..5de205a3f 100644 --- a/crates/components/src/slider.rs +++ b/crates/components/src/slider.rs @@ -104,7 +104,7 @@ pub fn Slider<'a>(cx: Scope<'a, SliderProps>) -> Element<'a> { let onmouseenter = move |_: MouseEvent| { *status.write_silent() = SliderStatus::Hovering; - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); }; let onmouseover = move |e: MouseEvent| { diff --git a/crates/components/src/switch.rs b/crates/components/src/switch.rs index d839a0a40..18e23f894 100644 --- a/crates/components/src/switch.rs +++ b/crates/components/src/switch.rs @@ -83,7 +83,7 @@ pub fn Switch<'a>(cx: Scope<'a, SwitchProps<'a>>) -> Element<'a> { let onmouseenter = move |_: MouseEvent| { *status.write_silent() = SwitchStatus::Hovering; - platform.set_cursor(CursorIcon::Hand); + platform.set_cursor(CursorIcon::Pointer); }; let onclick = |_: MouseEvent| { diff --git a/crates/core/src/accessibility/accessibility_provider.rs b/crates/core/src/accessibility/accessibility_provider.rs index 3349ee164..e30659ec0 100644 --- a/crates/core/src/accessibility/accessibility_provider.rs +++ b/crates/core/src/accessibility/accessibility_provider.rs @@ -8,13 +8,14 @@ use dioxus_native_core::{ NodeId, }; use freya_dom::prelude::{DioxusDOM, DioxusNode}; -use freya_node_state::AccessibilityState; +use freya_node_state::AccessibilityNodeState; use std::slice::Iter; -use tokio::sync::watch; use torin::{prelude::NodeAreas, torin::Torin}; use crate::layout::*; +use super::accessibility_state::ACCESSIBILITY_ROOT_ID; + /// Direction for the next Accessibility Node to be focused. #[derive(PartialEq)] pub enum AccessibilityFocusDirection { @@ -29,7 +30,7 @@ pub trait AccessibilityProvider { dioxus_node: &DioxusNode, node_areas: &NodeAreas, accessibility_id: AccessibilityId, - node_accessibility: &AccessibilityState, + node_accessibility: &AccessibilityNodeState, ) { let mut builder = NodeBuilder::new(Role::Unknown); @@ -88,20 +89,17 @@ pub trait AccessibilityProvider { fn nodes(&self) -> Iter<(AccessibilityId, Node)>; /// Get the currently focused Node's ID. - fn focus_id(&self) -> Option; + fn focus_id(&self) -> AccessibilityId; /// Update the focused Node ID. - fn set_focus(&mut self, new_focus_id: Option); + fn set_focus(&mut self, new_focus_id: AccessibilityId); /// Update the focused Node ID and generate a TreeUpdate if necessary. - fn set_focus_with_update( - &mut self, - new_focus_id: Option, - ) -> Option { + fn set_focus_with_update(&mut self, new_focus_id: AccessibilityId) -> Option { self.set_focus(new_focus_id); // Only focus the element if it exists - let node_focused_exists = self.nodes().any(|node| Some(node.0) == new_focus_id); + let node_focused_exists = self.nodes().any(|node| node.0 == new_focus_id); if node_focused_exists { Some(TreeUpdate { nodes: Vec::new(), @@ -133,13 +131,16 @@ pub trait AccessibilityProvider { nodes.extend(self.nodes().cloned()); nodes.reverse(); - let focus = self.nodes().find_map(|node| { - if Some(node.0) == self.focus_id() { - Some(node.0) - } else { - None - } - }); + let focus = self + .nodes() + .find_map(|node| { + if node.0 == self.focus_id() { + Some(node.0) + } else { + None + } + }) + .unwrap_or(ACCESSIBILITY_ROOT_ID); TreeUpdate { nodes, @@ -149,58 +150,42 @@ pub trait AccessibilityProvider { } /// Focus the next/previous Node starting from the currently focused Node. - fn set_focus_on_next_node( - &mut self, - direction: AccessibilityFocusDirection, - focus_sender: &watch::Sender>, - ) -> Option { - // Start from the focused node or from the first registered node - let focused_node_id = self.focus_id().or(self.nodes().nth(0).map(|node| node.0)); - if let Some(focused_node_id) = focused_node_id { - let current_node = self - .nodes() - .enumerate() - .find(|(_, node)| node.0 == focused_node_id) - .map(|(i, _)| i); - - if let Some(node_index) = current_node { - let target_node_index = if direction == AccessibilityFocusDirection::Forward { - // Find the next Node - if node_index == self.nodes().len() - 1 { - 0 - } else { - node_index + 1 - } - } else { - // Find the previous Node - if node_index == 0 { - self.nodes().len() - 1 - } else { - node_index - 1 - } - }; - - let target_node = self - .nodes() - .enumerate() - .find(|(i, _)| *i == target_node_index) - .map(|(_, node)| node.0); - - self.set_focus(target_node); + fn set_focus_on_next_node(&mut self, direction: AccessibilityFocusDirection) -> TreeUpdate { + let node_index = self + .nodes() + .enumerate() + .find(|(_, node)| node.0 == self.focus_id()) + .map(|(i, _)| i) + .unwrap(); + + let target_node_index = if direction == AccessibilityFocusDirection::Forward { + // Find the next Node + if node_index == self.nodes().len() - 1 { + 0 } else { - // Select the first Node - self.set_focus(self.nodes().next().map(|(id, _)| *id)) + node_index + 1 } + } else { + // Find the previous Node + if node_index == 0 { + self.nodes().len() - 1 + } else { + node_index - 1 + } + }; - focus_sender.send(self.focus_id()).ok(); + let target_node = self + .nodes() + .nth(target_node_index) + .map(|(id, _)| *id) + .unwrap_or(ACCESSIBILITY_ROOT_ID); - Some(TreeUpdate { - nodes: Vec::new(), - tree: None, - focus: self.focus_id(), - }) - } else { - None + self.set_focus(target_node); + + TreeUpdate { + nodes: Vec::new(), + tree: None, + focus: self.focus_id(), } } } @@ -227,13 +212,13 @@ impl NodeAccessibility for DioxusNode<'_> { } } - /// Collect all the AccessibilityIDs from a Node's children + /// Collect all the AccessibilityIDs from a Node's AccessibilityNodeState fn get_accessibility_children(&self) -> Vec { self.children() .iter() .filter_map(|child| { - let node_accessibility = &*child.get::().unwrap(); - node_accessibility.focus_id + let node_accessibility = &*child.get::().unwrap(); + node_accessibility.accessibility_id }) .collect::>() } @@ -250,8 +235,8 @@ pub fn process_accessibility( let node_areas = layout.get(*node_id).unwrap(); let dioxus_node = rdom.get(*node_id); if let Some(dioxus_node) = dioxus_node { - let node_accessibility = &*dioxus_node.get::().unwrap(); - if let Some(accessibility_id) = node_accessibility.focus_id { + let node_accessibility = &*dioxus_node.get::().unwrap(); + if let Some(accessibility_id) = node_accessibility.accessibility_id { access_provider.add_node( &dioxus_node, node_areas, diff --git a/crates/core/src/accessibility/accessibility_state.rs b/crates/core/src/accessibility/accessibility_state.rs index c8c6b3a88..0a4fd0d15 100644 --- a/crates/core/src/accessibility/accessibility_state.rs +++ b/crates/core/src/accessibility/accessibility_state.rs @@ -1,16 +1,12 @@ use crate::accessibility::*; use accesskit::{Node, NodeClassSet, NodeId as AccessibilityId}; -use std::{ - num::NonZeroU128, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; pub type SharedAccessibilityState = Arc>; -pub const ROOT_ID: AccessibilityId = AccessibilityId(unsafe { NonZeroU128::new_unchecked(1) }); +pub const ACCESSIBILITY_ROOT_ID: AccessibilityId = AccessibilityId(0); /// Manages the Accessibility integration. -#[derive(Default)] pub struct AccessibilityState { /// Accessibility Nodes pub nodes: Vec<(AccessibilityId, Node)>, @@ -19,12 +15,16 @@ pub struct AccessibilityState { pub node_classes: NodeClassSet, /// Current focused Accessibility Node. - pub focus: Option, + pub focused_id: AccessibilityId, } impl AccessibilityState { - pub fn new() -> Self { - Self::default() + pub fn new(focused_id: AccessibilityId) -> Self { + Self { + focused_id, + node_classes: NodeClassSet::default(), + nodes: Vec::default(), + } } /// Wrap it in a `Arc>`. @@ -47,12 +47,12 @@ impl AccessibilityProvider for AccessibilityState { self.nodes.iter() } - fn focus_id(&self) -> Option { - self.focus + fn focus_id(&self) -> AccessibilityId { + self.focused_id } - fn set_focus(&mut self, new_focus_id: Option) { - self.focus = new_focus_id; + fn set_focus(&mut self, new_focus_id: AccessibilityId) { + self.focused_id = new_focus_id; } fn push_node(&mut self, id: AccessibilityId, node: Node) { diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index b3369e605..17070ad05 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -1,14 +1,14 @@ use crate::events::{DomEvent, FreyaEvent}; use dioxus_native_core::NodeId; -use accesskit::NodeId as AccessibilityId; +pub use accesskit::NodeId as AccessibilityId; use rustc_hash::FxHashMap; use smallvec::SmallVec; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::sync::watch; -pub type FocusSender = watch::Sender>; -pub type FocusReceiver = watch::Receiver>; +pub type FocusSender = watch::Sender; +pub type FocusReceiver = watch::Receiver; pub type EventEmitter = UnboundedSender; pub type EventReceiver = UnboundedReceiver; pub type EventsQueue = SmallVec<[FreyaEvent; 2]>; diff --git a/crates/devtools/src/lib.rs b/crates/devtools/src/lib.rs index 3a56d272f..0296b6ae2 100644 --- a/crates/devtools/src/lib.rs +++ b/crates/devtools/src/lib.rs @@ -9,7 +9,7 @@ use freya_components::*; use freya_core::node::{get_node_state, NodeState}; use freya_dom::prelude::SafeDOM; use freya_elements::elements as dioxus_elements; -use freya_hooks::{use_init_accessibility, use_init_focus, use_init_theme, use_theme, DARK_THEME}; +use freya_hooks::{use_init_accessibility, use_init_theme, use_theme, DARK_THEME}; use freya_renderer::HoveredNode; use std::sync::Arc; @@ -52,7 +52,6 @@ struct AppWithDevtoolsProps { #[allow(non_snake_case)] fn AppWithDevtools(cx: Scope) -> Element { - use_init_focus(cx); use_init_accessibility(cx); #[allow(non_snake_case)] diff --git a/crates/dom/src/dom.rs b/crates/dom/src/dom.rs index 4d0a1391c..4fe598b2c 100644 --- a/crates/dom/src/dom.rs +++ b/crates/dom/src/dom.rs @@ -8,7 +8,7 @@ use dioxus_native_core::{ }; use freya_node_state::{ - AccessibilityState, CursorSettings, CustomAttributeValues, FontStyleState, LayoutState, + AccessibilityNodeState, CursorSettings, CustomAttributeValues, FontStyleState, LayoutState, References, Style, Transform, }; use std::sync::MutexGuard; @@ -93,7 +93,7 @@ impl Default for FreyaDOM { LayoutState::to_type_erased(), Style::to_type_erased(), Transform::to_type_erased(), - AccessibilityState::to_type_erased(), + AccessibilityNodeState::to_type_erased(), ]); let dioxus_integration_state = DioxusState::create(&mut rdom); Self { diff --git a/crates/elements/src/events/keyboard.rs b/crates/elements/src/events/keyboard.rs index f5223e984..4f57ab7e3 100644 --- a/crates/elements/src/events/keyboard.rs +++ b/crates/elements/src/events/keyboard.rs @@ -1,359 +1,311 @@ pub use keyboard_types::{Code, Key, Modifiers}; -use winit::event::{ModifiersState, VirtualKeyCode}; +use winit::keyboard::{self, NamedKey}; -// Map winit modifiers to keyboard_types modifiers -pub fn get_modifiers(modifiers: ModifiersState) -> Modifiers { +// Return the equivalent of Winit `ModifiersState` in keyboard_types +pub fn map_winit_modifiers(modifiers: keyboard::ModifiersState) -> Modifiers { let mut new_modifiers = Modifiers::empty(); - if modifiers.alt() { + if modifiers.alt_key() { new_modifiers.toggle(Modifiers::ALT); } - if modifiers.ctrl() { + if modifiers.control_key() { new_modifiers.toggle(Modifiers::CONTROL); } - if modifiers.shift() { + if modifiers.shift_key() { new_modifiers.toggle(Modifiers::SHIFT); } - if modifiers.logo() { + if modifiers.super_key() { new_modifiers.toggle(Modifiers::META); } new_modifiers } -/// Only return keys that aren't text -pub fn get_non_text_keys(key: &VirtualKeyCode) -> Key { +/// Return the equivalent Winit's `Key` in keyboard_types +pub fn map_winit_key(key: &keyboard::Key) -> Key { match key { - VirtualKeyCode::Key1 => Key::Unidentified, - VirtualKeyCode::Key2 => Key::Unidentified, - VirtualKeyCode::Key3 => Key::Unidentified, - VirtualKeyCode::Key4 => Key::Unidentified, - VirtualKeyCode::Key5 => Key::Unidentified, - VirtualKeyCode::Key6 => Key::Unidentified, - VirtualKeyCode::Key7 => Key::Unidentified, - VirtualKeyCode::Key8 => Key::Unidentified, - VirtualKeyCode::Key9 => Key::Unidentified, - VirtualKeyCode::Key0 => Key::Unidentified, - VirtualKeyCode::A => Key::Unidentified, - VirtualKeyCode::B => Key::Unidentified, - VirtualKeyCode::C => Key::Unidentified, - VirtualKeyCode::D => Key::Unidentified, - VirtualKeyCode::E => Key::Unidentified, - VirtualKeyCode::F => Key::Unidentified, - VirtualKeyCode::G => Key::Unidentified, - VirtualKeyCode::H => Key::Unidentified, - VirtualKeyCode::I => Key::Unidentified, - VirtualKeyCode::J => Key::Unidentified, - VirtualKeyCode::K => Key::Unidentified, - VirtualKeyCode::L => Key::Unidentified, - VirtualKeyCode::M => Key::Unidentified, - VirtualKeyCode::N => Key::Unidentified, - VirtualKeyCode::O => Key::Unidentified, - VirtualKeyCode::P => Key::Unidentified, - VirtualKeyCode::Q => Key::Unidentified, - VirtualKeyCode::R => Key::Unidentified, - VirtualKeyCode::S => Key::Unidentified, - VirtualKeyCode::T => Key::Unidentified, - VirtualKeyCode::U => Key::Unidentified, - VirtualKeyCode::V => Key::Unidentified, - VirtualKeyCode::W => Key::Unidentified, - VirtualKeyCode::X => Key::Unidentified, - VirtualKeyCode::Y => Key::Unidentified, - VirtualKeyCode::Z => Key::Unidentified, - VirtualKeyCode::Escape => Key::Escape, - VirtualKeyCode::F1 => Key::F1, - VirtualKeyCode::F2 => Key::F2, - VirtualKeyCode::F3 => Key::F3, - VirtualKeyCode::F4 => Key::F4, - VirtualKeyCode::F5 => Key::F5, - VirtualKeyCode::F6 => Key::F6, - VirtualKeyCode::F7 => Key::F7, - VirtualKeyCode::F8 => Key::F8, - VirtualKeyCode::F9 => Key::F9, - VirtualKeyCode::F10 => Key::F10, - VirtualKeyCode::F11 => Key::F11, - VirtualKeyCode::F12 => Key::F12, - VirtualKeyCode::F13 => Key::F13, - VirtualKeyCode::F14 => Key::F14, - VirtualKeyCode::F15 => Key::F15, - VirtualKeyCode::F16 => Key::F16, - VirtualKeyCode::F17 => Key::F17, - VirtualKeyCode::F18 => Key::F18, - VirtualKeyCode::F19 => Key::F19, - VirtualKeyCode::F20 => Key::F20, - VirtualKeyCode::F21 => Key::F21, - VirtualKeyCode::F22 => Key::F22, - VirtualKeyCode::F23 => Key::F23, - VirtualKeyCode::F24 => Key::F24, - VirtualKeyCode::Snapshot => Key::Unidentified, - VirtualKeyCode::Scroll => Key::Unidentified, - VirtualKeyCode::Pause => Key::Pause, - VirtualKeyCode::Insert => Key::Insert, - VirtualKeyCode::Home => Key::Home, - VirtualKeyCode::Delete => Key::Delete, - VirtualKeyCode::End => Key::End, - VirtualKeyCode::PageDown => Key::PageDown, - VirtualKeyCode::PageUp => Key::PageUp, - VirtualKeyCode::Left => Key::ArrowLeft, - VirtualKeyCode::Up => Key::ArrowUp, - VirtualKeyCode::Right => Key::ArrowRight, - VirtualKeyCode::Down => Key::ArrowDown, - VirtualKeyCode::Back => Key::Backspace, - VirtualKeyCode::Return => Key::Enter, - VirtualKeyCode::Space => Key::Unidentified, - VirtualKeyCode::Compose => Key::Compose, - VirtualKeyCode::Caret => Key::Unidentified, - VirtualKeyCode::Numlock => Key::NumLock, - VirtualKeyCode::Numpad0 => Key::Unidentified, - VirtualKeyCode::Numpad1 => Key::Unidentified, - VirtualKeyCode::Numpad2 => Key::Unidentified, - VirtualKeyCode::Numpad3 => Key::Unidentified, - VirtualKeyCode::Numpad4 => Key::Unidentified, - VirtualKeyCode::Numpad5 => Key::Unidentified, - VirtualKeyCode::Numpad6 => Key::Unidentified, - VirtualKeyCode::Numpad7 => Key::Unidentified, - VirtualKeyCode::Numpad8 => Key::Unidentified, - VirtualKeyCode::Numpad9 => Key::Unidentified, - VirtualKeyCode::NumpadAdd => Key::Unidentified, - VirtualKeyCode::NumpadDivide => Key::Unidentified, - VirtualKeyCode::NumpadDecimal => Key::Unidentified, - VirtualKeyCode::NumpadComma => Key::Unidentified, - VirtualKeyCode::NumpadEnter => Key::Unidentified, - VirtualKeyCode::NumpadEquals => Key::Unidentified, - VirtualKeyCode::NumpadMultiply => Key::Unidentified, - VirtualKeyCode::NumpadSubtract => Key::Unidentified, - VirtualKeyCode::AbntC1 => Key::Unidentified, - VirtualKeyCode::AbntC2 => Key::Unidentified, - VirtualKeyCode::Apostrophe => Key::Unidentified, - VirtualKeyCode::Apps => Key::Unidentified, - VirtualKeyCode::Asterisk => Key::Unidentified, - VirtualKeyCode::At => Key::Unidentified, - VirtualKeyCode::Ax => Key::Unidentified, - VirtualKeyCode::Backslash => Key::Unidentified, - VirtualKeyCode::Calculator => Key::Unidentified, - VirtualKeyCode::Capital => Key::Unidentified, - VirtualKeyCode::Colon => Key::Unidentified, - VirtualKeyCode::Comma => Key::Unidentified, - VirtualKeyCode::Convert => Key::Unidentified, - VirtualKeyCode::Equals => Key::Unidentified, - VirtualKeyCode::Grave => Key::Unidentified, - VirtualKeyCode::Kana => Key::Unidentified, - VirtualKeyCode::Kanji => Key::Unidentified, - VirtualKeyCode::LAlt => Key::Alt, - VirtualKeyCode::LBracket => Key::Unidentified, - VirtualKeyCode::LControl => Key::Control, - VirtualKeyCode::LShift => Key::Shift, - VirtualKeyCode::LWin => Key::Meta, - VirtualKeyCode::Mail => Key::Unidentified, - VirtualKeyCode::MediaSelect => Key::Unidentified, - VirtualKeyCode::MediaStop => Key::Unidentified, - VirtualKeyCode::Minus => Key::Unidentified, - VirtualKeyCode::Mute => Key::Unidentified, - VirtualKeyCode::MyComputer => Key::Unidentified, - VirtualKeyCode::NavigateForward => Key::Unidentified, - VirtualKeyCode::NavigateBackward => Key::Unidentified, - VirtualKeyCode::NextTrack => Key::Unidentified, - VirtualKeyCode::NoConvert => Key::Unidentified, - VirtualKeyCode::OEM102 => Key::Unidentified, - VirtualKeyCode::Period => Key::Unidentified, - VirtualKeyCode::PlayPause => Key::Unidentified, - VirtualKeyCode::Plus => Key::Unidentified, - VirtualKeyCode::Power => Key::Unidentified, - VirtualKeyCode::PrevTrack => Key::Unidentified, - VirtualKeyCode::RAlt => Key::AltGraph, - VirtualKeyCode::RBracket => Key::Unidentified, - VirtualKeyCode::RControl => Key::Control, - VirtualKeyCode::RShift => Key::Shift, - VirtualKeyCode::RWin => Key::Meta, - VirtualKeyCode::Semicolon => Key::Unidentified, - VirtualKeyCode::Slash => Key::Unidentified, - VirtualKeyCode::Sleep => Key::Unidentified, - VirtualKeyCode::Stop => Key::Unidentified, - VirtualKeyCode::Sysrq => Key::Unidentified, - VirtualKeyCode::Tab => Key::Unidentified, - VirtualKeyCode::Underline => Key::Unidentified, - VirtualKeyCode::Unlabeled => Key::Unidentified, - VirtualKeyCode::VolumeDown => Key::AudioVolumeDown, - VirtualKeyCode::VolumeUp => Key::AudioVolumeUp, - VirtualKeyCode::Wake => Key::Unidentified, - VirtualKeyCode::WebBack => Key::Unidentified, - VirtualKeyCode::WebFavorites => Key::Unidentified, - VirtualKeyCode::WebForward => Key::Unidentified, - VirtualKeyCode::WebHome => Key::Unidentified, - VirtualKeyCode::WebRefresh => Key::Unidentified, - VirtualKeyCode::WebSearch => Key::Unidentified, - VirtualKeyCode::WebStop => Key::Unidentified, - VirtualKeyCode::Yen => Key::Unidentified, - VirtualKeyCode::Copy => Key::Copy, - VirtualKeyCode::Paste => Key::Paste, - VirtualKeyCode::Cut => Key::Cut, + keyboard::Key::Character(c) => Key::Character(c.to_string()), + keyboard::Key::Named(named_key) => { + match named_key { + NamedKey::Delete => Key::Delete, + NamedKey::Backspace => Key::Backspace, + // Take every variant of NamedKey and add the matching variant of Key + NamedKey::ArrowDown => Key::ArrowDown, + NamedKey::ArrowLeft => Key::ArrowLeft, + NamedKey::ArrowRight => Key::ArrowRight, + NamedKey::ArrowUp => Key::ArrowUp, + NamedKey::End => Key::End, + NamedKey::Home => Key::Home, + NamedKey::PageDown => Key::PageDown, + NamedKey::PageUp => Key::PageUp, + NamedKey::Tab => Key::Tab, + NamedKey::Enter => Key::Enter, + NamedKey::Escape => Key::Escape, + NamedKey::F1 => Key::F1, + NamedKey::F2 => Key::F2, + NamedKey::F3 => Key::F3, + NamedKey::F4 => Key::F4, + NamedKey::F5 => Key::F5, + NamedKey::F6 => Key::F6, + NamedKey::F7 => Key::F7, + NamedKey::F8 => Key::F8, + NamedKey::F9 => Key::F9, + NamedKey::F10 => Key::F10, + NamedKey::F11 => Key::F11, + NamedKey::F12 => Key::F12, + NamedKey::F13 => Key::F13, + NamedKey::F14 => Key::F14, + NamedKey::F15 => Key::F15, + NamedKey::F16 => Key::F16, + NamedKey::F17 => Key::F17, + NamedKey::F18 => Key::F18, + NamedKey::F19 => Key::F19, + NamedKey::F20 => Key::F20, + NamedKey::F21 => Key::F21, + NamedKey::F22 => Key::F22, + NamedKey::F23 => Key::F23, + NamedKey::F24 => Key::F24, + NamedKey::Pause => Key::Pause, + NamedKey::Insert => Key::Insert, + NamedKey::ContextMenu => Key::ContextMenu, + NamedKey::BrowserBack => Key::BrowserBack, + NamedKey::BrowserFavorites => Key::BrowserFavorites, + NamedKey::BrowserForward => Key::BrowserForward, + NamedKey::BrowserHome => Key::BrowserHome, + NamedKey::BrowserRefresh => Key::BrowserRefresh, + NamedKey::BrowserSearch => Key::BrowserSearch, + NamedKey::BrowserStop => Key::BrowserStop, + NamedKey::MediaTrackNext => Key::MediaTrackNext, + NamedKey::MediaPlayPause => Key::MediaPlayPause, + NamedKey::MediaTrackPrevious => Key::MediaTrackPrevious, + NamedKey::MediaStop => Key::MediaStop, + NamedKey::AudioVolumeDown => Key::AudioVolumeDown, + NamedKey::AudioVolumeMute => Key::AudioVolumeMute, + NamedKey::AudioVolumeUp => Key::AudioVolumeUp, + NamedKey::LaunchApplication2 => Key::LaunchApplication2, + NamedKey::LaunchMail => Key::LaunchMail, + NamedKey::Convert => Key::Convert, + NamedKey::Alt => Key::Alt, + NamedKey::AltGraph => Key::AltGraph, + NamedKey::CapsLock => Key::CapsLock, + NamedKey::Control => Key::Control, + NamedKey::Fn => Key::Fn, + NamedKey::FnLock => Key::FnLock, + NamedKey::NumLock => Key::NumLock, + NamedKey::ScrollLock => Key::ScrollLock, + NamedKey::Shift => Key::Shift, + NamedKey::Symbol => Key::Symbol, + NamedKey::SymbolLock => Key::SymbolLock, + NamedKey::Meta => Key::Meta, + NamedKey::Hyper => Key::Hyper, + NamedKey::Super => Key::Super, + NamedKey::Clear => Key::Clear, + NamedKey::Copy => Key::Copy, + NamedKey::CrSel => Key::CrSel, + NamedKey::Cut => Key::Cut, + NamedKey::EraseEof => Key::EraseEof, + NamedKey::ExSel => Key::ExSel, + NamedKey::Paste => Key::Paste, + NamedKey::Redo => Key::Redo, + NamedKey::Undo => Key::Undo, + NamedKey::Accept => Key::Accept, + NamedKey::Again => Key::Again, + NamedKey::Attn => Key::Attn, + NamedKey::Cancel => Key::Cancel, + NamedKey::Execute => Key::Execute, + NamedKey::Find => Key::Find, + NamedKey::Help => Key::Help, + NamedKey::Play => Key::Play, + NamedKey::Props => Key::Props, + NamedKey::Select => Key::Select, + NamedKey::ZoomIn => Key::ZoomIn, + NamedKey::ZoomOut => Key::ZoomOut, + NamedKey::BrightnessDown => Key::BrightnessDown, + NamedKey::BrightnessUp => Key::BrightnessUp, + NamedKey::Eject => Key::Eject, + NamedKey::LogOff => Key::LogOff, + NamedKey::Power => Key::Power, + NamedKey::PowerOff => Key::PowerOff, + NamedKey::PrintScreen => Key::PrintScreen, + NamedKey::Hibernate => Key::Hibernate, + NamedKey::Standby => Key::Standby, + NamedKey::WakeUp => Key::WakeUp, + NamedKey::AllCandidates => Key::AllCandidates, + NamedKey::Alphanumeric => Key::Alphanumeric, + NamedKey::CodeInput => Key::CodeInput, + NamedKey::Compose => Key::Compose, + NamedKey::FinalMode => Key::FinalMode, + NamedKey::GroupFirst => Key::GroupFirst, + NamedKey::GroupLast => Key::GroupLast, + NamedKey::GroupNext => Key::GroupNext, + NamedKey::GroupPrevious => Key::GroupPrevious, + NamedKey::ModeChange => Key::ModeChange, + NamedKey::NextCandidate => Key::NextCandidate, + NamedKey::NonConvert => Key::NonConvert, + NamedKey::PreviousCandidate => Key::PreviousCandidate, + NamedKey::Process => Key::Process, + NamedKey::SingleCandidate => Key::SingleCandidate, + NamedKey::HangulMode => Key::HangulMode, + NamedKey::HanjaMode => Key::HanjaMode, + NamedKey::JunjaMode => Key::JunjaMode, + NamedKey::Eisu => Key::Eisu, + NamedKey::Hankaku => Key::Hankaku, + NamedKey::Hiragana => Key::Hiragana, + NamedKey::HiraganaKatakana => Key::HiraganaKatakana, + NamedKey::KanaMode => Key::KanaMode, + NamedKey::KanjiMode => Key::KanjiMode, + NamedKey::Katakana => Key::Katakana, + NamedKey::Romaji => Key::Romaji, + NamedKey::Zenkaku => Key::Zenkaku, + NamedKey::ZenkakuHankaku => Key::ZenkakuHankaku, + NamedKey::Soft1 => Key::Soft1, + NamedKey::Soft2 => Key::Soft2, + NamedKey::Soft3 => Key::Soft3, + NamedKey::Soft4 => Key::Soft4, + NamedKey::ChannelDown => Key::ChannelDown, + NamedKey::ChannelUp => Key::ChannelUp, + NamedKey::Close => Key::Close, + NamedKey::MailForward => Key::MailForward, + NamedKey::MailReply => Key::MailReply, + NamedKey::MailSend => Key::MailSend, + NamedKey::MediaClose => Key::MediaClose, + NamedKey::MediaFastForward => Key::MediaFastForward, + NamedKey::MediaPause => Key::MediaPause, + NamedKey::MediaPlay => Key::MediaPlay, + NamedKey::MediaRecord => Key::MediaRecord, + _ => Key::Unidentified, + } + } + _ => Key::Unidentified, } } -/// Return the equivalent code of Winit and `keyboard_types` -pub fn from_winit_to_code(key: &VirtualKeyCode) -> Code { - match key { - VirtualKeyCode::Key1 => Code::Digit1, - VirtualKeyCode::Key2 => Code::Digit2, - VirtualKeyCode::Key3 => Code::Digit3, - VirtualKeyCode::Key4 => Code::Digit4, - VirtualKeyCode::Key5 => Code::Digit5, - VirtualKeyCode::Key6 => Code::Digit6, - VirtualKeyCode::Key7 => Code::Digit7, - VirtualKeyCode::Key8 => Code::Digit8, - VirtualKeyCode::Key9 => Code::Digit9, - VirtualKeyCode::Key0 => Code::Digit0, - VirtualKeyCode::A => Code::KeyA, - VirtualKeyCode::B => Code::KeyB, - VirtualKeyCode::C => Code::KeyC, - VirtualKeyCode::D => Code::KeyD, - VirtualKeyCode::E => Code::KeyE, - VirtualKeyCode::F => Code::KeyF, - VirtualKeyCode::G => Code::KeyG, - VirtualKeyCode::H => Code::KeyH, - VirtualKeyCode::I => Code::KeyI, - VirtualKeyCode::J => Code::KeyJ, - VirtualKeyCode::K => Code::KeyK, - VirtualKeyCode::L => Code::KeyL, - VirtualKeyCode::M => Code::KeyM, - VirtualKeyCode::N => Code::KeyN, - VirtualKeyCode::O => Code::KeyO, - VirtualKeyCode::P => Code::KeyP, - VirtualKeyCode::Q => Code::KeyQ, - VirtualKeyCode::R => Code::KeyR, - VirtualKeyCode::S => Code::KeyS, - VirtualKeyCode::T => Code::KeyT, - VirtualKeyCode::U => Code::KeyU, - VirtualKeyCode::V => Code::KeyV, - VirtualKeyCode::W => Code::KeyW, - VirtualKeyCode::X => Code::KeyX, - VirtualKeyCode::Y => Code::KeyY, - VirtualKeyCode::Z => Code::KeyZ, - VirtualKeyCode::Escape => Code::Escape, - VirtualKeyCode::F1 => Code::F1, - VirtualKeyCode::F2 => Code::F2, - VirtualKeyCode::F3 => Code::F3, - VirtualKeyCode::F4 => Code::F4, - VirtualKeyCode::F5 => Code::F5, - VirtualKeyCode::F6 => Code::F6, - VirtualKeyCode::F7 => Code::F7, - VirtualKeyCode::F8 => Code::F8, - VirtualKeyCode::F9 => Code::F9, - VirtualKeyCode::F10 => Code::F10, - VirtualKeyCode::F11 => Code::F11, - VirtualKeyCode::F12 => Code::F12, - VirtualKeyCode::F13 => Code::F13, - VirtualKeyCode::F14 => Code::F14, - VirtualKeyCode::F15 => Code::F15, - VirtualKeyCode::F16 => Code::F16, - VirtualKeyCode::F17 => Code::F17, - VirtualKeyCode::F18 => Code::F18, - VirtualKeyCode::F19 => Code::F19, - VirtualKeyCode::F20 => Code::F20, - VirtualKeyCode::F21 => Code::F21, - VirtualKeyCode::F22 => Code::F22, - VirtualKeyCode::F23 => Code::F23, - VirtualKeyCode::F24 => Code::F24, - VirtualKeyCode::Snapshot => Code::Unidentified, - VirtualKeyCode::Scroll => Code::Unidentified, - VirtualKeyCode::Pause => Code::Pause, - VirtualKeyCode::Insert => Code::Insert, - VirtualKeyCode::Home => Code::Home, - VirtualKeyCode::Delete => Code::Delete, - VirtualKeyCode::End => Code::End, - VirtualKeyCode::PageDown => Code::PageDown, - VirtualKeyCode::PageUp => Code::PageUp, - VirtualKeyCode::Left => Code::ArrowLeft, - VirtualKeyCode::Up => Code::ArrowUp, - VirtualKeyCode::Right => Code::ArrowRight, - VirtualKeyCode::Down => Code::ArrowDown, - VirtualKeyCode::Back => Code::Backspace, - VirtualKeyCode::Return => Code::Enter, - VirtualKeyCode::Space => Code::Space, - VirtualKeyCode::Compose => Code::Unidentified, - VirtualKeyCode::Caret => Code::Unidentified, - VirtualKeyCode::Numlock => Code::NumLock, - VirtualKeyCode::Numpad0 => Code::Numpad0, - VirtualKeyCode::Numpad1 => Code::Numpad1, - VirtualKeyCode::Numpad2 => Code::Numpad2, - VirtualKeyCode::Numpad3 => Code::Numpad3, - VirtualKeyCode::Numpad4 => Code::Numpad4, - VirtualKeyCode::Numpad5 => Code::Numpad5, - VirtualKeyCode::Numpad6 => Code::Numpad6, - VirtualKeyCode::Numpad7 => Code::Numpad7, - VirtualKeyCode::Numpad8 => Code::Numpad8, - VirtualKeyCode::Numpad9 => Code::Numpad9, - VirtualKeyCode::NumpadAdd => Code::NumpadAdd, - VirtualKeyCode::NumpadDivide => Code::NumpadDivide, - VirtualKeyCode::NumpadDecimal => Code::NumpadDecimal, - VirtualKeyCode::NumpadComma => Code::NumpadComma, - VirtualKeyCode::NumpadEnter => Code::NumpadEnter, - VirtualKeyCode::NumpadEquals => Code::NumpadEqual, - VirtualKeyCode::NumpadMultiply => Code::NumpadMultiply, - VirtualKeyCode::NumpadSubtract => Code::NumpadSubtract, - VirtualKeyCode::AbntC1 => Code::Unidentified, - VirtualKeyCode::AbntC2 => Code::Unidentified, - VirtualKeyCode::Apostrophe => Code::Unidentified, - VirtualKeyCode::Apps => Code::Unidentified, - VirtualKeyCode::Asterisk => Code::Unidentified, - VirtualKeyCode::At => Code::Unidentified, - VirtualKeyCode::Ax => Code::Unidentified, - VirtualKeyCode::Backslash => Code::Backslash, - VirtualKeyCode::Calculator => Code::Unidentified, - VirtualKeyCode::Capital => Code::Unidentified, - VirtualKeyCode::Colon => Code::Unidentified, - VirtualKeyCode::Comma => Code::Comma, - VirtualKeyCode::Convert => Code::Convert, - VirtualKeyCode::Equals => Code::Equal, - VirtualKeyCode::Grave => Code::Unidentified, - VirtualKeyCode::Kana => Code::Unidentified, - VirtualKeyCode::Kanji => Code::Unidentified, - VirtualKeyCode::LAlt => Code::Unidentified, - VirtualKeyCode::LBracket => Code::BracketLeft, - VirtualKeyCode::LControl => Code::ControlLeft, - VirtualKeyCode::LShift => Code::ShiftLeft, - VirtualKeyCode::LWin => Code::MetaLeft, - VirtualKeyCode::Mail => Code::Unidentified, - VirtualKeyCode::MediaSelect => Code::Unidentified, - VirtualKeyCode::MediaStop => Code::Unidentified, - VirtualKeyCode::Minus => Code::Unidentified, - VirtualKeyCode::Mute => Code::Unidentified, - VirtualKeyCode::MyComputer => Code::Unidentified, - VirtualKeyCode::NavigateForward => Code::Unidentified, - VirtualKeyCode::NavigateBackward => Code::Unidentified, - VirtualKeyCode::NextTrack => Code::Unidentified, - VirtualKeyCode::NoConvert => Code::Unidentified, - VirtualKeyCode::OEM102 => Code::Unidentified, - VirtualKeyCode::Period => Code::Unidentified, - VirtualKeyCode::PlayPause => Code::Unidentified, - VirtualKeyCode::Plus => Code::Unidentified, - VirtualKeyCode::Power => Code::Unidentified, - VirtualKeyCode::PrevTrack => Code::Unidentified, - VirtualKeyCode::RAlt => Code::AltRight, - VirtualKeyCode::RBracket => Code::BracketRight, - VirtualKeyCode::RControl => Code::ControlRight, - VirtualKeyCode::RShift => Code::ShiftRight, - VirtualKeyCode::RWin => Code::MetaRight, - VirtualKeyCode::Semicolon => Code::Semicolon, - VirtualKeyCode::Slash => Code::Unidentified, - VirtualKeyCode::Sleep => Code::Unidentified, - VirtualKeyCode::Stop => Code::Unidentified, - VirtualKeyCode::Sysrq => Code::Unidentified, - VirtualKeyCode::Tab => Code::Tab, - VirtualKeyCode::Underline => Code::Unidentified, - VirtualKeyCode::Unlabeled => Code::Unidentified, - VirtualKeyCode::VolumeDown => Code::AudioVolumeDown, - VirtualKeyCode::VolumeUp => Code::AudioVolumeUp, - VirtualKeyCode::Wake => Code::Unidentified, - VirtualKeyCode::WebBack => Code::Unidentified, - VirtualKeyCode::WebFavorites => Code::Unidentified, - VirtualKeyCode::WebForward => Code::Unidentified, - VirtualKeyCode::WebHome => Code::Unidentified, - VirtualKeyCode::WebRefresh => Code::Unidentified, - VirtualKeyCode::WebSearch => Code::Unidentified, - VirtualKeyCode::WebStop => Code::Unidentified, - VirtualKeyCode::Yen => Code::IntlYen, - VirtualKeyCode::Copy => Code::Copy, - VirtualKeyCode::Paste => Code::Paste, - VirtualKeyCode::Cut => Code::Cut, +/// Return the equivalent of Winit's `PhysicalKey` in keyboard_types +pub fn map_winit_physical_key(key: &keyboard::PhysicalKey) -> Code { + if let keyboard::PhysicalKey::Code(key) = key { + match key { + keyboard::KeyCode::Digit1 => Code::Digit1, + keyboard::KeyCode::Digit2 => Code::Digit2, + keyboard::KeyCode::Digit3 => Code::Digit3, + keyboard::KeyCode::Digit4 => Code::Digit4, + keyboard::KeyCode::Digit5 => Code::Digit5, + keyboard::KeyCode::Digit6 => Code::Digit6, + keyboard::KeyCode::Digit7 => Code::Digit7, + keyboard::KeyCode::Digit8 => Code::Digit8, + keyboard::KeyCode::Digit9 => Code::Digit9, + keyboard::KeyCode::Digit0 => Code::Digit0, + keyboard::KeyCode::KeyA => Code::KeyA, + keyboard::KeyCode::KeyB => Code::KeyB, + keyboard::KeyCode::KeyC => Code::KeyC, + keyboard::KeyCode::KeyD => Code::KeyD, + keyboard::KeyCode::KeyE => Code::KeyE, + keyboard::KeyCode::KeyF => Code::KeyF, + keyboard::KeyCode::KeyG => Code::KeyG, + keyboard::KeyCode::KeyH => Code::KeyH, + keyboard::KeyCode::KeyI => Code::KeyI, + keyboard::KeyCode::KeyJ => Code::KeyJ, + keyboard::KeyCode::KeyK => Code::KeyK, + keyboard::KeyCode::KeyL => Code::KeyL, + keyboard::KeyCode::KeyM => Code::KeyM, + keyboard::KeyCode::KeyN => Code::KeyN, + keyboard::KeyCode::KeyO => Code::KeyO, + keyboard::KeyCode::KeyP => Code::KeyP, + keyboard::KeyCode::KeyQ => Code::KeyQ, + keyboard::KeyCode::KeyR => Code::KeyR, + keyboard::KeyCode::KeyS => Code::KeyS, + keyboard::KeyCode::KeyT => Code::KeyT, + keyboard::KeyCode::KeyU => Code::KeyU, + keyboard::KeyCode::KeyV => Code::KeyV, + keyboard::KeyCode::KeyW => Code::KeyW, + keyboard::KeyCode::KeyX => Code::KeyX, + keyboard::KeyCode::KeyY => Code::KeyY, + keyboard::KeyCode::KeyZ => Code::KeyZ, + keyboard::KeyCode::Escape => Code::Escape, + keyboard::KeyCode::F1 => Code::F1, + keyboard::KeyCode::F2 => Code::F2, + keyboard::KeyCode::F3 => Code::F3, + keyboard::KeyCode::F4 => Code::F4, + keyboard::KeyCode::F5 => Code::F5, + keyboard::KeyCode::F6 => Code::F6, + keyboard::KeyCode::F7 => Code::F7, + keyboard::KeyCode::F8 => Code::F8, + keyboard::KeyCode::F9 => Code::F9, + keyboard::KeyCode::F10 => Code::F10, + keyboard::KeyCode::F11 => Code::F11, + keyboard::KeyCode::F12 => Code::F12, + keyboard::KeyCode::F13 => Code::F13, + keyboard::KeyCode::F14 => Code::F14, + keyboard::KeyCode::F15 => Code::F15, + keyboard::KeyCode::F16 => Code::F16, + keyboard::KeyCode::F17 => Code::F17, + keyboard::KeyCode::F18 => Code::F18, + keyboard::KeyCode::F19 => Code::F19, + keyboard::KeyCode::F20 => Code::F20, + keyboard::KeyCode::F21 => Code::F21, + keyboard::KeyCode::F22 => Code::F22, + keyboard::KeyCode::F23 => Code::F23, + keyboard::KeyCode::F24 => Code::F24, + keyboard::KeyCode::Pause => Code::Pause, + keyboard::KeyCode::Insert => Code::Insert, + keyboard::KeyCode::Home => Code::Home, + keyboard::KeyCode::Delete => Code::Delete, + keyboard::KeyCode::End => Code::End, + keyboard::KeyCode::PageDown => Code::PageDown, + keyboard::KeyCode::PageUp => Code::PageUp, + keyboard::KeyCode::ArrowLeft => Code::ArrowLeft, + keyboard::KeyCode::ArrowUp => Code::ArrowUp, + keyboard::KeyCode::ArrowRight => Code::ArrowRight, + keyboard::KeyCode::ArrowDown => Code::ArrowDown, + keyboard::KeyCode::Backspace => Code::Backspace, + keyboard::KeyCode::Enter => Code::Enter, + keyboard::KeyCode::Space => Code::Space, + keyboard::KeyCode::NumLock => Code::NumLock, + keyboard::KeyCode::Numpad0 => Code::Numpad0, + keyboard::KeyCode::Numpad1 => Code::Numpad1, + keyboard::KeyCode::Numpad2 => Code::Numpad2, + keyboard::KeyCode::Numpad3 => Code::Numpad3, + keyboard::KeyCode::Numpad4 => Code::Numpad4, + keyboard::KeyCode::Numpad5 => Code::Numpad5, + keyboard::KeyCode::Numpad6 => Code::Numpad6, + keyboard::KeyCode::Numpad7 => Code::Numpad7, + keyboard::KeyCode::Numpad8 => Code::Numpad8, + keyboard::KeyCode::Numpad9 => Code::Numpad9, + keyboard::KeyCode::NumpadAdd => Code::NumpadAdd, + keyboard::KeyCode::NumpadDivide => Code::NumpadDivide, + keyboard::KeyCode::NumpadDecimal => Code::NumpadDecimal, + keyboard::KeyCode::NumpadComma => Code::NumpadComma, + keyboard::KeyCode::NumpadEnter => Code::NumpadEnter, + keyboard::KeyCode::NumpadEqual => Code::NumpadEqual, + keyboard::KeyCode::NumpadMultiply => Code::NumpadMultiply, + keyboard::KeyCode::NumpadSubtract => Code::NumpadSubtract, + keyboard::KeyCode::Backslash => Code::Backslash, + keyboard::KeyCode::Comma => Code::Comma, + keyboard::KeyCode::Convert => Code::Convert, + keyboard::KeyCode::Equal => Code::Equal, + keyboard::KeyCode::BracketLeft => Code::BracketLeft, + keyboard::KeyCode::BracketRight => Code::BracketRight, + keyboard::KeyCode::ShiftLeft => Code::ShiftLeft, + keyboard::KeyCode::Meta => Code::MetaLeft, + keyboard::KeyCode::MediaSelect => Code::Unidentified, + keyboard::KeyCode::MediaStop => Code::Unidentified, + keyboard::KeyCode::Minus => Code::Unidentified, + keyboard::KeyCode::Period => Code::Unidentified, + keyboard::KeyCode::Power => Code::Unidentified, + keyboard::KeyCode::AltRight => Code::AltRight, + keyboard::KeyCode::ControlLeft => Code::ControlLeft, + keyboard::KeyCode::ControlRight => Code::ControlRight, + keyboard::KeyCode::ShiftRight => Code::ShiftRight, + keyboard::KeyCode::Semicolon => Code::Semicolon, + keyboard::KeyCode::Slash => Code::Unidentified, + keyboard::KeyCode::Sleep => Code::Unidentified, + keyboard::KeyCode::Tab => Code::Tab, + keyboard::KeyCode::AudioVolumeUp => Code::AudioVolumeUp, + keyboard::KeyCode::IntlYen => Code::IntlYen, + keyboard::KeyCode::Copy => Code::Copy, + keyboard::KeyCode::Paste => Code::Paste, + keyboard::KeyCode::Cut => Code::Cut, + _ => Code::Unidentified, + } + } else { + Code::Unidentified } } diff --git a/crates/freya/src/launch.rs b/crates/freya/src/launch.rs index 3693ea9bf..9c4186627 100644 --- a/crates/freya/src/launch.rs +++ b/crates/freya/src/launch.rs @@ -233,7 +233,7 @@ fn with_accessibility(app: Component) -> VirtualDom { use dioxus_core::fc_to_builder; use dioxus_core::{Element, Scope}; use dioxus_core_macro::render; - use freya_hooks::{use_init_accessibility, use_init_focus}; + use freya_hooks::use_init_accessibility; struct RootProps { app: Component, @@ -241,7 +241,6 @@ fn with_accessibility(app: Component) -> VirtualDom { #[allow(non_snake_case)] fn Root(cx: Scope) -> Element { - use_init_focus(cx); use_init_accessibility(cx); #[allow(non_snake_case)] diff --git a/crates/hooks/src/use_accessibility.rs b/crates/hooks/src/use_accessibility.rs index a835d9c71..a0ceb530c 100644 --- a/crates/hooks/src/use_accessibility.rs +++ b/crates/hooks/src/use_accessibility.rs @@ -1,24 +1,32 @@ +use std::rc::Rc; + use dioxus_core::ScopeState; -use dioxus_hooks::{to_owned, use_memo, use_shared_state}; use freya_common::EventMessage; use freya_core::{navigation_mode::NavigatorState, types::FocusReceiver}; -use crate::{use_platform, FocusId}; +use dioxus_hooks::{ + to_owned, use_context_provider, use_memo, use_shared_state, use_shared_state_provider, RefCell, +}; +use freya_core::{accessibility::ACCESSIBILITY_ROOT_ID, types::AccessibilityId}; + +use crate::use_platform; + +pub type AccessibilityIdCounter = Rc>; /// Sync both the Focus shared state and the platform accessibility focus pub fn use_init_accessibility(cx: &ScopeState) { let platform = use_platform(cx); - let focused_id = use_shared_state::>(cx).unwrap(); + use_context_provider(cx, || Rc::new(RefCell::new(0u64))); + use_shared_state_provider::(cx, || ACCESSIBILITY_ROOT_ID); + let focused_id = use_shared_state::(cx).unwrap(); let current_focused_id = *focused_id.read(); - // Tell the renderer the new focused node + // Notify the platform that a new Node has been focused manually let _ = use_memo(cx, &(current_focused_id,), move |(focused_id,)| { - if let Some(focused_id) = focused_id { - platform - .send(EventMessage::FocusAccessibilityNode(focused_id)) - .unwrap(); - } + platform + .send(EventMessage::FocusAccessibilityNode(focused_id)) + .unwrap(); }); cx.use_hook(|| { @@ -53,6 +61,7 @@ pub fn use_init_accessibility(cx: &ScopeState) { #[cfg(test)] mod test { use freya::prelude::*; + use freya_core::accessibility::ACCESSIBILITY_ROOT_ID; use freya_testing::{ events::pointer::MouseButton, launch_test_with_config, FreyaEvent, TestingConfig, }; @@ -88,7 +97,7 @@ mod test { // Initial state utils.wait_for_update().await; - assert!(utils.focus_id().is_none()); + assert_eq!(utils.focus_id(), ACCESSIBILITY_ROOT_ID); // Click on the first rect utils.push_event(FreyaEvent::Mouse { @@ -101,7 +110,7 @@ mod test { utils.wait_for_update().await; utils.wait_for_update().await; let first_focus_id = utils.focus_id(); - assert!(first_focus_id.is_some()); + assert_ne!(first_focus_id, ACCESSIBILITY_ROOT_ID); // Click on the second rect utils.push_event(FreyaEvent::Mouse { @@ -115,6 +124,6 @@ mod test { utils.wait_for_update().await; let second_focus_id = utils.focus_id(); assert_ne!(first_focus_id, second_focus_id); - assert!(second_focus_id.is_some()); + assert_ne!(second_focus_id, ACCESSIBILITY_ROOT_ID); } } diff --git a/crates/hooks/src/use_focus.rs b/crates/hooks/src/use_focus.rs index ca8a1dbb4..df9c4347a 100644 --- a/crates/hooks/src/use_focus.rs +++ b/crates/hooks/src/use_focus.rs @@ -1,27 +1,25 @@ -use std::num::NonZeroU128; - use accesskit::NodeId as AccessibilityId; use dioxus_core::{AttributeValue, Scope, ScopeState}; -use dioxus_hooks::{use_shared_state, use_shared_state_provider, UseSharedState}; +use dioxus_hooks::{use_context, use_shared_state, UseSharedState}; +use freya_core::accessibility::ACCESSIBILITY_ROOT_ID; use freya_core::navigation_mode::{NavigationMode, NavigatorState}; use freya_elements::events::{keyboard::Code, KeyboardEvent}; use freya_node_state::CustomAttributeValues; -use uuid::Uuid; -pub type FocusId = AccessibilityId; +use crate::AccessibilityIdCounter; /// Manage the focus operations of given Node #[derive(Clone)] pub struct UseFocus { id: AccessibilityId, - focused_id: UseSharedState>, + focused_id: UseSharedState, navigation_state: NavigatorState, } impl UseFocus { /// Focus this node pub fn focus(&self) { - *self.focused_id.write() = Some(self.id) + *self.focused_id.write() = self.id } /// Get the node focus ID @@ -31,12 +29,12 @@ impl UseFocus { /// Create a node focus ID attribute pub fn attribute<'b, T>(&self, cx: Scope<'b, T>) -> AttributeValue<'b> { - cx.any_value(CustomAttributeValues::FocusId(self.id)) + cx.any_value(CustomAttributeValues::AccessibilityId(self.id)) } /// Check if this node is currently focused pub fn is_focused(&self) -> bool { - Some(self.id) == *self.focused_id.read() + self.id == *self.focused_id.read() } /// Check if this node is currently selected @@ -46,7 +44,7 @@ impl UseFocus { /// Unfocus the currently focused node. pub fn unfocus(&self) { - *self.focused_id.write() = None; + *self.focused_id.write() = ACCESSIBILITY_ROOT_ID; } /// Validate keydown event @@ -57,27 +55,26 @@ impl UseFocus { /// Create a focus manager for a node. pub fn use_focus(cx: &ScopeState) -> &UseFocus { - let focused_id = use_shared_state::>(cx); + let accessibility_id_counter = use_context::(cx).unwrap(); + let focused_id = use_shared_state::(cx).unwrap(); + + cx.use_hook(|| { + let mut counter = accessibility_id_counter.borrow_mut(); + *counter += 1; + let id = AccessibilityId(*counter); - cx.use_hook(move || { - let focused_id = focused_id.unwrap().clone(); - let id = AccessibilityId(NonZeroU128::new(Uuid::new_v4().as_u128()).unwrap()); let navigation_state = cx .consume_context::() .expect("This is not expected, and likely a bug. Please, report it."); + UseFocus { id, - focused_id, + focused_id: focused_id.clone(), navigation_state, } }) } -/// Create a focus provider. -pub fn use_init_focus(cx: &ScopeState) { - use_shared_state_provider::>(cx, || None); -} - #[cfg(test)] mod test { use crate::use_focus; diff --git a/crates/renderer/src/accessibility.rs b/crates/renderer/src/accessibility.rs index 45821af1c..b07409243 100644 --- a/crates/renderer/src/accessibility.rs +++ b/crates/renderer/src/accessibility.rs @@ -4,7 +4,7 @@ use freya_common::EventMessage; use freya_core::{ prelude::{ AccessibilityFocusDirection, AccessibilityProvider, AccessibilityState, - SharedAccessibilityState, ROOT_ID, + SharedAccessibilityState, ACCESSIBILITY_ROOT_ID, }, types::FocusSender, }; @@ -19,14 +19,14 @@ pub struct NativeAccessibility { impl NativeAccessibility { pub fn new(window: &Window, proxy: EventLoopProxy) -> Self { let title = window.title(); - let accessibility_state = AccessibilityState::new().wrap(); + let accessibility_state = AccessibilityState::new(ACCESSIBILITY_ROOT_ID).wrap(); let accessibility_adapter = { let accessibility_state = accessibility_state.clone(); Adapter::new( window, move || { let mut accessibility_state = accessibility_state.lock().unwrap(); - accessibility_state.process(ROOT_ID, title.as_str()) + accessibility_state.process(ACCESSIBILITY_ROOT_ID, title.as_str()) }, proxy, ) @@ -47,15 +47,15 @@ impl NativeAccessibility { .accessibility_state .lock() .unwrap() - .set_focus_with_update(Some(id)); + .set_focus_with_update(id); if let Some(tree) = tree { - self.accessibility_adapter.update(tree); + self.accessibility_adapter.update_if_active(|| tree); } } - /// Validate a winit event for accessibility - pub fn on_accessibility_window_event(&mut self, window: &Window, event: &WindowEvent) -> bool { - self.accessibility_adapter.on_event(window, event) + /// Process an Accessibility event + pub fn process_accessibility_event(&mut self, window: &Window, event: &WindowEvent) { + self.accessibility_adapter.process_event(window, event) } /// Remove the accessibility nodes @@ -69,8 +69,8 @@ impl NativeAccessibility { .accessibility_state .lock() .unwrap() - .process(ROOT_ID, title); - self.accessibility_adapter.update(tree); + .process(ACCESSIBILITY_ROOT_ID, title); + self.accessibility_adapter.update_if_active(|| tree); } /// Focus the next accessibility node @@ -83,9 +83,12 @@ impl NativeAccessibility { .accessibility_state .lock() .unwrap() - .set_focus_on_next_node(direction, focus_sender); - if let Some(tree) = tree { - self.accessibility_adapter.update(tree); - } + .set_focus_on_next_node(direction); + + focus_sender + .send(tree.focus) + .expect("Failed to focus the Node."); + + self.accessibility_adapter.update_if_active(|| tree); } } diff --git a/crates/renderer/src/app.rs b/crates/renderer/src/app.rs index b34567c9f..7bdee026e 100644 --- a/crates/renderer/src/app.rs +++ b/crates/renderer/src/app.rs @@ -101,7 +101,7 @@ impl App { font_collection.set_dynamic_font_manager(mgr); let (event_emitter, event_receiver) = mpsc::unbounded_channel::(); - let (focus_sender, focus_receiver) = watch::channel(None); + let (focus_sender, focus_receiver) = watch::channel(ACCESSIBILITY_ROOT_ID); plugins.send(PluginEvent::WindowCreated(window_env.window_mut())); @@ -352,9 +352,9 @@ impl App { &mut self.accessibility } - pub fn on_window_event(&mut self, event: &WindowEvent) -> bool { + pub fn process_accessibility_event(&mut self, event: &WindowEvent) { self.accessibility - .on_accessibility_window_event(&self.window_env.window, event) + .process_accessibility_event(&self.window_env.window, event) } pub fn focus_next_node(&mut self, direction: AccessibilityFocusDirection) { diff --git a/crates/renderer/src/event_loop.rs b/crates/renderer/src/event_loop.rs index ae96902a3..96a1a8a9d 100644 --- a/crates/renderer/src/event_loop.rs +++ b/crates/renderer/src/event_loop.rs @@ -3,14 +3,14 @@ use accesskit_winit::ActionRequestEvent; use freya_common::EventMessage; use freya_core::prelude::*; use freya_elements::events::keyboard::{ - from_winit_to_code, get_modifiers, get_non_text_keys, Code, Key, + map_winit_key, map_winit_modifiers, map_winit_physical_key, }; use torin::geometry::CursorPoint; use winit::event::{ - ElementState, Event, KeyboardInput, ModifiersState, MouseScrollDelta, StartCause, Touch, - TouchPhase, VirtualKeyCode, WindowEvent, + ElementState, Event, KeyEvent, MouseScrollDelta, StartCause, Touch, TouchPhase, WindowEvent, }; -use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; +use winit::event_loop::{EventLoop, EventLoopProxy}; +use winit::keyboard::{KeyCode, ModifiersState, PhysicalKey}; use crate::app::App; use crate::HoveredNode; @@ -27,17 +27,14 @@ pub fn run_event_loop( hovered_node: HoveredNode, ) { let mut cursor_pos = CursorPoint::default(); - let mut last_keydown = Key::Unidentified; - let mut last_code = Code::Unidentified; let mut modifiers_state = ModifiersState::empty(); let window_env = app.window_env(); window_env.run_on_setup(); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - match event { + event_loop + .run(move |event, event_loop| match event { Event::NewEvents(StartCause::Init) => { _ = proxy.send_event(EventMessage::PollVDOM); } @@ -76,14 +73,15 @@ pub fn run_event_loop( app.poll_vdom(); } } - Event::RedrawRequested(_) => { - app.process_layout(); - app.render(&hovered_node); - app.tick(); - } - Event::WindowEvent { event, .. } if app.on_window_event(&event) => { + Event::WindowEvent { event, .. } => { + app.process_accessibility_event(&event); match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::RedrawRequested => { + app.process_layout(); + app.render(&hovered_node); + app.tick(); + } WindowEvent::MouseInput { state, button, .. } => { app.set_navigation_mode(NavigationMode::NotKeyboard); @@ -118,32 +116,24 @@ pub fn run_event_loop( } } WindowEvent::ModifiersChanged(modifiers) => { - modifiers_state = modifiers; - } - WindowEvent::ReceivedCharacter(a) => { - // Emit the received character if the last pressed key wasn't text - if last_keydown == Key::Unidentified || !modifiers_state.is_empty() { - app.send_event(FreyaEvent::Keyboard { - name: "keydown".to_string(), - key: Key::Character(a.to_string()), - code: last_code, - modifiers: get_modifiers(modifiers_state), - }); - } + modifiers_state = modifiers.state(); } WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_keycode), + event: + KeyEvent { + physical_key, + logical_key, state, .. }, .. } => { - if state == ElementState::Pressed && virtual_keycode == VirtualKeyCode::Tab + if state == ElementState::Pressed + && physical_key == PhysicalKey::Code(KeyCode::Tab) { app.set_navigation_mode(NavigationMode::Keyboard); - let direction = if modifiers_state.shift() { + + let direction = if modifiers_state.shift_key() { AccessibilityFocusDirection::Backward } else { AccessibilityFocusDirection::Forward @@ -158,45 +148,12 @@ pub fn run_event_loop( ElementState::Pressed => "keydown", ElementState::Released => "keyup", }; - - // Only emit keys that aren't text (e.g ArrowUp isn't text) - // Text characters will be emitted by `WindowEvent::ReceivedCharacter` - let key = get_non_text_keys(&virtual_keycode); - if key != Key::Unidentified { - // Winit doesn't enable the alt modifier when pressing the AltGraph key, this is a workaround - if key == Key::AltGraph { - if state == ElementState::Pressed { - modifiers_state.insert(ModifiersState::ALT) - } else { - modifiers_state.remove(ModifiersState::ALT) - } - } - - if state == ElementState::Pressed { - // Cache this key so `WindowEvent::ReceivedCharacter` knows - // it shouldn't emit anything until this same key emits keyup - last_keydown = key.clone(); - } else { - // Uncache any key - last_keydown = Key::Unidentified; - } - app.send_event(FreyaEvent::Keyboard { - name: event_name.to_string(), - key, - code: from_winit_to_code(&virtual_keycode), - modifiers: get_modifiers(modifiers_state), - }); - } else { - last_keydown = Key::Unidentified; - } - - if state == ElementState::Pressed { - // Cache the key code on keydown event - last_code = from_winit_to_code(&virtual_keycode); - } else { - // Uncache any key code - last_code = Code::Unidentified; - } + app.send_event(FreyaEvent::Keyboard { + name: event_name.to_string(), + key: map_winit_key(&logical_key), + code: map_winit_physical_key(&physical_key), + modifiers: map_winit_modifiers(modifiers_state), + }) } WindowEvent::CursorMoved { position, .. } => { cursor_pos = CursorPoint::from((position.x, position.y)); @@ -237,10 +194,10 @@ pub fn run_event_loop( _ => {} } } - Event::LoopDestroyed => { + Event::LoopExiting => { app.window_env().run_on_exit(); } _ => (), - } - }); + }) + .expect("Failed to run Eventloop."); } diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index da278d55c..47fb2a486 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -41,7 +41,9 @@ impl DesktopRenderer { let _guard = rt.enter(); - let event_loop = EventLoopBuilder::::with_user_event().build(); + let event_loop = EventLoopBuilder::::with_user_event() + .build() + .expect("Failed to create event loop."); let proxy = event_loop.create_proxy(); // Hotreload support for Dioxus diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index 3383dab73..d7d33a183 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -3,19 +3,17 @@ use freya_common::EventMessage; use freya_core::prelude::*; use freya_dom::prelude::FreyaDOM; use freya_engine::prelude::*; -use glutin::prelude::{PossiblyCurrentContextGlSurfaceAccessor, PossiblyCurrentGlContext}; +use glutin::prelude::PossiblyCurrentGlContext; use std::ffi::CString; use std::num::NonZeroU32; use torin::geometry::{Area, Size2D}; use gl::{types::*, *}; use glutin::context::GlProfile; +use glutin::context::NotCurrentGlContext; use glutin::{ config::{ConfigTemplateBuilder, GlConfig}, - context::{ - ContextApi, ContextAttributesBuilder, NotCurrentGlContextSurfaceAccessor, - PossiblyCurrentContext, - }, + context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext}, display::{GetGlDisplay, GlDisplay}, prelude::GlSurface, surface::{Surface as GlutinSurface, SurfaceAttributesBuilder, WindowSurface}, diff --git a/crates/state/src/custom_attributes.rs b/crates/state/src/custom_attributes.rs index 73f73d3d6..052df7b09 100644 --- a/crates/state/src/custom_attributes.rs +++ b/crates/state/src/custom_attributes.rs @@ -111,7 +111,7 @@ pub enum CustomAttributeValues { CursorReference(CursorReference), Bytes(Vec), ImageReference(ImageReference), - FocusId(AccessibilityId), + AccessibilityId(AccessibilityId), TextHighlights(Vec<(usize, usize)>), Canvas(CanvasReference), } @@ -123,7 +123,7 @@ impl Debug for CustomAttributeValues { Self::CursorReference(_) => f.debug_tuple("CursorReference").finish(), Self::Bytes(_) => f.debug_tuple("Bytes").finish(), Self::ImageReference(_) => f.debug_tuple("ImageReference").finish(), - Self::FocusId(_) => f.debug_tuple("FocusId").finish(), + Self::AccessibilityId(_) => f.debug_tuple("AccessibilityId").finish(), Self::TextHighlights(_) => f.debug_tuple("TextHighlights").finish(), Self::Canvas(_) => f.debug_tuple("Canvas").finish(), } diff --git a/crates/state/src/values/accessibility.rs b/crates/state/src/values/accessibility.rs index e18f3d112..5e83a4948 100644 --- a/crates/state/src/values/accessibility.rs +++ b/crates/state/src/values/accessibility.rs @@ -11,8 +11,8 @@ use dioxus_native_core_macro::partial_derive_state; use crate::CustomAttributeValues; #[derive(Clone, Debug, PartialEq, Eq, Default, Component)] -pub struct AccessibilityState { - pub focus_id: Option, +pub struct AccessibilityNodeState { + pub accessibility_id: Option, pub role: Option, pub alt: Option, pub name: Option, @@ -20,7 +20,7 @@ pub struct AccessibilityState { } #[partial_derive_state] -impl State for AccessibilityState { +impl State for AccessibilityNodeState { type ParentDependencies = (); type ChildDependencies = (); @@ -44,17 +44,18 @@ impl State for AccessibilityState { _children: Vec<::ElementBorrowed<'a>>, _context: &SendAnyMap, ) -> bool { - let mut accessibility = AccessibilityState::default(); + let mut accessibility = AccessibilityNodeState::default(); if let Some(attributes) = node_view.attributes() { for attr in attributes { #[allow(clippy::single_match)] match attr.attribute.name.as_str() { "focus_id" => { - if let OwnedAttributeValue::Custom(CustomAttributeValues::FocusId(id)) = - attr.value + if let OwnedAttributeValue::Custom( + CustomAttributeValues::AccessibilityId(id), + ) = attr.value { - accessibility.focus_id = Some(*id); + accessibility.accessibility_id = Some(*id); } } "role" => { diff --git a/crates/testing/src/launch.rs b/crates/testing/src/launch.rs index bdd8575c5..ef2b35f45 100644 --- a/crates/testing/src/launch.rs +++ b/crates/testing/src/launch.rs @@ -6,7 +6,7 @@ use freya_common::EventMessage; use freya_core::prelude::*; use freya_dom::prelude::{FreyaDOM, SafeDOM}; use freya_engine::prelude::*; -use freya_hooks::{use_init_accessibility, use_init_focus}; +use freya_hooks::use_init_accessibility; use std::sync::{Arc, Mutex}; use tokio::sync::broadcast; use tokio::sync::mpsc::unbounded_channel; @@ -44,7 +44,7 @@ pub fn launch_test_with_config(root: Component<()>, config: TestingConfig) -> Te config, platform_event_emitter, platform_event_receiver, - accessibility_state: SharedAccessibilityState::default(), + accessibility_state: Arc::new(Mutex::new(AccessibilityState::new(ACCESSIBILITY_ROOT_ID))), ticker_sender: broadcast::channel(5).0, navigation_state: NavigatorState::new(NavigationMode::NotKeyboard), }; @@ -61,7 +61,6 @@ fn with_accessibility(app: Component) -> VirtualDom { #[allow(non_snake_case)] fn Root(cx: Scope) -> Element { - use_init_focus(cx); use_init_accessibility(cx); #[allow(non_snake_case)] diff --git a/crates/testing/src/test_handler.rs b/crates/testing/src/test_handler.rs index 3c6d4b2d2..05b1d7229 100644 --- a/crates/testing/src/test_handler.rs +++ b/crates/testing/src/test_handler.rs @@ -100,10 +100,7 @@ impl TestingHandler { } } EventMessage::FocusAccessibilityNode(node_id) => { - self.accessibility_state - .lock() - .unwrap() - .set_focus(Some(node_id)); + self.accessibility_state.lock().unwrap().set_focus(node_id); } _ => {} } @@ -182,7 +179,7 @@ impl TestingHandler { self.utils.get_node_by_id(root_id) } - pub fn focus_id(&self) -> Option { + pub fn focus_id(&self) -> AccessibilityId { self.accessibility_state.lock().unwrap().focus_id() } } From a03f1b312007aef998a7037c3730b0e452c45b2b Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 10:59:32 +0100 Subject: [PATCH 06/29] chore: Remove unnecessary mask flags on some node states (#481) --- crates/state/src/layout.rs | 5 +---- crates/state/src/transform.rs | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs index e459acc0e..b9e39b1dc 100644 --- a/crates/state/src/layout.rs +++ b/crates/state/src/layout.rs @@ -64,8 +64,7 @@ impl State for LayoutState { "position_bottom", "position_left", ])) - .with_tag() - .with_text(); + .with_tag(); fn update<'a>( &mut self, @@ -85,8 +84,6 @@ impl State for LayoutState { DirectionMode::Horizontal } else if let Some("text") = node_view.tag() { DirectionMode::Horizontal - } else if node_view.text().is_some() { - DirectionMode::Horizontal } else { DirectionMode::Vertical }, diff --git a/crates/state/src/transform.rs b/crates/state/src/transform.rs index 7941a68be..6f4f5f476 100644 --- a/crates/state/src/transform.rs +++ b/crates/state/src/transform.rs @@ -21,10 +21,8 @@ impl State for Transform { type NodeDependencies = (); - const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new() - .with_attrs(AttributeMaskBuilder::Some(&["rotate"])) - .with_tag() - .with_text(); + const NODE_MASK: NodeMaskBuilder<'static> = + NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&["rotate"])); fn update<'a>( &mut self, From 4ebcc4181d201d63969df049d864c321faf07105 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 11:01:46 +0100 Subject: [PATCH 07/29] chore: Move Accessibility node state (#475) --- crates/state/src/{values => }/accessibility.rs | 0 crates/state/src/lib.rs | 2 ++ crates/state/src/values/mod.rs | 2 -- 3 files changed, 2 insertions(+), 2 deletions(-) rename crates/state/src/{values => }/accessibility.rs (100%) diff --git a/crates/state/src/values/accessibility.rs b/crates/state/src/accessibility.rs similarity index 100% rename from crates/state/src/values/accessibility.rs rename to crates/state/src/accessibility.rs diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 42e476699..a8b918a24 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -1,3 +1,4 @@ +mod accessibility; mod cursor; mod custom_attributes; mod font_style; @@ -8,6 +9,7 @@ mod style; mod transform; mod values; +pub use accessibility::*; pub use cursor::*; pub use custom_attributes::*; pub use font_style::*; diff --git a/crates/state/src/values/mod.rs b/crates/state/src/values/mod.rs index 6d851aaaf..f8c6ab347 100644 --- a/crates/state/src/values/mod.rs +++ b/crates/state/src/values/mod.rs @@ -1,4 +1,3 @@ -mod accessibility; mod alignment; mod border; mod color; @@ -15,7 +14,6 @@ mod shadow; mod size; mod text_shadow; -pub use accessibility::*; pub use alignment::*; pub use border::*; pub use color::*; From aa2fa257424575139d142e58a7e641384efacc59 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 11:41:21 +0100 Subject: [PATCH 08/29] chore: Simplify accessibility integration (#484) * chore: Move Accessibility node state * chore: Simplify Accessibility integration by removing unnecessary abstractions * clean up --- .../accessibility/accessibility_manager.rs | 203 ++++++++++++++ .../accessibility/accessibility_provider.rs | 250 ------------------ .../src/accessibility/accessibility_state.rs | 61 ----- crates/core/src/accessibility/mod.rs | 83 +++++- crates/renderer/src/accessibility.rs | 32 +-- crates/renderer/src/app.rs | 10 +- crates/testing/src/launch.rs | 2 +- crates/testing/src/test_handler.rs | 6 +- 8 files changed, 307 insertions(+), 340 deletions(-) create mode 100644 crates/core/src/accessibility/accessibility_manager.rs delete mode 100644 crates/core/src/accessibility/accessibility_provider.rs delete mode 100644 crates/core/src/accessibility/accessibility_state.rs diff --git a/crates/core/src/accessibility/accessibility_manager.rs b/crates/core/src/accessibility/accessibility_manager.rs new file mode 100644 index 000000000..bbe14ec96 --- /dev/null +++ b/crates/core/src/accessibility/accessibility_manager.rs @@ -0,0 +1,203 @@ +use crate::accessibility::*; +use accesskit::{ + Action, DefaultActionVerb, Node, NodeBuilder, NodeClassSet, NodeId as AccessibilityId, Rect, + Role, Tree, TreeUpdate, +}; +use freya_dom::prelude::DioxusNode; +use freya_node_state::AccessibilityNodeState; +use std::sync::{Arc, Mutex}; +use torin::prelude::NodeAreas; + +pub type SharedAccessibilityManager = Arc>; + +pub const ACCESSIBILITY_ROOT_ID: AccessibilityId = AccessibilityId(0); + +/// Manages the Accessibility integration. +pub struct AccessibilityManager { + /// Accessibility Nodes + pub nodes: Vec<(AccessibilityId, Node)>, + /// Accessibility tree + pub node_classes: NodeClassSet, + /// Current focused Accessibility Node. + pub focused_id: AccessibilityId, +} + +impl AccessibilityManager { + pub fn new(focused_id: AccessibilityId) -> Self { + Self { + focused_id, + node_classes: NodeClassSet::default(), + nodes: Vec::default(), + } + } + + /// Wrap it in a `Arc>`. + pub fn wrap(self) -> SharedAccessibilityManager { + Arc::new(Mutex::new(self)) + } + + /// Clear the Accessibility Nodes. + pub fn clear(&mut self) { + self.nodes.clear(); + } + + pub fn push_node(&mut self, id: AccessibilityId, node: Node) { + self.nodes.push((id, node)) + } + + /// Add a Node to the Accessibility Tree. + pub fn add_node( + &mut self, + dioxus_node: &DioxusNode, + node_areas: &NodeAreas, + accessibility_id: AccessibilityId, + node_accessibility: &AccessibilityNodeState, + ) { + let mut builder = NodeBuilder::new(Role::Unknown); + + // Set children + let children = dioxus_node.get_accessibility_children(); + if !children.is_empty() { + builder.set_children(children); + } + + // Set text value + if let Some(alt) = &node_accessibility.alt { + builder.set_value(alt.to_owned()); + } else if let Some(value) = dioxus_node.get_inner_texts() { + builder.set_value(value); + } + + // Set name + if let Some(name) = &node_accessibility.name { + builder.set_name(name.to_owned()); + } + + // Set role + if let Some(role) = node_accessibility.role { + builder.set_role(role); + } + + // Set the area + let area = node_areas.area.to_f64(); + builder.set_bounds(Rect { + x0: area.min_x(), + x1: area.max_x(), + y0: area.min_y(), + y1: area.max_y(), + }); + + // Set focusable action + if node_accessibility.focusable { + builder.add_action(Action::Focus); + } else { + builder.add_action(Action::Default); + builder.set_default_action_verb(DefaultActionVerb::Focus); + } + + // Insert the node into the Tree + let node = builder.build(&mut self.node_classes); + self.push_node(accessibility_id, node); + } + + /// Update the focused Node ID and generate a TreeUpdate if necessary. + pub fn set_focus_with_update(&mut self, new_focus_id: AccessibilityId) -> Option { + self.focused_id = new_focus_id; + + // Only focus the element if it exists + let node_focused_exists = self.nodes.iter().any(|node| node.0 == new_focus_id); + if node_focused_exists { + Some(TreeUpdate { + nodes: Vec::new(), + tree: None, + focus: self.focused_id, + }) + } else { + None + } + } + + /// Create the root Accessibility Node. + pub fn build_root(&mut self, root_name: &str) -> Node { + let mut builder = NodeBuilder::new(Role::Window); + builder.set_name(root_name.to_string()); + builder.set_children( + self.nodes + .iter() + .map(|(id, _)| *id) + .collect::>(), + ); + + builder.build(&mut self.node_classes) + } + + /// Process the Nodes accessibility Tree + pub fn process(&mut self, root_id: AccessibilityId, root_name: &str) -> TreeUpdate { + let root = self.build_root(root_name); + let mut nodes = vec![(root_id, root)]; + nodes.extend(self.nodes.clone()); + nodes.reverse(); + + let focus = self + .nodes + .iter() + .find_map(|node| { + if node.0 == self.focused_id { + Some(node.0) + } else { + None + } + }) + .unwrap_or(ACCESSIBILITY_ROOT_ID); + + TreeUpdate { + nodes, + tree: Some(Tree::new(root_id)), + focus, + } + } + + /// Focus the next/previous Node starting from the currently focused Node. + pub fn set_focus_on_next_node(&mut self, direction: AccessibilityFocusDirection) -> TreeUpdate { + let node_index = self + .nodes + .iter() + .enumerate() + .find(|(_, node)| node.0 == self.focused_id) + .map(|(i, _)| i); + + let target_node = if direction == AccessibilityFocusDirection::Forward { + // Find the next Node + if let Some(node_index) = node_index { + if node_index == self.nodes.len() - 1 { + self.nodes.first() + } else { + self.nodes.get(node_index + 1) + } + } else { + self.nodes.first() + } + } else { + // Find the previous Node + if let Some(node_index) = node_index { + if node_index == 0 { + self.nodes.get(node_index - 1) + } else { + self.nodes.last() + } + } else { + self.nodes.last() + } + }; + + self.focused_id = target_node + .map(|(id, _)| *id) + .unwrap_or(ACCESSIBILITY_ROOT_ID); + + TreeUpdate { + nodes: Vec::new(), + tree: None, + focus: self.focused_id, + } + } +} diff --git a/crates/core/src/accessibility/accessibility_provider.rs b/crates/core/src/accessibility/accessibility_provider.rs deleted file mode 100644 index e30659ec0..000000000 --- a/crates/core/src/accessibility/accessibility_provider.rs +++ /dev/null @@ -1,250 +0,0 @@ -use accesskit::{ - Action, DefaultActionVerb, Node, NodeBuilder, NodeClassSet, NodeId as AccessibilityId, Rect, - Role, Tree, TreeUpdate, -}; -use dioxus_native_core::{ - prelude::{NodeType, TextNode}, - real_dom::NodeImmutable, - NodeId, -}; -use freya_dom::prelude::{DioxusDOM, DioxusNode}; -use freya_node_state::AccessibilityNodeState; -use std::slice::Iter; -use torin::{prelude::NodeAreas, torin::Torin}; - -use crate::layout::*; - -use super::accessibility_state::ACCESSIBILITY_ROOT_ID; - -/// Direction for the next Accessibility Node to be focused. -#[derive(PartialEq)] -pub enum AccessibilityFocusDirection { - Forward, - Backward, -} - -pub trait AccessibilityProvider { - /// Add a Node to the Accessibility Tree. - fn add_node( - &mut self, - dioxus_node: &DioxusNode, - node_areas: &NodeAreas, - accessibility_id: AccessibilityId, - node_accessibility: &AccessibilityNodeState, - ) { - let mut builder = NodeBuilder::new(Role::Unknown); - - // Set children - let children = dioxus_node.get_accessibility_children(); - if !children.is_empty() { - builder.set_children(children); - } - - // Set text value - if let Some(alt) = &node_accessibility.alt { - builder.set_value(alt.to_owned()); - } else if let Some(value) = dioxus_node.get_inner_texts() { - builder.set_value(value); - } - - // Set name - if let Some(name) = &node_accessibility.name { - builder.set_name(name.to_owned()); - } - - // Set role - if let Some(role) = node_accessibility.role { - builder.set_role(role); - } - - // Set the area - let area = node_areas.area.to_f64(); - builder.set_bounds(Rect { - x0: area.min_x(), - x1: area.max_x(), - y0: area.min_y(), - y1: area.max_y(), - }); - - // Set focusable action - if node_accessibility.focusable { - builder.add_action(Action::Focus); - } else { - builder.add_action(Action::Default); - builder.set_default_action_verb(DefaultActionVerb::Focus); - } - - // Insert the node into the Tree - let node = builder.build(self.node_classes()); - self.push_node(accessibility_id, node); - } - - /// Push a Node into the Accesibility Tree. - fn push_node(&mut self, id: AccessibilityId, node: Node); - - /// Mutable reference to the NodeClassSet. - fn node_classes(&mut self) -> &mut NodeClassSet; - - /// Iterator over the Accessibility Tree of Nodes. - fn nodes(&self) -> Iter<(AccessibilityId, Node)>; - - /// Get the currently focused Node's ID. - fn focus_id(&self) -> AccessibilityId; - - /// Update the focused Node ID. - fn set_focus(&mut self, new_focus_id: AccessibilityId); - - /// Update the focused Node ID and generate a TreeUpdate if necessary. - fn set_focus_with_update(&mut self, new_focus_id: AccessibilityId) -> Option { - self.set_focus(new_focus_id); - - // Only focus the element if it exists - let node_focused_exists = self.nodes().any(|node| node.0 == new_focus_id); - if node_focused_exists { - Some(TreeUpdate { - nodes: Vec::new(), - tree: None, - focus: self.focus_id(), - }) - } else { - None - } - } - - /// Create the root Accessibility Node. - fn build_root(&mut self, root_name: &str) -> Node { - let mut builder = NodeBuilder::new(Role::Window); - builder.set_name(root_name.to_string()); - builder.set_children( - self.nodes() - .map(|(id, _)| *id) - .collect::>(), - ); - - builder.build(self.node_classes()) - } - - /// Process the Nodes accessibility Tree - fn process(&mut self, root_id: AccessibilityId, root_name: &str) -> TreeUpdate { - let root = self.build_root(root_name); - let mut nodes = vec![(root_id, root)]; - nodes.extend(self.nodes().cloned()); - nodes.reverse(); - - let focus = self - .nodes() - .find_map(|node| { - if node.0 == self.focus_id() { - Some(node.0) - } else { - None - } - }) - .unwrap_or(ACCESSIBILITY_ROOT_ID); - - TreeUpdate { - nodes, - tree: Some(Tree::new(root_id)), - focus, - } - } - - /// Focus the next/previous Node starting from the currently focused Node. - fn set_focus_on_next_node(&mut self, direction: AccessibilityFocusDirection) -> TreeUpdate { - let node_index = self - .nodes() - .enumerate() - .find(|(_, node)| node.0 == self.focus_id()) - .map(|(i, _)| i) - .unwrap(); - - let target_node_index = if direction == AccessibilityFocusDirection::Forward { - // Find the next Node - if node_index == self.nodes().len() - 1 { - 0 - } else { - node_index + 1 - } - } else { - // Find the previous Node - if node_index == 0 { - self.nodes().len() - 1 - } else { - node_index - 1 - } - }; - - let target_node = self - .nodes() - .nth(target_node_index) - .map(|(id, _)| *id) - .unwrap_or(ACCESSIBILITY_ROOT_ID); - - self.set_focus(target_node); - - TreeUpdate { - nodes: Vec::new(), - tree: None, - focus: self.focus_id(), - } - } -} - -/// Shortcut functions to retrieve Acessibility info from a Dioxus Node -trait NodeAccessibility { - /// Return the first TextNode from this Node - fn get_inner_texts(&self) -> Option; - - /// Collect all the AccessibilityIDs from a Node's children - fn get_accessibility_children(&self) -> Vec; -} - -impl NodeAccessibility for DioxusNode<'_> { - /// Return the first TextNode from this Node - fn get_inner_texts(&self) -> Option { - let children = self.children(); - let first_child = children.first()?; - let node_type = first_child.node_type(); - if let NodeType::Text(TextNode { text, .. }) = &*node_type { - Some(text.to_owned()) - } else { - None - } - } - - /// Collect all the AccessibilityIDs from a Node's AccessibilityNodeState - fn get_accessibility_children(&self) -> Vec { - self.children() - .iter() - .filter_map(|child| { - let node_accessibility = &*child.get::().unwrap(); - node_accessibility.accessibility_id - }) - .collect::>() - } -} - -pub fn process_accessibility( - layers: &Layers, - layout: &Torin, - rdom: &DioxusDOM, - access_provider: &mut impl AccessibilityProvider, -) { - for layer in layers.layers.values() { - for node_id in layer { - let node_areas = layout.get(*node_id).unwrap(); - let dioxus_node = rdom.get(*node_id); - if let Some(dioxus_node) = dioxus_node { - let node_accessibility = &*dioxus_node.get::().unwrap(); - if let Some(accessibility_id) = node_accessibility.accessibility_id { - access_provider.add_node( - &dioxus_node, - node_areas, - accessibility_id, - node_accessibility, - ); - } - } - } - } -} diff --git a/crates/core/src/accessibility/accessibility_state.rs b/crates/core/src/accessibility/accessibility_state.rs deleted file mode 100644 index 0a4fd0d15..000000000 --- a/crates/core/src/accessibility/accessibility_state.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::accessibility::*; -use accesskit::{Node, NodeClassSet, NodeId as AccessibilityId}; -use std::sync::{Arc, Mutex}; - -pub type SharedAccessibilityState = Arc>; - -pub const ACCESSIBILITY_ROOT_ID: AccessibilityId = AccessibilityId(0); - -/// Manages the Accessibility integration. -pub struct AccessibilityState { - /// Accessibility Nodes - pub nodes: Vec<(AccessibilityId, Node)>, - - /// Accessibility tree - pub node_classes: NodeClassSet, - - /// Current focused Accessibility Node. - pub focused_id: AccessibilityId, -} - -impl AccessibilityState { - pub fn new(focused_id: AccessibilityId) -> Self { - Self { - focused_id, - node_classes: NodeClassSet::default(), - nodes: Vec::default(), - } - } - - /// Wrap it in a `Arc>`. - pub fn wrap(self) -> SharedAccessibilityState { - Arc::new(Mutex::new(self)) - } - - /// Clear the Accessibility Nodes. - pub fn clear(&mut self) { - self.nodes.clear(); - } -} - -impl AccessibilityProvider for AccessibilityState { - fn node_classes(&mut self) -> &mut NodeClassSet { - &mut self.node_classes - } - - fn nodes(&self) -> std::slice::Iter<(AccessibilityId, Node)> { - self.nodes.iter() - } - - fn focus_id(&self) -> AccessibilityId { - self.focused_id - } - - fn set_focus(&mut self, new_focus_id: AccessibilityId) { - self.focused_id = new_focus_id; - } - - fn push_node(&mut self, id: AccessibilityId, node: Node) { - self.nodes.push((id, node)) - } -} diff --git a/crates/core/src/accessibility/mod.rs b/crates/core/src/accessibility/mod.rs index 0ef7059dd..b202b16ed 100644 --- a/crates/core/src/accessibility/mod.rs +++ b/crates/core/src/accessibility/mod.rs @@ -1,5 +1,80 @@ -pub mod accessibility_provider; -pub mod accessibility_state; +pub mod accessibility_manager; +pub use accessibility_manager::*; -pub use accessibility_provider::*; -pub use accessibility_state::*; +use accesskit::NodeId as AccessibilityId; +use dioxus_native_core::{ + node::{NodeType, TextNode}, + real_dom::NodeImmutable, + NodeId, +}; +use freya_dom::dom::{DioxusDOM, DioxusNode}; +use freya_node_state::AccessibilityNodeState; +use torin::torin::Torin; + +use crate::layout::Layers; + +/// Direction for the next Accessibility Node to be focused. +#[derive(PartialEq)] +pub enum AccessibilityFocusDirection { + Forward, + Backward, +} + +/// Shortcut functions to retrieve Acessibility info from a Dioxus Node +trait NodeAccessibility { + /// Return the first TextNode from this Node + fn get_inner_texts(&self) -> Option; + + /// Collect all the AccessibilityIDs from a Node's children + fn get_accessibility_children(&self) -> Vec; +} + +impl NodeAccessibility for DioxusNode<'_> { + /// Return the first TextNode from this Node + fn get_inner_texts(&self) -> Option { + let children = self.children(); + let first_child = children.first()?; + let node_type = first_child.node_type(); + if let NodeType::Text(TextNode { text, .. }) = &*node_type { + Some(text.to_owned()) + } else { + None + } + } + + /// Collect all the AccessibilityIDs from a Node's children + fn get_accessibility_children(&self) -> Vec { + self.children() + .iter() + .filter_map(|child| { + let node_accessibility = &*child.get::().unwrap(); + node_accessibility.accessibility_id + }) + .collect::>() + } +} + +pub fn process_accessibility( + layers: &Layers, + layout: &Torin, + rdom: &DioxusDOM, + accessibility_manager: &mut AccessibilityManager, +) { + for layer in layers.layers.values() { + for node_id in layer { + let node_areas = layout.get(*node_id).unwrap(); + let dioxus_node = rdom.get(*node_id); + if let Some(dioxus_node) = dioxus_node { + let node_accessibility = &*dioxus_node.get::().unwrap(); + if let Some(accessibility_id) = node_accessibility.accessibility_id { + accessibility_manager.add_node( + &dioxus_node, + node_areas, + accessibility_id, + node_accessibility, + ); + } + } + } + } +} diff --git a/crates/renderer/src/accessibility.rs b/crates/renderer/src/accessibility.rs index b07409243..9419d6a6a 100644 --- a/crates/renderer/src/accessibility.rs +++ b/crates/renderer/src/accessibility.rs @@ -3,48 +3,48 @@ use accesskit_winit::Adapter; use freya_common::EventMessage; use freya_core::{ prelude::{ - AccessibilityFocusDirection, AccessibilityProvider, AccessibilityState, - SharedAccessibilityState, ACCESSIBILITY_ROOT_ID, + AccessibilityFocusDirection, AccessibilityManager, SharedAccessibilityManager, + ACCESSIBILITY_ROOT_ID, }, types::FocusSender, }; use winit::{event::WindowEvent, event_loop::EventLoopProxy, window::Window}; /// Manages the accessibility integration with Accesskit. -pub struct NativeAccessibility { - accessibility_state: SharedAccessibilityState, +pub struct AccessKitManager { + accessibility_manager: SharedAccessibilityManager, accessibility_adapter: Adapter, } -impl NativeAccessibility { +impl AccessKitManager { pub fn new(window: &Window, proxy: EventLoopProxy) -> Self { let title = window.title(); - let accessibility_state = AccessibilityState::new(ACCESSIBILITY_ROOT_ID).wrap(); + let accessibility_manager = AccessibilityManager::new(ACCESSIBILITY_ROOT_ID).wrap(); let accessibility_adapter = { - let accessibility_state = accessibility_state.clone(); + let accessibility_manager = accessibility_manager.clone(); Adapter::new( window, move || { - let mut accessibility_state = accessibility_state.lock().unwrap(); - accessibility_state.process(ACCESSIBILITY_ROOT_ID, title.as_str()) + let mut accessibility_manager = accessibility_manager.lock().unwrap(); + accessibility_manager.process(ACCESSIBILITY_ROOT_ID, title.as_str()) }, proxy, ) }; Self { - accessibility_state, + accessibility_manager, accessibility_adapter, } } - pub fn accessibility_state(&self) -> &SharedAccessibilityState { - &self.accessibility_state + pub fn accessibility_manager(&self) -> &SharedAccessibilityManager { + &self.accessibility_manager } /// Focus a new accessibility node pub fn set_accessibility_focus(&mut self, id: AccessibilityId) { let tree = self - .accessibility_state + .accessibility_manager .lock() .unwrap() .set_focus_with_update(id); @@ -60,13 +60,13 @@ impl NativeAccessibility { /// Remove the accessibility nodes pub fn clear_accessibility(&mut self) { - self.accessibility_state.lock().unwrap().clear(); + self.accessibility_manager.lock().unwrap().clear(); } /// Process the accessibility nodes pub fn render_accessibility(&mut self, title: &str) { let tree = self - .accessibility_state + .accessibility_manager .lock() .unwrap() .process(ACCESSIBILITY_ROOT_ID, title); @@ -80,7 +80,7 @@ impl NativeAccessibility { focus_sender: &FocusSender, ) { let tree = self - .accessibility_state + .accessibility_manager .lock() .unwrap() .set_focus_on_next_node(direction); diff --git a/crates/renderer/src/app.rs b/crates/renderer/src/app.rs index 7bdee026e..525d26e1a 100644 --- a/crates/renderer/src/app.rs +++ b/crates/renderer/src/app.rs @@ -20,7 +20,7 @@ use uuid::Uuid; use winit::event::WindowEvent; use winit::{dpi::PhysicalSize, event_loop::EventLoopProxy}; -use crate::accessibility::NativeAccessibility; +use crate::accessibility::AccessKitManager; use crate::{FontsConfig, HoveredNode, WindowEnv}; fn winit_waker(proxy: &EventLoopProxy) -> std::task::Waker { @@ -61,7 +61,7 @@ pub struct App { focus_sender: FocusSender, focus_receiver: FocusReceiver, - accessibility: NativeAccessibility, + accessibility: AccessKitManager, font_collection: FontCollection, @@ -82,7 +82,7 @@ impl App { fonts_config: FontsConfig, mut plugins: PluginsManager, ) -> Self { - let accessibility = NativeAccessibility::new(&window_env.window, proxy.clone()); + let accessibility = AccessKitManager::new(&window_env.window, proxy.clone()); window_env.window_mut().set_visible(true); @@ -283,7 +283,7 @@ impl App { layers, &layout, rdom, - &mut *self.accessibility.accessibility_state().lock().unwrap(), + &mut self.accessibility.accessibility_manager().lock().unwrap(), ); } @@ -348,7 +348,7 @@ impl App { &mut self.window_env } - pub fn accessibility(&mut self) -> &mut NativeAccessibility { + pub fn accessibility(&mut self) -> &mut AccessKitManager { &mut self.accessibility } diff --git a/crates/testing/src/launch.rs b/crates/testing/src/launch.rs index ef2b35f45..10b507e27 100644 --- a/crates/testing/src/launch.rs +++ b/crates/testing/src/launch.rs @@ -44,7 +44,7 @@ pub fn launch_test_with_config(root: Component<()>, config: TestingConfig) -> Te config, platform_event_emitter, platform_event_receiver, - accessibility_state: Arc::new(Mutex::new(AccessibilityState::new(ACCESSIBILITY_ROOT_ID))), + accessibility_manager: AccessibilityManager::new(ACCESSIBILITY_ROOT_ID).wrap(), ticker_sender: broadcast::channel(5).0, navigation_state: NavigatorState::new(NavigationMode::NotKeyboard), }; diff --git a/crates/testing/src/test_handler.rs b/crates/testing/src/test_handler.rs index 05b1d7229..b2cba2910 100644 --- a/crates/testing/src/test_handler.rs +++ b/crates/testing/src/test_handler.rs @@ -30,7 +30,7 @@ pub struct TestingHandler { pub(crate) elements_state: ElementsState, pub(crate) font_collection: FontCollection, pub(crate) viewports: Viewports, - pub(crate) accessibility_state: SharedAccessibilityState, + pub(crate) accessibility_manager: SharedAccessibilityManager, pub(crate) config: TestingConfig, @@ -100,7 +100,7 @@ impl TestingHandler { } } EventMessage::FocusAccessibilityNode(node_id) => { - self.accessibility_state.lock().unwrap().set_focus(node_id); + self.accessibility_manager.lock().unwrap().focused_id = node_id; } _ => {} } @@ -180,6 +180,6 @@ impl TestingHandler { } pub fn focus_id(&self) -> AccessibilityId { - self.accessibility_state.lock().unwrap().focus_id() + self.accessibility_manager.lock().unwrap().focused_id } } From 28f50085f9c813f7e9add5647801d22813e8c637 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 12:09:02 +0100 Subject: [PATCH 09/29] feat: Partial IME Support (#459) * feat: IME Support * chore: Update winit and accesskit * refinements * improvements * temp fix * improvements and fixes * improvements * fixes * fix * fix * clean up --- crates/components/src/input.rs | 2 ++ crates/hooks/src/text_editor.rs | 12 ++++++-- crates/renderer/src/accessibility.rs | 42 ++++++++++++++++++++++++++-- crates/renderer/src/app.rs | 19 +++++++++---- crates/renderer/src/event_loop.rs | 25 ++++++++++++----- crates/renderer/src/window.rs | 6 ++++ 6 files changed, 88 insertions(+), 18 deletions(-) diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs index 20ac7f65f..e391e4d4d 100644 --- a/crates/components/src/input.rs +++ b/crates/components/src/input.rs @@ -194,6 +194,8 @@ pub fn Input<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { corner_radius: "10", margin: "{margin}", cursor_reference: cursor_attr, + focus_id: focus_manager.attribute(cx), + role: "textInput", main_align: "center", paragraph { margin: "8 12", diff --git a/crates/hooks/src/text_editor.rs b/crates/hooks/src/text_editor.rs index 90dbc791d..587d74de5 100644 --- a/crates/hooks/src/text_editor.rs +++ b/crates/hooks/src/text_editor.rs @@ -387,8 +387,9 @@ pub trait TextEditor: Sized + Clone + Display { } _ => { if let Ok(ch) = character.parse::() { - if !ch.is_ascii_control() { - // Adds a new character + // https://github.com/marc2332/freya/issues/461 + if !ch.is_ascii_control() && ch.len_utf8() <= 2 { + // Inserts a character let char_idx = self.line_to_char(self.cursor_row()) + self.cursor_col(); self.insert(character, char_idx); @@ -396,6 +397,13 @@ pub trait TextEditor: Sized + Clone + Display { event.insert(TextEvent::TEXT_CHANGED); } + } else if character.is_ascii() { + // Inserts a text + let char_idx = self.line_to_char(self.cursor_row()) + self.cursor_col(); + self.insert(character, char_idx); + self.set_cursor_pos(char_idx + character.len()); + + event.insert(TextEvent::TEXT_CHANGED); } } } diff --git a/crates/renderer/src/accessibility.rs b/crates/renderer/src/accessibility.rs index 9419d6a6a..56670d39b 100644 --- a/crates/renderer/src/accessibility.rs +++ b/crates/renderer/src/accessibility.rs @@ -8,7 +8,12 @@ use freya_core::{ }, types::FocusSender, }; -use winit::{event::WindowEvent, event_loop::EventLoopProxy, window::Window}; +use winit::{ + dpi::{LogicalPosition, LogicalSize}, + event::WindowEvent, + event_loop::EventLoopProxy, + window::Window, +}; /// Manages the accessibility integration with Accesskit. pub struct AccessKitManager { @@ -42,17 +47,43 @@ impl AccessKitManager { } /// Focus a new accessibility node - pub fn set_accessibility_focus(&mut self, id: AccessibilityId) { + pub fn set_accessibility_focus(&self, id: AccessibilityId, window: &Window) { let tree = self .accessibility_manager .lock() .unwrap() .set_focus_with_update(id); if let Some(tree) = tree { + // Update the IME Cursor area + self.update_ime_position(tree.focus, window); + + // Update the adapter self.accessibility_adapter.update_if_active(|| tree); } } + fn update_ime_position(&self, accessibility_id: AccessibilityId, window: &Window) { + let accessibility_manager = self.accessibility_manager.lock().unwrap(); + let node = accessibility_manager + .nodes + .iter() + .find_map(|(id, n)| { + if *id == accessibility_id { + Some(n) + } else { + None + } + }) + .unwrap(); + let node_bounds = node.bounds(); + if let Some(node_bounds) = node_bounds { + window.set_ime_cursor_area( + LogicalPosition::new(node_bounds.min_x(), node_bounds.min_y()), + LogicalSize::new(node_bounds.width(), node_bounds.height()), + ) + } + } + /// Process an Accessibility event pub fn process_accessibility_event(&mut self, window: &Window, event: &WindowEvent) { self.accessibility_adapter.process_event(window, event) @@ -75,9 +106,10 @@ impl AccessKitManager { /// Focus the next accessibility node pub fn focus_next_node( - &mut self, + &self, direction: AccessibilityFocusDirection, focus_sender: &FocusSender, + window: &Window, ) { let tree = self .accessibility_manager @@ -89,6 +121,10 @@ impl AccessKitManager { .send(tree.focus) .expect("Failed to focus the Node."); + // Update the IME Cursor area + self.update_ime_position(tree.focus, window); + + // Update the Adapter self.accessibility_adapter.update_if_active(|| tree); } } diff --git a/crates/renderer/src/app.rs b/crates/renderer/src/app.rs index 525d26e1a..d5aea02e2 100644 --- a/crates/renderer/src/app.rs +++ b/crates/renderer/src/app.rs @@ -344,12 +344,16 @@ impl App { ); } - pub fn window_env(&mut self) -> &mut WindowEnv { + pub fn window_env(&self) -> &WindowEnv { + &self.window_env + } + + pub fn window_env_mut(&mut self) -> &mut WindowEnv { &mut self.window_env } - pub fn accessibility(&mut self) -> &mut AccessKitManager { - &mut self.accessibility + pub fn accessibility(&self) -> &AccessKitManager { + &self.accessibility } pub fn process_accessibility_event(&mut self, event: &WindowEvent) { @@ -357,9 +361,12 @@ impl App { .process_accessibility_event(&self.window_env.window, event) } - pub fn focus_next_node(&mut self, direction: AccessibilityFocusDirection) { - self.accessibility - .focus_next_node(direction, &self.focus_sender) + pub fn focus_next_node(&self, direction: AccessibilityFocusDirection) { + self.accessibility.focus_next_node( + direction, + &self.focus_sender, + self.window_env().window(), + ) } pub fn tick(&self) { diff --git a/crates/renderer/src/event_loop.rs b/crates/renderer/src/event_loop.rs index 96a1a8a9d..47f93c906 100644 --- a/crates/renderer/src/event_loop.rs +++ b/crates/renderer/src/event_loop.rs @@ -3,11 +3,12 @@ use accesskit_winit::ActionRequestEvent; use freya_common::EventMessage; use freya_core::prelude::*; use freya_elements::events::keyboard::{ - map_winit_key, map_winit_modifiers, map_winit_physical_key, + map_winit_key, map_winit_modifiers, map_winit_physical_key, Code, Key, }; use torin::geometry::CursorPoint; use winit::event::{ - ElementState, Event, KeyEvent, MouseScrollDelta, StartCause, Touch, TouchPhase, WindowEvent, + ElementState, Event, Ime, KeyEvent, MouseScrollDelta, StartCause, Touch, TouchPhase, + WindowEvent, }; use winit::event_loop::{EventLoop, EventLoopProxy}; use winit::keyboard::{KeyCode, ModifiersState, PhysicalKey}; @@ -29,7 +30,7 @@ pub fn run_event_loop( let mut cursor_pos = CursorPoint::default(); let mut modifiers_state = ModifiersState::empty(); - let window_env = app.window_env(); + let window_env = app.window_env_mut(); window_env.run_on_setup(); @@ -39,10 +40,11 @@ pub fn run_event_loop( _ = proxy.send_event(EventMessage::PollVDOM); } Event::UserEvent(EventMessage::FocusAccessibilityNode(id)) => { - app.accessibility().set_accessibility_focus(id); + app.accessibility() + .set_accessibility_focus(id, app.window_env().window()); } Event::UserEvent(EventMessage::RequestRerender) => { - app.window_env().window_mut().request_redraw(); + app.window_env_mut().window_mut().request_redraw(); } Event::UserEvent(EventMessage::RequestRedraw) => app.render(&hovered_node), Event::UserEvent(EventMessage::RequestRelayout) => { @@ -56,7 +58,8 @@ pub fn run_event_loop( .. })) => { if Action::Focus == request.action { - app.accessibility().set_accessibility_focus(request.target); + app.accessibility() + .set_accessibility_focus(request.target, app.window_env().window()); } } Event::UserEvent(EventMessage::SetCursorIcon(icon)) => { @@ -77,6 +80,14 @@ pub fn run_event_loop( app.process_accessibility_event(&event); match event { WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::Ime(Ime::Commit(text)) => { + app.send_event(FreyaEvent::Keyboard { + name: "keydown".to_string(), + key: Key::Character(text), + code: Code::Unidentified, + modifiers: map_winit_modifiers(modifiers_state), + }); + } WindowEvent::RedrawRequested => { app.process_layout(); app.render(&hovered_node); @@ -195,7 +206,7 @@ pub fn run_event_loop( } } Event::LoopExiting => { - app.window_env().run_on_exit(); + app.window_env_mut().run_on_exit(); } _ => (), }) diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index d7d33a183..ea083288c 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -102,6 +102,7 @@ impl WindowEnv { .unwrap(); let mut window = window.expect("Could not create window with OpenGL context"); + window.set_ime_allowed(true); let raw_window_handle = window.raw_window_handle(); let context_attributes = ContextAttributesBuilder::new() @@ -210,6 +211,11 @@ impl WindowEnv { &mut self.window } + /// Get a reference to the Window. + pub fn window(&self) -> &Window { + &self.window + } + /// Measure the layout pub fn process_layout( &mut self, From 9545d109542f7c702f8612741bb4efffc07a74c6 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 28 Jan 2024 12:09:11 +0100 Subject: [PATCH 10/29] feat: Support individual vertical gaps but shared horizontal gaps (#476) * feat: Add support for individual vertical gaps but same horizontal * docs * typo * improvement --- book/src/guides/layout.md | 4 ++-- .../elements/src/_docs/attributes/margin.md | 3 ++- .../elements/src/_docs/attributes/padding.md | 3 ++- crates/state/src/values/gaps.rs | 19 ++++++++++++++++++ crates/state/tests/parse_gaps.rs | 6 ++++++ examples/paddings.rs | 20 +++++++++++++++---- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/book/src/guides/layout.md b/book/src/guides/layout.md index 7ebcd4d57..ea49c7752 100644 --- a/book/src/guides/layout.md +++ b/book/src/guides/layout.md @@ -170,7 +170,7 @@ fn app(cx: Scope) -> Element { ### padding -Specify the inner paddings of an element. You can do so by three different ways, just like in CSS. +Specify the inner paddings of an element. You can do so by three different ways. ```rust, no_run fn app(cx: Scope) -> Element { @@ -209,7 +209,7 @@ fn app(cx: Scope) -> Element { ### margin -Specify the margin of an element. You can do so by three different ways, just like in CSS. +Specify the margin of an element. You can do so by three different ways. ```rust, no_run fn app(cx: Scope) -> Element { diff --git a/crates/elements/src/_docs/attributes/margin.md b/crates/elements/src/_docs/attributes/margin.md index b8c049b37..05dfa3e37 100644 --- a/crates/elements/src/_docs/attributes/margin.md +++ b/crates/elements/src/_docs/attributes/margin.md @@ -1,5 +1,5 @@ Specify the margin of an element. -You can do so by three different ways, just like in CSS. +You can do so by four different ways, just like in CSS. ### Example @@ -10,6 +10,7 @@ fn app(cx: Scope) -> Element { rect { margin: "25", // 25 in all sides margin: "100 50", // 100 in top and bottom, and 50 in left and right + margin: "2 15 25", // 2 in top, 15 in left and right, and 25 in bottom margin: "5 7 3 9" // 5 in top, 7 in right, 3 in bottom and 9 in left } ) diff --git a/crates/elements/src/_docs/attributes/padding.md b/crates/elements/src/_docs/attributes/padding.md index 1d4243df1..f04c2fd94 100644 --- a/crates/elements/src/_docs/attributes/padding.md +++ b/crates/elements/src/_docs/attributes/padding.md @@ -1,4 +1,4 @@ -Specify the inner paddings of an element. You can do so by three different ways, just like in CSS. +Specify the inner paddings of an element. You can do so by four different ways, just like in CSS. ### Example @@ -9,6 +9,7 @@ fn app(cx: Scope) -> Element { rect { padding: "25", // 25 in all sides padding: "100 50", // 100 in top and bottom, and 50 in left and right + padding: "2 15 25", // 2 in top, 15 in left and right, and 25 in bottom padding: "5 7 3 9" // 5 in top, 7 in right, 3 in bottom and 9 in left } ) diff --git a/crates/state/src/values/gaps.rs b/crates/state/src/values/gaps.rs index b86d260fe..ddad6455a 100644 --- a/crates/state/src/values/gaps.rs +++ b/crates/state/src/values/gaps.rs @@ -43,6 +43,25 @@ impl Parse for Gaps { .map_err(|_| ParseGapError)?, ) } + // Individual vertical but same horizontal + 3 => { + let top = values + .next() + .ok_or(ParseGapError)? + .parse::() + .map_err(|_| ParseGapError)?; + let left_and_right = values + .next() + .ok_or(ParseGapError)? + .parse::() + .map_err(|_| ParseGapError)?; + let bottom = values + .next() + .ok_or(ParseGapError)? + .parse::() + .map_err(|_| ParseGapError)?; + paddings = Gaps::new(top, left_and_right, bottom, left_and_right); + } // Each directions 4 => { paddings = Gaps::new( diff --git a/crates/state/tests/parse_gaps.rs b/crates/state/tests/parse_gaps.rs index 53b16931f..9150a9244 100644 --- a/crates/state/tests/parse_gaps.rs +++ b/crates/state/tests/parse_gaps.rs @@ -18,3 +18,9 @@ fn parse_sides_gaps() { let gaps = Gaps::parse("1 2 3 4"); assert_eq!(gaps, Ok(Gaps::new(1.0, 2.0, 3.0, 4.0))); } + +#[test] +fn parse_horizontal_axis_and_vertical_sides() { + let gaps = Gaps::parse("5 50 30"); + assert_eq!(gaps, Ok(Gaps::new(5.0, 50.0, 30.0, 50.0))); +} diff --git a/examples/paddings.rs b/examples/paddings.rs index 7612a0741..d5de303e5 100644 --- a/examples/paddings.rs +++ b/examples/paddings.rs @@ -13,7 +13,7 @@ fn app(cx: Scope) -> Element { render!( rect { overflow: "clip", - height: "33%", + height: "25%", width: "100%", padding: "15", background: "black", @@ -25,7 +25,7 @@ fn app(cx: Scope) -> Element { } rect { overflow: "clip", - height: "33%", + height: "25%", width: "100%", padding: "10 30 50 70", background: "gray", @@ -37,10 +37,22 @@ fn app(cx: Scope) -> Element { } rect { overflow: "clip", - height: "33%", + height: "25%", width: "100%", padding: "25 125", - background: "white", + background: "black", + rect { + height: "100%", + width: "100%", + background: "yellow" + } + } + rect { + overflow: "clip", + height: "25%", + width: "100%", + padding: "30 50 10", + background: "gray", rect { height: "100%", width: "100%", From aa6447912736f3c0dd61d9f37e93be29aa4a338a Mon Sep 17 00:00:00 2001 From: tigerros Date: Mon, 29 Jan 2024 19:20:50 +0100 Subject: [PATCH 11/29] docs: Remove outdated info from theming docs (#467) * Update theming.rs * Update theming.rs --- crates/freya/src/_docs/theming.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/freya/src/_docs/theming.rs b/crates/freya/src/_docs/theming.rs index a9602cb24..583a848d4 100644 --- a/crates/freya/src/_docs/theming.rs +++ b/crates/freya/src/_docs/theming.rs @@ -1,9 +1,9 @@ //! # Theming //! -//! Freya has built-in support for Theming. -//! //!
⚠️ As of 2023-12-19, extending the base theme is not supported.
//! +//! Freya has built-in support for Theming. +//! //! ### Accessing the current theme //! //! You can access the current theme via the `use_get_theme` hook. @@ -147,8 +147,6 @@ //! } //! ``` //! -//!
⚠️ The comma after the last field in the `theme_with!` macro is required.
-//! //! As you can see, it removes the need for the "With" suffix, because that is already in the macro name. //! More importantly, though, it wraps each field in a `Some`, and adds the spread. //! From 2bdbd6ee786d1299619e589218f32eb178e71076 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 19:29:36 +0100 Subject: [PATCH 12/29] chore(deps): update codecov/codecov-action action to v4 (#488) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8162790a8..54de89320 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -51,7 +51,7 @@ jobs: rustup component add llvm-tools-preview curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - ./grcov . --binary-path ./target/debug/deps -s . -t lcov --branch --ignore-not-existing --ignore "../*" --ignore "/*" -o cov.lcov - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 if: runner.os == 'Linux' with: token: ${{ secrets.CODECOV_TOKEN }} From 10ec040e83586582c99ebea42e3c86df694eba1e Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 1 Feb 2024 15:33:31 +0000 Subject: [PATCH 13/29] =?UTF-8?q?Deploying=20to=20main=20from=20@=20marc23?= =?UTF-8?q?32/freya@2bdbd6ee786d1299619e589218f32eb178e71076=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f38c6e2fa..44d14844a 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ fn app(cx: Scope) -> Element { Thanks to my sponsors for supporting this project! 😄 -Alberto MendezStephen Andary +Alberto MendezAndar1an ### Want to try it? 🤔 From 9abeb8cfd32aa4315fc84b835e469c9535facb91 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 14:58:49 +0100 Subject: [PATCH 14/29] bump dx --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c54e543ed..b7404d453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,16 +26,16 @@ freya-testing = { path = "crates/testing", version = "0.1" } freya-engine = { path = "crates/engine", version = "0.1" } torin = { path = "crates/torin", version = "0.1" } -dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} -dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be" } -dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be", features = ["hot_reload"] } -dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be", features = ["dioxus"] } -dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be" } -dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be" } -dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be" } -dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be" } -dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be", features = ["file_watcher"], default-features = false } -dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "53380c9956c7dda54d9251d3bc48eaa0ec4e89be", default-features = false } +dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} +dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } +dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["hot_reload"] } +dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["dioxus"] } +dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } +dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } +dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } +dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } +dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["file_watcher"], default-features = false } +dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", default-features = false } skia-safe = { version = "0.67.0", features = ["gl", "textlayout", "svg"] } From f5c2f6aa4ddaf3837c9080c76ba547384f08a5b2 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 18:23:07 +0100 Subject: [PATCH 15/29] improvement --- examples/floating_editors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/floating_editors.rs b/examples/floating_editors.rs index 4c74775b2..64e181c76 100644 --- a/examples/floating_editors.rs +++ b/examples/floating_editors.rs @@ -320,7 +320,7 @@ fn Editor() -> Element { } }; - let onclick = { + let onglobalclick = { to_owned![editable]; move |_: MouseEvent| { editable.process_event(&EditableEvent::Click); @@ -361,7 +361,7 @@ fn Editor() -> Element { cursor_id: "{cursor_id}", onmousedown, onmouseover, - onclick, + onglobalclick, highlights: highlights, text { color: "rgb(240, 240, 240)", From 722cbc54e51dfd757cae081be76a438bdbd2288f Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 20:41:09 +0100 Subject: [PATCH 16/29] fixes --- crates/components/src/cursor_area.rs | 2 +- crates/components/src/drag_drop.rs | 24 ++++++------------- crates/components/src/dropdown.rs | 6 ++--- crates/components/src/external_link.rs | 2 +- crates/components/src/gesture_area.rs | 1 - .../src/scroll_views/scroll_view.rs | 2 +- .../src/scroll_views/virtual_scroll_view.rs | 8 +++---- crates/components/src/switch.rs | 2 +- crates/hooks/src/use_accessibility.rs | 8 +++---- crates/hooks/src/use_animation_transition.rs | 5 ++-- crates/hooks/src/use_camera.rs | 4 ++-- crates/hooks/src/use_canvas.rs | 3 ++- crates/hooks/src/use_editable.rs | 2 +- crates/hooks/src/use_node.rs | 4 ++-- examples/app_dog.rs | 1 - examples/corner_radius.rs | 1 - examples/counter.rs | 1 - examples/floating_editors.rs | 1 - examples/image.rs | 1 - examples/input.rs | 1 - examples/massive.rs | 1 - examples/switch_theme.rs | 1 - examples/virtual_scroll_view.rs | 1 - 23 files changed, 32 insertions(+), 50 deletions(-) diff --git a/crates/components/src/cursor_area.rs b/crates/components/src/cursor_area.rs index 6ec97df05..39b6c3c92 100644 --- a/crates/components/src/cursor_area.rs +++ b/crates/components/src/cursor_area.rs @@ -39,7 +39,7 @@ pub struct CursorAreaProps { #[allow(non_snake_case)] pub fn CursorArea(CursorAreaProps { children, icon }: CursorAreaProps) -> Element { let platform = use_platform(); - let is_hovering = use_signal(|| false); + let mut is_hovering = use_signal(|| false); let onmouseover = { to_owned![platform, is_hovering]; diff --git a/crates/components/src/drag_drop.rs b/crates/components/src/drag_drop.rs index 461534f2b..8c224092f 100644 --- a/crates/components/src/drag_drop.rs +++ b/crates/components/src/drag_drop.rs @@ -24,7 +24,7 @@ pub fn DragProvider(DragProviderProps { children }: DragProviderProp /// [`DragZone`] component properties. #[derive(Props, Clone, PartialEq)] -pub struct DragZoneProps { +pub struct DragZoneProps { /// Element visible when dragging the element. This follows the cursor. drag_element: Element, /// Inner children for the DropZone. @@ -39,14 +39,14 @@ pub struct DragZoneProps { /// See [`DragZoneProps`]. /// #[allow(non_snake_case)] -pub fn DragZone( +pub fn DragZone( DragZoneProps { data, children, drag_element, }: DragZoneProps, ) -> Element { - let drags = use_context::>>(); + let mut drags = use_context::>>(); let mut dragging = use_signal(|| false); let mut pos = use_signal(CursorPoint::default); let (node_reference, size) = use_node_signal(); @@ -108,31 +108,22 @@ pub fn DragZone( } /// [`DropZone`] component properties. -#[derive(Props, PartialEq)] -pub struct DropZoneProps { +#[derive(Props, PartialEq, Clone)] +pub struct DropZoneProps { /// Inner children for the DropZone. children: Element, /// Handler for the `ondrop` event. ondrop: EventHandler, } -impl Clone for DropZoneProps { - fn clone(&self) -> Self { - Self { - children: self.children.clone(), - ondrop: self.ondrop.clone(), - } - } -} - /// Elements from [`DragZone`]s can be dropped here. /// /// # Props /// See [`DropZoneProps`]. /// #[allow(non_snake_case)] -pub fn DropZone(props: DropZoneProps) -> Element { - let drags = use_context::>>(); +pub fn DropZone(props: DropZoneProps) -> Element { + let mut drags = use_context::>>(); let onclick = move |_: MouseEvent| { if let Some(current_drags) = &*drags.read() { @@ -153,7 +144,6 @@ pub fn DropZone(props: DropZoneProps) -> Element { #[cfg(test)] mod test { - use dioxus::signals::use_signal; use freya::prelude::*; use freya_testing::{events::pointer::MouseButton, launch_test, FreyaEvent}; diff --git a/crates/components/src/dropdown.rs b/crates/components/src/dropdown.rs index 7dc3a8d91..71e6ca875 100644 --- a/crates/components/src/dropdown.rs +++ b/crates/components/src/dropdown.rs @@ -14,7 +14,7 @@ use winit::window::CursorIcon; /// [`DropdownItem`] component properties. #[derive(Props, Clone, PartialEq)] -pub struct DropdownItemProps { +pub struct DropdownItemProps { /// Theme override. pub theme: Option, /// Selectable items, like [`DropdownItem`] @@ -132,7 +132,7 @@ where /// [`Dropdown`] component properties. #[derive(Props, Clone, PartialEq)] -pub struct DropdownProps { +pub struct DropdownProps { /// Theme override. pub theme: Option, /// Selectable items, like [`DropdownItem`] @@ -188,7 +188,7 @@ pub fn Dropdown(props: DropdownProps) -> Element where T: PartialEq + Clone + Display + 'static, { - let selected = use_context_provider(|| Signal::new(props.value.clone())); + let mut selected = use_context_provider(|| Signal::new(props.value.clone())); let theme = use_applied_theme!(&props.theme, dropdown); let mut focus = use_focus(); let mut status = use_signal(DropdownStatus::default); diff --git a/crates/components/src/external_link.rs b/crates/components/src/external_link.rs index 408fa6f28..6c56a9a59 100644 --- a/crates/components/src/external_link.rs +++ b/crates/components/src/external_link.rs @@ -50,7 +50,7 @@ pub struct ExternalLinkProps { #[allow(non_snake_case)] pub fn ExternalLink(props: ExternalLinkProps) -> Element { let theme = use_applied_theme!(&props.theme, external_link); - let is_hovering = use_signal(|| false); + let mut is_hovering = use_signal(|| false); let onmouseover = move |_: MouseEvent| { *is_hovering.write() = true; diff --git a/crates/components/src/gesture_area.rs b/crates/components/src/gesture_area.rs index 6fba5379b..1fa83505d 100644 --- a/crates/components/src/gesture_area.rs +++ b/crates/components/src/gesture_area.rs @@ -173,7 +173,6 @@ pub fn GestureArea(props: GestureAreaProps) -> Element { mod test { use std::time::Duration; - use dioxus::signals::use_signal; use freya::prelude::*; use freya_elements::events::touch::TouchPhase; use freya_testing::{launch_test, FreyaEvent}; diff --git a/crates/components/src/scroll_views/scroll_view.rs b/crates/components/src/scroll_views/scroll_view.rs index 27246a7df..4a7356632 100644 --- a/crates/components/src/scroll_views/scroll_view.rs +++ b/crates/components/src/scroll_views/scroll_view.rs @@ -56,7 +56,7 @@ pub struct ScrollViewProps { /// #[allow(non_snake_case)] pub fn ScrollView(props: ScrollViewProps) -> Element { - let clicking_scrollbar = use_signal::>(|| None); + let mut clicking_scrollbar = use_signal::>(|| None); let mut clicking_shift = use_signal(|| false); let mut clicking_alt = use_signal(|| false); let mut scrolled_y = use_signal(|| 0); diff --git a/crates/components/src/scroll_views/virtual_scroll_view.rs b/crates/components/src/scroll_views/virtual_scroll_view.rs index 45940be5b..b1475662a 100644 --- a/crates/components/src/scroll_views/virtual_scroll_view.rs +++ b/crates/components/src/scroll_views/virtual_scroll_view.rs @@ -16,8 +16,8 @@ use crate::{ type BuilderFunction = dyn Fn((usize, usize, &Option)) -> Element; /// [`VirtualScrollView`] component properties. -#[derive(Props, Clone)] -pub struct VirtualScrollViewProps { +#[derive(Props, Clone, PartialEq)] +pub struct VirtualScrollViewProps { /// Theme override. pub theme: Option, /// Quantity of items in the VirtualScrollView. @@ -90,8 +90,8 @@ fn get_render_range( /// } /// ``` #[allow(non_snake_case)] -pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element { - let clicking_scrollbar = use_signal::>(|| None); +pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element { + let mut clicking_scrollbar = use_signal::>(|| None); let mut clicking_shift = use_signal(|| false); let mut clicking_alt = use_signal(|| false); let mut scrolled_y = use_signal(|| 0); diff --git a/crates/components/src/switch.rs b/crates/components/src/switch.rs index 4475c492a..603f2ee08 100644 --- a/crates/components/src/switch.rs +++ b/crates/components/src/switch.rs @@ -59,7 +59,7 @@ pub fn Switch(props: SwitchProps) -> Element { let mut animation = use_animation(|| 0.0); let theme = use_applied_theme!(&props.theme, switch); let platform = use_platform(); - let status = use_signal(SwitchStatus::default); + let mut status = use_signal(SwitchStatus::default); let focus = use_focus(); let focus_id = focus.attribute(); diff --git a/crates/hooks/src/use_accessibility.rs b/crates/hooks/src/use_accessibility.rs index 8b52f12a5..584de70e9 100644 --- a/crates/hooks/src/use_accessibility.rs +++ b/crates/hooks/src/use_accessibility.rs @@ -4,8 +4,8 @@ use dioxus_core::{ prelude::{consume_context, spawn, try_consume_context}, use_hook, }; -use dioxus_hooks::use_context_provider; -use dioxus_signals::{use_effect, Readable, Signal, Writable}; +use dioxus_hooks::{use_context_provider, use_effect}; +use dioxus_signals::{Readable, Signal, Writable}; use freya_common::EventMessage; use freya_core::{ navigation_mode::{NavigationMode, NavigatorState}, @@ -20,9 +20,9 @@ pub type AccessibilityIdCounter = Rc>; /// Sync both the Focus shared state and the platform accessibility focus pub fn use_init_accessibility() { - let focused_id = + let mut focused_id = use_context_provider::>(|| Signal::new(ACCESSIBILITY_ROOT_ID)); - let navigation_mode = + let mut navigation_mode = use_context_provider::>(|| Signal::new(NavigationMode::NotKeyboard)); use_context_provider(|| Rc::new(RefCell::new(0u64))); let platform = use_platform(); diff --git a/crates/hooks/src/use_animation_transition.rs b/crates/hooks/src/use_animation_transition.rs index 0af3f9c42..dbe1eb257 100644 --- a/crates/hooks/src/use_animation_transition.rs +++ b/crates/hooks/src/use_animation_transition.rs @@ -1,5 +1,6 @@ use dioxus_core::prelude::spawn; -use dioxus_signals::{use_memo_with_dependencies, Dependency, Readable, Signal, Writable}; +use dioxus_hooks::{use_memo_with_dependencies, Dependency}; +use dioxus_signals::{Readable, Signal, Writable}; use freya_engine::prelude::Color; use freya_node_state::Parse; use tokio::time::Instant; @@ -165,7 +166,7 @@ impl TransitionsManager { let platform = self.platform.clone(); let mut ticker = platform.new_ticker(); let transitions = self.transitions; - let transitions_storage = self.transitions_storage; + let mut transitions_storage = self.transitions_storage; let mut current_animation_id = self.current_animation_id; // Set as current this new animation diff --git a/crates/hooks/src/use_camera.rs b/crates/hooks/src/use_camera.rs index 37f299068..43122a9ce 100644 --- a/crates/hooks/src/use_camera.rs +++ b/crates/hooks/src/use_camera.rs @@ -2,8 +2,8 @@ use std::sync::{Arc, Mutex}; use crate::use_platform; use dioxus_core::{prelude::spawn, use_hook, AttributeValue}; -use dioxus_hooks::to_owned; -use dioxus_signals::{use_signal, Signal, Writable}; +use dioxus_hooks::{to_owned, use_signal}; +use dioxus_signals::{Signal, Writable}; use freya_node_state::{CustomAttributeValues, ImageReference}; pub use nokhwa::utils::{CameraIndex, RequestedFormatType, Resolution}; use nokhwa::{pixel_format::RgbFormat, utils::RequestedFormat, Camera, NokhwaError}; diff --git a/crates/hooks/src/use_canvas.rs b/crates/hooks/src/use_canvas.rs index a7d46a13b..946dcde68 100644 --- a/crates/hooks/src/use_canvas.rs +++ b/crates/hooks/src/use_canvas.rs @@ -1,7 +1,8 @@ use std::sync::Arc; use dioxus_core::AttributeValue; -use dioxus_signals::{use_memo_with_dependencies, Dependency, Readable}; +use dioxus_hooks::{use_memo_with_dependencies, Dependency}; +use dioxus_signals::Readable; use freya_node_state::{CanvasReference, CanvasRunner, CustomAttributeValues}; /// Holds a rendering hook callback that allows to render to the Canvas. diff --git a/crates/hooks/src/use_editable.rs b/crates/hooks/src/use_editable.rs index 1942327da..953c85c74 100644 --- a/crates/hooks/src/use_editable.rs +++ b/crates/hooks/src/use_editable.rs @@ -156,7 +156,7 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode use_hook(|| { let text_id = Uuid::new_v4(); let config = initializer(); - let editor = Signal::new(RopeEditor::new(config.content, config.cursor, mode)); + let mut editor = Signal::new(RopeEditor::new(config.content, config.cursor, mode)); let selecting_text_with_mouse = Signal::new(None); let (cursor_sender, mut cursor_receiver) = unbounded_channel::(); let cursor_reference = CursorReference { diff --git a/crates/hooks/src/use_node.rs b/crates/hooks/src/use_node.rs index 0273a8456..a8fd8bc42 100644 --- a/crates/hooks/src/use_node.rs +++ b/crates/hooks/src/use_node.rs @@ -1,6 +1,6 @@ use dioxus_core::{prelude::spawn, use_hook, AttributeValue}; -use dioxus_hooks::to_owned; -use dioxus_signals::{use_signal, Readable, Signal, Writable}; +use dioxus_hooks::{to_owned, use_signal}; +use dioxus_signals::{Readable, Signal, Writable}; use freya_common::NodeReferenceLayout; use freya_node_state::{CustomAttributeValues, NodeReference}; use tokio::sync::mpsc::unbounded_channel; diff --git a/examples/app_dog.rs b/examples/app_dog.rs index 6ecdc0316..6a5578b2c 100644 --- a/examples/app_dog.rs +++ b/examples/app_dog.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; use reqwest::Url; use serde::Deserialize; diff --git a/examples/corner_radius.rs b/examples/corner_radius.rs index 3d5d0c9c1..3fb1c4745 100644 --- a/examples/corner_radius.rs +++ b/examples/corner_radius.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/counter.rs b/examples/counter.rs index a471634cd..25160e129 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/floating_editors.rs b/examples/floating_editors.rs index 64e181c76..593009ef4 100644 --- a/examples/floating_editors.rs +++ b/examples/floating_editors.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::events::MouseEvent; use freya::prelude::*; diff --git a/examples/image.rs b/examples/image.rs index f207c016c..bc640853b 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/input.rs b/examples/input.rs index afea326f6..f3b7e50a5 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/massive.rs b/examples/massive.rs index 3efa60605..4f0a7b3f4 100644 --- a/examples/massive.rs +++ b/examples/massive.rs @@ -1,4 +1,3 @@ -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/switch_theme.rs b/examples/switch_theme.rs index fefd1b3f5..8ced79170 100644 --- a/examples/switch_theme.rs +++ b/examples/switch_theme.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; fn main() { diff --git a/examples/virtual_scroll_view.rs b/examples/virtual_scroll_view.rs index 92703c94d..669b14ac7 100644 --- a/examples/virtual_scroll_view.rs +++ b/examples/virtual_scroll_view.rs @@ -3,7 +3,6 @@ windows_subsystem = "windows" )] -use dioxus::signals::use_signal; use freya::prelude::*; use std::rc::Rc; From e6a1d7249cfcd8a90da822bf10e5d5d571737303 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 21:05:08 +0100 Subject: [PATCH 17/29] fixes --- .../src/scroll_views/virtual_scroll_view.rs | 18 +++++++++++++++--- crates/devtools/src/lib.rs | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/components/src/scroll_views/virtual_scroll_view.rs b/crates/components/src/scroll_views/virtual_scroll_view.rs index b1475662a..cd96816d7 100644 --- a/crates/components/src/scroll_views/virtual_scroll_view.rs +++ b/crates/components/src/scroll_views/virtual_scroll_view.rs @@ -16,8 +16,8 @@ use crate::{ type BuilderFunction = dyn Fn((usize, usize, &Option)) -> Element; /// [`VirtualScrollView`] component properties. -#[derive(Props, Clone, PartialEq)] -pub struct VirtualScrollViewProps { +#[derive(Props, Clone)] +pub struct VirtualScrollViewProps { /// Theme override. pub theme: Option, /// Quantity of items in the VirtualScrollView. @@ -39,6 +39,18 @@ pub struct VirtualScrollViewProps { pub scroll_with_arrows: bool, } +impl PartialEq for VirtualScrollViewProps { + fn eq(&self, other: &Self) -> bool { + self.theme == other.theme + && self.length == other.length + && self.item_size == other.item_size + && Rc::ptr_eq(&self.builder, &other.builder) + && self.direction == other.direction + && self.show_scrollbar == other.show_scrollbar + && self.scroll_with_arrows == other.scroll_with_arrows + } +} + fn get_render_range( viewport_size: f32, scroll_position: f32, @@ -90,7 +102,7 @@ fn get_render_range( /// } /// ``` #[allow(non_snake_case)] -pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element { +pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element { let mut clicking_scrollbar = use_signal::>(|| None); let mut clicking_shift = use_signal(|| false); let mut clicking_alt = use_signal(|| false); diff --git a/crates/devtools/src/lib.rs b/crates/devtools/src/lib.rs index c6c2d699b..4ee371b14 100644 --- a/crates/devtools/src/lib.rs +++ b/crates/devtools/src/lib.rs @@ -119,7 +119,7 @@ impl PartialEq for DevToolsProps { #[allow(non_snake_case)] pub fn DevTools(props: DevToolsProps) -> Element { - let children = use_context_provider(|| Signal::new(Vec::::new())); + let mut children = use_context_provider(|| Signal::new(Vec::::new())); use_context_provider::>(|| Signal::new(props.hovered_node.clone())); use_init_theme(DARK_THEME); let theme = use_theme(); From eb91fe996aef362fd7ba4d99ccc68e3eac92af3d Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 21:15:11 +0100 Subject: [PATCH 18/29] fixes --- examples/corner_radius.rs | 2 +- examples/drag_drop.rs | 2 +- examples/input.rs | 2 +- examples/mouse.rs | 4 ++-- examples/mouse_trace.rs | 2 +- examples/push_remove.rs | 2 +- examples/rotate.rs | 2 +- examples/switch_theme.rs | 2 +- examples/tic_tac_toe.rs | 4 ++-- examples/ui.rs | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/corner_radius.rs b/examples/corner_radius.rs index 3fb1c4745..2280c7d61 100644 --- a/examples/corner_radius.rs +++ b/examples/corner_radius.rs @@ -10,7 +10,7 @@ fn main() { } fn app() -> Element { - let radius = use_signal(|| 30f32); + let mut radius = use_signal(|| 30f32); let onwheel = move |e: WheelEvent| { let y = e.get_delta_y() as f32; diff --git a/examples/drag_drop.rs b/examples/drag_drop.rs index 0cc42cd69..f2956c31a 100644 --- a/examples/drag_drop.rs +++ b/examples/drag_drop.rs @@ -51,7 +51,7 @@ fn Column( data: Signal<(Vec, Vec)>, column: Vec, ) -> Element { - let swap = move |el: String, direction: &SwapDirection| { + let mut swap = move |el: String, direction: &SwapDirection| { data.with_mut(|data| { data.0.retain(|e| e != &el); data.1.retain(|e| e != &el); diff --git a/examples/input.rs b/examples/input.rs index f3b7e50a5..d1462554a 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -10,7 +10,7 @@ fn main() { } fn app() -> Element { - let values = use_signal(|| (String::new(), String::new())); + let mut values = use_signal(|| (String::new(), String::new())); rsx!( rect { diff --git a/examples/mouse.rs b/examples/mouse.rs index b11706886..e091b8dcb 100644 --- a/examples/mouse.rs +++ b/examples/mouse.rs @@ -27,8 +27,8 @@ fn app() -> Element { #[allow(non_snake_case)] fn Area() -> Element { - let cursor_pos_over = use_signal(|| (0f64, 0f64)); - let cursor_pos_click = use_signal(|| (0f64, 0f64)); + let mut cursor_pos_over = use_signal(|| (0f64, 0f64)); + let mut cursor_pos_click = use_signal(|| (0f64, 0f64)); let cursor_moved = move |e: MouseEvent| { let pos = e.get_screen_coordinates(); diff --git a/examples/mouse_trace.rs b/examples/mouse_trace.rs index 9d7b847ee..37da95cf5 100644 --- a/examples/mouse_trace.rs +++ b/examples/mouse_trace.rs @@ -46,7 +46,7 @@ fn Box() -> Element { } fn app() -> Element { - let positions = use_signal::>(Vec::new); + let mut positions = use_signal::>(Vec::new); let onmouseover = move |e: MouseEvent| { let coordinates = e.get_screen_coordinates(); diff --git a/examples/push_remove.rs b/examples/push_remove.rs index 26e1b6de0..cb0d3b655 100644 --- a/examples/push_remove.rs +++ b/examples/push_remove.rs @@ -11,7 +11,7 @@ fn main() { } fn app() -> Element { - let elements = use_signal(|| Vec::new()); + let mut elements = use_signal(|| Vec::new()); let add = move |_| { let mut rng = rand::thread_rng(); diff --git a/examples/rotate.rs b/examples/rotate.rs index 2a64ea92b..6786e898d 100644 --- a/examples/rotate.rs +++ b/examples/rotate.rs @@ -15,7 +15,7 @@ fn main() { const SPEEDS: (i16, i16, i16) = (2, 3, 6); fn app() -> Element { - let degrees = use_signal(|| (0, 0, 0)); + let mut degrees = use_signal(|| (0, 0, 0)); use_hook(|| { spawn(async move { diff --git a/examples/switch_theme.rs b/examples/switch_theme.rs index 8ced79170..94cda4e82 100644 --- a/examples/switch_theme.rs +++ b/examples/switch_theme.rs @@ -11,7 +11,7 @@ fn main() { #[allow(non_snake_case)] fn TheOtherSwitch() -> Element { - let theme = use_theme(); + let mut theme = use_theme(); let is_enabled = theme.read().name == "dark"; diff --git a/examples/tic_tac_toe.rs b/examples/tic_tac_toe.rs index 07599a673..36cbce41d 100644 --- a/examples/tic_tac_toe.rs +++ b/examples/tic_tac_toe.rs @@ -148,8 +148,8 @@ impl Board { } fn app() -> Element { - let board = use_signal(|| Board::new(3)); - let current_player = use_signal(|| Player::X); + let mut board = use_signal(|| Board::new(3)); + let mut current_player = use_signal(|| Player::X); let message = match &board.read().winner { Some(winner) => format!("Winner is player {winner:?}!!"), diff --git a/examples/ui.rs b/examples/ui.rs index f2a56d18e..120b6c92e 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -155,8 +155,8 @@ fn Card(props: CardProps) -> Element { #[allow(non_snake_case)] fn Area() -> Element { - let cursor_pos_over = use_signal(|| (0f64, 0f64)); - let cursor_pos_click = use_signal(|| (0f64, 0f64)); + let mut cursor_pos_over = use_signal(|| (0f64, 0f64)); + let mut cursor_pos_click = use_signal(|| (0f64, 0f64)); let cursor_moved = move |e: MouseEvent| { let pos = e.get_screen_coordinates(); From 458364408afea40d787930af826abc74730057f2 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Fri, 2 Feb 2024 23:53:24 +0100 Subject: [PATCH 19/29] bump dx --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7404d453..efbc2f351 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,16 +26,16 @@ freya-testing = { path = "crates/testing", version = "0.1" } freya-engine = { path = "crates/engine", version = "0.1" } torin = { path = "crates/torin", version = "0.1" } -dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} -dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } -dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["hot_reload"] } -dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["dioxus"] } -dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } -dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } -dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } -dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798" } -dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", features = ["file_watcher"], default-features = false } -dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "894a79d3dff47ba5ce4fcf8ae6ec9836c6ae3798", default-features = false } +dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} +dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } +dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["hot_reload"] } +dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["dioxus"] } +dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } +dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } +dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } +dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } +dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["file_watcher"], default-features = false } +dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", default-features = false } skia-safe = { version = "0.67.0", features = ["gl", "textlayout", "svg"] } From 32dc45b0d79a85e77b7b6699b1a843b2f765f93e Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Sun, 4 Feb 2024 10:30:50 +0100 Subject: [PATCH 20/29] docs: Fix hotreload typo in docs --- book/src/guides/hot_reload.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/guides/hot_reload.md b/book/src/guides/hot_reload.md index a463f7bde..1d61f57dc 100644 --- a/book/src/guides/hot_reload.md +++ b/book/src/guides/hot_reload.md @@ -8,7 +8,7 @@ Before launching your app, you need to initialize the hot-reload context: ```rust, no_run use freya::prelude::*; -use freya::hot_reload::FreyaCtx; +use freya::hotreload::FreyaCtx; fn main() { dioxus_hot_reload::hot_reload_init!(Config::::default()); From bfa5d78b3cfe2681b288b7649a740805cbdaddc4 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 4 Feb 2024 12:39:52 +0100 Subject: [PATCH 21/29] updated guide --- book/src/guides/getting_started.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/src/guides/getting_started.md b/book/src/guides/getting_started.md index 3a303f5e9..9afd38ac4 100644 --- a/book/src/guides/getting_started.md +++ b/book/src/guides/getting_started.md @@ -50,10 +50,12 @@ fn app() -> Element { rect { height: "100%", width: "100%", - background: "rgb(35, 35, 35)", + background: "rgb(0, 119, 182)", color: "white", - padding: "12", + main_align: "center", + cross_align: "center", onclick: move |_| count += 1, + font_size: "35", label { "Click to increase -> {count}" } } ) From 1c0b20ff5ce76a5323381f40ba9ca4e2f2570f8f Mon Sep 17 00:00:00 2001 From: marc2332 Date: Mon, 5 Feb 2024 14:41:17 +0100 Subject: [PATCH 22/29] bump dx --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efbc2f351..089d3cdd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,16 +26,16 @@ freya-testing = { path = "crates/testing", version = "0.1" } freya-engine = { path = "crates/engine", version = "0.1" } torin = { path = "crates/torin", version = "0.1" } -dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} -dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } -dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["hot_reload"] } -dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["dioxus"] } -dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } -dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } -dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } -dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4" } -dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", features = ["file_watcher"], default-features = false } -dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "6bde8b0ce0dd162b9f855ec1d57c6569907ef7e4", default-features = false } +dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} +dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } +dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["hot_reload"] } +dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["dioxus"] } +dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } +dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } +dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } +dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } +dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["file_watcher"], default-features = false } +dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", default-features = false } skia-safe = { version = "0.67.0", features = ["gl", "textlayout", "svg"] } From 07d02b3153160849d57c563b0ae91be6d3057043 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 20:23:51 +0100 Subject: [PATCH 23/29] chore: Add note to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 07883ce5d..18af6fd20 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Thanks to my sponsors for supporting this project! 😄 ### Want to try it? 🤔 +Note: `main` branch currently depends on Dioxus 0.5. + ⚠️ First, see [Environment setup](https://book.freyaui.dev/setup.html). Clone this repo and run: From 6ec98432aca5c650c7b9d81281a9e6e7a24311e6 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 20:27:43 +0100 Subject: [PATCH 24/29] reverse book --- book/src/guides/animating.md | 18 ++++---- book/src/guides/effects.md | 4 +- book/src/guides/elements.md | 24 +++++------ book/src/guides/font_style.md | 68 +++++++++++++++--------------- book/src/guides/getting_started.md | 12 +++--- book/src/guides/layout.md | 44 +++++++++---------- book/src/guides/style.md | 20 ++++----- book/src/guides/testing.md | 29 +++++++------ book/src/guides/theming.md | 38 ++++++++--------- book/src/guides/virtualizing.md | 14 +++--- book/src/index.md | 6 +-- 11 files changed, 139 insertions(+), 138 deletions(-) diff --git a/book/src/guides/animating.md b/book/src/guides/animating.md index 08e24d37a..9afdf8808 100644 --- a/book/src/guides/animating.md +++ b/book/src/guides/animating.md @@ -18,16 +18,16 @@ fn main() { launch(app); } - fn app() -> Element { - let mut animation = use_animation(|| 0.0); + fn app(cx: Scope) -> Element { + let animation = use_animation(cx, || 0.0); let progress = animation.value(); - use_hook(move || { + use_memo(cx, (), move |_| { animation.start(Animation::new_linear(0.0..=100.0, 50)); - }) + }); - rsx!(rect { + render!(rect { width: "{progress}", }) } @@ -53,8 +53,8 @@ fn main() { const TARGET: f64 = 500.0; -fn app() -> Element { - let mut animation = use_animation_transition(TransitionAnimation::new_sine_in_out(200), (), || { +fn app(cx: Scope) -> Element { + let animation = use_animation_transition(cx, TransitionAnimation::new_sine_in_out(200), (), || { vec![ Animate::new_size(0.0, TARGET), Animate::new_color("rgb(33, 158, 188)", "white"), @@ -72,7 +72,7 @@ fn app() -> Element { } }; - rsx!( + render!( rect { overflow: "clip", background: "black", @@ -83,7 +83,7 @@ fn app() -> Element { height: "100%", width: "200", background: "{background}", - onclick, + onclick: onclick, } } ) diff --git a/book/src/guides/effects.md b/book/src/guides/effects.md index 5f0c9703a..494fba317 100644 --- a/book/src/guides/effects.md +++ b/book/src/guides/effects.md @@ -11,8 +11,8 @@ The `rotate` attribute let's you rotate an element. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { rotate: "180deg", "Hello, World!" diff --git a/book/src/guides/elements.md b/book/src/guides/elements.md index 660c50e20..eb7afd058 100644 --- a/book/src/guides/elements.md +++ b/book/src/guides/elements.md @@ -16,8 +16,8 @@ You can specify things like [`width`](/guides/layout.html#width), [`paddings`](/ Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { direction: "vertical", label { "Hi!" } @@ -34,8 +34,8 @@ The `label` element simply shows some text. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { "Hello World" } @@ -53,9 +53,9 @@ Example: static FERRIS: &[u8] = include_bytes!("./ferris.svg"); -fn app() -> Element { - let ferris = bytes_to_data(FERRIS); - rsx!( +fn app(cx: Scope) -> Element { + let ferris = bytes_to_data(cx, FERRIS); + render!( svg { svg_data: ferris, } @@ -70,9 +70,9 @@ The `image` element, just like `svg` element, require you to pass the image byte ```rust, no_run static RUST_LOGO: &[u8] = include_bytes!("./rust_logo.png"); -fn app() -> Element { - let image_data = bytes_to_data(RUST_LOGO); - rsx!( +fn app(cx: Scope) -> Element { + let image_data = bytes_to_data(cx, RUST_LOGO); + render!( image { image_data: image_data, width: "{size}", @@ -87,8 +87,8 @@ fn app() -> Element { Both `paragraph` and `text` elements are used together. They will let you build texts with different styles. ``` rust -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( paragraph { text { font_size: "15", diff --git a/book/src/guides/font_style.md b/book/src/guides/font_style.md index d63cf10dd..3fd67d1e4 100644 --- a/book/src/guides/font_style.md +++ b/book/src/guides/font_style.md @@ -29,8 +29,8 @@ You can learn about the syntax of this attribute in [`Color Syntax`](/guides/sty Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { color: "green", "Hello, World!" @@ -42,8 +42,8 @@ fn app() -> Element { Another example showing [inheritance](#inheritance): ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { color: "blue", label { @@ -66,8 +66,8 @@ Limitation: Only fonts installed in the system are supported for now. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { font_family: "Inter", "Hello, World!" @@ -85,8 +85,8 @@ You can specify the size of the text using `font_size`. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { font_size: "50", "Hellooooo!" @@ -106,8 +106,8 @@ Accepted values: `center`, `end`, `justify`, `left`, `right`, `start` Example ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { align: "right", "Hello, World!" @@ -127,8 +127,8 @@ Accepted values: `upright` (default), `italic` and `oblique`. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { font_style: "italic", "Hello, World!" @@ -170,8 +170,8 @@ Accepted values: Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { font_weight: "bold", "Hello, World!" @@ -199,8 +199,8 @@ Accepted values: Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { font_weight: "bold", "Hello, World!" @@ -220,8 +220,8 @@ Specify the height of the lines of the text. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { lines_height: "3", "Hello, World! \n Hello, again!" @@ -237,8 +237,8 @@ Determines the amount of lines that the text can have. It has unlimited lines by Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { "Hello, World! \n Hello, World! \n Hello, world!" // Will show all three lines } @@ -259,8 +259,8 @@ Specify the spacing between characters of the text. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { letter_spacing: "10", "Hello, World!" @@ -278,8 +278,8 @@ Specify the spacing between words of the text. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { word_spacing: "10", "Hello, World!" @@ -302,8 +302,8 @@ Accpted values: Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { decoration: "line-through", "Hello, World!" @@ -328,8 +328,8 @@ Accpted values: Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { decoration: "line-through", decoration_style: "dotted", @@ -350,8 +350,8 @@ You can learn about the syntax of this attribute in [`Color Syntax`](/guides/sty Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { decoration: "line-through", decoration_color: "orange", @@ -372,8 +372,8 @@ Syntax: ` ` Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { text_shadow: "0 18 12 rgb(0, 0, 0)", "Hello, World!" @@ -395,8 +395,8 @@ Accepted values: Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( label { max_lines: "3", text_overflow: "ellipsis", diff --git a/book/src/guides/getting_started.md b/book/src/guides/getting_started.md index 9afd38ac4..3ce82c631 100644 --- a/book/src/guides/getting_started.md +++ b/book/src/guides/getting_started.md @@ -43,19 +43,17 @@ fn main() { launch(app); } -fn app() -> Element { - let mut count = use_signal(|| 0); +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); - rsx!( + render!( rect { height: "100%", width: "100%", - background: "rgb(0, 119, 182)", + background: "rgb(35, 35, 35)", color: "white", - main_align: "center", - cross_align: "center", + padding: "12", onclick: move |_| count += 1, - font_size: "35", label { "Click to increase -> {count}" } } ) diff --git a/book/src/guides/layout.md b/book/src/guides/layout.md index bc5de08b3..ea49c7752 100644 --- a/book/src/guides/layout.md +++ b/book/src/guides/layout.md @@ -24,8 +24,8 @@ See syntax for [`Size Units`](#size-units). ##### Usage ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { background: "red", width: "15", @@ -44,8 +44,8 @@ See syntax for [`Size Units`](#size-units). ##### Usage ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { background: "red", min_width: "100", @@ -66,8 +66,8 @@ See syntax for [`Size Units`](#size-units). ##### Usage ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { background: "red", max_width: "50%", @@ -85,8 +85,8 @@ fn app() -> Element { Will use it's inner children as size, so in this case, the `rect` width will be equivalent to the width of `label`: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "auto", height: "33", @@ -101,8 +101,8 @@ fn app() -> Element { #### Logical pixels ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "50", height: "33" @@ -115,8 +115,8 @@ fn app() -> Element { Relative percentage to the parent equivalent value. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "50%", // Half the parent height: "75%" // 3/4 the parent @@ -130,8 +130,8 @@ fn app() -> Element { For more complex logic you can use the `calc()` function. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "calc(33% - 60 + 15%)", // (1/3 of the parent minus 60) plus 15% of parent height: "calc(100% - 10)" // 100% of the parent minus 10 @@ -147,8 +147,8 @@ Control how the inner elements will be stacked, possible values are `horizontal` ##### Usage ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "100%", height: "100%", @@ -173,8 +173,8 @@ fn app() -> Element { Specify the inner paddings of an element. You can do so by three different ways. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { padding: "25" // 25 in all sides padding: "100 50" // 100 in top and bottom, and 50 in left and right @@ -190,8 +190,8 @@ fn app() -> Element { Control how the inner elements are displayed, possible values are `normal` (default) or `center`. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { width: "100%", height: "100%", @@ -212,8 +212,8 @@ fn app() -> Element { Specify the margin of an element. You can do so by three different ways. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { margin: "25" // 25 in all sides margin: "100 50" // 100 in top and bottom, and 50 in left and right diff --git a/book/src/guides/style.md b/book/src/guides/style.md index ce9773c04..2053e54de 100644 --- a/book/src/guides/style.md +++ b/book/src/guides/style.md @@ -21,8 +21,8 @@ You can learn about the syntax of this attribute [here](#color-syntax). Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { background: "red" } @@ -42,8 +42,8 @@ Syntax: ` ` Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { shadow: "0 0 25 2 rgb(0, 0, 0, 120)" } @@ -60,8 +60,8 @@ The `corner_radius` attribute let's you smooth the corners of the element, with Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { corner_radius: "10", corner_smoothing: "75%" @@ -82,8 +82,8 @@ You can add a border to an element using the `border` and `border_align` attribu Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { border: "2 solid black", border_align: "inner" @@ -103,8 +103,8 @@ Accepted values: `clip | none`. Example: ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( rect { overflow: "clip" width: "100", diff --git a/book/src/guides/testing.md b/book/src/guides/testing.md index 296480191..0cf151773 100644 --- a/book/src/guides/testing.md +++ b/book/src/guides/testing.md @@ -12,8 +12,8 @@ For example, this will launch a state-less component and assert that it renders ```rust, no_run #[tokio::test] async fn test() { - fn our_component() -> Element { - rsx!( + fn our_component(cx: Scope) -> Element { + render!( label { "Hello World!" } @@ -41,12 +41,15 @@ Here, the component has a state that is `false` by default, but, once mounted it ```rust, no_run #[tokio::test] async fn dynamic_test() { - fn dynamic_component() -> Element { - let mut state = use_signal(|| false); + fn dynamic_component(cx: Scope) -> Element { + let state = use_state(cx, || false); - use_hook(move || state.set(true)); + use_effect(cx, (), |_| { + state.set(true); + async move { } + }); - rsx!( + render!( label { "Is enabled? {state}" } @@ -60,7 +63,7 @@ async fn dynamic_test() { assert_eq!(label.get(0).text(), Some("Is enabled? false")); - // This will run the `use_hook` and update the state. + // This will run the `use_effect` and update the state. utils.wait_for_update().await; assert_eq!(label.get(0).text(), Some("Is enabled? true")); @@ -74,14 +77,14 @@ We can also simulate events on the component, for example, we can simulate a cli ```rust, no_run #[tokio::test] async fn event_test() { - fn event_component() -> Element { - let mut enabled = use_signal(|| false); - rsx!( + fn event_component(cx: Scope) -> Element { + let enabled = use_state(cx, || false); + render!( rect { width: "100%", height: "100%", background: "red", - onclick: move |_| { + onclick: |_| { enabled.set(true); }, label { @@ -126,8 +129,8 @@ Here is an example of how to can set our custom window size: ```rust, no_run #[tokio::test] async fn test() { - fn our_component() -> Element { - rsx!( + fn our_component(cx: Scope) -> Element { + render!( label { "Hello World!" } diff --git a/book/src/guides/theming.md b/book/src/guides/theming.md index a172b218f..e192dc95f 100644 --- a/book/src/guides/theming.md +++ b/book/src/guides/theming.md @@ -9,8 +9,8 @@ Freya has built-in support for Theming. You can access the whole current theme via the `use_get_theme` hook. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( ThemeProvider { Component { } } @@ -18,12 +18,12 @@ fn app() -> Element { } #[allow(non_snake_case)] -fn Component() -> Element { - let theme = use_get_theme(); +fn Component(cx: Scope) -> Element { + let theme = use_get_theme(cx); let button_theme = &theme.button; - rsx!( + render!( rect { background: "{button_theme.background}", } @@ -35,8 +35,8 @@ fn Component() -> Element { By default, the selected theme is `LIGHT_THEME`. You can use the alternative, `DARK_THEME`. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( ThemeProvider { theme: LIGHT_THEME, Component { } @@ -45,12 +45,12 @@ fn app() -> Element { } #[allow(non_snake_case)] -fn Component() -> Element { - let theme = use_get_theme(); +fn Component(cx: Scope) -> Element { + let theme = use_get_theme(cx); let button_theme = &theme.button; - rsx!( + render!( rect { background: "{button_theme.background}", } @@ -63,8 +63,8 @@ fn Component() -> Element { Changing the selected theme at runtime is possible by using the `use_theme` hook. ```rust, no_run -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( ThemeProvider { Component { } } @@ -72,16 +72,16 @@ fn app() -> Element { } #[allow(non_snake_case)] -fn Component() -> Element { - let mut theme = use_theme(); +fn Component(cx: Scope) -> Element { + let theme = use_theme(cx); - let onclick = move |_| { + let onclick = |_| { *theme.write() = LIGHT_THEME; }; - rsx!( + render!( Button { - onclick, + onclick: onclick, label { "Use Light theme" } @@ -104,8 +104,8 @@ const CUSTOM_THEME: Theme = Theme { ..LIGHT_THEME }; -fn app() -> Element { - rsx!( +fn app(cx: Scope) -> Element { + render!( ThemeProvider { theme: CUSTOM_THEME, rect { diff --git a/book/src/guides/virtualizing.md b/book/src/guides/virtualizing.md index 5de8fd9f0..9a22020da 100644 --- a/book/src/guides/virtualizing.md +++ b/book/src/guides/virtualizing.md @@ -19,19 +19,19 @@ fn main() { launch(app); } -fn app() -> Element { - let values = use_signal(|| vec!["Hello World"].repeat(400)); +fn app(cx: Scope) -> Element { + let values = use_state(cx, || vec!["Hello World"].repeat(400)); - rsx!( + render!( VirtualScrollView { width: "100%", height: "100%", show_scrollbar: true, direction: "vertical", - length: values.read().len(), + length: values.get().len(), item_size: 25.0, - builder_values: values.read().clone(), - builder: Rc::new(move |(key, index, values)| { + builder_values: values.get(), + builder: Box::new(move |(key, index, _cx, values)| { let values = values.unwrap(); let value = values[index]; rsx! { @@ -72,4 +72,4 @@ Used to calculate how many elements can be fit in the viewport. Any data that you might need in the `builder` function #### `builder` -This is a function that dinamically creates an element for the given index in the list. It receives 4 arguments, a `key` for the element, the `index` of the element and the `builder_values` you previously passed. \ No newline at end of file +This is a function that dinamically creates an element for the given index in the list. It receives 4 arguments, a `key` for the element, the `index` of the element, the Scope (`cx`) and the `builder_values` you previously passed. \ No newline at end of file diff --git a/book/src/index.md b/book/src/index.md index 1d352c2cc..6eea72d1d 100644 --- a/book/src/index.md +++ b/book/src/index.md @@ -16,10 +16,10 @@ ```rust, no_run -fn app() -> Element { - let mut count = use_signal(|| 0); +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); - rsx!( + render!( rect { height: "20%", width: "100%", From 5be3e9ca6512c3574b34485dc46e88a72f7e5651 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 20:34:13 +0100 Subject: [PATCH 25/29] reverse website --- website/src/pages/index.astro | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 9fa5e96ca..80d23af29 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -6,10 +6,10 @@ import { FaGithub, FaDiscord } from 'react-icons/fa' import { GoBook } from 'react-icons/go' const EXAMPLE_CODE = ` -fn app() -> Element { - let mut count = use_signal(|| 0); +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); - rsx!( + render!( rect { height: "20%", width: "100%", @@ -145,4 +145,4 @@ fn app() -> Element { height: 481px; } } - + \ No newline at end of file From a6ce252073aa0a0be784f1c8efaee46eea212bea Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 20:58:24 +0100 Subject: [PATCH 26/29] bump dx --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 089d3cdd7..dc095e769 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,16 +26,16 @@ freya-testing = { path = "crates/testing", version = "0.1" } freya-engine = { path = "crates/engine", version = "0.1" } torin = { path = "crates/torin", version = "0.1" } -dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} -dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } -dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["hot_reload"] } -dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["dioxus"] } -dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } -dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } -dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } -dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591" } -dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", features = ["file_watcher"], default-features = false } -dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "8324f5290a317b06c5e451ab4bb58d1c73deb591", default-features = false } +dioxus = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892", default-features = false, features = ["macro", "signals", "hooks", "hot-reload", "html"]} +dioxus-native-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892" } +dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892", features = ["hot_reload"] } +dioxus-native-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892", features = ["dioxus"] } +dioxus-core-macro = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892" } +dioxus-hooks = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892" } +dioxus-signals = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892" } +dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892" } +dioxus-hot-reload = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892", features = ["file_watcher"], default-features = false } +dioxus-router = { git = "https://github.com/DioxusLabs/dioxus", rev = "ef101dd876ee8ecdd702a7e22addfd47f2ebd892", default-features = false } skia-safe = { version = "0.67.0", features = ["gl", "textlayout", "svg"] } From 5f4502c9bb808c748eef4233824c166f8f6959ec Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 21:05:26 +0100 Subject: [PATCH 27/29] revert readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 18af6fd20..98902c637 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ ```rust, no_run -fn app() -> Element { - let mut count = use_signal(|| 0); +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); - rsx!( + render!( rect { height: "20%", width: "100%", From 7c2d31deadc215e9fa69d076d54d84f413dd351b Mon Sep 17 00:00:00 2001 From: marc2332 Date: Tue, 6 Feb 2024 21:50:56 +0100 Subject: [PATCH 28/29] ci: Change cache key --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 54de89320..71cebd48a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,7 +25,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@v2 with: - prefix-key: "5" + prefix-key: "6" - name: Install linux dependencies if: runner.os == 'Linux' run: | From 213627754bed77d8a39a8a9de349a825a3b980f4 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Tue, 6 Feb 2024 22:33:11 +0100 Subject: [PATCH 29/29] feat: Proper event bubbling (#268) * feat: Proper event bubbling * update * ux improvements * improvements * fixes and improvements * fix * clean up * fixes * cleanup * fixes and improvements * fixes * event bubbling for VirtualScrollView --- .../src/scroll_views/scroll_view.rs | 16 +- .../src/scroll_views/virtual_scroll_view.rs | 16 +- crates/components/src/slider.rs | 9 +- crates/components/src/switch.rs | 15 +- crates/core/src/events/events_measurer.rs | 113 ++++++----- crates/core/src/events/freya_events.rs | 4 + crates/core/tests/event_bubbling.rs | 189 ++++++++++++++++++ crates/renderer/src/app.rs | 2 +- crates/testing/src/test_handler.rs | 2 +- examples/floating_editors.rs | 2 + 10 files changed, 305 insertions(+), 63 deletions(-) create mode 100644 crates/core/tests/event_bubbling.rs diff --git a/crates/components/src/scroll_views/scroll_view.rs b/crates/components/src/scroll_views/scroll_view.rs index 4a7356632..bc1733b2d 100644 --- a/crates/components/src/scroll_views/scroll_view.rs +++ b/crates/components/src/scroll_views/scroll_view.rs @@ -116,7 +116,13 @@ pub fn ScrollView(props: ScrollViewProps) -> Element { corrected_scrolled_y, ); - *scrolled_y.write() = scroll_position_y; + // Only scroll when there is still area to scroll + if *scrolled_y.peek() != scroll_position_y { + e.stop_propagation(); + *scrolled_y.write() = scroll_position_y; + } else { + return; + } } let wheel_x = if *clicking_shift.read() { @@ -132,7 +138,13 @@ pub fn ScrollView(props: ScrollViewProps) -> Element { corrected_scrolled_x, ); - *scrolled_x.write() = scroll_position_x; + // Only scroll when there is still area to scroll + if *scrolled_x.peek() != scroll_position_x { + e.stop_propagation(); + *scrolled_x.write() = scroll_position_x; + } else { + return; + } focus.focus(); } diff --git a/crates/components/src/scroll_views/virtual_scroll_view.rs b/crates/components/src/scroll_views/virtual_scroll_view.rs index cd96816d7..71a197237 100644 --- a/crates/components/src/scroll_views/virtual_scroll_view.rs +++ b/crates/components/src/scroll_views/virtual_scroll_view.rs @@ -161,7 +161,13 @@ pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element corrected_scrolled_y, ); - *scrolled_y.write() = scroll_position_y; + // Only scroll when there is still area to scroll + if *scrolled_y.peek() != scroll_position_y { + e.stop_propagation(); + *scrolled_y.write() = scroll_position_y; + } else { + return; + } } let wheel_x = if *clicking_shift.peek() { @@ -177,7 +183,13 @@ pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element corrected_scrolled_x, ); - *scrolled_x.write() = scroll_position_x; + // Only scroll when there is still area to scroll + if *scrolled_x.peek() != scroll_position_x { + e.stop_propagation(); + *scrolled_x.write() = scroll_position_x; + } else { + return; + } focus.focus(); } diff --git a/crates/components/src/slider.rs b/crates/components/src/slider.rs index 21b77ed1f..10cb3f2f4 100644 --- a/crates/components/src/slider.rs +++ b/crates/components/src/slider.rs @@ -104,7 +104,8 @@ pub fn Slider( let onmouseleave = { to_owned![platform, status]; - move |_: MouseEvent| { + move |e: MouseEvent| { + e.stop_propagation(); *status.write() = SliderStatus::Idle; platform.set_cursor(CursorIcon::default()); } @@ -112,7 +113,8 @@ pub fn Slider( let onmouseenter = { to_owned![status]; - move |_: MouseEvent| { + move |e: MouseEvent| { + e.stop_propagation(); *status.write() = SliderStatus::Hovering; platform.set_cursor(CursorIcon::Pointer); } @@ -121,6 +123,7 @@ pub fn Slider( let onmouseover = { to_owned![clicking, onmoved]; move |e: MouseEvent| { + e.stop_propagation(); if *clicking.peek() { let coordinates = e.get_element_coordinates(); let x = coordinates.x - size.area.min_x() as f64 - 6.0; @@ -135,6 +138,7 @@ pub fn Slider( let onmousedown = { to_owned![clicking, onmoved, focus]; move |e: MouseEvent| { + e.stop_propagation(); focus.focus(); clicking.set(true); let coordinates = e.get_element_coordinates(); @@ -151,6 +155,7 @@ pub fn Slider( }; let onwheel = move |e: WheelEvent| { + e.stop_propagation(); let wheel_y = e.get_delta_y().clamp(-1.0, 1.0); let percentage = value + (wheel_y * 2.0); let percentage = percentage.clamp(0.0, 100.0); diff --git a/crates/components/src/switch.rs b/crates/components/src/switch.rs index 603f2ee08..cfbe4e3bb 100644 --- a/crates/components/src/switch.rs +++ b/crates/components/src/switch.rs @@ -73,15 +73,21 @@ pub fn Switch(props: SwitchProps) -> Element { } }); + let onmousedown = |e: MouseEvent| { + e.stop_propagation(); + }; + let onmouseleave = { to_owned![platform]; - move |_: MouseEvent| { + move |e: MouseEvent| { + e.stop_propagation(); *status.write() = SwitchStatus::Idle; platform.set_cursor(CursorIcon::default()); } }; - let onmouseenter = move |_: MouseEvent| { + let onmouseenter = move |e: MouseEvent| { + e.stop_propagation(); *status.write() = SwitchStatus::Hovering; platform.set_cursor(CursorIcon::Pointer); }; @@ -89,7 +95,8 @@ pub fn Switch(props: SwitchProps) -> Element { let onclick = { let ontoggled = props.ontoggled.clone(); to_owned![focus]; - move |_: MouseEvent| { + move |e: MouseEvent| { + e.stop_propagation(); focus.focus(); ontoggled.call(()); } @@ -142,7 +149,7 @@ pub fn Switch(props: SwitchProps) -> Element { corner_radius: "50", background: "{background}", border: "{border}", - onmousedown: |_| {}, + onmousedown, onmouseenter, onmouseleave, onkeydown, diff --git a/crates/core/src/events/events_measurer.rs b/crates/core/src/events/events_measurer.rs index edcf40bbb..b0f83fbc6 100644 --- a/crates/core/src/events/events_measurer.rs +++ b/crates/core/src/events/events_measurer.rs @@ -1,8 +1,8 @@ use crate::layout::{Layers, Viewports}; -use dioxus_native_core::prelude::NodeImmutableDioxusExt; use dioxus_native_core::real_dom::NodeImmutable; use dioxus_native_core::NodeId; -use freya_dom::prelude::FreyaDOM; +use dioxus_native_core::{prelude::NodeImmutableDioxusExt, tree::TreeRef}; +use freya_dom::{dom::DioxusDOM, prelude::FreyaDOM}; use freya_engine::prelude::*; use freya_node_state::{Fill, Style}; @@ -26,18 +26,18 @@ pub fn process_events( let global_events = measure_global_events(events); // 2. Get potential events that could be emitted based on the elements layout and viewports - let mut potential_events = measure_potential_event_listeners(layers, events, viewports, dom); + let potential_events = measure_potential_event_listeners(layers, events, viewports, dom); // 3. Get what events can be actually emitted based on what elements are listening - let dom_events = measure_dom_events(&mut potential_events, dom, scale_factor); + let dom_events = measure_dom_events(potential_events, dom, scale_factor); // 4. Filter the dom events and get potential derived events, e.g mouseover -> mouseenter - let (mut potential_colateral_events, mut to_emit_dom_events) = + let (potential_colateral_events, mut to_emit_dom_events) = elements_state.process_events(&dom_events, events); // 5. Get what derived events can actually be emitted let to_emit_dom_colateral_events = - measure_dom_events(&mut potential_colateral_events, dom, scale_factor); + measure_dom_events(potential_colateral_events, dom, scale_factor); // 6. Join both the dom and colateral dom events and sort them to_emit_dom_events.extend(to_emit_dom_colateral_events); @@ -161,76 +161,87 @@ fn get_derivated_events(event_name: &str) -> Vec<&str> { } } -const STACKED_EVENTS: [&str; 13] = [ - "mouseover", - "mouseenter", - "mouseleave", - "click", - "keydown", - "keyup", - "touchcancel", - "touchend", - "touchmove", - "touchstart", - "pointerover", - "pointerenter", - "pointerleave", -]; - -const FIRST_CAPTURED_EVENTS: [&str; 1] = ["wheel"]; - -const LAST_CAPTURED_EVENTS: [&str; 3] = ["click", "touchstart", "touchend"]; +fn is_node_parent_of(rdom: &DioxusDOM, node: NodeId, parent_node: NodeId) -> bool { + let mut stack = vec![parent_node]; + while let Some(id) = stack.pop() { + let tree = rdom.tree_ref(); + let mut children = tree.children_ids(id); + drop(tree); + if children.contains(&node) { + return true; + } + + if rdom.contains(id) { + children.reverse(); + stack.extend(children.iter()); + } + } + + false +} /// Measure what DOM events could be emited fn measure_dom_events( - potential_events: &mut PotentialEvents, + potential_events: PotentialEvents, fdom: &FreyaDOM, scale_factor: f64, ) -> Vec { let mut new_events = Vec::new(); let rdom = fdom.rdom(); - for (event_name, event_nodes) in potential_events.iter_mut() { + // Iterate over all the events + for (event_name, event_nodes) in potential_events { let derivated_events = get_derivated_events(event_name.as_str()); let mut found_nodes: Vec<(&NodeId, FreyaEvent)> = Vec::new(); - for derivated_event_name in derivated_events { + + // Iterate over the derivated event (including the source) + 'event: for derivated_event_name in derivated_events.iter() { + let mut child_node: Option = None; + let listeners = rdom.get_listening_sorted(derivated_event_name); - 'event_nodes: for (node_id, request) in event_nodes.iter() { + + // Iterate over the event nodes + for (node_id, event) in event_nodes.iter().rev() { + let Some(node) = rdom.get(*node_id) else { + continue; + }; + + // Iterate over the event listeners for listener in &listeners { if listener.id() == *node_id { - let Style { background, .. } = &*listener.get::