patches: fix C-n/C-p VoiceOver regression - exclude isCtrlNP from re-anchor
When Emacs moves the cursor (emacsMovedCursor=YES), we post
FocusedUIElementChanged on the NSWindow to re-anchor VoiceOver's
browse cursor. For C-n/C-p this notification races with
AXSelectedTextChanged(granularity=line) and causes VoiceOver to
drop the line-read speech.
Arrow key movement works because VoiceOver intercepts those as AX
selection changes (setAccessibilitySelectedTextRange:), making
voiceoverSetPoint=YES and emacsMovedCursor=NO, so no
FocusedUIElementChanged is posted.
Fix: skip FocusedUIElementChanged for sequential C-n/C-p moves
(isCtrlNP). AXSelectedTextChanged with direction=next/previous +
granularity=line is sufficient for VoiceOver to read the new line.
FocusedUIElementChanged is only needed for discontiguous jumps
(]], M-<, isearch, xref etc.) where VoiceOver must re-anchor.
Also merge duplicate comment blocks and fix two compile errors
from a64d24c that Martin caught during testing.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
From 7c042d446bddee16a83e4cd8f0050e24e262ef77 Mon Sep 17 00:00:00 2001
|
||||
From d4cda4bda0bee73c14946f20322975edd1580d46 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Sukany <martin@sukany.cz>
|
||||
Date: Sat, 28 Feb 2026 12:58:11 +0100
|
||||
Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay
|
||||
@@ -23,8 +23,8 @@ com.apple.accessibility.api distributed notification.
|
||||
(accessibilityAttributeValue:forParameter:): New methods.
|
||||
---
|
||||
etc/NEWS | 13 ++
|
||||
src/nsterm.m | 430 +++++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 431 insertions(+), 12 deletions(-)
|
||||
src/nsterm.m | 474 +++++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 475 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/etc/NEWS b/etc/NEWS
|
||||
index 4c149e41d6..7f917f93b2 100644
|
||||
@@ -51,7 +51,7 @@ index 4c149e41d6..7f917f93b2 100644
|
||||
** Re-introduced dictation, lost in Emacs v30 (macOS).
|
||||
We lost macOS dictation in v30 when migrating to NSTextInputClient.
|
||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||
index b460beb00c..95a5b378c1 100644
|
||||
index b460beb00c..7c118045bd 100644
|
||||
--- a/src/nsterm.m
|
||||
+++ b/src/nsterm.m
|
||||
@@ -1275,7 +1275,7 @@ If a completion candidate is selected (overlay or child frame),
|
||||
@@ -165,7 +165,58 @@ index b460beb00c..95a5b378c1 100644
|
||||
/* ===================================================================
|
||||
EmacsAccessibilityBuffer (Notifications) — AX event dispatch
|
||||
|
||||
@@ -9347,7 +9396,6 @@ - (NSRect)accessibilityFrame
|
||||
@@ -9235,6 +9284,50 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
|
||||
granularity = ns_ax_text_selection_granularity_line;
|
||||
}
|
||||
|
||||
+ /* Programmatic jumps that cross a line boundary (]], [[, M-<,
|
||||
+ xref, imenu, …) are discontiguous: the cursor teleported to an
|
||||
+ arbitrary position, not one sequential step forward/backward.
|
||||
+ Reporting AXTextSelectionDirectionDiscontiguous causes VoiceOver
|
||||
+ to re-anchor its rotor browse cursor at the new
|
||||
+ accessibilitySelectedTextRange rather than advancing linearly
|
||||
+ from its previous internal position. */
|
||||
+ if (!isCtrlNP && granularity == ns_ax_text_selection_granularity_line)
|
||||
+ direction = ns_ax_text_selection_direction_discontiguous;
|
||||
+
|
||||
+ /* If Emacs moved the cursor (not VoiceOver), force discontiguous
|
||||
+ so VoiceOver re-anchors its browse cursor to the current
|
||||
+ accessibilitySelectedTextRange. This covers all Emacs-initiated
|
||||
+ moves: editing commands, ELisp, isearch, etc.
|
||||
+ Exception: C-n/C-p (isCtrlNP) already uses next/previous with
|
||||
+ line granularity; those are already sequential and VoiceOver
|
||||
+ handles them correctly. */
|
||||
+ if (emacsMovedCursor && !isCtrlNP)
|
||||
+ direction = ns_ax_text_selection_direction_discontiguous;
|
||||
+
|
||||
+ /* Re-anchor VoiceOver's browse cursor for discontiguous (teleport)
|
||||
+ moves only. For sequential C-n/C-p (isCtrlNP), posting
|
||||
+ FocusedUIElementChanged on the window races with the
|
||||
+ AXSelectedTextChanged(granularity=line) notification and
|
||||
+ causes VoiceOver to drop the line-read speech. Sequential
|
||||
+ moves are already handled correctly by AXSelectedTextChanged
|
||||
+ with direction=next/previous + granularity=line. */
|
||||
+ if (emacsMovedCursor && !isCtrlNP && [self isAccessibilityFocused])
|
||||
+ {
|
||||
+ NSWindow *win = [self.emacsView window];
|
||||
+ if (win)
|
||||
+ ns_ax_post_notification (
|
||||
+ win,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+
|
||||
+ NSDictionary *layoutInfo = @{
|
||||
+ NSAccessibilityUIElementsKey: @[self]
|
||||
+ };
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ self.emacsView,
|
||||
+ NSAccessibilityLayoutChangedNotification,
|
||||
+ layoutInfo);
|
||||
+ }
|
||||
+
|
||||
/* Post notifications for focused and non-focused elements. */
|
||||
if ([self isAccessibilityFocused])
|
||||
[self postFocusedCursorNotification:point
|
||||
@@ -9347,7 +9440,6 @@ - (NSRect)accessibilityFrame
|
||||
@end
|
||||
|
||||
|
||||
@@ -173,7 +224,7 @@ index b460beb00c..95a5b378c1 100644
|
||||
/* ===================================================================
|
||||
EmacsAccessibilityInteractiveSpan --- helpers and implementation
|
||||
=================================================================== */
|
||||
@@ -9682,6 +9730,7 @@ - (void)dealloc
|
||||
@@ -9682,6 +9774,7 @@ - (void)dealloc
|
||||
[layer release];
|
||||
#endif
|
||||
|
||||
@@ -181,7 +232,7 @@ index b460beb00c..95a5b378c1 100644
|
||||
[[self menu] release];
|
||||
[super dealloc];
|
||||
}
|
||||
@@ -11030,6 +11079,32 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||
@@ -11030,6 +11123,32 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||
XSETFRAME (event.frame_or_window, emacsframe);
|
||||
kbd_buffer_store_event (&event);
|
||||
ns_send_appdefined (-1); // Kick main loop
|
||||
@@ -214,7 +265,7 @@ index b460beb00c..95a5b378c1 100644
|
||||
}
|
||||
|
||||
|
||||
@@ -12267,6 +12342,332 @@ - (int) fullscreenState
|
||||
@@ -12267,6 +12386,332 @@ - (int) fullscreenState
|
||||
return fs_state;
|
||||
}
|
||||
|
||||
@@ -547,7 +598,7 @@ index b460beb00c..95a5b378c1 100644
|
||||
@end /* EmacsView */
|
||||
|
||||
|
||||
@@ -14263,12 +14664,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
|
||||
@@ -14263,12 +14708,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
|
||||
ns_use_srgb_colorspace = YES;
|
||||
|
||||
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,
|
||||
|
||||
Reference in New Issue
Block a user