Update for AccessKit refactor that drastically reduces memory usage (#2678)
* Update for AccessKit refactor that drastically reduces memory usage * changelog entry * satisfy clippy
This commit is contained in:
parent
d15ce22e2e
commit
853d492724
12 changed files with 131 additions and 169 deletions
|
@ -34,6 +34,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
|
||||||
* Default `ComboBox` is now controlled with `Spacing::combo_width` ([#2621](https://github.com/emilk/egui/pull/2621)).
|
* Default `ComboBox` is now controlled with `Spacing::combo_width` ([#2621](https://github.com/emilk/egui/pull/2621)).
|
||||||
* `DragValue` and `Slider` now use the proportional font ([#2638](https://github.com/emilk/egui/pull/2638)).
|
* `DragValue` and `Slider` now use the proportional font ([#2638](https://github.com/emilk/egui/pull/2638)).
|
||||||
* `ScrollArea` is less aggressive about clipping its contents ([#2665](https://github.com/emilk/egui/pull/2665)).
|
* `ScrollArea` is less aggressive about clipping its contents ([#2665](https://github.com/emilk/egui/pull/2665)).
|
||||||
|
* Updated to be compatible with a major breaking change in AccessKit that drastically reduces memory usage when accessibility is enabled ([#2678](https://github.com/emilk/egui/pull/2678)).
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* Trigger `PointerEvent::Released` for drags ([#2507](https://github.com/emilk/egui/pull/2507)).
|
* Trigger `PointerEvent::Released` for drags ([#2507](https://github.com/emilk/egui/pull/2507)).
|
||||||
|
|
87
Cargo.lock
generated
87
Cargo.lock
generated
|
@ -20,20 +20,19 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit"
|
name = "accesskit"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3083ac5a97521e35388ca80cf365b6be5210962cc59f11ee238cd92ac2fa9524"
|
checksum = "4803cf8c252f374ae6bfbb341e49e5a37f7601f2ce74a105927a663eba952c67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumset",
|
"enumn",
|
||||||
"kurbo",
|
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_consumer"
|
name = "accesskit_consumer"
|
||||||
version = "0.12.1"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f47393f706a2d2f9d1ebd109351f886afd256a09d2308861a6dec0853a625e2"
|
checksum = "cee8cf1202a4f94d31837f1902ab0a75c77b65bf59719e093703abe83efd74ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
@ -41,9 +40,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_macos"
|
name = "accesskit_macos"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fabafb94d8a4dd6b20fe4112f943756ff8dc9778e3d742fb5478bf7f000a3282"
|
checksum = "10be25f2b27bc33aa1647072e86b948b41596f1af1ae43a2b4b9be5d2011cbda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_consumer",
|
"accesskit_consumer",
|
||||||
|
@ -54,9 +53,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_unix"
|
name = "accesskit_unix"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fbf322ecf51ac3fb9d3016382e5515122650d3fe70afe544322215e9a54f68f"
|
checksum = "630e7ee8f93c6246478bf0df6760db899b28d9ad54353a5f2d3157138ba817fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_consumer",
|
"accesskit_consumer",
|
||||||
|
@ -70,9 +69,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_windows"
|
name = "accesskit_windows"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "620160ad7d0aec2b4ae487bc8fbf0367982651d27bf8908a49d03548cfce73ec"
|
checksum = "a13c462fabdd950ef14308a9390b07fa2e2e3aabccba1f3ea36ea2231bb942ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_consumer",
|
"accesskit_consumer",
|
||||||
|
@ -85,9 +84,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_winit"
|
name = "accesskit_winit"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad401ccee3adde31edbbf7e8c7dd3fcb3fb916f7e519c135608b6b5231d633d4"
|
checksum = "17727888757ec027ec221db33070e226ee07df44425b583bc67684204d35eff9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_macos",
|
"accesskit_macos",
|
||||||
|
@ -1071,18 +1070,8 @@ version = "0.13.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core 0.13.4",
|
"darling_core",
|
||||||
"darling_macro 0.13.4",
|
"darling_macro",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling"
|
|
||||||
version = "0.14.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core 0.14.2",
|
|
||||||
"darling_macro 0.14.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1099,37 +1088,13 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_core"
|
|
||||||
version = "0.14.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"ident_case",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core 0.13.4",
|
"darling_core",
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_macro"
|
|
||||||
version = "0.14.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core 0.14.2",
|
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
@ -1519,22 +1484,11 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset"
|
name = "enumn"
|
||||||
version = "1.0.12"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753"
|
checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumset_derive",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enumset_derive"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"
|
|
||||||
dependencies = [
|
|
||||||
"darling 0.14.2",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -2223,7 +2177,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
|
checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2451,7 +2404,7 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
|
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling 0.13.4",
|
"darling",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -380,7 +380,7 @@ impl EpiIntegration {
|
||||||
egui_ctx.enable_accesskit();
|
egui_ctx.enable_accesskit();
|
||||||
// Enqueue a repaint so we'll receive a full tree update soon.
|
// Enqueue a repaint so we'll receive a full tree update soon.
|
||||||
egui_ctx.request_repaint();
|
egui_ctx.request_repaint();
|
||||||
egui::accesskit_placeholder_tree_update()
|
egui_ctx.accesskit_placeholder_tree_update()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ winit = { version = "0.28", default-features = false }
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
|
|
||||||
# feature accesskit
|
# feature accesskit
|
||||||
accesskit_winit = { version = "0.9.0", optional = true }
|
accesskit_winit = { version = "0.10.0", optional = true }
|
||||||
|
|
||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
|
@ -70,7 +70,7 @@ nohash-hasher = "0.2"
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
## Exposes detailed accessibility implementation required by platform
|
## Exposes detailed accessibility implementation required by platform
|
||||||
## accessibility APIs. Also requires support in the egui integration.
|
## accessibility APIs. Also requires support in the egui integration.
|
||||||
accesskit = { version = "0.8.1", optional = true }
|
accesskit = { version = "0.9.0", optional = true }
|
||||||
|
|
||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
|
@ -70,6 +70,8 @@ struct ContextImpl {
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
is_accesskit_enabled: bool,
|
is_accesskit_enabled: bool,
|
||||||
|
#[cfg(feature = "accesskit")]
|
||||||
|
accesskit_node_classes: accesskit::NodeClassSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextImpl {
|
impl ContextImpl {
|
||||||
|
@ -113,17 +115,14 @@ impl ContextImpl {
|
||||||
if self.is_accesskit_enabled {
|
if self.is_accesskit_enabled {
|
||||||
use crate::frame_state::AccessKitFrameState;
|
use crate::frame_state::AccessKitFrameState;
|
||||||
let id = crate::accesskit_root_id();
|
let id = crate::accesskit_root_id();
|
||||||
let node = Box::new(accesskit::Node {
|
let mut builder = accesskit::NodeBuilder::new(accesskit::Role::Window);
|
||||||
role: accesskit::Role::Window,
|
builder.set_transform(accesskit::Affine::scale(
|
||||||
transform: Some(
|
self.input.pixels_per_point().into(),
|
||||||
accesskit::kurbo::Affine::scale(self.input.pixels_per_point().into()).into(),
|
));
|
||||||
),
|
let mut node_builders = IdMap::default();
|
||||||
..Default::default()
|
node_builders.insert(id, builder);
|
||||||
});
|
|
||||||
let mut nodes = IdMap::default();
|
|
||||||
nodes.insert(id, node);
|
|
||||||
self.frame_state.accesskit_state = Some(AccessKitFrameState {
|
self.frame_state.accesskit_state = Some(AccessKitFrameState {
|
||||||
nodes,
|
node_builders,
|
||||||
parent_stack: vec![id],
|
parent_stack: vec![id],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -156,16 +155,16 @@ impl ContextImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
fn accesskit_node(&mut self, id: Id) -> &mut accesskit::Node {
|
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::NodeBuilder {
|
||||||
let state = self.frame_state.accesskit_state.as_mut().unwrap();
|
let state = self.frame_state.accesskit_state.as_mut().unwrap();
|
||||||
let nodes = &mut state.nodes;
|
let builders = &mut state.node_builders;
|
||||||
if let std::collections::hash_map::Entry::Vacant(entry) = nodes.entry(id) {
|
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
||||||
entry.insert(Default::default());
|
entry.insert(Default::default());
|
||||||
let parent_id = state.parent_stack.last().unwrap();
|
let parent_id = state.parent_stack.last().unwrap();
|
||||||
let parent = nodes.get_mut(parent_id).unwrap();
|
let parent_builder = builders.get_mut(parent_id).unwrap();
|
||||||
parent.children.push(id.accesskit_id());
|
parent_builder.push_child(id.accesskit_id());
|
||||||
}
|
}
|
||||||
nodes.get_mut(&id).unwrap()
|
builders.get_mut(&id).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,7 +654,7 @@ impl Context {
|
||||||
// Make sure anything that can receive focus has an AccessKit node.
|
// Make sure anything that can receive focus has an AccessKit node.
|
||||||
// TODO(mwcampbell): For nodes that are filled from widget info,
|
// TODO(mwcampbell): For nodes that are filled from widget info,
|
||||||
// some information is written to the node twice.
|
// some information is written to the node twice.
|
||||||
self.accesskit_node(id, |node| response.fill_accesskit_node_common(node));
|
self.accesskit_node_builder(id, |builder| response.fill_accesskit_node_common(builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
let clicked_elsewhere = response.clicked_elsewhere();
|
let clicked_elsewhere = response.clicked_elsewhere();
|
||||||
|
@ -1128,12 +1127,20 @@ impl Context {
|
||||||
if let Some(state) = state {
|
if let Some(state) = state {
|
||||||
let has_focus = self.input(|i| i.raw.has_focus);
|
let has_focus = self.input(|i| i.raw.has_focus);
|
||||||
let root_id = crate::accesskit_root_id().accesskit_id();
|
let root_id = crate::accesskit_root_id().accesskit_id();
|
||||||
platform_output.accesskit_update = Some(accesskit::TreeUpdate {
|
let nodes = self.write(|ctx| {
|
||||||
nodes: state
|
state
|
||||||
.nodes
|
.node_builders
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, node)| (id.accesskit_id(), Arc::from(node)))
|
.map(|(id, builder)| {
|
||||||
.collect(),
|
(
|
||||||
|
id.accesskit_id(),
|
||||||
|
builder.build(&mut ctx.accesskit_node_classes),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
platform_output.accesskit_update = Some(accesskit::TreeUpdate {
|
||||||
|
nodes,
|
||||||
tree: Some(accesskit::Tree::new(root_id)),
|
tree: Some(accesskit::Tree::new(root_id)),
|
||||||
focus: has_focus.then(|| {
|
focus: has_focus.then(|| {
|
||||||
let focus_id = self.memory(|mem| mem.interaction.focus.id);
|
let focus_id = self.memory(|mem| mem.interaction.focus.id);
|
||||||
|
@ -1720,8 +1727,8 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If AccessKit support is active for the current frame, get or create
|
/// If AccessKit support is active for the current frame, get or create
|
||||||
/// a node with the specified ID and return a mutable reference to it.
|
/// a node builder with the specified ID and return a mutable reference to it.
|
||||||
/// For newly crated nodes, the parent is the node with the ID at the top
|
/// For newly created nodes, the parent is the node with the ID at the top
|
||||||
/// of the stack managed by [`Context::with_accessibility_parent`].
|
/// of the stack managed by [`Context::with_accessibility_parent`].
|
||||||
///
|
///
|
||||||
/// The `Context` lock is held while the given closure is called!
|
/// The `Context` lock is held while the given closure is called!
|
||||||
|
@ -1729,16 +1736,16 @@ impl Context {
|
||||||
/// Returns `None` if acesskit is off.
|
/// Returns `None` if acesskit is off.
|
||||||
// TODO: consider making both RO and RW versions
|
// TODO: consider making both RO and RW versions
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub fn accesskit_node<R>(
|
pub fn accesskit_node_builder<R>(
|
||||||
&self,
|
&self,
|
||||||
id: Id,
|
id: Id,
|
||||||
writer: impl FnOnce(&mut accesskit::Node) -> R,
|
writer: impl FnOnce(&mut accesskit::NodeBuilder) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
ctx.frame_state
|
ctx.frame_state
|
||||||
.accesskit_state
|
.accesskit_state
|
||||||
.is_some()
|
.is_some()
|
||||||
.then(|| ctx.accesskit_node(id))
|
.then(|| ctx.accesskit_node_builder(id))
|
||||||
.map(writer)
|
.map(writer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1750,12 +1757,30 @@ impl Context {
|
||||||
/// being called by the AccessKit adapter to provide the initial tree update,
|
/// being called by the AccessKit adapter to provide the initial tree update,
|
||||||
/// then it should do so, to provide a complete AccessKit tree to the adapter
|
/// then it should do so, to provide a complete AccessKit tree to the adapter
|
||||||
/// immediately. Otherwise, it should enqueue a repaint and use the
|
/// immediately. Otherwise, it should enqueue a repaint and use the
|
||||||
/// placeholder tree update from [`crate::accesskit_placeholder_tree_update`]
|
/// placeholder tree update from [`Context::accesskit_placeholder_tree_update`]
|
||||||
/// in the meantime.
|
/// in the meantime.
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub fn enable_accesskit(&self) {
|
pub fn enable_accesskit(&self) {
|
||||||
self.write(|ctx| ctx.is_accesskit_enabled = true);
|
self.write(|ctx| ctx.is_accesskit_enabled = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a tree update that the egui integration should provide to the
|
||||||
|
/// AccessKit adapter if it cannot immediately run the egui application
|
||||||
|
/// to get a full tree update after running [`Context::enable_accesskit`].
|
||||||
|
#[cfg(feature = "accesskit")]
|
||||||
|
pub fn accesskit_placeholder_tree_update(&self) -> accesskit::TreeUpdate {
|
||||||
|
use accesskit::{NodeBuilder, Role, Tree, TreeUpdate};
|
||||||
|
|
||||||
|
let root_id = crate::accesskit_root_id().accesskit_id();
|
||||||
|
self.write(|ctx| TreeUpdate {
|
||||||
|
nodes: vec![(
|
||||||
|
root_id,
|
||||||
|
NodeBuilder::new(Role::Window).build(&mut ctx.accesskit_node_classes),
|
||||||
|
)],
|
||||||
|
tree: Some(Tree::new(root_id)),
|
||||||
|
focus: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub(crate) struct TooltipFrameState {
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct AccessKitFrameState {
|
pub(crate) struct AccessKitFrameState {
|
||||||
pub(crate) nodes: IdMap<Box<accesskit::Node>>,
|
pub(crate) node_builders: IdMap<accesskit::NodeBuilder>,
|
||||||
pub(crate) parent_stack: Vec<Id>,
|
pub(crate) parent_stack: Vec<Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -559,25 +559,3 @@ pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) {
|
||||||
pub fn accesskit_root_id() -> Id {
|
pub fn accesskit_root_id() -> Id {
|
||||||
Id::new("accesskit_root")
|
Id::new("accesskit_root")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a tree update that the egui integration should provide to the
|
|
||||||
/// AccessKit adapter if it cannot immediately run the egui application
|
|
||||||
/// to get a full tree update after running [`Context::enable_accesskit`].
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn accesskit_placeholder_tree_update() -> accesskit::TreeUpdate {
|
|
||||||
use accesskit::{Node, Role, Tree, TreeUpdate};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
let root_id = accesskit_root_id().accesskit_id();
|
|
||||||
TreeUpdate {
|
|
||||||
nodes: vec![(
|
|
||||||
root_id,
|
|
||||||
Arc::new(Node {
|
|
||||||
role: Role::Window,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
)],
|
|
||||||
tree: Some(Tree::new(root_id)),
|
|
||||||
focus: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -573,47 +573,47 @@ impl Response {
|
||||||
self.output_event(event);
|
self.output_event(event);
|
||||||
} else {
|
} else {
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
self.ctx.accesskit_node(self.id, |node| {
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
self.fill_accesskit_node_from_widget_info(node, make_info());
|
self.fill_accesskit_node_from_widget_info(builder, make_info());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_event(&self, event: crate::output::OutputEvent) {
|
pub fn output_event(&self, event: crate::output::OutputEvent) {
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
self.ctx.accesskit_node(self.id, |node| {
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
self.fill_accesskit_node_from_widget_info(node, event.widget_info().clone());
|
self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());
|
||||||
});
|
});
|
||||||
self.ctx.output_mut(|o| o.events.push(event));
|
self.ctx.output_mut(|o| o.events.push(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
pub(crate) fn fill_accesskit_node_common(&self, node: &mut accesskit::Node) {
|
pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::NodeBuilder) {
|
||||||
node.bounds = Some(accesskit::kurbo::Rect {
|
builder.set_bounds(accesskit::Rect {
|
||||||
x0: self.rect.min.x.into(),
|
x0: self.rect.min.x.into(),
|
||||||
y0: self.rect.min.y.into(),
|
y0: self.rect.min.y.into(),
|
||||||
x1: self.rect.max.x.into(),
|
x1: self.rect.max.x.into(),
|
||||||
y1: self.rect.max.y.into(),
|
y1: self.rect.max.y.into(),
|
||||||
});
|
});
|
||||||
if self.sense.focusable {
|
if self.sense.focusable {
|
||||||
node.focusable = true;
|
builder.add_action(accesskit::Action::Focus);
|
||||||
}
|
}
|
||||||
if self.sense.click && node.default_action_verb.is_none() {
|
if self.sense.click && builder.default_action_verb().is_none() {
|
||||||
node.default_action_verb = Some(accesskit::DefaultActionVerb::Click);
|
builder.set_default_action_verb(accesskit::DefaultActionVerb::Click);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
fn fill_accesskit_node_from_widget_info(
|
fn fill_accesskit_node_from_widget_info(
|
||||||
&self,
|
&self,
|
||||||
node: &mut accesskit::Node,
|
builder: &mut accesskit::NodeBuilder,
|
||||||
info: crate::WidgetInfo,
|
info: crate::WidgetInfo,
|
||||||
) {
|
) {
|
||||||
use crate::WidgetType;
|
use crate::WidgetType;
|
||||||
use accesskit::{CheckedState, Role};
|
use accesskit::{CheckedState, Role};
|
||||||
|
|
||||||
self.fill_accesskit_node_common(node);
|
self.fill_accesskit_node_common(builder);
|
||||||
node.role = match info.typ {
|
builder.set_role(match info.typ {
|
||||||
WidgetType::Label => Role::StaticText,
|
WidgetType::Label => Role::StaticText,
|
||||||
WidgetType::Link => Role::Link,
|
WidgetType::Link => Role::Link,
|
||||||
WidgetType::TextEdit => Role::TextField,
|
WidgetType::TextEdit => Role::TextField,
|
||||||
|
@ -628,18 +628,18 @@ impl Response {
|
||||||
WidgetType::DragValue => Role::SpinButton,
|
WidgetType::DragValue => Role::SpinButton,
|
||||||
WidgetType::ColorButton => Role::ColorWell,
|
WidgetType::ColorButton => Role::ColorWell,
|
||||||
WidgetType::Other => Role::Unknown,
|
WidgetType::Other => Role::Unknown,
|
||||||
};
|
});
|
||||||
if let Some(label) = info.label {
|
if let Some(label) = info.label {
|
||||||
node.name = Some(label.into());
|
builder.set_name(label);
|
||||||
}
|
}
|
||||||
if let Some(value) = info.current_text_value {
|
if let Some(value) = info.current_text_value {
|
||||||
node.value = Some(value.into());
|
builder.set_value(value);
|
||||||
}
|
}
|
||||||
if let Some(value) = info.value {
|
if let Some(value) = info.value {
|
||||||
node.numeric_value = Some(value);
|
builder.set_numeric_value(value);
|
||||||
}
|
}
|
||||||
if let Some(selected) = info.selected {
|
if let Some(selected) = info.selected {
|
||||||
node.checked_state = Some(if selected {
|
builder.set_checked_state(if selected {
|
||||||
CheckedState::True
|
CheckedState::True
|
||||||
} else {
|
} else {
|
||||||
CheckedState::False
|
CheckedState::False
|
||||||
|
@ -662,8 +662,9 @@ impl Response {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn labelled_by(self, id: Id) -> Self {
|
pub fn labelled_by(self, id: Id) -> Self {
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
self.ctx
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
.accesskit_node(self.id, |node| node.labelled_by.push(id.accesskit_id()));
|
builder.push_labelled_by(id.accesskit_id());
|
||||||
|
});
|
||||||
#[cfg(not(feature = "accesskit"))]
|
#[cfg(not(feature = "accesskit"))]
|
||||||
{
|
{
|
||||||
let _ = id;
|
let _ = id;
|
||||||
|
|
|
@ -557,28 +557,28 @@ impl<'a> Widget for DragValue<'a> {
|
||||||
response.widget_info(|| WidgetInfo::drag_value(value));
|
response.widget_info(|| WidgetInfo::drag_value(value));
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
ui.ctx().accesskit_node(response.id, |node| {
|
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
||||||
use accesskit::Action;
|
use accesskit::Action;
|
||||||
// If either end of the range is unbounded, it's better
|
// If either end of the range is unbounded, it's better
|
||||||
// to leave the corresponding AccessKit field set to None,
|
// to leave the corresponding AccessKit field set to None,
|
||||||
// to allow for platform-specific default behavior.
|
// to allow for platform-specific default behavior.
|
||||||
if clamp_range.start().is_finite() {
|
if clamp_range.start().is_finite() {
|
||||||
node.min_numeric_value = Some(*clamp_range.start());
|
builder.set_min_numeric_value(*clamp_range.start());
|
||||||
}
|
}
|
||||||
if clamp_range.end().is_finite() {
|
if clamp_range.end().is_finite() {
|
||||||
node.max_numeric_value = Some(*clamp_range.end());
|
builder.set_max_numeric_value(*clamp_range.end());
|
||||||
}
|
}
|
||||||
node.numeric_value_step = Some(speed);
|
builder.set_numeric_value_step(speed);
|
||||||
node.actions |= Action::SetValue;
|
builder.add_action(Action::SetValue);
|
||||||
if value < *clamp_range.end() {
|
if value < *clamp_range.end() {
|
||||||
node.actions |= Action::Increment;
|
builder.add_action(Action::Increment);
|
||||||
}
|
}
|
||||||
if value > *clamp_range.start() {
|
if value > *clamp_range.start() {
|
||||||
node.actions |= Action::Decrement;
|
builder.add_action(Action::Decrement);
|
||||||
}
|
}
|
||||||
// The name field is set to the current value by the button,
|
// The name field is set to the current value by the button,
|
||||||
// but we don't want it set that way on this widget type.
|
// but we don't want it set that way on this widget type.
|
||||||
node.name = None;
|
builder.clear_name();
|
||||||
// Always expose the value as a string. This makes the widget
|
// Always expose the value as a string. This makes the widget
|
||||||
// more stable to accessibility users as it switches
|
// more stable to accessibility users as it switches
|
||||||
// between edit and button modes. This is particularly important
|
// between edit and button modes. This is particularly important
|
||||||
|
@ -599,7 +599,7 @@ impl<'a> Widget for DragValue<'a> {
|
||||||
// when in edit mode.
|
// when in edit mode.
|
||||||
if !is_kb_editing {
|
if !is_kb_editing {
|
||||||
let value_text = format!("{}{}{}", prefix, value_text, suffix);
|
let value_text = format!("{}{}{}", prefix, value_text, suffix);
|
||||||
node.value = Some(value_text.into());
|
builder.set_value(value_text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -792,18 +792,20 @@ impl<'a> Slider<'a> {
|
||||||
response.widget_info(|| WidgetInfo::slider(value, self.text.text()));
|
response.widget_info(|| WidgetInfo::slider(value, self.text.text()));
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
ui.ctx().accesskit_node(response.id, |node| {
|
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
||||||
use accesskit::Action;
|
use accesskit::Action;
|
||||||
node.min_numeric_value = Some(*self.range.start());
|
builder.set_min_numeric_value(*self.range.start());
|
||||||
node.max_numeric_value = Some(*self.range.end());
|
builder.set_max_numeric_value(*self.range.end());
|
||||||
node.numeric_value_step = self.step;
|
if let Some(step) = self.step {
|
||||||
node.actions |= Action::SetValue;
|
builder.set_numeric_value_step(step);
|
||||||
|
}
|
||||||
|
builder.add_action(Action::SetValue);
|
||||||
let clamp_range = self.clamp_range();
|
let clamp_range = self.clamp_range();
|
||||||
if value < *clamp_range.end() {
|
if value < *clamp_range.end() {
|
||||||
node.actions |= Action::Increment;
|
builder.add_action(Action::Increment);
|
||||||
}
|
}
|
||||||
if value > *clamp_range.start() {
|
if value > *clamp_range.start() {
|
||||||
node.actions |= Action::Decrement;
|
builder.add_action(Action::Decrement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -666,7 +666,7 @@ impl<'t> TextEdit<'t> {
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
{
|
{
|
||||||
let parent_id = ui.ctx().accesskit_node(response.id, |node| {
|
let parent_id = ui.ctx().accesskit_node_builder(response.id, |builder| {
|
||||||
use accesskit::{TextPosition, TextSelection};
|
use accesskit::{TextPosition, TextSelection};
|
||||||
|
|
||||||
let parent_id = response.id;
|
let parent_id = response.id;
|
||||||
|
@ -674,7 +674,7 @@ impl<'t> TextEdit<'t> {
|
||||||
if let Some(cursor_range) = &cursor_range {
|
if let Some(cursor_range) = &cursor_range {
|
||||||
let anchor = &cursor_range.secondary.rcursor;
|
let anchor = &cursor_range.secondary.rcursor;
|
||||||
let focus = &cursor_range.primary.rcursor;
|
let focus = &cursor_range.primary.rcursor;
|
||||||
node.text_selection = Some(TextSelection {
|
builder.set_text_selection(TextSelection {
|
||||||
anchor: TextPosition {
|
anchor: TextPosition {
|
||||||
node: parent_id.with(anchor.row).accesskit_id(),
|
node: parent_id.with(anchor.row).accesskit_id(),
|
||||||
character_index: anchor.column,
|
character_index: anchor.column,
|
||||||
|
@ -686,8 +686,10 @@ impl<'t> TextEdit<'t> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
node.default_action_verb = Some(accesskit::DefaultActionVerb::Focus);
|
builder.set_default_action_verb(accesskit::DefaultActionVerb::Focus);
|
||||||
node.multiline = self.multiline;
|
if self.multiline {
|
||||||
|
builder.set_multiline();
|
||||||
|
}
|
||||||
|
|
||||||
parent_id
|
parent_id
|
||||||
});
|
});
|
||||||
|
@ -699,16 +701,16 @@ impl<'t> TextEdit<'t> {
|
||||||
ui.ctx().with_accessibility_parent(parent_id, || {
|
ui.ctx().with_accessibility_parent(parent_id, || {
|
||||||
for (i, row) in galley.rows.iter().enumerate() {
|
for (i, row) in galley.rows.iter().enumerate() {
|
||||||
let id = parent_id.with(i);
|
let id = parent_id.with(i);
|
||||||
ui.ctx().accesskit_node(id, |node| {
|
ui.ctx().accesskit_node_builder(id, |builder| {
|
||||||
node.role = Role::InlineTextBox;
|
builder.set_role(Role::InlineTextBox);
|
||||||
let rect = row.rect.translate(text_draw_pos.to_vec2());
|
let rect = row.rect.translate(text_draw_pos.to_vec2());
|
||||||
node.bounds = Some(accesskit::kurbo::Rect {
|
builder.set_bounds(accesskit::Rect {
|
||||||
x0: rect.min.x.into(),
|
x0: rect.min.x.into(),
|
||||||
y0: rect.min.y.into(),
|
y0: rect.min.y.into(),
|
||||||
x1: rect.max.x.into(),
|
x1: rect.max.x.into(),
|
||||||
y1: rect.max.y.into(),
|
y1: rect.max.y.into(),
|
||||||
});
|
});
|
||||||
node.text_direction = Some(TextDirection::LeftToRight);
|
builder.set_text_direction(TextDirection::LeftToRight);
|
||||||
// TODO(mwcampbell): Set more node fields for the row
|
// TODO(mwcampbell): Set more node fields for the row
|
||||||
// once AccessKit adapters expose text formatting info.
|
// once AccessKit adapters expose text formatting info.
|
||||||
|
|
||||||
|
@ -748,11 +750,11 @@ impl<'t> TextEdit<'t> {
|
||||||
}
|
}
|
||||||
word_lengths.push((character_lengths.len() - last_word_start) as _);
|
word_lengths.push((character_lengths.len() - last_word_start) as _);
|
||||||
|
|
||||||
node.value = Some(value.into());
|
builder.set_value(value);
|
||||||
node.character_lengths = character_lengths.into();
|
builder.set_character_lengths(character_lengths);
|
||||||
node.character_positions = Some(character_positions.into());
|
builder.set_character_positions(character_positions);
|
||||||
node.character_widths = Some(character_widths.into());
|
builder.set_character_widths(character_widths);
|
||||||
node.word_lengths = word_lengths.into();
|
builder.set_word_lengths(word_lengths);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue