v14.1: post notifications on focused virtual element, not EmacsView
Root cause of typing echo + cursor movement failure: VoiceOver tracks the focused element (EmacsAccessibilityBuffer, AXTextArea). Notifications from EmacsView (AXGroup) were ignored because VoiceOver doesn't monitor non-focused elements for text changes. Fix: ns_draw_window_cursor now calls [view accessibilityFocusedUIElement] to find the right EmacsAccessibilityBuffer, and posts ValueChanged + SelectedTextChanged on IT instead of on the view. Also: postAccessibilityUpdatesForWindow now uses [self accessibilityStringForRange:] instead of [view accessibilityStringForRange:] for typing echo text.
This commit is contained in:
@@ -2,12 +2,9 @@ From: Martin Sukany <martin@sukany.cz>
|
||||
Date: Wed, 26 Feb 2026 00:00:00 +0100
|
||||
Subject: [PATCH] ns: add macOS Zoom cursor tracking and VoiceOver accessibility
|
||||
|
||||
Dual accessibility support:
|
||||
1. UAZoomChangeFocus + accessibilityBoundsForRange on EmacsView for Zoom
|
||||
2. Virtual element tree (EmacsAccessibilityBuffer) for VoiceOver
|
||||
3. Typing echo from ns_draw_window_cursor on EmacsView
|
||||
4. Full hierarchy plumbing: accessibilityWindow, accessibilityTopLevelUIElement,
|
||||
accessibilityParent, isAccessibilityFocused, FocusedUIElementChanged
|
||||
Dual accessibility: UAZoomChangeFocus for Zoom + virtual element tree for
|
||||
VoiceOver. Notifications target the focused virtual element, not EmacsView.
|
||||
Full hierarchy plumbing (accessibilityWindow, accessibilityParent, etc.).
|
||||
MRC compatible.
|
||||
---
|
||||
--- a/src/nsterm.h 2026-02-26 08:46:18.118172281 +0100
|
||||
@@ -74,7 +71,7 @@ MRC compatible.
|
||||
|
||||
|
||||
--- a/src/nsterm.m 2026-02-26 08:46:18.124172384 +0100
|
||||
+++ b/src/nsterm.m 2026-02-26 10:19:39.112307036 +0100
|
||||
+++ b/src/nsterm.m 2026-02-26 10:46:54.607568839 +0100
|
||||
@@ -1104,6 +1104,11 @@
|
||||
|
||||
unblock_input ();
|
||||
@@ -87,7 +84,7 @@ MRC compatible.
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3232,6 +3237,75 @@
|
||||
@@ -3232,6 +3237,82 @@
|
||||
/* Prevent the cursor from being drawn outside the text area. */
|
||||
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
||||
|
||||
@@ -103,13 +100,20 @@ MRC compatible.
|
||||
+ /* Store cursor rect for accessibilityBoundsForRange: queries. */
|
||||
+ view->lastAccessibilityCursorRect = r;
|
||||
+
|
||||
+ /* Find the focused virtual element — VoiceOver tracks IT,
|
||||
+ not the EmacsView (AXGroup). Notifications must come from
|
||||
+ the element VoiceOver is monitoring. */
|
||||
+ id axTarget = [view accessibilityFocusedUIElement];
|
||||
+ if (!axTarget)
|
||||
+ axTarget = view;
|
||||
+
|
||||
+ struct buffer *curbuf
|
||||
+ = XBUFFER (XWINDOW (f->selected_window)->contents);
|
||||
+
|
||||
+ if (curbuf && BUF_MODIFF (curbuf) != view->lastAccessibilityModiff)
|
||||
+ {
|
||||
+ /* Buffer content changed — typing echo. Post ValueChanged
|
||||
+ with rich userInfo on the VIEW so VoiceOver can speak.
|
||||
+ with rich userInfo on the FOCUSED ELEMENT.
|
||||
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */
|
||||
+ view->lastAccessibilityModiff = BUF_MODIFF (curbuf);
|
||||
+
|
||||
@@ -133,12 +137,12 @@ MRC compatible.
|
||||
+ @"AXTextChangeValues": @[change]
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ view, NSAccessibilityValueChangedNotification, userInfo);
|
||||
+ axTarget, NSAccessibilityValueChangedNotification, userInfo);
|
||||
+ }
|
||||
+
|
||||
+ /* Always notify cursor movement. */
|
||||
+ /* Always notify cursor movement on the focused element. */
|
||||
+ NSAccessibilityPostNotification (
|
||||
+ view, NSAccessibilitySelectedTextChangedNotification);
|
||||
+ axTarget, NSAccessibilitySelectedTextChangedNotification);
|
||||
+
|
||||
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
|
||||
+ expects top-left origin (CG coordinate space). */
|
||||
@@ -163,7 +167,7 @@ MRC compatible.
|
||||
ns_focus (f, NULL, 0);
|
||||
|
||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||
@@ -6849,6 +6923,650 @@
|
||||
@@ -6849,6 +6930,646 @@
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
@@ -772,15 +776,11 @@ MRC compatible.
|
||||
+ ptrdiff_t pt = BUF_PT (b);
|
||||
+ if (pt > BUF_BEGV (b))
|
||||
+ {
|
||||
+ EmacsView *view = self.emacsView;
|
||||
+ if (view)
|
||||
+ {
|
||||
+ NSRange charRange = NSMakeRange (
|
||||
+ (NSUInteger)(pt - BUF_BEGV (b) - 1), 1);
|
||||
+ changedText = [view accessibilityStringForRange:charRange];
|
||||
+ if (!changedText)
|
||||
+ changedText = @"";
|
||||
+ }
|
||||
+ NSRange charRange = NSMakeRange (
|
||||
+ (NSUInteger)(pt - BUF_BEGV (b) - 1), 1);
|
||||
+ changedText = [self accessibilityStringForRange:charRange];
|
||||
+ if (!changedText)
|
||||
+ changedText = @"";
|
||||
+ }
|
||||
+
|
||||
+ NSDictionary *change = @{
|
||||
@@ -814,7 +814,7 @@ MRC compatible.
|
||||
EmacsView implementation
|
||||
|
||||
========================================================================== */
|
||||
@@ -6889,6 +7607,7 @@
|
||||
@@ -6889,6 +7610,7 @@
|
||||
[layer release];
|
||||
#endif
|
||||
|
||||
@@ -822,7 +822,7 @@ MRC compatible.
|
||||
[[self menu] release];
|
||||
[super dealloc];
|
||||
}
|
||||
@@ -8237,6 +8956,18 @@
|
||||
@@ -8237,6 +8959,18 @@
|
||||
XSETFRAME (event.frame_or_window, emacsframe);
|
||||
kbd_buffer_store_event (&event);
|
||||
ns_send_appdefined (-1); // Kick main loop
|
||||
@@ -841,7 +841,7 @@ MRC compatible.
|
||||
}
|
||||
|
||||
|
||||
@@ -9474,6 +10205,391 @@
|
||||
@@ -9474,6 +10208,391 @@
|
||||
return fs_state;
|
||||
}
|
||||
|
||||
@@ -1233,7 +1233,7 @@ MRC compatible.
|
||||
@end /* EmacsView */
|
||||
|
||||
|
||||
@@ -9941,6 +11057,14 @@
|
||||
@@ -9941,6 +11060,14 @@
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user