ns: fix VoiceOver cursor sync — 8 changes from pipeline review
Changes applied (from vo-cursor pipeline review, 7 workers):
1. (Change 8) Add ns_ax_index_for_charpos helper, refactor
ns_ax_index_for_point as thin wrapper — shared coordinate
mapping for all accessibility attribute methods.
2. (Change 1) Remove Site A notifications from ns_draw_window_cursor.
Eliminates duplicate VoiceOver notifications (Site A + Site B both
fired for same events). Zoom cursor tracking (UAZoomChangeFocus)
preserved.
3. (Change 7) Remove redundant bare ValueChanged before rich
userInfo version in postAccessibilityUpdatesForWindow:.
4. (Change 2) Fix typing echo character extraction to use glyph-based
index (ns_ax_index_for_point) instead of buffer-relative
(pt - BUF_BEGV - 1).
5. (Change 3) Add AXTextStateChangeType:@2 (SelectionMove) userInfo
to SelectedTextChanged notification for cursor movement —
enables VoiceOver line-by-line reading on arrow keys.
6. (Change 4) Fix accessibilityRangeForPosition: to return
glyph-based index via ns_ax_index_for_charpos instead of
buffer-relative (charpos - BUF_BEGV).
7. (Change 5) Fix accessibilitySelectedTextRange mark branch to use
ns_ax_index_for_charpos for both endpoints instead of mixing
glyph-based point with buffer-relative mark.
8. Remove 10 redundant text methods from EmacsView (Group role
should not expose text attributes — eliminates coordinate
system divergence with EmacsAccessibilityBuffer).
9. Fix MRC leak: release EmacsAccessibilityBuffer after addObject:
in ns_ax_collect_windows.
10. Remove dead lastAccessibilityModiff ivar (was only used by
removed Site A).
Enum values verified from WebKit AXTextStateChangeIntent.h:
AXTextStateChangeTypeEdit = 1
AXTextStateChangeTypeSelectionMove = 2
AXTextEditTypeTyping = 3
This commit is contained in:
@@ -5,14 +5,19 @@ Subject: [PATCH] ns: add macOS Zoom cursor tracking and VoiceOver accessibility
|
|||||||
Dual accessibility: UAZoomChangeFocus for Zoom + virtual element tree for
|
Dual accessibility: UAZoomChangeFocus for Zoom + virtual element tree for
|
||||||
VoiceOver. Notifications target the focused virtual element, not EmacsView.
|
VoiceOver. Notifications target the focused virtual element, not EmacsView.
|
||||||
Full hierarchy plumbing (accessibilityWindow, accessibilityParent, etc.).
|
Full hierarchy plumbing (accessibilityWindow, accessibilityParent, etc.).
|
||||||
MRC compatible.
|
MRC compatible. Single notification path (post-redisplay only) with
|
||||||
|
glyph-consistent coordinate system throughout.
|
||||||
---
|
---
|
||||||
--- a/src/nsterm.h 2026-02-26 08:46:18.118172281 +0100
|
diff --git a/src/nsterm.h b/src/nsterm.h
|
||||||
+++ b/src/nsterm.h 2026-02-26 09:08:57.204955708 +0100
|
index 7c1ee4c..1f9639c 100644
|
||||||
@@ -455,6 +455,34 @@
|
--- a/src/nsterm.h
|
||||||
|
+++ b/src/nsterm.h
|
||||||
|
@@ -453,6 +453,34 @@ enum ns_return_frame_mode
|
||||||
|
@end
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
|
|
||||||
|
+/* ==========================================================================
|
||||||
|
+
|
||||||
+ Accessibility virtual elements (macOS / Cocoa only)
|
+ Accessibility virtual elements (macOS / Cocoa only)
|
||||||
+
|
+
|
||||||
+ ========================================================================== */
|
+ ========================================================================== */
|
||||||
@@ -39,12 +44,10 @@ MRC compatible.
|
|||||||
+#endif /* NS_IMPL_COCOA */
|
+#endif /* NS_IMPL_COCOA */
|
||||||
+
|
+
|
||||||
+
|
+
|
||||||
+/* ==========================================================================
|
/* ==========================================================================
|
||||||
+
|
|
||||||
The main Emacs view
|
|
||||||
|
|
||||||
========================================================================== */
|
The main Emacs view
|
||||||
@@ -471,6 +499,12 @@
|
@@ -471,6 +499,11 @@ enum ns_return_frame_mode
|
||||||
#ifdef NS_IMPL_COCOA
|
#ifdef NS_IMPL_COCOA
|
||||||
char *old_title;
|
char *old_title;
|
||||||
BOOL maximizing_resize;
|
BOOL maximizing_resize;
|
||||||
@@ -52,12 +55,11 @@ MRC compatible.
|
|||||||
+ Lisp_Object lastSelectedWindow;
|
+ Lisp_Object lastSelectedWindow;
|
||||||
+ @public
|
+ @public
|
||||||
+ NSRect lastAccessibilityCursorRect;
|
+ NSRect lastAccessibilityCursorRect;
|
||||||
+ ptrdiff_t lastAccessibilityModiff;
|
|
||||||
+ @protected
|
+ @protected
|
||||||
#endif
|
#endif
|
||||||
BOOL font_panel_active;
|
BOOL font_panel_active;
|
||||||
NSFont *font_panel_result;
|
NSFont *font_panel_result;
|
||||||
@@ -528,6 +562,12 @@
|
@@ -528,6 +561,12 @@ enum ns_return_frame_mode
|
||||||
- (void)windowWillExitFullScreen;
|
- (void)windowWillExitFullScreen;
|
||||||
- (void)windowDidExitFullScreen;
|
- (void)windowDidExitFullScreen;
|
||||||
- (void)windowDidBecomeKey;
|
- (void)windowDidBecomeKey;
|
||||||
@@ -70,9 +72,11 @@ MRC compatible.
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
--- a/src/nsterm.m 2026-02-26 08:46:18.124172384 +0100
|
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||||
+++ b/src/nsterm.m 2026-02-26 10:46:54.607568839 +0100
|
index 932d209..077f092 100644
|
||||||
@@ -1104,6 +1104,11 @@
|
--- a/src/nsterm.m
|
||||||
|
+++ b/src/nsterm.m
|
||||||
|
@@ -1104,6 +1104,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
||||||
|
|
||||||
unblock_input ();
|
unblock_input ();
|
||||||
ns_updating_frame = NULL;
|
ns_updating_frame = NULL;
|
||||||
@@ -84,66 +88,21 @@ MRC compatible.
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -3232,6 +3237,82 @@
|
@@ -3232,6 +3237,37 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
|
||||||
/* Prevent the cursor from being drawn outside the text area. */
|
/* Prevent the cursor from being drawn outside the text area. */
|
||||||
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
||||||
|
|
||||||
+#ifdef NS_IMPL_COCOA
|
+#ifdef NS_IMPL_COCOA
|
||||||
+ /* Accessibility cursor tracking for macOS Zoom and VoiceOver.
|
+ /* Accessibility: store cursor rect for Zoom and bounds queries.
|
||||||
+ Only notify AT when drawing the cursor in the active (selected)
|
+ VoiceOver notifications are handled solely by
|
||||||
+ window. Without this guard, C-x o triggers UAZoomChangeFocus
|
+ postAccessibilityUpdatesForWindow: (called from ns_update_end)
|
||||||
+ for the old window last, snapping Zoom back. */
|
+ to avoid duplicate notifications and mid-redisplay fragility. */
|
||||||
+ {
|
+ {
|
||||||
+ EmacsView *view = FRAME_NS_VIEW (f);
|
+ EmacsView *view = FRAME_NS_VIEW (f);
|
||||||
+ if (view && on_p && active_p)
|
+ if (view && on_p && active_p)
|
||||||
+ {
|
+ {
|
||||||
+ /* Store cursor rect for accessibilityBoundsForRange: queries. */
|
|
||||||
+ view->lastAccessibilityCursorRect = r;
|
+ 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 FOCUSED ELEMENT.
|
|
||||||
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */
|
|
||||||
+ view->lastAccessibilityModiff = BUF_MODIFF (curbuf);
|
|
||||||
+
|
|
||||||
+ NSString *changedText = @"";
|
|
||||||
+ ptrdiff_t pt = BUF_PT (curbuf);
|
|
||||||
+ if (pt > BUF_BEGV (curbuf))
|
|
||||||
+ {
|
|
||||||
+ NSRange charRange = NSMakeRange (
|
|
||||||
+ (NSUInteger)(pt - BUF_BEGV (curbuf) - 1), 1);
|
|
||||||
+ changedText = [view accessibilityStringForRange:charRange];
|
|
||||||
+ if (!changedText)
|
|
||||||
+ changedText = @"";
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ NSDictionary *change = @{
|
|
||||||
+ @"AXTextEditType": @3,
|
|
||||||
+ @"AXTextChangeValue": changedText
|
|
||||||
+ };
|
|
||||||
+ NSDictionary *userInfo = @{
|
|
||||||
+ @"AXTextStateChangeType": @1,
|
|
||||||
+ @"AXTextChangeValues": @[change]
|
|
||||||
+ };
|
|
||||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
|
||||||
+ axTarget, NSAccessibilityValueChangedNotification, userInfo);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Always notify cursor movement on the focused element. */
|
|
||||||
+ NSAccessibilityPostNotification (
|
|
||||||
+ axTarget, NSAccessibilitySelectedTextChangedNotification);
|
|
||||||
+
|
|
||||||
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
|
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
|
||||||
+ expects top-left origin (CG coordinate space). */
|
+ expects top-left origin (CG coordinate space). */
|
||||||
+ if (UAZoomEnabled ())
|
+ if (UAZoomEnabled ())
|
||||||
@@ -167,10 +126,12 @@ MRC compatible.
|
|||||||
ns_focus (f, NULL, 0);
|
ns_focus (f, NULL, 0);
|
||||||
|
|
||||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||||
@@ -6849,6 +6930,646 @@
|
@@ -6847,6 +6883,652 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
||||||
|
}
|
||||||
/* ==========================================================================
|
#endif
|
||||||
|
|
||||||
|
+/* ==========================================================================
|
||||||
|
+
|
||||||
+ Accessibility virtual elements (macOS / Cocoa only)
|
+ Accessibility virtual elements (macOS / Cocoa only)
|
||||||
+
|
+
|
||||||
+ ========================================================================== */
|
+ ========================================================================== */
|
||||||
@@ -401,19 +362,16 @@ MRC compatible.
|
|||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+
|
+
|
||||||
+/* Compute the character index within glyph-extracted text that
|
+/* Compute the glyph-based character index for an arbitrary buffer
|
||||||
+ corresponds to the buffer point position. */
|
+ charpos within the visible glyph rows of window W. All accessibility
|
||||||
|
+ attribute methods must use this (or its wrapper ns_ax_index_for_point)
|
||||||
|
+ to stay in the same coordinate space as accessibilityValue. */
|
||||||
+static NSUInteger
|
+static NSUInteger
|
||||||
+ns_ax_index_for_point (struct window *w)
|
+ns_ax_index_for_charpos (struct window *w, ptrdiff_t charpos)
|
||||||
+{
|
+{
|
||||||
+ if (!w || !w->current_matrix || !WINDOW_LEAF_P(w))
|
+ if (!w || !w->current_matrix || !WINDOW_LEAF_P(w))
|
||||||
+ return 0;
|
+ return 0;
|
||||||
+
|
+
|
||||||
+ struct buffer *b = XBUFFER(w->contents);
|
|
||||||
+ if (!b)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ ptrdiff_t point = BUF_PT(b);
|
|
||||||
+ struct glyph_matrix *matrix = w->current_matrix;
|
+ struct glyph_matrix *matrix = w->current_matrix;
|
||||||
+ NSUInteger pos = 0;
|
+ NSUInteger pos = 0;
|
||||||
+
|
+
|
||||||
@@ -428,10 +386,10 @@ MRC compatible.
|
|||||||
+ ptrdiff_t row_start = MATRIX_ROW_START_CHARPOS (row);
|
+ ptrdiff_t row_start = MATRIX_ROW_START_CHARPOS (row);
|
||||||
+ ptrdiff_t row_end = MATRIX_ROW_END_CHARPOS (row);
|
+ ptrdiff_t row_end = MATRIX_ROW_END_CHARPOS (row);
|
||||||
+
|
+
|
||||||
+ if (point >= row_start && point < row_end)
|
+ if (charpos >= row_start && charpos < row_end)
|
||||||
+ {
|
+ {
|
||||||
+ /* Point is within this row. Count visible glyphs whose
|
+ /* Charpos is within this row. Count visible glyphs whose
|
||||||
+ buffer charpos is before point. */
|
+ buffer charpos is before the target. */
|
||||||
+ int chars_before = 0;
|
+ int chars_before = 0;
|
||||||
+ struct glyph *g = row->glyphs[TEXT_AREA];
|
+ struct glyph *g = row->glyphs[TEXT_AREA];
|
||||||
+ struct glyph *gend = g + row->used[TEXT_AREA];
|
+ struct glyph *gend = g + row->used[TEXT_AREA];
|
||||||
@@ -439,7 +397,7 @@ MRC compatible.
|
|||||||
+ {
|
+ {
|
||||||
+ if (g->type == CHAR_GLYPH && !g->padding_p
|
+ if (g->type == CHAR_GLYPH && !g->padding_p
|
||||||
+ && g->charpos >= row_start
|
+ && g->charpos >= row_start
|
||||||
+ && g->charpos < point)
|
+ && g->charpos < charpos)
|
||||||
+ {
|
+ {
|
||||||
+ unsigned ch = g->u.ch;
|
+ unsigned ch = g->u.ch;
|
||||||
+ if (ch != '\n' && ch != '\r' && ch >= 32)
|
+ if (ch != '\n' && ch != '\r' && ch >= 32)
|
||||||
@@ -467,6 +425,18 @@ MRC compatible.
|
|||||||
+ return pos > 0 ? pos - 1 : 0;
|
+ return pos > 0 ? pos - 1 : 0;
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
|
+/* Convenience wrapper: glyph-based index for buffer point. */
|
||||||
|
+static NSUInteger
|
||||||
|
+ns_ax_index_for_point (struct window *w)
|
||||||
|
+{
|
||||||
|
+ if (!w || !WINDOW_LEAF_P(w))
|
||||||
|
+ return 0;
|
||||||
|
+ struct buffer *b = XBUFFER(w->contents);
|
||||||
|
+ if (!b)
|
||||||
|
+ return 0;
|
||||||
|
+ return ns_ax_index_for_charpos (w, BUF_PT(b));
|
||||||
|
+}
|
||||||
|
+
|
||||||
+
|
+
|
||||||
+@implementation EmacsAccessibilityElement
|
+@implementation EmacsAccessibilityElement
|
||||||
+
|
+
|
||||||
@@ -596,17 +566,14 @@ MRC compatible.
|
|||||||
+ if (NILP(BVAR(b, mark_active)))
|
+ if (NILP(BVAR(b, mark_active)))
|
||||||
+ return NSMakeRange(point_idx, 0);
|
+ return NSMakeRange(point_idx, 0);
|
||||||
+
|
+
|
||||||
+ /* With active mark, report the selection range. Map mark
|
+ /* With active mark, report the selection range. Both endpoints
|
||||||
+ position to accessibility index using the same glyph-based
|
+ must be mapped to glyph-based indices consistent with
|
||||||
+ mapping as point. */
|
+ accessibilityValue (via ns_ax_index_for_charpos). */
|
||||||
+ ptrdiff_t mark_pos = marker_position (BVAR (b, mark));
|
+ ptrdiff_t mark_pos = marker_position (BVAR (b, mark));
|
||||||
+ ptrdiff_t pt_pos = BUF_PT (b);
|
+ NSUInteger mark_idx = ns_ax_index_for_charpos (w, mark_pos);
|
||||||
+ ptrdiff_t begv = BUF_BEGV (b);
|
+ NSUInteger start_idx = MIN (point_idx, mark_idx);
|
||||||
+ ptrdiff_t sel_start = (mark_pos < pt_pos) ? mark_pos : pt_pos;
|
+ NSUInteger end_idx = MAX (point_idx, mark_idx);
|
||||||
+ ptrdiff_t sel_end = (mark_pos < pt_pos) ? pt_pos : mark_pos;
|
+ return NSMakeRange (start_idx, end_idx - start_idx);
|
||||||
+ NSUInteger start_idx = (NSUInteger) (sel_start - begv);
|
|
||||||
+ NSUInteger len = (NSUInteger) (sel_end - sel_start);
|
|
||||||
+ return NSMakeRange(start_idx, len);
|
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+- (NSInteger)accessibilityInsertionPointLineNumber
|
+- (NSInteger)accessibilityInsertionPointLineNumber
|
||||||
@@ -719,15 +686,10 @@ MRC compatible.
|
|||||||
+ glyph_x += glyph->pixel_width;
|
+ glyph_x += glyph->pixel_width;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ /* Convert buffer charpos to accessibility index. */
|
+ /* Convert buffer charpos to glyph-based accessibility index,
|
||||||
+ struct buffer *b = XBUFFER (w->contents);
|
+ consistent with accessibilityValue coordinate space. */
|
||||||
+ if (!b)
|
+ NSUInteger ax_idx = ns_ax_index_for_charpos (w, best_charpos);
|
||||||
+ return NSMakeRange (0, 0);
|
+ return NSMakeRange (ax_idx, 1);
|
||||||
+
|
|
||||||
+ ptrdiff_t idx = best_charpos - BUF_BEGV (b);
|
|
||||||
+ if (idx < 0) idx = 0;
|
|
||||||
+
|
|
||||||
+ return NSMakeRange ((NSUInteger) idx, 1);
|
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+- (NSRange)accessibilityVisibleCharacterRange
|
+- (NSRange)accessibilityVisibleCharacterRange
|
||||||
@@ -762,22 +724,19 @@ MRC compatible.
|
|||||||
+ ptrdiff_t modiff = BUF_MODIFF(b);
|
+ ptrdiff_t modiff = BUF_MODIFF(b);
|
||||||
+ ptrdiff_t point = BUF_PT(b);
|
+ ptrdiff_t point = BUF_PT(b);
|
||||||
+
|
+
|
||||||
+ /* Text content changed? */
|
+ /* Text content changed? Post ValueChanged with typing echo userInfo.
|
||||||
|
+ Single notification (no bare ValueChanged first) — Chromium pattern.
|
||||||
|
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3
|
||||||
|
+ (verified from WebKit AXTextStateChangeIntent.h). */
|
||||||
+ if (modiff != self.cachedModiff)
|
+ if (modiff != self.cachedModiff)
|
||||||
+ {
|
+ {
|
||||||
+ self.cachedModiff = modiff;
|
+ self.cachedModiff = modiff;
|
||||||
+ NSAccessibilityPostNotification(self,
|
|
||||||
+ NSAccessibilityValueChangedNotification);
|
|
||||||
+
|
+
|
||||||
+ /* Rich typing echo for VoiceOver.
|
|
||||||
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3.
|
|
||||||
+ Must include AXTextChangeValues array for VoiceOver to speak. */
|
|
||||||
+ NSString *changedText = @"";
|
+ NSString *changedText = @"";
|
||||||
+ ptrdiff_t pt = BUF_PT (b);
|
+ NSUInteger point_idx = ns_ax_index_for_point (w);
|
||||||
+ if (pt > BUF_BEGV (b))
|
+ if (point_idx > 0)
|
||||||
+ {
|
+ {
|
||||||
+ NSRange charRange = NSMakeRange (
|
+ NSRange charRange = NSMakeRange (point_idx - 1, 1);
|
||||||
+ (NSUInteger)(pt - BUF_BEGV (b) - 1), 1);
|
|
||||||
+ changedText = [self accessibilityStringForRange:charRange];
|
+ changedText = [self accessibilityStringForRange:charRange];
|
||||||
+ if (!changedText)
|
+ if (!changedText)
|
||||||
+ changedText = @"";
|
+ changedText = @"";
|
||||||
@@ -795,12 +754,20 @@ MRC compatible.
|
|||||||
+ self, NSAccessibilityValueChangedNotification, userInfo);
|
+ self, NSAccessibilityValueChangedNotification, userInfo);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ /* Cursor moved? */
|
+ /* Cursor moved? Post with userInfo so VoiceOver distinguishes
|
||||||
|
+ navigation from programmatic selection changes and triggers
|
||||||
|
+ line-by-line reading.
|
||||||
|
+ kAXTextStateChangeTypeSelectionMove = 2. */
|
||||||
+ if (point != self.cachedPoint)
|
+ if (point != self.cachedPoint)
|
||||||
+ {
|
+ {
|
||||||
+ self.cachedPoint = point;
|
+ self.cachedPoint = point;
|
||||||
+ NSAccessibilityPostNotification(self,
|
+ NSDictionary *moveInfo = @{
|
||||||
+ NSAccessibilitySelectedTextChangedNotification);
|
+ @"AXTextStateChangeType": @2
|
||||||
|
+ };
|
||||||
|
+ NSAccessibilityPostNotificationWithUserInfo(
|
||||||
|
+ self,
|
||||||
|
+ NSAccessibilitySelectedTextChangedNotification,
|
||||||
|
+ moveInfo);
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
@@ -809,12 +776,10 @@ MRC compatible.
|
|||||||
+#endif /* NS_IMPL_COCOA */
|
+#endif /* NS_IMPL_COCOA */
|
||||||
+
|
+
|
||||||
+
|
+
|
||||||
+/* ==========================================================================
|
/* ==========================================================================
|
||||||
+
|
|
||||||
EmacsView implementation
|
|
||||||
|
|
||||||
========================================================================== */
|
EmacsView implementation
|
||||||
@@ -6889,6 +7610,7 @@
|
@@ -6889,6 +7571,7 @@ - (void)dealloc
|
||||||
[layer release];
|
[layer release];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -822,7 +787,7 @@ MRC compatible.
|
|||||||
[[self menu] release];
|
[[self menu] release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
@@ -8237,6 +8959,18 @@
|
@@ -8237,6 +8920,18 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||||
XSETFRAME (event.frame_or_window, emacsframe);
|
XSETFRAME (event.frame_or_window, emacsframe);
|
||||||
kbd_buffer_store_event (&event);
|
kbd_buffer_store_event (&event);
|
||||||
ns_send_appdefined (-1); // Kick main loop
|
ns_send_appdefined (-1); // Kick main loop
|
||||||
@@ -841,7 +806,7 @@ MRC compatible.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -9474,6 +10208,391 @@
|
@@ -9474,6 +10169,213 @@ - (int) fullscreenState
|
||||||
return fs_state;
|
return fs_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,6 +841,7 @@ MRC compatible.
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ [elements addObject:elem];
|
+ [elements addObject:elem];
|
||||||
|
+ [elem release]; /* array retains; balance the alloc */
|
||||||
+ }
|
+ }
|
||||||
+ else
|
+ else
|
||||||
+ {
|
+ {
|
||||||
@@ -1016,185 +982,6 @@ MRC compatible.
|
|||||||
+ return [self accessibilityBoundsForRange:range];
|
+ return [self accessibilityBoundsForRange:range];
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+/* ---- Text content methods (for Zoom and legacy AT) ---- */
|
|
||||||
+
|
|
||||||
+- (id)accessibilityValue
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return @"";
|
|
||||||
+
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return @"";
|
|
||||||
+
|
|
||||||
+ ptrdiff_t start_byte = BUF_BEGV_BYTE (curbuf);
|
|
||||||
+ ptrdiff_t byte_range = BUF_ZV_BYTE (curbuf) - start_byte;
|
|
||||||
+ ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
|
|
||||||
+
|
|
||||||
+ if (range > 10000)
|
|
||||||
+ {
|
|
||||||
+ range = 10000;
|
|
||||||
+ ptrdiff_t end_byte = buf_charpos_to_bytepos (curbuf,
|
|
||||||
+ BUF_BEGV (curbuf) + range);
|
|
||||||
+ byte_range = end_byte - start_byte;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ Lisp_Object str;
|
|
||||||
+ if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
|
|
||||||
+ str = make_uninit_multibyte_string (range, byte_range);
|
|
||||||
+ else
|
|
||||||
+ str = make_uninit_string (range);
|
|
||||||
+ memcpy (SDATA (str), BYTE_POS_ADDR (start_byte), byte_range);
|
|
||||||
+
|
|
||||||
+ return [NSString stringWithLispString:str];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSRange)accessibilitySelectedTextRange
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+
|
|
||||||
+ ptrdiff_t pt = BUF_PT (curbuf) - BUF_BEGV (curbuf);
|
|
||||||
+ return NSMakeRange ((NSUInteger) pt, 0);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSString *)accessibilityStringForRange:(NSRange)nsrange
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return @"";
|
|
||||||
+
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return @"";
|
|
||||||
+
|
|
||||||
+ ptrdiff_t start = BUF_BEGV (curbuf) + (ptrdiff_t) nsrange.location;
|
|
||||||
+ ptrdiff_t end = start + (ptrdiff_t) nsrange.length;
|
|
||||||
+ ptrdiff_t buf_end = BUF_ZV (curbuf);
|
|
||||||
+
|
|
||||||
+ if (start < BUF_BEGV (curbuf)) start = BUF_BEGV (curbuf);
|
|
||||||
+ if (end > buf_end) end = buf_end;
|
|
||||||
+ if (start >= end) return @"";
|
|
||||||
+
|
|
||||||
+ ptrdiff_t start_byte = buf_charpos_to_bytepos (curbuf, start);
|
|
||||||
+ ptrdiff_t end_byte = buf_charpos_to_bytepos (curbuf, end);
|
|
||||||
+ ptrdiff_t char_range = end - start;
|
|
||||||
+ ptrdiff_t brange = end_byte - start_byte;
|
|
||||||
+
|
|
||||||
+ Lisp_Object str;
|
|
||||||
+ if (! NILP (BVAR (curbuf, enable_multibyte_characters)))
|
|
||||||
+ str = make_uninit_multibyte_string (char_range, brange);
|
|
||||||
+ else
|
|
||||||
+ str = make_uninit_string (char_range);
|
|
||||||
+ memcpy (SDATA (str), BUF_BYTE_ADDRESS (curbuf, start_byte), brange);
|
|
||||||
+
|
|
||||||
+ return [NSString stringWithLispString:str];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSInteger)accessibilityNumberOfCharacters
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return 0;
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return 0;
|
|
||||||
+ ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
|
|
||||||
+ return (NSInteger) MIN (range, 10000);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSString *)accessibilitySelectedText
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return @"";
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf || NILP (BVAR (curbuf, mark_active)))
|
|
||||||
+ return @"";
|
|
||||||
+ return @"";
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSInteger)accessibilityInsertionPointLineNumber
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return 0;
|
|
||||||
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
|
||||||
+ if (!w)
|
|
||||||
+ return 0;
|
|
||||||
+ return (NSInteger) (w->cursor.vpos);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSRange)accessibilityVisibleCharacterRange
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ struct buffer *curbuf
|
|
||||||
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
|
|
||||||
+ return NSMakeRange (0, (NSUInteger) MIN (range, 10000));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSAttributedString *)accessibilityAttributedStringForRange:(NSRange)range
|
|
||||||
+{
|
|
||||||
+ NSString *str = [self accessibilityStringForRange:range];
|
|
||||||
+ return [[[NSAttributedString alloc] initWithString:str] autorelease];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSInteger)accessibilityLineForIndex:(NSInteger)index
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return 0;
|
|
||||||
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
|
||||||
+ if (!w || !w->current_matrix)
|
|
||||||
+ return 0;
|
|
||||||
+ struct buffer *curbuf = XBUFFER (w->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return 0;
|
|
||||||
+ ptrdiff_t charpos = BUF_BEGV (curbuf) + (ptrdiff_t) index;
|
|
||||||
+ struct glyph_matrix *matrix = w->current_matrix;
|
|
||||||
+ for (int i = 0; i < matrix->nrows; i++)
|
|
||||||
+ {
|
|
||||||
+ struct glyph_row *row = matrix->rows + i;
|
|
||||||
+ if (!row->enabled_p)
|
|
||||||
+ continue;
|
|
||||||
+ if (MATRIX_ROW_START_CHARPOS (row) <= charpos
|
|
||||||
+ && charpos < MATRIX_ROW_END_CHARPOS (row))
|
|
||||||
+ return (NSInteger) i;
|
|
||||||
+ }
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+- (NSRange)accessibilityRangeForLine:(NSInteger)line
|
|
||||||
+{
|
|
||||||
+ if (!emacsframe)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
|
||||||
+ if (!w || !w->current_matrix)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ struct buffer *curbuf = XBUFFER (w->contents);
|
|
||||||
+ if (!curbuf)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ struct glyph_matrix *matrix = w->current_matrix;
|
|
||||||
+ if (line < 0 || line >= matrix->nrows)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ struct glyph_row *row = matrix->rows + line;
|
|
||||||
+ if (!row->enabled_p)
|
|
||||||
+ return NSMakeRange (0, 0);
|
|
||||||
+ ptrdiff_t start = MATRIX_ROW_START_CHARPOS (row) - BUF_BEGV (curbuf);
|
|
||||||
+ ptrdiff_t end = MATRIX_ROW_END_CHARPOS (row) - BUF_BEGV (curbuf);
|
|
||||||
+ if (start < 0) start = 0;
|
|
||||||
+ if (end < start) end = start;
|
|
||||||
+ return NSMakeRange ((NSUInteger) start, (NSUInteger) (end - start));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* ---- Legacy parameterized attribute APIs (Zoom uses these) ---- */
|
+/* ---- Legacy parameterized attribute APIs (Zoom uses these) ---- */
|
||||||
+
|
+
|
||||||
+- (NSArray *)accessibilityParameterizedAttributeNames
|
+- (NSArray *)accessibilityParameterizedAttributeNames
|
||||||
@@ -1233,7 +1020,7 @@ MRC compatible.
|
|||||||
@end /* EmacsView */
|
@end /* EmacsView */
|
||||||
|
|
||||||
|
|
||||||
@@ -9941,6 +11060,14 @@
|
@@ -9941,6 +10843,14 @@ - (id)accessibilityAttributeValue:(NSString *)attribute
|
||||||
|
|
||||||
return [super accessibilityAttributeValue:attribute];
|
return [super accessibilityAttributeValue:attribute];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user