patches: isolate C-n/C-p keypath from arrow navigation
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
From 442f21965862e63ddd0234777918b999b97c8f9c Mon Sep 17 00:00:00 2001
|
||||
From 6a256eb9269e0cfc4d1270ef61c228dd9f9989da Mon Sep 17 00:00:00 2001
|
||||
From: Daneel <daneel@sukany.cz>
|
||||
Date: Thu, 26 Feb 2026 18:36:53 +0100
|
||||
Date: Thu, 26 Feb 2026 18:41:28 +0100
|
||||
Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction
|
||||
fixes
|
||||
|
||||
---
|
||||
nsterm.h | 71 ++
|
||||
nsterm.m | 2168 ++++++++++++++++++++++++++++++++++++++++++++++++++----
|
||||
2 files changed, 2096 insertions(+), 143 deletions(-)
|
||||
nsterm.m | 2201 ++++++++++++++++++++++++++++++++++++++++++++++++++----
|
||||
2 files changed, 2133 insertions(+), 139 deletions(-)
|
||||
|
||||
diff --git a/src/nsterm.h b/src/nsterm.h
|
||||
index 7c1ee4c..22828f2 100644
|
||||
@@ -106,7 +106,7 @@ index 7c1ee4c..22828f2 100644
|
||||
|
||||
|
||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||
index 932d209..b7f0614 100644
|
||||
index 932d209..da40369 100644
|
||||
--- a/src/nsterm.m
|
||||
+++ b/src/nsterm.m
|
||||
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
|
||||
@@ -159,7 +159,7 @@ index 932d209..b7f0614 100644
|
||||
ns_focus (f, NULL, 0);
|
||||
|
||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||
@@ -6849,219 +6885,1743 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
||||
@@ -6849,214 +6885,1779 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
@@ -585,31 +585,43 @@ index 932d209..b7f0614 100644
|
||||
}
|
||||
|
||||
-- (void) noteUserCancelledSelection
|
||||
+extern Lisp_Object last_command_event;
|
||||
+
|
||||
+static bool
|
||||
+ns_ax_command_is_basic_line_move (void)
|
||||
+ns_ax_event_is_ctrl_n_or_p (int *which)
|
||||
{
|
||||
- font_panel_active = NO;
|
||||
+ if (!SYMBOLP (real_this_command))
|
||||
+ return false;
|
||||
+ Lisp_Object ev = last_command_event;
|
||||
+ if (CONSP (ev))
|
||||
+ ev = EVENT_HEAD (ev);
|
||||
|
||||
- if (font_panel_result)
|
||||
- [font_panel_result release];
|
||||
- font_panel_result = nil;
|
||||
-
|
||||
+ if (!FIXNUMP (ev))
|
||||
+ return false;
|
||||
|
||||
- [NSApp stop: self];
|
||||
+ Lisp_Object next = intern ("next-line");
|
||||
+ Lisp_Object prev = intern ("previous-line");
|
||||
+ return EQ (real_this_command, next) || EQ (real_this_command, prev);
|
||||
+ EMACS_INT c = XFIXNUM (ev);
|
||||
+ if (c == '\C-n')
|
||||
+ {
|
||||
+ if (which)
|
||||
+ *which = 1;
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (c == '\C-p')
|
||||
+ {
|
||||
+ if (which)
|
||||
+ *which = -1;
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
}
|
||||
-#endif
|
||||
|
||||
-- (Lisp_Object) showFontPanel
|
||||
+static NSString *
|
||||
+ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
+ struct buffer *b,
|
||||
+ ptrdiff_t start,
|
||||
+ ptrdiff_t end,
|
||||
+ NSString *cachedText)
|
||||
+static bool
|
||||
+ns_ax_command_is_basic_line_move (void)
|
||||
{
|
||||
- id fm = [NSFontManager sharedFontManager];
|
||||
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
|
||||
@@ -619,14 +631,18 @@ index 932d209..b7f0614 100644
|
||||
- NSView *buttons;
|
||||
- BOOL canceled;
|
||||
-#endif
|
||||
-
|
||||
+ if (!SYMBOLP (real_this_command))
|
||||
+ return false;
|
||||
|
||||
-#ifdef NS_IMPL_GNUSTEP
|
||||
- nsfont = ((struct nsfont_info *) font)->nsfont;
|
||||
-#else
|
||||
- nsfont = (NSFont *) macfont_get_nsctfont (font);
|
||||
-#endif
|
||||
+ if (!elem || !b || !cachedText || end <= start)
|
||||
+ return nil;
|
||||
+ Lisp_Object next = intern ("next-line");
|
||||
+ Lisp_Object prev = intern ("previous-line");
|
||||
+ return EQ (real_this_command, next) || EQ (real_this_command, prev);
|
||||
+}
|
||||
|
||||
-#ifdef NS_IMPL_COCOA
|
||||
- buttons
|
||||
@@ -636,13 +652,25 @@ index 932d209..b7f0614 100644
|
||||
- [[fm fontPanel: YES] setAccessoryView: buttons];
|
||||
- [buttons release];
|
||||
-#endif
|
||||
+static NSString *
|
||||
+ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
+ struct buffer *b,
|
||||
+ ptrdiff_t start,
|
||||
+ ptrdiff_t end,
|
||||
+ NSString *cachedText)
|
||||
+{
|
||||
+ if (!elem || !b || !cachedText || end <= start)
|
||||
+ return nil;
|
||||
|
||||
- [fm setSelectedFont: nsfont isMultiple: NO];
|
||||
- [fm orderFrontFontPanel: NSApp];
|
||||
+ NSString *text = nil;
|
||||
+ struct buffer *oldb = current_buffer;
|
||||
+ if (b != current_buffer)
|
||||
+ set_buffer_internal_1 (b);
|
||||
|
||||
- [fm setSelectedFont: nsfont isMultiple: NO];
|
||||
- [fm orderFrontFontPanel: NSApp];
|
||||
- font_panel_active = YES;
|
||||
- timeout = make_timespec (0, 100000000);
|
||||
+ /* Prefer canonical completion candidate string from text property. */
|
||||
+ ptrdiff_t probes[2] = { start, end - 1 };
|
||||
+ for (int i = 0; i < 2 && !text; i++)
|
||||
@@ -655,16 +683,6 @@ index 932d209..b7f0614 100644
|
||||
+ text = [NSString stringWithLispString:cstr];
|
||||
+ }
|
||||
|
||||
- font_panel_active = YES;
|
||||
- timeout = make_timespec (0, 100000000);
|
||||
+ if (!text)
|
||||
+ {
|
||||
+ NSUInteger ax_s = [elem accessibilityIndexForCharpos:start];
|
||||
+ NSUInteger ax_e = [elem accessibilityIndexForCharpos:end];
|
||||
+ if (ax_e > ax_s && ax_e <= [cachedText length])
|
||||
+ text = [cachedText substringWithRange:NSMakeRange (ax_s, ax_e - ax_s)];
|
||||
+ }
|
||||
|
||||
- block_input ();
|
||||
- while (font_panel_active
|
||||
-#ifdef NS_IMPL_COCOA
|
||||
@@ -675,11 +693,23 @@ index 932d209..b7f0614 100644
|
||||
- )
|
||||
- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES);
|
||||
- unblock_input ();
|
||||
+ if (b != oldb)
|
||||
+ set_buffer_internal_1 (oldb);
|
||||
+ if (!text)
|
||||
+ {
|
||||
+ NSUInteger ax_s = [elem accessibilityIndexForCharpos:start];
|
||||
+ NSUInteger ax_e = [elem accessibilityIndexForCharpos:end];
|
||||
+ if (ax_e > ax_s && ax_e <= [cachedText length])
|
||||
+ text = [cachedText substringWithRange:NSMakeRange (ax_s, ax_e - ax_s)];
|
||||
+ }
|
||||
|
||||
- if (font_panel_result)
|
||||
- [font_panel_result autorelease];
|
||||
+ if (b != oldb)
|
||||
+ set_buffer_internal_1 (oldb);
|
||||
|
||||
-#ifdef NS_IMPL_COCOA
|
||||
- if (!canceled)
|
||||
- font_panel_result = nil;
|
||||
-#endif
|
||||
+ if (text)
|
||||
+ {
|
||||
+ text = [text stringByTrimmingCharactersInSet:
|
||||
@@ -688,30 +718,26 @@ index 932d209..b7f0614 100644
|
||||
+ text = nil;
|
||||
+ }
|
||||
|
||||
-#ifdef NS_IMPL_COCOA
|
||||
- if (!canceled)
|
||||
- result = font_panel_result;
|
||||
- font_panel_result = nil;
|
||||
-#endif
|
||||
+ return text;
|
||||
+}
|
||||
|
||||
- result = font_panel_result;
|
||||
- font_panel_result = nil;
|
||||
|
||||
- [[fm fontPanel: YES] setIsVisible: NO];
|
||||
- font_panel_active = NO;
|
||||
+@implementation EmacsAccessibilityElement
|
||||
|
||||
- if (result)
|
||||
- return ns_font_desc_to_font_spec ([result fontDescriptor],
|
||||
- result);
|
||||
+@implementation EmacsAccessibilityElement
|
||||
|
||||
- return Qnil;
|
||||
+- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh
|
||||
+{
|
||||
+ EmacsView *view = self.emacsView;
|
||||
+ if (!view || ![view window])
|
||||
+ return NSZeroRect;
|
||||
|
||||
- return Qnil;
|
||||
+
|
||||
+ NSRect r = NSMakeRect (x, y, ew, eh);
|
||||
+ NSRect winRect = [view convertRect:r toView:nil];
|
||||
+ return [[view window] convertRectToScreen:winRect];
|
||||
@@ -736,25 +762,20 @@ index 932d209..b7f0614 100644
|
||||
+ return NSAccessibilityUnignoredAncestor (self.emacsView);
|
||||
}
|
||||
-- (void)resetCursorRects
|
||||
+
|
||||
+- (id)accessibilityWindow
|
||||
{
|
||||
-{
|
||||
- NSRect visible = [self visibleRect];
|
||||
- NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
|
||||
- NSTRACE ("[EmacsView resetCursorRects]");
|
||||
+ return [self.emacsView window];
|
||||
+}
|
||||
|
||||
-
|
||||
- if (currentCursor == nil)
|
||||
- currentCursor = [NSCursor arrowCursor];
|
||||
+- (id)accessibilityTopLevelUIElement
|
||||
+{
|
||||
+ return [self.emacsView window];
|
||||
+}
|
||||
|
||||
- if (!NSIsEmptyRect (visible))
|
||||
- [self addCursorRect: visible cursor: currentCursor];
|
||||
+@end
|
||||
+- (id)accessibilityWindow
|
||||
+{
|
||||
+ return [self.emacsView window];
|
||||
+}
|
||||
|
||||
-#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
|
||||
-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
|
||||
@@ -762,8 +783,17 @@ index 932d209..b7f0614 100644
|
||||
-#endif
|
||||
- [currentCursor setOnMouseEntered: YES];
|
||||
-#endif
|
||||
-}
|
||||
+- (id)accessibilityTopLevelUIElement
|
||||
+{
|
||||
+ return [self.emacsView window];
|
||||
}
|
||||
|
||||
+@end
|
||||
|
||||
|
||||
-/*****************************************************************************/
|
||||
-/* Keyboard handling. */
|
||||
-#define NS_KEYLOG 0
|
||||
+@implementation EmacsAccessibilityBuffer
|
||||
+@synthesize cachedText;
|
||||
+@synthesize cachedTextModiff;
|
||||
@@ -775,7 +805,7 @@ index 932d209..b7f0614 100644
|
||||
+@synthesize cachedCompletionOverlayStart;
|
||||
+@synthesize cachedCompletionOverlayEnd;
|
||||
+@synthesize cachedCompletionPoint;
|
||||
|
||||
+
|
||||
+- (void)dealloc
|
||||
+{
|
||||
+ [cachedText release];
|
||||
@@ -784,17 +814,11 @@ index 932d209..b7f0614 100644
|
||||
+ xfree (visibleRuns);
|
||||
+ [super dealloc];
|
||||
+}
|
||||
|
||||
-/*****************************************************************************/
|
||||
-/* Keyboard handling. */
|
||||
-#define NS_KEYLOG 0
|
||||
+
|
||||
+/* ---- Text cache ---- */
|
||||
|
||||
-- (void)keyDown: (NSEvent *)theEvent
|
||||
+
|
||||
+- (void)invalidateTextCache
|
||||
{
|
||||
- Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
|
||||
- int code;
|
||||
+{
|
||||
+ [cachedText release];
|
||||
+ cachedText = nil;
|
||||
+ if (visibleRuns)
|
||||
@@ -1417,6 +1441,9 @@ index 932d209..b7f0614 100644
|
||||
+ else if (point < oldPoint)
|
||||
+ direction = ns_ax_text_selection_direction_previous;
|
||||
+
|
||||
+ int ctrlNP = 0;
|
||||
+ bool isCtrlNP = ns_ax_event_is_ctrl_n_or_p (&ctrlNP);
|
||||
+
|
||||
+ /* Compute granularity from movement distance.
|
||||
+ Prefer robust line-range comparison for vertical movement,
|
||||
+ otherwise single char (1) or unknown (0). */
|
||||
@@ -1447,6 +1474,16 @@ index 932d209..b7f0614 100644
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Force line semantics for explicit C-n/C-p keystrokes.
|
||||
+ This isolates the key-path difference from arrow-down/up. */
|
||||
+ if (isCtrlNP)
|
||||
+ {
|
||||
+ direction = (ctrlNP > 0
|
||||
+ ? ns_ax_text_selection_direction_next
|
||||
+ : ns_ax_text_selection_direction_previous);
|
||||
+ granularity = ns_ax_text_selection_granularity_line;
|
||||
+ }
|
||||
+
|
||||
+ NSDictionary *moveInfo = @{
|
||||
+ @"AXTextStateChangeType": @(ns_ax_text_state_change_selection_move),
|
||||
+ @"AXTextSelectionDirection": @(direction),
|
||||
@@ -1464,7 +1501,7 @@ index 932d209..b7f0614 100644
|
||||
+ line-motion path so VoiceOver tracks the Emacs point reliably. */
|
||||
+ if ([self isAccessibilityFocused]
|
||||
+ && cachedText
|
||||
+ && ns_ax_command_is_basic_line_move ()
|
||||
+ && (isCtrlNP || ns_ax_command_is_basic_line_move ())
|
||||
+ && (direction == ns_ax_text_selection_direction_next
|
||||
+ || direction == ns_ax_text_selection_direction_previous))
|
||||
+ {
|
||||
@@ -2038,15 +2075,10 @@ index 932d209..b7f0614 100644
|
||||
+/*****************************************************************************/
|
||||
+/* Keyboard handling. */
|
||||
+#define NS_KEYLOG 0
|
||||
+
|
||||
+- (void)keyDown: (NSEvent *)theEvent
|
||||
+{
|
||||
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
|
||||
+ int code;
|
||||
unsigned fnKeysym = 0;
|
||||
static NSMutableArray *nsEvArray;
|
||||
unsigned int flags = [theEvent modifierFlags];
|
||||
@@ -8237,6 +9797,28 @@ ns_in_echo_area (void)
|
||||
|
||||
- (void)keyDown: (NSEvent *)theEvent
|
||||
{
|
||||
@@ -8237,6 +9838,28 @@ ns_in_echo_area (void)
|
||||
XSETFRAME (event.frame_or_window, emacsframe);
|
||||
kbd_buffer_store_event (&event);
|
||||
ns_send_appdefined (-1); // Kick main loop
|
||||
@@ -2075,7 +2107,7 @@ index 932d209..b7f0614 100644
|
||||
}
|
||||
|
||||
|
||||
@@ -9474,6 +11056,298 @@ ns_in_echo_area (void)
|
||||
@@ -9474,6 +11097,298 @@ ns_in_echo_area (void)
|
||||
return fs_state;
|
||||
}
|
||||
|
||||
@@ -2374,7 +2406,7 @@ index 932d209..b7f0614 100644
|
||||
@end /* EmacsView */
|
||||
|
||||
|
||||
@@ -9941,6 +11815,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
|
||||
@@ -9941,6 +11856,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user