mirror of
https://github.com/zed-industries/zed.git
synced 2024-10-06 10:57:40 +03:00
WIP: Start on accesskit integration
This commit is contained in:
parent
1c361ac579
commit
63e9a7069f
81
Cargo.lock
generated
81
Cargo.lock
generated
@ -2,6 +2,34 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb10ed32c63247e4e39a8f42e8e30fb9442fbf7878c8e4a9849e7e381619bea"
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9846654e84f26a31d269c0a8ea44fd8848319ee9cdf8908de18f51356f61f598"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4354e6f1a880a3a368fe6339fb4c1b94ccd2c8a053f6752abb09f4497758b3"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
"icrate",
|
||||
"objc2",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "activity_indicator"
|
||||
version = "0.1.0"
|
||||
@ -1457,6 +1485,25 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f"
|
||||
dependencies = [
|
||||
"block-sys",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.3.1"
|
||||
@ -4070,6 +4117,8 @@ dependencies = [
|
||||
name = "gpui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_macos",
|
||||
"anyhow",
|
||||
"as-raw-xcb-connection",
|
||||
"ashpd",
|
||||
@ -4500,6 +4549,16 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icrate"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e286f4b975ac6c054971a0600a9b76438b332edace54bff79c71c9d3adfc9772"
|
||||
dependencies = [
|
||||
"block2",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
@ -6101,6 +6160,28 @@ dependencies = [
|
||||
"objc_exception",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459"
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a9c7f0d511a4ce26b078183179dca908171cfc69f88986fe36c5138e1834476"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ff06a6505cde0766484f38d8479ac8e6d31c66fbc2d5492f65ca8c091456379"
|
||||
|
||||
[[package]]
|
||||
name = "objc_exception"
|
||||
version = "0.1.2"
|
||||
|
@ -68,6 +68,7 @@ usvg = { version = "0.14", features = [] }
|
||||
util.workspace = true
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
waker-fn = "1.1.0"
|
||||
accesskit = { version = "0.12" }
|
||||
|
||||
[dev-dependencies]
|
||||
backtrace = "0.3"
|
||||
@ -93,6 +94,7 @@ log.workspace = true
|
||||
media.workspace = true
|
||||
metal = "0.21.0"
|
||||
objc = "0.2"
|
||||
accesskit_macos = "0.11.0"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
flume = "0.11"
|
||||
|
58
crates/gpui/src/access_kit.rs
Normal file
58
crates/gpui/src/access_kit.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use collections::{hash_map::Entry, HashMap};
|
||||
|
||||
use crate::{BorrowWindow, ElementContext, ElementId, GlobalElementId, WindowContext};
|
||||
|
||||
pub type AccessKitState = HashMap<accesskit::NodeId, accesskit::NodeBuilder>;
|
||||
|
||||
impl From<&GlobalElementId> for accesskit::NodeId {
|
||||
fn from(value: &GlobalElementId) -> Self {
|
||||
let mut hasher = std::hash::DefaultHasher::new();
|
||||
value.0.hash(&mut hasher);
|
||||
accesskit::NodeId(hasher.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ElementContext<'a> {
|
||||
|
||||
// TODO: What's a good, useful signature for this? Need to expose this from the div as well.
|
||||
fn accesskit_action(&mut self, id: impl Into<ElementId>, action: accesskit::Action, f: impl FnOnce(accesskit::ActionRequest)) {
|
||||
self.with_element_id(Some(id), |cx| {
|
||||
// Get the access kit actions from somewhere
|
||||
// call f with the action request and cx
|
||||
// egui impl:
|
||||
// let accesskit_id = id.accesskit_id();
|
||||
// self.events.iter().filter_map(move |event| {
|
||||
// if let Event::AccessKitActionRequest(request) = event {
|
||||
// if request.target == accesskit_id && request.action == action {
|
||||
// return Some(request);
|
||||
// }
|
||||
// }
|
||||
// None
|
||||
// })
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Expose this through the div API
|
||||
fn with_accesskit_node(&mut self, id: impl Into<ElementId>, f: impl FnOnce(&mut accesskit::NodeBuilder)) {
|
||||
let id = id.into();
|
||||
let window = self.window_mut();
|
||||
let parent_id: accesskit::NodeId = (&window.element_id_stack).into();
|
||||
self.with_element_id(Some(id), |cx| {
|
||||
let window = cx.window_mut();
|
||||
let this_id: accesskit::NodeId = (&window.element_id_stack).into();
|
||||
|
||||
window.next_frame.accesskit.as_mut().map(|nodes| {
|
||||
if let Entry::Vacant(entry) = nodes.entry(this_id) {
|
||||
entry.insert(Default::default());
|
||||
let parent = nodes.get_mut(&parent_id).unwrap();
|
||||
parent.push_child(this_id);
|
||||
}
|
||||
|
||||
f(nodes.get_mut(&this_id).unwrap());
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
@ -241,6 +241,7 @@ pub struct AppContext {
|
||||
pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
|
||||
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
|
||||
pub(crate) propagate_event: bool,
|
||||
pub(crate) screen_reader_enabled: bool,
|
||||
}
|
||||
|
||||
impl AppContext {
|
||||
@ -299,6 +300,7 @@ impl AppContext {
|
||||
quit_observers: SubscriberSet::new(),
|
||||
layout_id_buffer: Default::default(),
|
||||
propagate_event: true,
|
||||
screen_reader_enabled: false,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -314,6 +316,7 @@ impl AppContext {
|
||||
app
|
||||
}
|
||||
|
||||
|
||||
/// Quit the application gracefully. Handlers registered with [`ModelContext::on_app_quit`]
|
||||
/// will be given 100ms to complete before exiting.
|
||||
pub fn shutdown(&mut self) {
|
||||
|
@ -38,9 +38,10 @@ use crate::{
|
||||
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
|
||||
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
};
|
||||
use collections::FxHashSet;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
use std::{any::Any, fmt::Debug, ops::DerefMut};
|
||||
use std::{any::Any, fmt::Debug, hash::{Hash, Hasher, SipHasher}, ops::DerefMut};
|
||||
|
||||
/// Implemented by types that participate in laying out and painting the contents of a window.
|
||||
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
|
||||
@ -222,7 +223,8 @@ impl<C: RenderOnce> IntoElement for Component<C> {
|
||||
|
||||
/// A globally unique identifier for an element, used to track state across frames.
|
||||
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
|
||||
pub(crate) struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>);
|
||||
|
||||
|
||||
trait ElementObject {
|
||||
fn element_id(&self) -> Option<ElementId>;
|
||||
|
@ -67,6 +67,7 @@
|
||||
mod action;
|
||||
mod app;
|
||||
|
||||
mod access_kit;
|
||||
mod arena;
|
||||
mod assets;
|
||||
mod color;
|
||||
|
@ -222,6 +222,22 @@ unsafe fn build_classes() {
|
||||
accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
);
|
||||
|
||||
// AccesKit integration poitns
|
||||
decl.add_method(
|
||||
sel!(accessibilityChildren),
|
||||
accessibility_children as extern "C" fn(&mut Object, Sel) -> id,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(accessibilityFocusedUIElement),
|
||||
accessibility_focused as extern "C" fn(&mut Object, Sel) -> id,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(accessibilityHitTest:),
|
||||
accessibility_hit_test as extern "C" fn(&mut Object, Sel, NSPoint) -> id,
|
||||
);
|
||||
|
||||
decl.register()
|
||||
};
|
||||
}
|
||||
@ -343,6 +359,7 @@ struct MacWindowState {
|
||||
input_during_keydown: Option<SmallVec<[ImeInput; 1]>>,
|
||||
previous_keydown_inserted_text: Option<String>,
|
||||
external_files_dragged: bool,
|
||||
accesskit: Option<accesskit_macos::Adapter>
|
||||
}
|
||||
|
||||
impl MacWindowState {
|
||||
@ -467,6 +484,30 @@ impl MacWindowState {
|
||||
msg_send![self.native_window, convertPointToScreen: point]
|
||||
}
|
||||
}
|
||||
|
||||
fn get_accesskit_adapter(&mut self) -> &accesskit_macos::Adapter {
|
||||
self.accesskit.get_or_insert_with(|| {
|
||||
fn handler() -> (accesskit::TreeUpdate, bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
let (initial_state, focused) = handler();// self.handler.accesskit_tree();
|
||||
|
||||
struct Handler {}
|
||||
impl accesskit::ActionHandler for Handler {
|
||||
fn do_action(&mut self, _action: accesskit::ActionRequest) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
let action_handler = Box::new(Handler {});
|
||||
|
||||
// SAFETY: The view pointer is based on a valid borrowed reference
|
||||
// to the view.
|
||||
unsafe { accesskit_macos::Adapter::new(self.native_view.as_ptr() as *mut _, initial_state, self.is_active, action_handler) }
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsafe impl Send for MacWindowState {}
|
||||
@ -541,6 +582,8 @@ impl MacWindow {
|
||||
let native_view = NSView::init(native_view);
|
||||
assert!(!native_view.is_null());
|
||||
|
||||
// let accesskit_adapter = accesskit_macos::Adapter::new(native_view, accesskit::TreeUpdate { nodes: Default::default(), tree: Default::default(), focus: accesskit::NodeId(0) }, false, action_handler);
|
||||
|
||||
let window = Self(Arc::new(Mutex::new(MacWindowState {
|
||||
handle,
|
||||
executor,
|
||||
@ -570,6 +613,7 @@ impl MacWindow {
|
||||
input_during_keydown: None,
|
||||
previous_keydown_inserted_text: None,
|
||||
external_files_dragged: false,
|
||||
accesskit: None,
|
||||
})));
|
||||
|
||||
(*native_window).set_ivar(
|
||||
@ -1737,6 +1781,36 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn accessibility_children(this: &mut Object, _: Sel) -> id {
|
||||
unsafe {
|
||||
let state = get_window_state(this);
|
||||
let mut lock = state.as_ref().lock();
|
||||
let adapter = lock.get_accesskit_adapter();
|
||||
adapter.view_children() as *mut _
|
||||
}
|
||||
}
|
||||
extern "C" fn accessibility_focused(this: &mut Object, _: Sel) -> id {
|
||||
unsafe {
|
||||
let state = get_window_state(this);
|
||||
let mut lock = state.as_ref().lock();
|
||||
let adapter = lock.get_accesskit_adapter();
|
||||
adapter.focus() as *mut _
|
||||
}
|
||||
}
|
||||
extern "C" fn accessibility_hit_test(this: &mut Object, _: Sel, point: NSPoint) -> id {
|
||||
unsafe {
|
||||
let state = get_window_state(this);
|
||||
let mut lock = state.as_ref().lock();
|
||||
let adapter = lock.get_accesskit_adapter();
|
||||
|
||||
let point = accesskit_macos::NSPoint {
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
};
|
||||
adapter.hit_test(point) as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
|
||||
let window_state = unsafe { get_window_state(this) };
|
||||
if send_new_event(&window_state, {
|
||||
|
@ -1,13 +1,5 @@
|
||||
use crate::{
|
||||
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
|
||||
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
|
||||
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
|
||||
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
|
||||
Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
|
||||
PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
|
||||
TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowAppearance, WindowBounds,
|
||||
WindowOptions, WindowTextSystem,
|
||||
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId, Hsla, IntoElement, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowAppearance, WindowBounds, WindowOptions, WindowTextSystem
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::FxHashSet;
|
||||
@ -17,20 +9,10 @@ use parking_lot::RwLock;
|
||||
use slotmap::SlotMap;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Display},
|
||||
future::Future,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
any::{Any, TypeId}, borrow::{Borrow, BorrowMut}, cell::{Cell, RefCell}, collections::HashMap, fmt::{Debug, Display}, future::Future, hash::{Hash, Hasher}, marker::PhantomData, mem, rc::Rc, sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
}, time::{Duration, Instant}
|
||||
};
|
||||
use util::{measure, ResultExt};
|
||||
|
||||
@ -950,6 +932,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
let root_view = self.window.root_view.take().unwrap();
|
||||
|
||||
self.with_element_context(|cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
|
||||
|
@ -29,14 +29,7 @@ use smallvec::SmallVec;
|
||||
use util::post_inc;
|
||||
|
||||
use crate::{
|
||||
prelude::*, size, AnyTooltip, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask,
|
||||
Corners, CursorStyle, DevicePixels, DispatchPhase, DispatchTree, ElementId, ElementStateBox,
|
||||
EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
|
||||
InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
|
||||
Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
|
||||
RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext,
|
||||
StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline, UnderlineStyle,
|
||||
Window, WindowContext, SUBPIXEL_VARIANTS,
|
||||
access_kit::AccessKitState, prelude::*, size, AnyTooltip, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask, Corners, CursorStyle, DevicePixels, DispatchPhase, DispatchTree, ElementId, ElementStateBox, EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext, StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline, UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS
|
||||
};
|
||||
|
||||
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
|
||||
@ -70,6 +63,7 @@ pub(crate) struct Frame {
|
||||
pub(crate) requested_cursor_style: Option<CursorStyle>,
|
||||
pub(crate) view_stack: Vec<EntityId>,
|
||||
pub(crate) reused_views: FxHashSet<EntityId>,
|
||||
pub(crate) accesskit: Option<AccessKitState>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
||||
@ -96,6 +90,7 @@ impl Frame {
|
||||
requested_cursor_style: None,
|
||||
view_stack: Vec::new(),
|
||||
reused_views: FxHashSet::default(),
|
||||
accesskit: None,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
debug_bounds: FxHashMap::default(),
|
||||
@ -386,6 +381,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Updates the cursor style at the platform level.
|
||||
pub fn set_cursor_style(&mut self, style: CursorStyle) {
|
||||
let view_id = self.parent_view_id();
|
||||
|
Loading…
Reference in New Issue
Block a user