diff --git a/Cargo.toml b/Cargo.toml
index c05a7f57c..315f39f6a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -79,6 +79,7 @@ skia-safe = { workspace = true }
tokio = { workspace = true, features = ["fs"]}
dioxus = { workspace = true }
freya = { workspace = true }
+freya-hooks = { workspace = true }
freya-core = { workspace = true }
freya-testing = { workspace = true }
reqwest = { version = "0.12.0", features = ["json"] }
@@ -96,6 +97,7 @@ tree-sitter-rust = "0.23.0"
rfd = "0.14.1"
bytes = "1.5.0"
dioxus-sdk = { workspace = true }
+winit = { workspace = true }
[profile.release]
lto = true
diff --git a/README.md b/README.md
index 3b1cef692..1cb7d9d80 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
fn app() -> Element {
let mut count = use_signal(|| 0);
- render!(
+ rsx!(
rect {
height: "50%",
width: "100%",
@@ -70,7 +70,7 @@ fn app() -> Element {
Thanks to my sponsors for supporting this project! 😄
-
+
### Want to try it? 🤔
diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs
index f29017ab0..3443d18bc 100644
--- a/crates/components/src/input.rs
+++ b/crates/components/src/input.rs
@@ -217,7 +217,7 @@ pub fn Input(
main_align: "center",
cursor_reference,
a11y_id,
- a11y_role: "textInput",
+ a11y_role: "text-input",
a11y_auto_focus: "{auto_focus}",
onkeydown,
onkeyup,
diff --git a/crates/components/src/network_image.rs b/crates/components/src/network_image.rs
index 45d096d47..54d60b776 100644
--- a/crates/components/src/network_image.rs
+++ b/crates/components/src/network_image.rs
@@ -124,7 +124,7 @@ pub fn NetworkImage(props: NetworkImageProps) -> Element {
a11y_id,
image_data,
a11y_role: "image",
- a11y_alt: alt
+ a11y_name: alt
})
} else if *status.read() == ImageState::Loading {
if let Some(loading_element) = &props.loading {
diff --git a/crates/components/src/scroll_views/scroll_bar.rs b/crates/components/src/scroll_views/scroll_bar.rs
index 80c0ba7da..e726ca9a2 100644
--- a/crates/components/src/scroll_views/scroll_bar.rs
+++ b/crates/components/src/scroll_views/scroll_bar.rs
@@ -56,7 +56,7 @@ pub fn ScrollBar(
rsx!(
rect {
overflow: "clip",
- a11y_role:"scrollBar",
+ a11y_role: "scroll-bar",
width: "{width}",
height: "{height}",
offset_x: "{offset_x}",
diff --git a/crates/components/src/scroll_views/scroll_view.rs b/crates/components/src/scroll_views/scroll_view.rs
index d82d768d7..0f4ac98f9 100644
--- a/crates/components/src/scroll_views/scroll_view.rs
+++ b/crates/components/src/scroll_views/scroll_view.rs
@@ -372,7 +372,7 @@ pub fn ScrollView(
rsx!(
rect {
- a11y_role:"scrollView",
+ a11y_role:"scroll-view",
overflow: "clip",
direction: "horizontal",
width,
diff --git a/crates/components/src/scroll_views/use_scroll_controller.rs b/crates/components/src/scroll_views/use_scroll_controller.rs
index c20b3b5b1..8e5736b93 100644
--- a/crates/components/src/scroll_views/use_scroll_controller.rs
+++ b/crates/components/src/scroll_views/use_scroll_controller.rs
@@ -75,6 +75,14 @@ impl ScrollController {
}
}
+ pub fn x(&self) -> Signal {
+ self.x
+ }
+
+ pub fn y(&self) -> Signal {
+ self.y
+ }
+
pub fn use_apply(&mut self, width: f32, height: f32) {
let scope_id = current_scope_id().unwrap();
diff --git a/crates/components/src/scroll_views/virtual_scroll_view.rs b/crates/components/src/scroll_views/virtual_scroll_view.rs
index 74f9d51ff..2365ed697 100644
--- a/crates/components/src/scroll_views/virtual_scroll_view.rs
+++ b/crates/components/src/scroll_views/virtual_scroll_view.rs
@@ -447,7 +447,7 @@ pub fn VirtualScrollView<
rsx!(
rect {
- a11y_role:"scrollView",
+ a11y_role: "scroll-view",
overflow: "clip",
direction: "horizontal",
width: "{width}",
diff --git a/crates/core/src/accessibility/mod.rs b/crates/core/src/accessibility/mod.rs
index 52f3cc600..e590e541e 100644
--- a/crates/core/src/accessibility/mod.rs
+++ b/crates/core/src/accessibility/mod.rs
@@ -4,6 +4,7 @@ use freya_native_core::{
real_dom::NodeImmutable,
};
use freya_node_state::AccessibilityNodeState;
+use itertools::Itertools;
pub use tree::*;
use crate::{
@@ -54,7 +55,9 @@ impl NodeAccessibility for DioxusNode<'_> {
/// Collect all descendant accessibility node ids
fn get_accessibility_children(&self) -> Vec {
- let node_accessibility = &*self.get::().unwrap();
- node_accessibility.descencent_accessibility_ids.clone()
+ self.children()
+ .into_iter()
+ .filter_map(|child| child.get_accessibility_id())
+ .collect_vec()
}
}
diff --git a/crates/core/src/accessibility/tree.rs b/crates/core/src/accessibility/tree.rs
index ef5b1bfd9..f54c61f18 100644
--- a/crates/core/src/accessibility/tree.rs
+++ b/crates/core/src/accessibility/tree.rs
@@ -152,19 +152,20 @@ impl AccessibilityTree {
// Mark the ancestors as modified
for node_id in added_or_updated_ids.clone() {
let node_ref = rdom.get(node_id).unwrap();
- let node_accessibility_state = node_ref.get::().unwrap();
- added_or_updated_ids.insert(
- node_accessibility_state
- .closest_accessibility_node_id
- .unwrap_or(rdom.root_id()),
- );
+ let node_ref_parent = node_ref.parent_id().unwrap_or(rdom.root_id());
+ added_or_updated_ids.insert(node_ref_parent);
self.map
.insert(node_ref.get_accessibility_id().unwrap(), node_id);
}
// Mark the still existing ancenstors as modified
- for (node_id, ancestor_node_id) in removed_ids {
- added_or_updated_ids.insert(ancestor_node_id);
+ for (_, ancestor_node_id) in removed_ids.iter() {
+ added_or_updated_ids.insert(*ancestor_node_id);
+ }
+
+ // Remove all the deleted noeds from the added_or_update list
+ for (node_id, _) in removed_ids {
+ added_or_updated_ids.remove(&node_id);
self.map.retain(|_, id| *id != node_id);
}
@@ -180,7 +181,6 @@ impl AccessibilityTree {
{
let accessibility_node =
Self::create_node(&node_ref, layout_node, node_accessibility_state);
-
let accessibility_id = node_ref.get_accessibility_id().unwrap();
nodes.push((accessibility_id, accessibility_node));
@@ -332,7 +332,18 @@ impl AccessibilityTree {
let transform_state = &*node_ref.get::().unwrap();
let node_type = node_ref.node_type();
- let mut builder = NodeBuilder::new(Role::default());
+ let mut builder = match node_type.tag() {
+ // Make the root accessibility node.
+ Some(&TagName::Root) => NodeBuilder::new(Role::Window),
+
+ // All other node types will either don't have a builder (but don't support
+ // accessibility attributes like with `text`) or have their builder made for
+ // them already.
+ Some(_) => node_accessibility.builder.clone().unwrap(),
+
+ // Tag-less nodes can't have accessibility state
+ None => unreachable!(),
+ };
// Set children
let children = node_ref.get_accessibility_children();
@@ -347,6 +358,14 @@ impl AccessibilityTree {
y1: area.max_y(),
});
+ if let NodeType::Element(node) = &*node_type {
+ if matches!(node.tag, TagName::Label | TagName::Paragraph) && builder.name().is_none() {
+ if let Some(inner_text) = node_ref.get_inner_texts() {
+ builder.set_name(inner_text);
+ }
+ }
+ }
+
// Set focusable action
// This will cause assistive technology to offer the user an option
// to focus the current element if it supports it.
@@ -453,28 +472,6 @@ impl AccessibilityTree {
));
}
- // Set text value
- if let Some(alt) = &node_accessibility.a11y_alt {
- builder.set_value(alt.to_owned());
- } else if let Some(value) = node_ref.get_inner_texts() {
- builder.set_value(value);
- builder.set_role(Role::Label);
- }
-
- // Set name
- if let Some(name) = &node_accessibility.a11y_name {
- builder.set_name(name.to_owned());
- }
-
- // Set role
- if let Some(role) = node_accessibility.a11y_role {
- builder.set_role(role);
- }
- // Set root role
- if node_ref.id() == node_ref.real_dom().root_id() {
- builder.set_role(Role::Window);
- }
-
builder.build()
}
}
diff --git a/crates/core/src/dom/mutations_writer.rs b/crates/core/src/dom/mutations_writer.rs
index 8db949c0b..aeb1d59be 100644
--- a/crates/core/src/dom/mutations_writer.rs
+++ b/crates/core/src/dom/mutations_writer.rs
@@ -17,7 +17,6 @@ use freya_native_core::{
NodeId,
};
use freya_node_state::{
- AccessibilityNodeState,
CursorState,
CustomAttributeValues,
LayerState,
@@ -91,9 +90,8 @@ impl<'a> MutationsWriter<'a> {
// Remove from the accessibility tree
if node.get_accessibility_id().is_some() {
- let node_accessibility_state = node.get::().unwrap();
- let closed_accessibility_node_id = node_accessibility_state
- .closest_accessibility_node_id
+ let closed_accessibility_node_id = node
+ .parent_id()
.unwrap_or(self.native_writer.rdom.root_id());
self.accessibility_dirty_nodes
.remove(node.id(), closed_accessibility_node_id);
diff --git a/crates/core/src/render/utils/label.rs b/crates/core/src/render/utils/label.rs
index a71eeb3eb..e1d1619b2 100644
--- a/crates/core/src/render/utils/label.rs
+++ b/crates/core/src/render/utils/label.rs
@@ -38,11 +38,13 @@ pub fn create_label(
}
let mut paragraph = paragraph_builder.build();
- paragraph.layout(if font_style.max_lines == Some(1) {
- f32::MAX
- } else {
- area_size.width + 1.0
- });
+ paragraph.layout(
+ if font_style.max_lines == Some(1) && font_style.text_align == TextAlign::default() {
+ f32::MAX
+ } else {
+ area_size.width + 1.0
+ },
+ );
paragraph
}
diff --git a/crates/core/src/render/utils/paragraph.rs b/crates/core/src/render/utils/paragraph.rs
index 9fe6e89dc..6e8754a28 100644
--- a/crates/core/src/render/utils/paragraph.rs
+++ b/crates/core/src/render/utils/paragraph.rs
@@ -10,7 +10,6 @@ use freya_node_state::{
FontStyleState,
HighlightMode,
LayoutState,
- TextOverflow,
};
use torin::prelude::{
Alignment,
@@ -36,8 +35,8 @@ pub fn create_paragraph(
paragraph_style.set_max_lines(font_style.max_lines);
paragraph_style.set_replace_tab_characters(true);
- if font_style.text_overflow == TextOverflow::Ellipsis {
- paragraph_style.set_ellipsis("…");
+ if let Some(ellipsis) = font_style.text_overflow.get_ellipsis() {
+ paragraph_style.set_ellipsis(ellipsis);
}
let mut paragraph_builder = ParagraphBuilder::new(¶graph_style, font_collection);
@@ -69,11 +68,13 @@ pub fn create_paragraph(
}
let mut paragraph = paragraph_builder.build();
- paragraph.layout(if font_style.max_lines == Some(1) {
- f32::MAX
- } else {
- area_size.width + 1.0
- });
+ paragraph.layout(
+ if font_style.max_lines == Some(1) && font_style.text_align == TextAlign::default() {
+ f32::MAX
+ } else {
+ area_size.width + 1.0
+ },
+ );
paragraph
}
diff --git a/crates/devtools/src/lib.rs b/crates/devtools/src/lib.rs
index 5c651e353..f79b7b510 100644
--- a/crates/devtools/src/lib.rs
+++ b/crates/devtools/src/lib.rs
@@ -12,6 +12,7 @@ use freya_components::*;
use freya_core::prelude::EventMessage;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{
+ use_applied_theme,
use_init_theme,
use_platform,
DARK_THEME,
@@ -116,15 +117,15 @@ impl PartialEq for DevToolsProps {
#[allow(non_snake_case)]
pub fn DevTools(props: DevToolsProps) -> Element {
- let theme = use_init_theme(|| DARK_THEME);
+ use_init_theme(|| DARK_THEME);
use_init_radio_station::(|| DevtoolsState {
hovered_node: props.hovered_node.clone(),
devtools_receiver: props.devtools_receiver.clone(),
devtools_tree: HashSet::default(),
});
- let theme = theme.read();
- let color = &theme.body.color;
+ let theme = use_applied_theme!(None, body);
+ let color = &theme.color;
rsx!(
rect {
diff --git a/crates/elements/src/definitions.rs b/crates/elements/src/definitions.rs
index ad5c9bdcb..abeb5c600 100644
--- a/crates/elements/src/definitions.rs
+++ b/crates/elements/src/definitions.rs
@@ -232,18 +232,81 @@ builder_constructors! {
#[doc = include_str!("_docs/attributes/spacing.md")]
spacing: String,
- a11y_auto_focus: String,
- a11y_name: String,
- a11y_role:String,
- a11y_id: AccessibilityId,
- a11y_alt: String,
- a11y_focusable: String,
canvas_reference: String,
layer: String,
offset_y: String,
offset_x: String,
reference: Reference,
cursor_reference: CursorReference,
+
+ a11y_id: String,
+ a11y_focusable: String,
+ a11y_auto_focus: String,
+ a11y_name: String,
+ a11y_description: String,
+ a11y_value: String,
+ a11y_access_key: String,
+ a11y_author_id: String,
+ a11y_keyboard_shortcut: String,
+ a11y_language: String,
+ a11y_placeholder: String,
+ a11y_role_description: String,
+ a11y_state_description: String,
+ a11y_tooltip: String,
+ a11y_url: String,
+ a11y_row_index_text: String,
+ a11y_column_index_text: String,
+ a11y_scroll_x: String,
+ a11y_scroll_x_min: String,
+ a11y_scroll_x_max: String,
+ a11y_scroll_y: String,
+ a11y_scroll_y_min: String,
+ a11y_scroll_y_max: String,
+ a11y_numeric_value: String,
+ a11y_min_numeric_value: String,
+ a11y_max_numeric_value: String,
+ a11y_numeric_value_step: String,
+ a11y_numeric_value_jump: String,
+ a11y_row_count: String,
+ a11y_column_count: String,
+ a11y_row_index: String,
+ a11y_column_index: String,
+ a11y_row_span: String,
+ a11y_column_span: String,
+ a11y_level: String,
+ a11y_size_of_set: String,
+ a11y_position_in_set: String,
+ a11y_color_value: String,
+ a11y_expanded: String,
+ a11y_selected: String,
+ a11y_hovered: String,
+ a11y_hidden: String,
+ a11y_linked: String,
+ a11y_multiselectable: String,
+ a11y_required: String,
+ a11y_visited: String,
+ a11y_busy: String,
+ a11y_live_atomic: String,
+ a11y_modal: String,
+ a11y_touch_transparent: String,
+ a11y_read_only: String,
+ a11y_disabled: String,
+ a11y_is_spelling_error: String,
+ a11y_is_grammar_error: String,
+ a11y_is_search_match: String,
+ a11y_is_suggestion: String,
+ a11y_role: String,
+ a11y_invalid: String,
+ a11y_toggled: String,
+ a11y_live: String,
+ a11y_default_action_verb: String,
+ a11y_orientation: String,
+ a11y_sort_direction: String,
+ a11y_current: String,
+ a11y_auto_complete: String,
+ a11y_has_popup: String,
+ a11y_list_style: String,
+ a11y_vertical_offset: String,
};
/// `label` simply let's you display some text.
///
@@ -305,12 +368,75 @@ builder_constructors! {
opacity: String,
layer: String,
+
+ a11y_id: String,
a11y_auto_focus: String,
- a11y_name: String,
- a11y_role:String,
- a11y_id: AccessibilityId,
- a11y_alt: String,
a11y_focusable: String,
+ a11y_name: String,
+ a11y_description: String,
+ a11y_value: String,
+ a11y_access_key: String,
+ a11y_author_id: String,
+ a11y_keyboard_shortcut: String,
+ a11y_language: String,
+ a11y_placeholder: String,
+ a11y_role_description: String,
+ a11y_state_description: String,
+ a11y_tooltip: String,
+ a11y_url: String,
+ a11y_row_index_text: String,
+ a11y_column_index_text: String,
+ a11y_scroll_x: String,
+ a11y_scroll_x_min: String,
+ a11y_scroll_x_max: String,
+ a11y_scroll_y: String,
+ a11y_scroll_y_min: String,
+ a11y_scroll_y_max: String,
+ a11y_numeric_value: String,
+ a11y_min_numeric_value: String,
+ a11y_max_numeric_value: String,
+ a11y_numeric_value_step: String,
+ a11y_numeric_value_jump: String,
+ a11y_row_count: String,
+ a11y_column_count: String,
+ a11y_row_index: String,
+ a11y_column_index: String,
+ a11y_row_span: String,
+ a11y_column_span: String,
+ a11y_level: String,
+ a11y_size_of_set: String,
+ a11y_position_in_set: String,
+ a11y_color_value: String,
+ a11y_expanded: String,
+ a11y_selected: String,
+ a11y_hovered: String,
+ a11y_hidden: String,
+ a11y_linked: String,
+ a11y_multiselectable: String,
+ a11y_required: String,
+ a11y_visited: String,
+ a11y_busy: String,
+ a11y_live_atomic: String,
+ a11y_modal: String,
+ a11y_touch_transparent: String,
+ a11y_read_only: String,
+ a11y_disabled: String,
+ a11y_is_spelling_error: String,
+ a11y_is_grammar_error: String,
+ a11y_is_search_match: String,
+ a11y_is_suggestion: String,
+ a11y_role: String,
+ a11y_invalid: String,
+ a11y_toggled: String,
+ a11y_live: String,
+ a11y_default_action_verb: String,
+ a11y_orientation: String,
+ a11y_sort_direction: String,
+ a11y_current: String,
+ a11y_auto_complete: String,
+ a11y_has_popup: String,
+ a11y_list_style: String,
+ a11y_vertical_offset: String,
};
/// `paragraph` element let's you build texts with different styles.
///
@@ -384,15 +510,79 @@ builder_constructors! {
cursor_color: String,
cursor_mode: String,
cursor_id: String,
- a11y_auto_focus: String,
- a11y_name: String,
- a11y_role:String,
- a11y_id: AccessibilityId,
- a11y_alt: String,
- a11y_focusable: String,
+
highlights: String,
highlight_color: String,
highlight_mode: String,
+
+ a11y_id: String,
+ a11y_focusable: String,
+ a11y_auto_focus: String,
+ a11y_name: String,
+ a11y_description: String,
+ a11y_value: String,
+ a11y_access_key: String,
+ a11y_author_id: String,
+ a11y_keyboard_shortcut: String,
+ a11y_language: String,
+ a11y_placeholder: String,
+ a11y_role_description: String,
+ a11y_state_description: String,
+ a11y_tooltip: String,
+ a11y_url: String,
+ a11y_row_index_text: String,
+ a11y_column_index_text: String,
+ a11y_scroll_x: String,
+ a11y_scroll_x_min: String,
+ a11y_scroll_x_max: String,
+ a11y_scroll_y: String,
+ a11y_scroll_y_min: String,
+ a11y_scroll_y_max: String,
+ a11y_numeric_value: String,
+ a11y_min_numeric_value: String,
+ a11y_max_numeric_value: String,
+ a11y_numeric_value_step: String,
+ a11y_numeric_value_jump: String,
+ a11y_row_count: String,
+ a11y_column_count: String,
+ a11y_row_index: String,
+ a11y_column_index: String,
+ a11y_row_span: String,
+ a11y_column_span: String,
+ a11y_level: String,
+ a11y_size_of_set: String,
+ a11y_position_in_set: String,
+ a11y_color_value: String,
+ a11y_expanded: String,
+ a11y_selected: String,
+ a11y_hovered: String,
+ a11y_hidden: String,
+ a11y_linked: String,
+ a11y_multiselectable: String,
+ a11y_required: String,
+ a11y_visited: String,
+ a11y_busy: String,
+ a11y_live_atomic: String,
+ a11y_modal: String,
+ a11y_touch_transparent: String,
+ a11y_read_only: String,
+ a11y_disabled: String,
+ a11y_is_spelling_error: String,
+ a11y_is_grammar_error: String,
+ a11y_is_search_match: String,
+ a11y_is_suggestion: String,
+ a11y_role: String,
+ a11y_invalid: String,
+ a11y_toggled: String,
+ a11y_live: String,
+ a11y_default_action_verb: String,
+ a11y_orientation: String,
+ a11y_sort_direction: String,
+ a11y_current: String,
+ a11y_auto_complete: String,
+ a11y_has_popup: String,
+ a11y_list_style: String,
+ a11y_vertical_offset: String,
};
/// `text` element is simply a text span used for the `paragraph` element.
text {
@@ -400,9 +590,6 @@ builder_constructors! {
color: String,
#[doc = include_str!("_docs/attributes/font_size.md")]
text_shadow: String,
- #[doc = include_str!("_docs/attributes/width_height.md")]
- height: String,
- width: String,
#[doc = include_str!("_docs/attributes/font_size.md")]
font_size: String,
#[doc = include_str!("_docs/attributes/font_family.md")]
@@ -456,11 +643,75 @@ builder_constructors! {
image_data: String,
image_reference: String,
+
+ a11y_id: String,
a11y_auto_focus: String,
+ a11y_focusable: String,
a11y_name: String,
- a11y_role:String,
- a11y_id: AccessibilityId,
- a11y_alt: String,
+ a11y_description: String,
+ a11y_value: String,
+ a11y_access_key: String,
+ a11y_author_id: String,
+ a11y_keyboard_shortcut: String,
+ a11y_language: String,
+ a11y_placeholder: String,
+ a11y_role_description: String,
+ a11y_state_description: String,
+ a11y_tooltip: String,
+ a11y_url: String,
+ a11y_row_index_text: String,
+ a11y_column_index_text: String,
+ a11y_scroll_x: String,
+ a11y_scroll_x_min: String,
+ a11y_scroll_x_max: String,
+ a11y_scroll_y: String,
+ a11y_scroll_y_min: String,
+ a11y_scroll_y_max: String,
+ a11y_numeric_value: String,
+ a11y_min_numeric_value: String,
+ a11y_max_numeric_value: String,
+ a11y_numeric_value_step: String,
+ a11y_numeric_value_jump: String,
+ a11y_row_count: String,
+ a11y_column_count: String,
+ a11y_row_index: String,
+ a11y_column_index: String,
+ a11y_row_span: String,
+ a11y_column_span: String,
+ a11y_level: String,
+ a11y_size_of_set: String,
+ a11y_position_in_set: String,
+ a11y_color_value: String,
+ a11y_expanded: String,
+ a11y_selected: String,
+ a11y_hovered: String,
+ a11y_hidden: String,
+ a11y_linked: String,
+ a11y_multiselectable: String,
+ a11y_required: String,
+ a11y_visited: String,
+ a11y_busy: String,
+ a11y_live_atomic: String,
+ a11y_modal: String,
+ a11y_touch_transparent: String,
+ a11y_read_only: String,
+ a11y_disabled: String,
+ a11y_is_spelling_error: String,
+ a11y_is_grammar_error: String,
+ a11y_is_search_match: String,
+ a11y_is_suggestion: String,
+ a11y_role: String,
+ a11y_invalid: String,
+ a11y_toggled: String,
+ a11y_live: String,
+ a11y_default_action_verb: String,
+ a11y_orientation: String,
+ a11y_sort_direction: String,
+ a11y_current: String,
+ a11y_auto_complete: String,
+ a11y_has_popup: String,
+ a11y_list_style: String,
+ a11y_vertical_offset: String,
};
/// `svg` element let's you display SVG code.
///
@@ -495,12 +746,75 @@ builder_constructors! {
svg_data: String,
svg_content: String,
+
+ a11y_id: String,
+ a11y_focusable: String,
a11y_auto_focus: String,
a11y_name: String,
- a11y_role:String,
- a11y_id: AccessibilityId,
- a11y_alt: String,
- a11y_focusable: String,
+ a11y_description: String,
+ a11y_value: String,
+ a11y_access_key: String,
+ a11y_author_id: String,
+ a11y_keyboard_shortcut: String,
+ a11y_language: String,
+ a11y_placeholder: String,
+ a11y_role_description: String,
+ a11y_state_description: String,
+ a11y_tooltip: String,
+ a11y_url: String,
+ a11y_row_index_text: String,
+ a11y_column_index_text: String,
+ a11y_scroll_x: String,
+ a11y_scroll_x_min: String,
+ a11y_scroll_x_max: String,
+ a11y_scroll_y: String,
+ a11y_scroll_y_min: String,
+ a11y_scroll_y_max: String,
+ a11y_numeric_value: String,
+ a11y_min_numeric_value: String,
+ a11y_max_numeric_value: String,
+ a11y_numeric_value_step: String,
+ a11y_numeric_value_jump: String,
+ a11y_row_count: String,
+ a11y_column_count: String,
+ a11y_row_index: String,
+ a11y_column_index: String,
+ a11y_row_span: String,
+ a11y_column_span: String,
+ a11y_level: String,
+ a11y_size_of_set: String,
+ a11y_position_in_set: String,
+ a11y_color_value: String,
+ a11y_expanded: String,
+ a11y_selected: String,
+ a11y_hovered: String,
+ a11y_hidden: String,
+ a11y_linked: String,
+ a11y_multiselectable: String,
+ a11y_required: String,
+ a11y_visited: String,
+ a11y_busy: String,
+ a11y_live_atomic: String,
+ a11y_modal: String,
+ a11y_touch_transparent: String,
+ a11y_read_only: String,
+ a11y_disabled: String,
+ a11y_is_spelling_error: String,
+ a11y_is_grammar_error: String,
+ a11y_is_search_match: String,
+ a11y_is_suggestion: String,
+ a11y_role: String,
+ a11y_invalid: String,
+ a11y_toggled: String,
+ a11y_live: String,
+ a11y_default_action_verb: String,
+ a11y_orientation: String,
+ a11y_sort_direction: String,
+ a11y_current: String,
+ a11y_auto_complete: String,
+ a11y_has_popup: String,
+ a11y_list_style: String,
+ a11y_vertical_offset: String,
};
}
diff --git a/crates/freya/src/plugins/performance_overlay.rs b/crates/freya/src/plugins/performance_overlay.rs
index 11d257808..3d4b79852 100644
--- a/crates/freya/src/plugins/performance_overlay.rs
+++ b/crates/freya/src/plugins/performance_overlay.rs
@@ -82,7 +82,7 @@ impl FreyaPlugin for PerformanceOverlayPlugin {
// FPS
add_text(
&mut paragraph_builder,
- format!("{} \n", self.frames.len()),
+ format!("{} FPS\n", self.frames.len()),
30.0,
);
diff --git a/crates/hooks/src/text_editor.rs b/crates/hooks/src/text_editor.rs
index ad932c235..936c3f13f 100644
--- a/crates/hooks/src/text_editor.rs
+++ b/crates/hooks/src/text_editor.rs
@@ -459,9 +459,8 @@ pub trait TextEditor {
// Remove selected text
let selection = self.get_selection_range();
if let Some((start, end)) = selection {
- let cursor_pos = self.cursor_pos();
- let removed_text_len = self.remove(start..end);
- self.set_cursor_pos(cursor_pos - removed_text_len);
+ self.remove(start..end);
+ self.set_cursor_pos(start);
event.insert(TextEvent::TEXT_CHANGED);
}
diff --git a/crates/hooks/src/theming/base.rs b/crates/hooks/src/theming/base.rs
index d8ab036ee..6371c4eea 100644
--- a/crates/hooks/src/theming/base.rs
+++ b/crates/hooks/src/theming/base.rs
@@ -21,6 +21,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
solid: cow_borrowed!(""),
color: cow_borrowed!(""),
placeholder_color: cow_borrowed!(""),
+ highlight_color: cow_borrowed!(""),
},
body: BodyTheme {
background: cow_borrowed!("key(background)"),
@@ -55,7 +56,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
color: cow_borrowed!("key(color)"),
},
placeholder_font_theme: FontTheme {
- color: cow_borrowed!("rgb(100, 100, 100)"),
+ color: cow_borrowed!("key(placeholder_color)"),
},
border_fill: cow_borrowed!("key(surface)"),
width: cow_borrowed!("150"),
@@ -94,7 +95,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
color: cow_borrowed!("key(color)"),
},
border_fill: cow_borrowed!("key(surface)"),
- arrow_fill: cow_borrowed!("rgb(40, 40, 40)"),
+ arrow_fill: cow_borrowed!("key(solid)"),
},
dropdown_item: DropdownItemTheme {
background: cow_borrowed!("key(background)"),
@@ -105,7 +106,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
},
},
accordion: AccordionTheme {
- color: cow_borrowed!("black"),
+ color: cow_borrowed!("key(color)"),
background: cow_borrowed!("key(neutral_surface)"),
border_fill: cow_borrowed!("key(surface)"),
},
@@ -113,7 +114,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
primary_color: cow_borrowed!("key(tertiary_opposite_surface)"),
},
link: LinkTheme {
- highlight_color: cow_borrowed!("rgb(43,106,208)"),
+ highlight_color: cow_borrowed!("key(highlight_color)"),
},
progress_bar: ProgressBarTheme {
color: cow_borrowed!("white"),
@@ -124,10 +125,10 @@ pub(crate) const BASE_THEME: Theme = Theme {
},
table: TableTheme {
font_theme: FontTheme {
- color: cow_borrowed!("black"),
+ color: cow_borrowed!("key(color)"),
},
background: cow_borrowed!("key(background)"),
- arrow_fill: cow_borrowed!("rgb(40, 40, 40)"),
+ arrow_fill: cow_borrowed!("key(solid)"),
row_background: cow_borrowed!("transparent"),
alternate_row_background: cow_borrowed!("key(neutral_surface)"),
divider_fill: cow_borrowed!("key(secondary_surface)"),
@@ -200,7 +201,7 @@ pub(crate) const BASE_THEME: Theme = Theme {
},
popup: PopupTheme {
background: cow_borrowed!("key(background)"),
- color: cow_borrowed!("black"),
+ color: cow_borrowed!("key(color)"),
cross_fill: cow_borrowed!("key(solid)"),
width: cow_borrowed!("350"),
height: cow_borrowed!("200"),
diff --git a/crates/hooks/src/theming/mod.rs b/crates/hooks/src/theming/mod.rs
index ae3f32f7d..10d9d23e3 100644
--- a/crates/hooks/src/theming/mod.rs
+++ b/crates/hooks/src/theming/mod.rs
@@ -545,6 +545,7 @@ pub struct ColorsSheet {
pub solid: Cow<'static, str>,
pub color: Cow<'static, str>,
pub placeholder_color: Cow<'static, str>,
+ pub highlight_color: Cow<'static, str>,
}
impl ColorsSheet {
@@ -567,6 +568,7 @@ impl ColorsSheet {
"solid" => self.solid.clone(),
"color" => self.color.clone(),
"placeholder_color" => self.placeholder_color.clone(),
+ "highlight_color" => self.highlight_color.clone(),
_ => self.primary.clone(),
}
} else {
diff --git a/crates/hooks/src/theming/themes.rs b/crates/hooks/src/theming/themes.rs
index 289bec04e..5afb9f2d4 100644
--- a/crates/hooks/src/theming/themes.rs
+++ b/crates/hooks/src/theming/themes.rs
@@ -14,14 +14,15 @@ pub const DARK_THEME: Theme = Theme {
secondary_surface: cow_borrowed!("rgb(45, 45, 45)"),
neutral_surface: cow_borrowed!("rgb(25, 25, 25)"),
focused_surface: cow_borrowed!("rgb(15, 15, 15)"),
- opposite_surface: cow_borrowed!("rgb(210, 210, 210)"),
- secondary_opposite_surface: cow_borrowed!("rgb(225, 225, 225)"),
- tertiary_opposite_surface: cow_borrowed!("rgb(235, 235, 235)"),
+ opposite_surface: cow_borrowed!("rgb(125, 125, 125)"),
+ secondary_opposite_surface: cow_borrowed!("rgb(150, 150, 150)"),
+ tertiary_opposite_surface: cow_borrowed!("rgb(170, 170, 170)"),
background: cow_borrowed!("rgb(20, 20, 20)"),
focused_border: cow_borrowed!("rgb(110, 110, 110)"),
solid: cow_borrowed!("rgb(240, 240, 240)"),
color: cow_borrowed!("rgb(250, 250, 250)"),
placeholder_color: cow_borrowed!("rgb(210, 210, 210)"),
+ highlight_color: cow_borrowed!("rgb(96, 145, 224)"),
},
..BASE_THEME
};
@@ -44,6 +45,7 @@ pub const LIGHT_THEME: Theme = Theme {
focused_border: cow_borrowed!("rgb(180, 180, 180)"),
color: cow_borrowed!("rgb(10, 10, 10)"),
placeholder_color: cow_borrowed!("rgb(100, 100, 100)"),
+ highlight_color: cow_borrowed!("rgb(38, 89, 170)"),
},
..BASE_THEME
};
@@ -66,6 +68,7 @@ pub const BANANA_THEME: Theme = Theme {
focused_border: cow_borrowed!("rgb(255, 239, 151)"),
color: cow_borrowed!("rgb(85, 60, 5)"),
placeholder_color: cow_borrowed!("rgb(56, 44, 5)"),
+ highlight_color: cow_borrowed!("rgb(143, 114, 6)"),
},
..BASE_THEME
};
diff --git a/crates/hooks/src/use_init_native_platform.rs b/crates/hooks/src/use_init_native_platform.rs
index bb5dffd64..aa267f673 100644
--- a/crates/hooks/src/use_init_native_platform.rs
+++ b/crates/hooks/src/use_init_native_platform.rs
@@ -155,7 +155,6 @@ mod test {
let focus = use_focus();
rsx!(rect {
a11y_id: focus.attribute(),
- a11y_role: "genericContainer",
width: "100%",
height: "50%",
})
@@ -223,12 +222,10 @@ mod test {
rsx!(
rect {
a11y_id: focus_1.attribute(),
- a11y_role: "genericContainer",
a11y_auto_focus: "true",
}
rect {
a11y_id: focus_2.attribute(),
- a11y_role: "genericContainer",
a11y_auto_focus: "true",
}
)
diff --git a/crates/native-core/src/attributes.rs b/crates/native-core/src/attributes.rs
index f91f42fdc..1e7f2713a 100644
--- a/crates/native-core/src/attributes.rs
+++ b/crates/native-core/src/attributes.rs
@@ -43,12 +43,6 @@ pub enum AttributeName {
PositionLeft,
Opacity,
Content,
- A11YAutoFocus,
- A11YName,
- A11YFocusable,
- A11YRole,
- A11YId,
- A11YAlt,
CanvasReference,
Layer,
OffsetY,
@@ -67,6 +61,160 @@ pub enum AttributeName {
SvgData,
SvgContent,
Spacing,
+
+ // Focus
+ A11yId,
+ A11yFocusable,
+ A11yAutoFocus,
+
+ // Some internal notes about these accessibility attributes:
+ //
+ // - These are mostly derived from AccessKit's [`Node`] struct, with minor
+ // modifications to fit Freya's needs. These modifications are documented.
+ //
+ // - Some properties are commented out, meaning they are yet to be implemented.
+ // This is typically due to it being unclear how to represent these in Freya's
+ // attribute system (such as the association types, which will likely need
+ // some kind of ID system).
+ //
+ // - Any AccessKit properties that can be automatically calculated from style
+ // attributes or measured from torin are not included here, and are instead
+ // added in Freya's [`AccessibilityManager`] struct.
+
+ // Vec associations
+ // A11yControls,
+ // A11yDetails,
+ // A11yDescribedBy,
+ // A11yFlowTo,
+ // A11yLabelledBy,
+ // A11yOwns,
+ // A11yRadioGroup,
+
+ // NodeId associations
+ // ActiveDescendant,
+ // A11yErrorMessage,
+ // A11yInPageLinkTarget,
+ // A11yMemberOf,
+ // A11yNextOnLine,
+ // A11yPreviousOnLine,
+ // A11yPopupFor,
+
+ // String
+ A11yName,
+ A11yDescription,
+ A11yValue,
+ A11yAccessKey,
+ A11yAuthorId,
+ // These three attributes are intended for assistive tech that parse MathML,
+ // which we don't support at the moment anyways. Unlikely to be implemented.
+ // A11yClassName,
+ // A11yHtmlTag,
+ // A11yInnerHtml,
+ A11yKeyboardShortcut,
+ A11yLanguage,
+ A11yPlaceholder,
+ A11yRoleDescription,
+ A11yStateDescription,
+ A11yTooltip,
+ A11yUrl,
+ A11yRowIndexText,
+ A11yColumnIndexText,
+
+ // f64
+ A11yScrollX,
+ A11yScrollXMin,
+ A11yScrollXMax,
+ A11yScrollY,
+ A11yScrollYMin,
+ A11yScrollYMax,
+ A11yNumericValue,
+ A11yMinNumericValue,
+ A11yMaxNumericValue,
+ A11yNumericValueStep,
+ A11yNumericValueJump,
+
+ // usize
+ A11yRowCount,
+ A11yColumnCount,
+ A11yRowIndex,
+ A11yColumnIndex,
+ A11yRowSpan,
+ A11yColumnSpan,
+ A11yLevel,
+ A11ySizeOfSet,
+ A11yPositionInSet,
+
+ // Color
+ A11yColorValue,
+
+ // TODO: The following two categories are for inline text. They should be implemented
+ // automatically in [`AccessibilityManager`] based on Skia text measurement on text.
+ // spans. These really shouldn't be here (they should never have to be manually provided
+ // as an attribute), but I've left them here as a reminder to implement inline text data.
+ //
+ // See AccessKit's documentation for inline text measurements here:
+ // -
+ //
+ // Chromium also has a good writeup on how it measures inline text spans:
+ // -
+
+ // LengthSlice
+ // A11yCharacterLengths,
+ // A11yWordLengths,
+
+ // CoordSlice
+ // A11yCharacterPositions,
+ // A11yCharacterWidths,
+
+ // bool
+ A11yExpanded,
+ A11ySelected,
+
+ // bitflag
+ // TODO: This might be able to be determined automatically,
+ // but i'm not sure what ARIA property it corresponds to
+ // or its actual purpose.
+ A11yHovered,
+ A11yHidden,
+ A11yLinked,
+ A11yMultiselectable,
+ A11yRequired,
+ A11yVisited,
+ A11yBusy,
+ A11yLiveAtomic,
+ A11yModal,
+ A11yTouchTransparent,
+ A11yReadOnly,
+ A11yDisabled,
+ A11yIsSpellingError,
+ A11yIsGrammarError,
+ A11yIsSearchMatch,
+ A11yIsSuggestion,
+
+ // Unique enums
+ A11yRole,
+ A11yInvalid,
+ A11yToggled,
+ A11yLive,
+ A11yDefaultActionVerb,
+ A11yOrientation,
+ A11ySortDirection,
+ A11yCurrent, // called AriaCurrent in accesskit, but that's a pretty poor name
+ A11yAutoComplete,
+ A11yHasPopup,
+ // This one is kind of weird to include, given it's reflecting a CSS property
+ // not in Freya for the HTML /- tags, but it can maybe be useful for
+ // language-specific semantics.
+ A11yListStyle,
+ A11yVerticalOffset,
+ // Other
+ // This could probably be inferred from Freya's text editing hook, but it's also
+ // a little strange in the data it expects.
+ // A11yTextSelection,
+ // A11yCustomActions, // Needs a special syntax or custom attribute value'
+
+ // TODO: Some way to specify builtin AccessKit actions, as well as a way to
+ // handle actions in the form of an event.
}
impl FromStr for AttributeName {
@@ -115,12 +263,6 @@ impl FromStr for AttributeName {
"position_left" => Ok(AttributeName::PositionLeft),
"opacity" => Ok(AttributeName::Opacity),
"content" => Ok(AttributeName::Content),
- "a11y_auto_focus" => Ok(AttributeName::A11YAutoFocus),
- "a11y_name" => Ok(AttributeName::A11YName),
- "a11y_role" => Ok(AttributeName::A11YRole),
- "a11y_id" => Ok(AttributeName::A11YId),
- "a11y_alt" => Ok(AttributeName::A11YAlt),
- "a11y_focusable" => Ok(AttributeName::A11YFocusable),
"canvas_reference" => Ok(AttributeName::CanvasReference),
"layer" => Ok(AttributeName::Layer),
"offset_y" => Ok(AttributeName::OffsetY),
@@ -139,6 +281,74 @@ impl FromStr for AttributeName {
"svg_data" => Ok(AttributeName::SvgData),
"svg_content" => Ok(AttributeName::SvgContent),
"spacing" => Ok(AttributeName::Spacing),
+ "a11y_id" => Ok(AttributeName::A11yId),
+ "a11y_focusable" => Ok(AttributeName::A11yFocusable),
+ "a11y_auto_focus" => Ok(AttributeName::A11yAutoFocus),
+ "a11y_name" => Ok(AttributeName::A11yName),
+ "a11y_description" => Ok(AttributeName::A11yDescription),
+ "a11y_value" => Ok(AttributeName::A11yValue),
+ "a11y_access_key" => Ok(AttributeName::A11yAccessKey),
+ "a11y_author_id" => Ok(AttributeName::A11yAuthorId),
+ "a11y_keyboard_shortcut" => Ok(AttributeName::A11yKeyboardShortcut),
+ "a11y_language" => Ok(AttributeName::A11yLanguage),
+ "a11y_placeholder" => Ok(AttributeName::A11yPlaceholder),
+ "a11y_role_description" => Ok(AttributeName::A11yRoleDescription),
+ "a11y_state_description" => Ok(AttributeName::A11yStateDescription),
+ "a11y_tooltip" => Ok(AttributeName::A11yTooltip),
+ "a11y_url" => Ok(AttributeName::A11yUrl),
+ "a11y_row_index_text" => Ok(AttributeName::A11yRowIndexText),
+ "a11y_column_index_text" => Ok(AttributeName::A11yColumnIndexText),
+ "a11y_scroll_x" => Ok(AttributeName::A11yScrollX),
+ "a11y_scroll_x_min" => Ok(AttributeName::A11yScrollXMin),
+ "a11y_scroll_x_max" => Ok(AttributeName::A11yScrollXMax),
+ "a11y_scroll_y" => Ok(AttributeName::A11yScrollY),
+ "a11y_scroll_y_min" => Ok(AttributeName::A11yScrollYMin),
+ "a11y_scroll_y_max" => Ok(AttributeName::A11yScrollYMax),
+ "a11y_numeric_value" => Ok(AttributeName::A11yNumericValue),
+ "a11y_min_numeric_value" => Ok(AttributeName::A11yMinNumericValue),
+ "a11y_max_numeric_value" => Ok(AttributeName::A11yMaxNumericValue),
+ "a11y_numeric_value_step" => Ok(AttributeName::A11yNumericValueStep),
+ "a11y_numeric_value_jump" => Ok(AttributeName::A11yNumericValueJump),
+ "a11y_row_count" => Ok(AttributeName::A11yRowCount),
+ "a11y_column_count" => Ok(AttributeName::A11yColumnCount),
+ "a11y_row_index" => Ok(AttributeName::A11yRowIndex),
+ "a11y_column_index" => Ok(AttributeName::A11yColumnIndex),
+ "a11y_row_span" => Ok(AttributeName::A11yRowSpan),
+ "a11y_column_span" => Ok(AttributeName::A11yColumnSpan),
+ "a11y_level" => Ok(AttributeName::A11yLevel),
+ "a11y_size_of_set" => Ok(AttributeName::A11ySizeOfSet),
+ "a11y_position_in_set" => Ok(AttributeName::A11yPositionInSet),
+ "a11y_color_value" => Ok(AttributeName::A11yColorValue),
+ "a11y_expanded" => Ok(AttributeName::A11yExpanded),
+ "a11y_selected" => Ok(AttributeName::A11ySelected),
+ "a11y_hovered" => Ok(AttributeName::A11yHovered),
+ "a11y_hidden" => Ok(AttributeName::A11yHidden),
+ "a11y_linked" => Ok(AttributeName::A11yLinked),
+ "a11y_multiselectable" => Ok(AttributeName::A11yMultiselectable),
+ "a11y_required" => Ok(AttributeName::A11yRequired),
+ "a11y_visited" => Ok(AttributeName::A11yVisited),
+ "a11y_busy" => Ok(AttributeName::A11yBusy),
+ "a11y_live_atomic" => Ok(AttributeName::A11yLiveAtomic),
+ "a11y_modal" => Ok(AttributeName::A11yModal),
+ "a11y_touch_transparent" => Ok(AttributeName::A11yTouchTransparent),
+ "a11y_read_only" => Ok(AttributeName::A11yReadOnly),
+ "a11y_disabled" => Ok(AttributeName::A11yDisabled),
+ "a11y_is_spelling_error" => Ok(AttributeName::A11yIsSpellingError),
+ "a11y_is_grammar_error" => Ok(AttributeName::A11yIsGrammarError),
+ "a11y_is_search_match" => Ok(AttributeName::A11yIsSearchMatch),
+ "a11y_is_suggestion" => Ok(AttributeName::A11yIsSuggestion),
+ "a11y_role" => Ok(AttributeName::A11yRole),
+ "a11y_invalid" => Ok(AttributeName::A11yInvalid),
+ "a11y_toggled" => Ok(AttributeName::A11yToggled),
+ "a11y_live" => Ok(AttributeName::A11yLive),
+ "a11y_default_action_verb" => Ok(AttributeName::A11yDefaultActionVerb),
+ "a11y_orientation" => Ok(AttributeName::A11yOrientation),
+ "a11y_sort_direction" => Ok(AttributeName::A11ySortDirection),
+ "a11y_current" => Ok(AttributeName::A11yCurrent),
+ "a11y_auto_complete" => Ok(AttributeName::A11yAutoComplete),
+ "a11y_has_popup" => Ok(AttributeName::A11yHasPopup),
+ "a11y_list_style" => Ok(AttributeName::A11yListStyle),
+ "a11y_vertical_offset" => Ok(AttributeName::A11yVerticalOffset),
_ => Err(format!("{attr} not supported.")),
}
}
diff --git a/crates/renderer/src/config.rs b/crates/renderer/src/config.rs
index 4466ac4a4..226cf6e4d 100644
--- a/crates/renderer/src/config.rs
+++ b/crates/renderer/src/config.rs
@@ -8,18 +8,23 @@ use freya_core::{
FreyaPlugin,
PluginsManager,
},
+ prelude::EventMessage,
style::default_fonts,
};
use freya_engine::prelude::Color;
use freya_node_state::Parse;
use image::ImageReader;
-use winit::window::{
- Icon,
- Window,
- WindowAttributes,
+use winit::{
+ event_loop::EventLoopBuilder,
+ window::{
+ Icon,
+ Window,
+ WindowAttributes,
+ },
};
-pub type WindowBuilderHook = Box WindowAttributes>;
+pub type EventLoopBuilderHook = Box)>;
+pub type WindowBuilderHook = Box WindowAttributes>;
pub type EmbeddedFonts<'a> = Vec<(&'a str, &'a [u8])>;
/// Configuration for a Window.
@@ -48,6 +53,8 @@ pub struct WindowConfig {
pub on_exit: Option,
/// Hook function called with the Window Attributes.
pub window_attributes_hook: Option,
+ /// Hook function called with the Event Loop Builder.
+ pub event_loop_builder_hook: Option,
}
impl Default for WindowConfig {
@@ -65,6 +72,7 @@ impl Default for WindowConfig {
on_setup: None,
on_exit: None,
window_attributes_hook: None,
+ event_loop_builder_hook: None,
}
}
}
@@ -213,9 +221,18 @@ impl<'a, T: Clone> LaunchConfig<'a, T> {
/// Register a Window Attributes hook.
pub fn with_window_attributes(
mut self,
- window_attributes_hook: impl Fn(WindowAttributes) -> WindowAttributes + 'static,
+ window_attributes_hook: impl FnOnce(WindowAttributes) -> WindowAttributes + 'static,
) -> Self {
self.window_config.window_attributes_hook = Some(Box::new(window_attributes_hook));
self
}
+
+ /// Register an Event Loop Builder hook.
+ pub fn with_event_loop_builder(
+ mut self,
+ event_loop_builder_hook: impl FnOnce(&mut EventLoopBuilder) + 'static,
+ ) -> Self {
+ self.window_config.event_loop_builder_hook = Some(Box::new(event_loop_builder_hook));
+ self
+ }
}
diff --git a/crates/renderer/src/renderer.rs b/crates/renderer/src/renderer.rs
index 49fd15fa3..d5796e5b7 100644
--- a/crates/renderer/src/renderer.rs
+++ b/crates/renderer/src/renderer.rs
@@ -71,11 +71,16 @@ impl<'a, State: Clone + 'static> DesktopRenderer<'a, State> {
pub fn launch(
vdom: VirtualDom,
sdom: SafeDOM,
- config: LaunchConfig,
+ mut config: LaunchConfig,
devtools: Option,
hovered_node: HoveredNode,
) {
- let event_loop = EventLoop::::with_user_event()
+ let mut event_loop_builder = EventLoop::::with_user_event();
+ let event_loop_builder_hook = config.window_config.event_loop_builder_hook.take();
+ if let Some(event_loop_builder_hook) = event_loop_builder_hook {
+ event_loop_builder_hook(&mut event_loop_builder);
+ }
+ let event_loop = event_loop_builder
.build()
.expect("Failed to create event loop.");
let proxy = event_loop.create_proxy();
diff --git a/crates/renderer/src/window_state.rs b/crates/renderer/src/window_state.rs
index 57a136264..2027682dd 100644
--- a/crates/renderer/src/window_state.rs
+++ b/crates/renderer/src/window_state.rs
@@ -94,7 +94,7 @@ impl<'a, State: Clone + 'a> WindowState<'a, State> {
window_attributes.with_max_inner_size(LogicalSize::::from(max_size));
}
- if let Some(with_window_attributes) = &config.window_config.window_attributes_hook {
+ if let Some(with_window_attributes) = config.window_config.window_attributes_hook.take() {
window_attributes = (with_window_attributes)(window_attributes);
}
diff --git a/crates/state/src/accessibility.rs b/crates/state/src/accessibility.rs
index 1fb90b874..751ee14ca 100644
--- a/crates/state/src/accessibility.rs
+++ b/crates/state/src/accessibility.rs
@@ -4,13 +4,26 @@ use std::sync::{
};
use accesskit::{
+ AriaCurrent,
+ AutoComplete,
+ DefaultActionVerb,
+ HasPopup,
+ Invalid,
+ ListStyle,
+ Live,
+ NodeBuilder,
NodeId as AccessibilityId,
+ Orientation,
Role,
+ SortDirection,
+ Toggled,
+ VerticalOffset,
};
use freya_common::{
AccessibilityDirtyNodes,
AccessibilityGenerator,
};
+use freya_engine::prelude::Color;
use freya_native_core::{
attributes::AttributeName,
exports::shipyard::Component,
@@ -22,6 +35,7 @@ use freya_native_core::{
NodeMaskBuilder,
State,
},
+ tags::TagName,
NodeId,
SendAnyMap,
};
@@ -35,17 +49,13 @@ use crate::{
ParseError,
};
-#[derive(Clone, Debug, PartialEq, Eq, Default, Component)]
+#[derive(Clone, Debug, PartialEq, Default, Component)]
pub struct AccessibilityNodeState {
- pub closest_accessibility_node_id: Option,
- pub descencent_accessibility_ids: Vec,
pub node_id: NodeId,
pub a11y_id: Option,
- pub a11y_role: Option,
- pub a11y_alt: Option,
- pub a11y_name: Option,
pub a11y_auto_focus: bool,
pub a11y_focusable: Focusable,
+ pub builder: Option,
}
impl ParseAttribute for AccessibilityNodeState {
@@ -54,47 +64,248 @@ impl ParseAttribute for AccessibilityNodeState {
attr: freya_native_core::prelude::OwnedAttributeView,
) -> Result<(), crate::ParseError> {
match attr.attribute {
- AttributeName::A11YId => {
+ AttributeName::A11yId => {
if let OwnedAttributeValue::Custom(CustomAttributeValues::AccessibilityId(id)) =
attr.value
{
self.a11y_id = Some(*id);
-
// Enable focus on nodes that pass a custom a11y id
if self.a11y_focusable.is_unknown() {
self.a11y_focusable = Focusable::Enabled;
}
}
}
- AttributeName::A11YRole => {
+ AttributeName::A11yFocusable => {
if let OwnedAttributeValue::Text(attr) = attr.value {
- self.a11y_role = Some(
- serde_json::from_str::(&format!("\"{attr}\""))
- .map_err(|_| ParseError)?,
- )
- }
- }
- AttributeName::A11YAlt => {
- if let OwnedAttributeValue::Text(attr) = attr.value {
- self.a11y_alt = Some(attr.to_owned())
- }
- }
- AttributeName::A11YName => {
- if let OwnedAttributeValue::Text(attr) = attr.value {
- self.a11y_name = Some(attr.to_owned())
+ self.a11y_focusable = Focusable::parse(attr)?;
}
}
- AttributeName::A11YAutoFocus => {
+ AttributeName::A11yAutoFocus => {
if let OwnedAttributeValue::Text(attr) = attr.value {
self.a11y_auto_focus = attr.parse().unwrap_or_default()
}
}
- AttributeName::A11YFocusable => {
+ a11y_attr => {
if let OwnedAttributeValue::Text(attr) = attr.value {
- self.a11y_focusable = Focusable::parse(attr)?;
+ if let Some(builder) = self.builder.as_mut() {
+ match a11y_attr {
+ AttributeName::A11yName => builder.set_name(attr.clone()),
+ AttributeName::A11yDescription => builder.set_description(attr.clone()),
+ AttributeName::A11yValue => builder.set_value(attr.clone()),
+ AttributeName::A11yAccessKey => builder.set_access_key(attr.clone()),
+ AttributeName::A11yAuthorId => builder.set_author_id(attr.clone()),
+ AttributeName::A11yKeyboardShortcut => {
+ builder.set_keyboard_shortcut(attr.clone())
+ }
+ AttributeName::A11yLanguage => builder.set_language(attr.clone()),
+ AttributeName::A11yPlaceholder => builder.set_placeholder(attr.clone()),
+ AttributeName::A11yRoleDescription => {
+ builder.set_role_description(attr.clone())
+ }
+ AttributeName::A11yStateDescription => {
+ builder.set_state_description(attr.clone())
+ }
+ AttributeName::A11yTooltip => builder.set_tooltip(attr.clone()),
+ AttributeName::A11yUrl => builder.set_url(attr.clone()),
+ AttributeName::A11yRowIndexText => {
+ builder.set_row_index_text(attr.clone())
+ }
+ AttributeName::A11yColumnIndexText => {
+ builder.set_column_index_text(attr.clone())
+ }
+ AttributeName::A11yScrollX => {
+ builder.set_scroll_x(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yScrollXMin => {
+ builder.set_scroll_x_min(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yScrollXMax => {
+ builder.set_scroll_x_max(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yScrollY => {
+ builder.set_scroll_y(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yScrollYMin => {
+ builder.set_scroll_y_min(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yScrollYMax => {
+ builder.set_scroll_y_max(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yNumericValue => {
+ builder.set_numeric_value(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yMinNumericValue => {
+ builder.set_min_numeric_value(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yMaxNumericValue => {
+ builder.set_max_numeric_value(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yNumericValueStep => builder
+ .set_numeric_value_step(attr.parse().map_err(|_| ParseError)?),
+ AttributeName::A11yNumericValueJump => builder
+ .set_numeric_value_jump(attr.parse().map_err(|_| ParseError)?),
+ AttributeName::A11yRowCount => {
+ builder.set_row_count(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yColumnCount => {
+ builder.set_column_count(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yRowIndex => {
+ builder.set_row_index(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yColumnIndex => {
+ builder.set_column_index(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yRowSpan => {
+ builder.set_row_span(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yColumnSpan => {
+ builder.set_column_span(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yLevel => {
+ builder.set_level(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11ySizeOfSet => {
+ builder.set_size_of_set(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yPositionInSet => {
+ builder.set_position_in_set(attr.parse().map_err(|_| ParseError)?)
+ }
+ AttributeName::A11yColorValue => {
+ let color = Color::parse(attr)?;
+ builder.set_color_value(
+ ((color.a() as u32) << 24)
+ | ((color.b() as u32) << 16)
+ | (((color.g() as u32) << 8) + (color.r() as u32)),
+ );
+ }
+ AttributeName::A11yExpanded => {
+ builder.set_expanded(attr.parse::().map_err(|_| ParseError)?);
+ }
+ AttributeName::A11ySelected => {
+ builder.set_selected(attr.parse::().map_err(|_| ParseError)?);
+ }
+ AttributeName::A11yHovered => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_hovered();
+ }
+ }
+ AttributeName::A11yHidden => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_hidden();
+ }
+ }
+ AttributeName::A11yLinked => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_linked();
+ }
+ }
+ AttributeName::A11yMultiselectable => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_multiselectable();
+ }
+ }
+ AttributeName::A11yRequired => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_required();
+ }
+ }
+ AttributeName::A11yVisited => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_visited();
+ }
+ }
+ AttributeName::A11yBusy => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_busy();
+ }
+ }
+ AttributeName::A11yLiveAtomic => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_live_atomic();
+ }
+ }
+ AttributeName::A11yModal => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_modal();
+ }
+ }
+ AttributeName::A11yTouchTransparent => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_touch_transparent();
+ }
+ }
+ AttributeName::A11yReadOnly => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_read_only();
+ }
+ }
+ AttributeName::A11yDisabled => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_disabled();
+ }
+ }
+ AttributeName::A11yIsSpellingError => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_is_spelling_error();
+ }
+ }
+ AttributeName::A11yIsGrammarError => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_is_grammar_error();
+ }
+ }
+ AttributeName::A11yIsSearchMatch => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_is_search_match();
+ }
+ }
+ AttributeName::A11yIsSuggestion => {
+ if attr.parse::().map_err(|_| ParseError)? {
+ builder.set_is_suggestion();
+ }
+ }
+ AttributeName::A11yRole => {
+ builder.set_role(Role::parse(attr)?);
+ }
+ AttributeName::A11yInvalid => {
+ builder.set_invalid(Invalid::parse(attr)?);
+ }
+ AttributeName::A11yToggled => {
+ builder.set_toggled(Toggled::parse(attr)?);
+ }
+ AttributeName::A11yLive => {
+ builder.set_live(Live::parse(attr)?);
+ }
+ AttributeName::A11yDefaultActionVerb => {
+ builder.set_default_action_verb(DefaultActionVerb::parse(attr)?);
+ }
+ AttributeName::A11yOrientation => {
+ builder.set_orientation(Orientation::parse(attr)?);
+ }
+ AttributeName::A11ySortDirection => {
+ builder.set_sort_direction(SortDirection::parse(attr)?);
+ }
+ AttributeName::A11yCurrent => {
+ builder.set_aria_current(AriaCurrent::parse(attr)?);
+ }
+ AttributeName::A11yAutoComplete => {
+ builder.set_auto_complete(AutoComplete::parse(attr)?);
+ }
+ AttributeName::A11yHasPopup => {
+ builder.set_has_popup(HasPopup::parse(attr)?);
+ }
+ AttributeName::A11yListStyle => {
+ builder.set_list_style(ListStyle::parse(attr)?);
+ }
+ AttributeName::A11yVerticalOffset => {
+ builder.set_vertical_offset(VerticalOffset::parse(attr)?);
+ }
+ _ => {}
+ }
+ }
}
}
- _ => {}
}
Ok(())
@@ -103,27 +314,91 @@ impl ParseAttribute for AccessibilityNodeState {
#[partial_derive_state]
impl State for AccessibilityNodeState {
- type ParentDependencies = (Self,);
+ type ParentDependencies = ();
- type ChildDependencies = (Self,);
+ type ChildDependencies = ();
type NodeDependencies = ();
- const NODE_MASK: NodeMaskBuilder<'static> =
- NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&[
- AttributeName::A11YId,
- AttributeName::A11YRole,
- AttributeName::A11YAlt,
- AttributeName::A11YName,
- AttributeName::A11YAutoFocus,
- ]));
+ const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
+ .with_attrs(AttributeMaskBuilder::Some(&[
+ AttributeName::A11yId,
+ AttributeName::A11yFocusable,
+ AttributeName::A11yAutoFocus,
+ AttributeName::A11yName,
+ AttributeName::A11yDescription,
+ AttributeName::A11yValue,
+ AttributeName::A11yAccessKey,
+ AttributeName::A11yAuthorId,
+ AttributeName::A11yKeyboardShortcut,
+ AttributeName::A11yLanguage,
+ AttributeName::A11yPlaceholder,
+ AttributeName::A11yRoleDescription,
+ AttributeName::A11yStateDescription,
+ AttributeName::A11yTooltip,
+ AttributeName::A11yUrl,
+ AttributeName::A11yRowIndexText,
+ AttributeName::A11yColumnIndexText,
+ AttributeName::A11yScrollX,
+ AttributeName::A11yScrollXMin,
+ AttributeName::A11yScrollXMax,
+ AttributeName::A11yScrollY,
+ AttributeName::A11yScrollYMin,
+ AttributeName::A11yScrollYMax,
+ AttributeName::A11yNumericValue,
+ AttributeName::A11yMinNumericValue,
+ AttributeName::A11yMaxNumericValue,
+ AttributeName::A11yNumericValueStep,
+ AttributeName::A11yNumericValueJump,
+ AttributeName::A11yRowCount,
+ AttributeName::A11yColumnCount,
+ AttributeName::A11yRowIndex,
+ AttributeName::A11yColumnIndex,
+ AttributeName::A11yRowSpan,
+ AttributeName::A11yColumnSpan,
+ AttributeName::A11yLevel,
+ AttributeName::A11ySizeOfSet,
+ AttributeName::A11yPositionInSet,
+ AttributeName::A11yColorValue,
+ AttributeName::A11yExpanded,
+ AttributeName::A11ySelected,
+ AttributeName::A11yHovered,
+ AttributeName::A11yHidden,
+ AttributeName::A11yLinked,
+ AttributeName::A11yMultiselectable,
+ AttributeName::A11yRequired,
+ AttributeName::A11yVisited,
+ AttributeName::A11yBusy,
+ AttributeName::A11yLiveAtomic,
+ AttributeName::A11yModal,
+ AttributeName::A11yTouchTransparent,
+ AttributeName::A11yReadOnly,
+ AttributeName::A11yDisabled,
+ AttributeName::A11yIsSpellingError,
+ AttributeName::A11yIsGrammarError,
+ AttributeName::A11yIsSearchMatch,
+ AttributeName::A11yIsSuggestion,
+ AttributeName::A11yRole,
+ AttributeName::A11yInvalid,
+ AttributeName::A11yToggled,
+ AttributeName::A11yLive,
+ AttributeName::A11yDefaultActionVerb,
+ AttributeName::A11yOrientation,
+ AttributeName::A11ySortDirection,
+ AttributeName::A11yCurrent,
+ AttributeName::A11yAutoComplete,
+ AttributeName::A11yHasPopup,
+ AttributeName::A11yListStyle,
+ AttributeName::A11yVerticalOffset,
+ ]))
+ .with_tag();
fn update<'a>(
&mut self,
node_view: NodeView,
_node: ::ElementBorrowed<'a>,
- parent: Option<::ElementBorrowed<'a>>,
- children: Vec<::ElementBorrowed<'a>>,
+ _parent: Option<::ElementBorrowed<'a>>,
+ _children: Vec<::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
let root_id = context.get::().unwrap();
@@ -134,6 +409,18 @@ impl State for AccessibilityNodeState {
let mut accessibility = AccessibilityNodeState {
node_id: node_view.node_id(),
a11y_id: self.a11y_id,
+ builder: node_view.tag().and_then(|tag| {
+ match tag {
+ TagName::Image => Some(NodeBuilder::new(Role::Image)),
+ TagName::Label => Some(NodeBuilder::new(Role::Label)),
+ TagName::Paragraph => Some(NodeBuilder::new(Role::Paragraph)),
+ TagName::Rect => Some(NodeBuilder::new(Role::GenericContainer)),
+ TagName::Svg => Some(NodeBuilder::new(Role::GraphicsObject)),
+ TagName::Root => Some(NodeBuilder::new(Role::Window)),
+ // TODO: make this InlineTextBox and supply computed text span properties
+ TagName::Text => None,
+ }
+ }),
..Default::default()
};
@@ -143,36 +430,19 @@ impl State for AccessibilityNodeState {
}
}
- for (child,) in children {
- if let Some(child_id) = child.a11y_id {
- // Mark this child as descendent if it has an ID
- accessibility.descencent_accessibility_ids.push(child_id)
- } else {
- // If it doesn't have an ID then use its descencent accessibility IDs
- accessibility
- .descencent_accessibility_ids
- .extend(child.descencent_accessibility_ids.iter());
- }
- }
-
- if let Some(parent) = parent {
- // Mark the parent accessibility ID as the closest to this node or
- // fallback to its closest ID.
- accessibility.closest_accessibility_node_id = parent
- .0
- .a11y_id
- .map(|_| parent.0.node_id)
- .or(parent.0.closest_accessibility_node_id);
- }
-
let changed = &accessibility != self;
let had_id = self.a11y_id.is_some();
*self = accessibility;
- if changed {
- // Assign an accessibility ID if none was passed but the node has a role
- if self.a11y_id.is_none() && self.a11y_role.is_some() {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && !is_orphan {
+ // Assign an accessibility ID if none was passed but the node has a valid builder
+ //
+ // In our case, builder will be `None` if the node's tag cannot be added to accessibility
+ // tree.
+ if self.a11y_id.is_none() && self.builder.is_some() {
let id = AccessibilityId(accessibility_generator.new_id());
#[cfg(debug_assertions)]
tracing::info!("Assigned {id:?} to {:?}", node_view.node_id());
diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs
index 6dcb5bbc2..f629507a0 100644
--- a/crates/state/src/cursor.rs
+++ b/crates/state/src/cursor.rs
@@ -21,6 +21,7 @@ use freya_native_core::{
State,
},
tags::TagName,
+ NodeId,
SendAnyMap,
};
use freya_native_core_macro::partial_derive_state;
@@ -151,6 +152,7 @@ impl State for CursorState {
_children: Vec<::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
+ let root_id = context.get::().unwrap();
let paragraphs = context.get::>>().unwrap();
let compositor_dirty_nodes = context.get::>>().unwrap();
let mut cursor = parent.map(|(p,)| p.clone()).unwrap_or_default();
@@ -162,7 +164,9 @@ impl State for CursorState {
}
let changed = &cursor != self;
- if changed && CursorMode::Editable == cursor.mode {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && CursorMode::Editable == cursor.mode && !is_orphan {
if let Some((tag, cursor_ref)) = node_view.tag().zip(cursor.cursor_ref.as_ref()) {
if *tag == TagName::Paragraph {
paragraphs
diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs
index 54f215a0a..81005d484 100644
--- a/crates/state/src/font_style.rs
+++ b/crates/state/src/font_style.rs
@@ -277,6 +277,7 @@ impl State for FontStyleState {
_children: Vec<::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
+ let root_id = context.get::().unwrap();
let torin_layout = context.get::>>>().unwrap();
let compositor_dirty_nodes = context.get::>>().unwrap();
@@ -290,7 +291,9 @@ impl State for FontStyleState {
let changed = &font_style != self;
- if changed {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && !is_orphan {
torin_layout.lock().unwrap().invalidate(node_view.node_id());
compositor_dirty_nodes
.lock()
diff --git a/crates/state/src/layer.rs b/crates/state/src/layer.rs
index fc5d7c616..24bed3c99 100644
--- a/crates/state/src/layer.rs
+++ b/crates/state/src/layer.rs
@@ -14,6 +14,7 @@ use freya_native_core::{
NodeMaskBuilder,
State,
},
+ NodeId,
SendAnyMap,
};
use freya_native_core_macro::partial_derive_state;
@@ -75,6 +76,7 @@ impl State for LayerState {
return false;
}
+ let root_id = context.get::().unwrap();
let layers = context.get::>>().unwrap();
let inherited_layer = parent.map(|(p,)| p.layer_for_children).unwrap_or(0i16);
@@ -91,7 +93,9 @@ impl State for LayerState {
let changed = &layer_state != self;
- if changed {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && !is_orphan {
layers
.lock()
.unwrap()
diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs
index 939ebcd01..753123534 100644
--- a/crates/state/src/layout.rs
+++ b/crates/state/src/layout.rs
@@ -221,6 +221,7 @@ impl State for LayoutState {
_children: Vec<::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
+ let root_id = context.get::().unwrap();
let torin_layout = context.get::>>>().unwrap();
let compositor_dirty_nodes = context.get::>>().unwrap();
@@ -237,7 +238,9 @@ impl State for LayoutState {
let changed = layout != *self;
- if changed {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && !is_orphan {
torin_layout.lock().unwrap().invalidate(node_view.node_id());
compositor_dirty_nodes
.lock()
diff --git a/crates/state/src/transform.rs b/crates/state/src/transform.rs
index d7dfc634d..2d0569722 100644
--- a/crates/state/src/transform.rs
+++ b/crates/state/src/transform.rs
@@ -85,6 +85,7 @@ impl State for TransformState {
_children: Vec<::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
+ let root_id = context.get::().unwrap();
let compositor_dirty_nodes = context.get::>>().unwrap();
let inherited_transform = parent.map(|(p,)| p.clone()).unwrap_or_default();
@@ -101,7 +102,9 @@ impl State for TransformState {
let changed = transform_state != *self;
- if changed {
+ let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
+
+ if changed && !is_orphan {
compositor_dirty_nodes
.lock()
.unwrap()
diff --git a/crates/state/src/values/accessibility.rs b/crates/state/src/values/accessibility.rs
new file mode 100644
index 000000000..35101fffb
--- /dev/null
+++ b/crates/state/src/values/accessibility.rs
@@ -0,0 +1,349 @@
+use accesskit::{
+ AriaCurrent,
+ AutoComplete,
+ DefaultActionVerb,
+ HasPopup,
+ Invalid,
+ ListStyle,
+ Live,
+ Orientation,
+ Role,
+ SortDirection,
+ Toggled,
+ VerticalOffset,
+};
+
+use crate::{
+ Parse,
+ ParseError,
+};
+
+impl Parse for Role {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "unknown" => Self::Unknown,
+ "inline-text-box" => Self::InlineTextBox,
+ "cell" => Self::Cell,
+ "label" => Self::Label,
+ "image" => Self::Image,
+ "link" => Self::Link,
+ "row" => Self::Row,
+ "list-item" => Self::ListItem,
+ "list-marker" => Self::ListMarker,
+ "tree-item" => Self::TreeItem,
+ "list-box-option" => Self::ListBoxOption,
+ "menu-item" => Self::MenuItem,
+ "menu-list-option" => Self::MenuListOption,
+ "paragraph" => Self::Paragraph,
+ "generic-container" => Self::GenericContainer,
+ "check-box" => Self::CheckBox,
+ "radio-button" => Self::RadioButton,
+ "text-input" => Self::TextInput,
+ "button" => Self::Button,
+ "default-button" => Self::DefaultButton,
+ "pane" => Self::Pane,
+ "row-header" => Self::RowHeader,
+ "column-header" => Self::ColumnHeader,
+ "row-group" => Self::RowGroup,
+ "list" => Self::List,
+ "table" => Self::Table,
+ "layout-table-cell" => Self::LayoutTableCell,
+ "layout-table-row" => Self::LayoutTableRow,
+ "layout-table" => Self::LayoutTable,
+ "switch" => Self::Switch,
+ "menu" => Self::Menu,
+ "multiline-text-input" => Self::MultilineTextInput,
+ "search-input" => Self::SearchInput,
+ "date-input" => Self::DateInput,
+ "date-time-input" => Self::DateTimeInput,
+ "week-input" => Self::WeekInput,
+ "month-input" => Self::MonthInput,
+ "time-input" => Self::TimeInput,
+ "email-input" => Self::EmailInput,
+ "number-input" => Self::NumberInput,
+ "password-input" => Self::PasswordInput,
+ "phone-number-input" => Self::PhoneNumberInput,
+ "url-input" => Self::UrlInput,
+ "abbr" => Self::Abbr,
+ "alert" => Self::Alert,
+ "alert-dialog" => Self::AlertDialog,
+ "application" => Self::Application,
+ "article" => Self::Article,
+ "audio" => Self::Audio,
+ "banner" => Self::Banner,
+ "blockquote" => Self::Blockquote,
+ "canvas" => Self::Canvas,
+ "caption" => Self::Caption,
+ "caret" => Self::Caret,
+ "code" => Self::Code,
+ "color-well" => Self::ColorWell,
+ "combo-box" => Self::ComboBox,
+ "editable-combo-box" => Self::EditableComboBox,
+ "complementary" => Self::Complementary,
+ "comment" => Self::Comment,
+ "content-deletion" => Self::ContentDeletion,
+ "content-insertion" => Self::ContentInsertion,
+ "content-info" => Self::ContentInfo,
+ "definition" => Self::Definition,
+ "description-list" => Self::DescriptionList,
+ "description-list-detail" => Self::DescriptionListDetail,
+ "description-list-term" => Self::DescriptionListTerm,
+ "details" => Self::Details,
+ "dialog" => Self::Dialog,
+ "directory" => Self::Directory,
+ "disclosure-triangle" => Self::DisclosureTriangle,
+ "document" => Self::Document,
+ "embedded-object" => Self::EmbeddedObject,
+ "emphasis" => Self::Emphasis,
+ "feed" => Self::Feed,
+ "figure-caption" => Self::FigureCaption,
+ "figure" => Self::Figure,
+ "footer" => Self::Footer,
+ "footer-as-non-landmark" => Self::FooterAsNonLandmark,
+ "form" => Self::Form,
+ "grid" => Self::Grid,
+ "group" => Self::Group,
+ "header" => Self::Header,
+ "header-as-non-landmark" => Self::HeaderAsNonLandmark,
+ "heading" => Self::Heading,
+ "iframe" => Self::Iframe,
+ "iframe-presentational" => Self::IframePresentational,
+ "ime-candidate" => Self::ImeCandidate,
+ "keyboard" => Self::Keyboard,
+ "legend" => Self::Legend,
+ "line-break" => Self::LineBreak,
+ "list-box" => Self::ListBox,
+ "log" => Self::Log,
+ "main" => Self::Main,
+ "mark" => Self::Mark,
+ "marquee" => Self::Marquee,
+ "math" => Self::Math,
+ "menu-bar" => Self::MenuBar,
+ "menu-item-check-box" => Self::MenuItemCheckBox,
+ "menu-item-radio" => Self::MenuItemRadio,
+ "menu-list-popup" => Self::MenuListPopup,
+ "meter" => Self::Meter,
+ "navigation" => Self::Navigation,
+ "note" => Self::Note,
+ "plugin-object" => Self::PluginObject,
+ "portal" => Self::Portal,
+ "pre" => Self::Pre,
+ "progress-indicator" => Self::ProgressIndicator,
+ "radio-group" => Self::RadioGroup,
+ "region" => Self::Region,
+ "root-web-area" => Self::RootWebArea,
+ "ruby" => Self::Ruby,
+ "ruby-annotation" => Self::RubyAnnotation,
+ "scroll-bar" => Self::ScrollBar,
+ "scroll-view" => Self::ScrollView,
+ "search" => Self::Search,
+ "section" => Self::Section,
+ "slider" => Self::Slider,
+ "spin-button" => Self::SpinButton,
+ "splitter" => Self::Splitter,
+ "status" => Self::Status,
+ "strong" => Self::Strong,
+ "suggestion" => Self::Suggestion,
+ "svg-root" => Self::SvgRoot,
+ "tab" => Self::Tab,
+ "tab-list" => Self::TabList,
+ "tab-panel" => Self::TabPanel,
+ "term" => Self::Term,
+ "time" => Self::Time,
+ "timer" => Self::Timer,
+ "title-bar" => Self::TitleBar,
+ "toolbar" => Self::Toolbar,
+ "tooltip" => Self::Tooltip,
+ "tree" => Self::Tree,
+ "tree-grid" => Self::TreeGrid,
+ "video" => Self::Video,
+ "web-view" => Self::WebView,
+ "window" => Self::Window,
+ "pdf-actionable-highlight" => Self::PdfActionableHighlight,
+ "pdf-root" => Self::PdfRoot,
+ "graphics-document" => Self::GraphicsDocument,
+ "graphics-object" => Self::GraphicsObject,
+ "graphics-symbol" => Self::GraphicsSymbol,
+ "doc-abstract" => Self::DocAbstract,
+ "doc-acknowledgements" => Self::DocAcknowledgements,
+ "doc-afterword" => Self::DocAfterword,
+ "doc-appendix" => Self::DocAppendix,
+ "doc-back-link" => Self::DocBackLink,
+ "doc-biblio-entry" => Self::DocBiblioEntry,
+ "doc-bibliography" => Self::DocBibliography,
+ "doc-biblio-ref" => Self::DocBiblioRef,
+ "doc-chapter" => Self::DocChapter,
+ "doc-colophon" => Self::DocColophon,
+ "doc-conclusion" => Self::DocConclusion,
+ "doc-cover" => Self::DocCover,
+ "doc-credit" => Self::DocCredit,
+ "doc-credits" => Self::DocCredits,
+ "doc-dedication" => Self::DocDedication,
+ "doc-endnote" => Self::DocEndnote,
+ "doc-endnotes" => Self::DocEndnotes,
+ "doc-epigraph" => Self::DocEpigraph,
+ "doc-epilogue" => Self::DocEpilogue,
+ "doc-errata" => Self::DocErrata,
+ "doc-example" => Self::DocExample,
+ "doc-footnote" => Self::DocFootnote,
+ "doc-foreword" => Self::DocForeword,
+ "doc-glossary" => Self::DocGlossary,
+ "doc-gloss-ref" => Self::DocGlossRef,
+ "doc-index" => Self::DocIndex,
+ "doc-introduction" => Self::DocIntroduction,
+ "doc-note-ref" => Self::DocNoteRef,
+ "doc-notice" => Self::DocNotice,
+ "doc-page-break" => Self::DocPageBreak,
+ "doc-page-footer" => Self::DocPageFooter,
+ "doc-page-header" => Self::DocPageHeader,
+ "doc-page-list" => Self::DocPageList,
+ "doc-part" => Self::DocPart,
+ "doc-preface" => Self::DocPreface,
+ "doc-prologue" => Self::DocPrologue,
+ "doc-pullquote" => Self::DocPullquote,
+ "doc-qna" => Self::DocQna,
+ "doc-subtitle" => Self::DocSubtitle,
+ "doc-tip" => Self::DocTip,
+ "doc-toc" => Self::DocToc,
+ "list-grid" => Self::ListGrid,
+ "terminal" => Self::Terminal,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for Invalid {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "true" => Invalid::True,
+ "grammar" => Invalid::Grammar,
+ "spelling" => Invalid::Spelling,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for Toggled {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "true" => Toggled::True,
+ "false" => Toggled::False,
+ "mixed" => Toggled::Mixed,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for Live {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "assertive" => Live::Assertive,
+ "off" => Live::Off,
+ "polite" => Live::Polite,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for DefaultActionVerb {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "click" => DefaultActionVerb::Click,
+ "focus" => DefaultActionVerb::Focus,
+ "check" => DefaultActionVerb::Check,
+ "uncheck" => DefaultActionVerb::Uncheck,
+ "click-ancestor" => DefaultActionVerb::ClickAncestor,
+ "jump" => DefaultActionVerb::Jump,
+ "open" => DefaultActionVerb::Open,
+ "press" => DefaultActionVerb::Press,
+ "select" => DefaultActionVerb::Select,
+ "unselect" => DefaultActionVerb::Unselect,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for Orientation {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "horizontal" => Orientation::Horizontal,
+ "vertical" => Orientation::Vertical,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for SortDirection {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "ascending" => SortDirection::Ascending,
+ "descending" => SortDirection::Descending,
+ "other" => SortDirection::Other,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for AriaCurrent {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "false" => AriaCurrent::False,
+ "true" => AriaCurrent::True,
+ "page" => AriaCurrent::Page,
+ "step" => AriaCurrent::Step,
+ "location" => AriaCurrent::Location,
+ "date" => AriaCurrent::Date,
+ "time" => AriaCurrent::Time,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for AutoComplete {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "inline" => AutoComplete::Inline,
+ "list" => AutoComplete::List,
+ "both" => AutoComplete::Both,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for HasPopup {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "true" => HasPopup::True,
+ "menu" => HasPopup::Menu,
+ "listbox" => HasPopup::Listbox,
+ "tree" => HasPopup::Tree,
+ "grid" => HasPopup::Grid,
+ "dialog" => HasPopup::Dialog,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for ListStyle {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "circle" => ListStyle::Circle,
+ "disc" => ListStyle::Disc,
+ "image" => ListStyle::Image,
+ "numeric" => ListStyle::Numeric,
+ "square" => ListStyle::Square,
+ "other" => ListStyle::Other,
+ _ => Err(ParseError)?,
+ })
+ }
+}
+
+impl Parse for VerticalOffset {
+ fn parse(value: &str) -> Result {
+ Ok(match value {
+ "subscript" => VerticalOffset::Subscript,
+ "superscript" => VerticalOffset::Superscript,
+ _ => Err(ParseError)?,
+ })
+ }
+}
diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs
index 5295a0e38..b3202ae43 100644
--- a/crates/state/src/values/gradient.rs
+++ b/crates/state/src/values/gradient.rs
@@ -148,7 +148,7 @@ impl RadialGradient {
Shader::radial_gradient(
Point::new(center.x, center.y),
- bounds.width().max(bounds.height()),
+ bounds.width().max(bounds.height()) / 2.0,
GradientShaderColors::Colors(&colors[..]),
Some(&offsets[..]),
TileMode::Clamp,
diff --git a/crates/state/src/values/mod.rs b/crates/state/src/values/mod.rs
index 4f2b435e4..7cabda902 100644
--- a/crates/state/src/values/mod.rs
+++ b/crates/state/src/values/mod.rs
@@ -1,3 +1,4 @@
+mod accessibility;
mod alignment;
mod border;
mod color;
diff --git a/examples/accessibility.rs b/examples/accessibility.rs
index 56f07101c..27aa9d0cc 100644
--- a/examples/accessibility.rs
+++ b/examples/accessibility.rs
@@ -55,7 +55,7 @@ fn app() -> Element {
width: "100%",
height: "50%",
a11y_role:"label",
- a11y_alt: "This is a rectangle",
+ a11y_name: "This is a rectangle",
onclick: move |_| {
focus_b.focus();
},
diff --git a/examples/cloned_editor.rs b/examples/cloned_editor.rs
index e611f8833..6fb3edda7 100644
--- a/examples/cloned_editor.rs
+++ b/examples/cloned_editor.rs
@@ -21,8 +21,7 @@ fn app() -> Element {
#[allow(non_snake_case)]
fn Body() -> Element {
- let theme = use_theme();
- let theme = theme.read();
+ let theme = use_applied_theme!(None, body);
let mut editable = use_editable(
|| {
@@ -55,7 +54,7 @@ fn Body() -> Element {
cursor_reference,
direction: "horizontal",
onglobalclick: onclick,
- background: "{theme.body.background}",
+ background: "{theme.background}",
VirtualScrollView {
width: "50%",
length: editor.len_lines(),
diff --git a/examples/input.rs b/examples/input.rs
index 79bfb88b5..09f2381e3 100644
--- a/examples/input.rs
+++ b/examples/input.rs
@@ -18,7 +18,6 @@ fn app() -> Element {
padding: "7",
width: "100%",
height: "100%",
- font_size: "10",
label {
color: "black",
"Your name:"
@@ -39,13 +38,6 @@ fn app() -> Element {
values.write().1 = txt;
}
},
- rect {
- background: "red",
- label {
- color: "black",
- "You are {values.read().0} and you are {values.read().1} years old."
- }
- }
}
)
}
diff --git a/examples/switch_theme.rs b/examples/switch_theme.rs
index befb14012..b3283ce6c 100644
--- a/examples/switch_theme.rs
+++ b/examples/switch_theme.rs
@@ -6,7 +6,7 @@
use freya::prelude::*;
fn main() {
- launch(app);
+ launch_with_props(app, "Switch Theme", (600., 700.));
}
#[component]
@@ -57,7 +57,12 @@ fn ThemeChanger() -> Element {
label { "Light" }
}
}
-
+ Link {
+ to: "https://freyaui.dev",
+ label {
+ "https://freyaui.dev"
+ }
+ }
)
}
diff --git a/examples/website.rs b/examples/website.rs
index b2413be4c..6796ddcc1 100644
--- a/examples/website.rs
+++ b/examples/website.rs
@@ -272,7 +272,7 @@ fn Counter() -> Element {
const CODE: &str = r#"fn app() -> Element {
let mut count = use_signal(|| 0);
- render!(
+ rsx!(
rect {
height: "50%",
width: "100%",
diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro
index ee38c08a3..7fb494534 100644
--- a/website/src/pages/index.astro
+++ b/website/src/pages/index.astro
@@ -6,7 +6,7 @@ const EXAMPLE_CODE = `
fn app() -> Element {
let mut count = use_signal(|| 0);
- render!(
+ rsx!(
rect {
height: "50%",
width: "100%",