patches: fix completion announcement (CONSP completion--string, focused+unfocused paths)
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
From 7d2c92820f1f7b716f5123e6d371eb051bd99eaa Mon Sep 17 00:00:00 2001
|
From 507eb75584d56337a9f7edd8259c62aa40f3945a Mon Sep 17 00:00:00 2001
|
||||||
From: Martin Sukany <martin@sukany.cz>
|
From: Martin Sukany <martin@sukany.cz>
|
||||||
Date: Fri, 27 Feb 2026 10:47:48 +0100
|
Date: Fri, 27 Feb 2026 11:01:18 +0100
|
||||||
Subject: [PATCH] ns: implement VoiceOver accessibility (AXBoundsForRange, line
|
Subject: [PATCH] ns: implement VoiceOver accessibility (AXBoundsForRange, line
|
||||||
nav, completions, interactive spans)
|
nav, completions, interactive spans)
|
||||||
|
|
||||||
---
|
---
|
||||||
src/nsterm.h | 109 +++
|
src/nsterm.h | 109 ++
|
||||||
src/nsterm.m | 2646 +++++++++++++++++++++++++++++++++++++++++++++++---
|
src/nsterm.m | 2683 +++++++++++++++++++++++++++++++++++++++++++++++---
|
||||||
2 files changed, 2605 insertions(+), 150 deletions(-)
|
2 files changed, 2642 insertions(+), 150 deletions(-)
|
||||||
|
|
||||||
diff --git a/src/nsterm.h b/src/nsterm.h
|
diff --git a/src/nsterm.h b/src/nsterm.h
|
||||||
index 7c1ee4c..6c95673 100644
|
index 7c1ee4c..6c95673 100644
|
||||||
@@ -144,7 +144,7 @@ index 7c1ee4c..6c95673 100644
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||||
index 932d209..603688d 100644
|
index 932d209..76b1ae8 100644
|
||||||
--- a/src/nsterm.m
|
--- a/src/nsterm.m
|
||||||
+++ b/src/nsterm.m
|
+++ b/src/nsterm.m
|
||||||
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
||||||
@@ -205,7 +205,7 @@ index 932d209..603688d 100644
|
|||||||
ns_focus (f, NULL, 0);
|
ns_focus (f, NULL, 0);
|
||||||
|
|
||||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||||
@@ -6849,220 +6886,2206 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
@@ -6849,220 +6886,2243 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
|
|
||||||
@@ -780,6 +780,20 @@ index 932d209..603688d 100644
|
|||||||
-#endif
|
-#endif
|
||||||
+/* Return the Emacs buffer Lisp object for window W, or Qnil. */
|
+/* Return the Emacs buffer Lisp object for window W, or Qnil. */
|
||||||
+static Lisp_Object
|
+static Lisp_Object
|
||||||
|
+/* Extract announcement string from completion--string property value.
|
||||||
|
+ The property can be a plain Lisp string (simple completion) or
|
||||||
|
+ a list ("candidate" "annotation") for annotated completions.
|
||||||
|
+ Returns nil on failure. */
|
||||||
|
+static NSString *
|
||||||
|
+ns_ax_completion_string_from_prop (Lisp_Object cstr)
|
||||||
|
+{
|
||||||
|
+ if (STRINGP (cstr))
|
||||||
|
+ return [NSString stringWithLispString: cstr];
|
||||||
|
+ if (CONSP (cstr) && STRINGP (XCAR (cstr)))
|
||||||
|
+ return [NSString stringWithLispString: XCAR (cstr)];
|
||||||
|
+ return nil;
|
||||||
|
}
|
||||||
|
|
||||||
+ns_ax_window_buffer_object (struct window *w)
|
+ns_ax_window_buffer_object (struct window *w)
|
||||||
+{
|
+{
|
||||||
+ if (!w)
|
+ if (!w)
|
||||||
@@ -787,7 +801,7 @@ index 932d209..603688d 100644
|
|||||||
+ if (!BUFFERP (w->contents))
|
+ if (!BUFFERP (w->contents))
|
||||||
+ return Qnil;
|
+ return Qnil;
|
||||||
+ return w->contents;
|
+ return w->contents;
|
||||||
}
|
+}
|
||||||
|
|
||||||
+/* Compute visible-end charpos for window W.
|
+/* Compute visible-end charpos for window W.
|
||||||
+ Emacs stores it as BUF_Z - window_end_pos. */
|
+ Emacs stores it as BUF_Z - window_end_pos. */
|
||||||
@@ -797,6 +811,9 @@ index 932d209..603688d 100644
|
|||||||
+ return BUF_Z (b) - w->window_end_pos;
|
+ return BUF_Z (b) - w->window_end_pos;
|
||||||
+}
|
+}
|
||||||
|
|
||||||
|
-/*****************************************************************************/
|
||||||
|
-/* Keyboard handling. */
|
||||||
|
-#define NS_KEYLOG 0
|
||||||
+/* Fetch text property PROP at charpos POS in BUF_OBJ. */
|
+/* Fetch text property PROP at charpos POS in BUF_OBJ. */
|
||||||
+static Lisp_Object
|
+static Lisp_Object
|
||||||
+ns_ax_text_prop_at (ptrdiff_t pos, Lisp_Object prop, Lisp_Object buf_obj)
|
+ns_ax_text_prop_at (ptrdiff_t pos, Lisp_Object prop, Lisp_Object buf_obj)
|
||||||
@@ -805,30 +822,27 @@ index 932d209..603688d 100644
|
|||||||
+ return Fplist_get (plist, prop, Qnil);
|
+ return Fplist_get (plist, prop, Qnil);
|
||||||
+}
|
+}
|
||||||
|
|
||||||
-/*****************************************************************************/
|
-- (void)keyDown: (NSEvent *)theEvent
|
||||||
-/* Keyboard handling. */
|
|
||||||
-#define NS_KEYLOG 0
|
|
||||||
+/* Next charpos where PROP changes, capped at LIMIT. */
|
+/* Next charpos where PROP changes, capped at LIMIT. */
|
||||||
+static ptrdiff_t
|
+static ptrdiff_t
|
||||||
+ns_ax_next_prop_change (ptrdiff_t pos, Lisp_Object prop,
|
+ns_ax_next_prop_change (ptrdiff_t pos, Lisp_Object prop,
|
||||||
+ Lisp_Object buf_obj, ptrdiff_t limit)
|
+ Lisp_Object buf_obj, ptrdiff_t limit)
|
||||||
+{
|
{
|
||||||
|
- Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
|
||||||
|
- int code;
|
||||||
|
- unsigned fnKeysym = 0;
|
||||||
+ Lisp_Object result
|
+ Lisp_Object result
|
||||||
+ = Fnext_single_property_change (make_fixnum (pos), prop,
|
+ = Fnext_single_property_change (make_fixnum (pos), prop,
|
||||||
+ buf_obj, make_fixnum (limit));
|
+ buf_obj, make_fixnum (limit));
|
||||||
+ return FIXNUMP (result) ? XFIXNUM (result) : limit;
|
+ return FIXNUMP (result) ? XFIXNUM (result) : limit;
|
||||||
+}
|
+}
|
||||||
|
+
|
||||||
-- (void)keyDown: (NSEvent *)theEvent
|
|
||||||
+/* Build label for span [START, END) in BUF_OBJ.
|
+/* Build label for span [START, END) in BUF_OBJ.
|
||||||
+ Priority: completion--string → buffer text → help-echo. */
|
+ Priority: completion--string → buffer text → help-echo. */
|
||||||
+static NSString *
|
+static NSString *
|
||||||
+ns_ax_get_span_label (ptrdiff_t start, ptrdiff_t end,
|
+ns_ax_get_span_label (ptrdiff_t start, ptrdiff_t end,
|
||||||
+ Lisp_Object buf_obj)
|
+ Lisp_Object buf_obj)
|
||||||
{
|
+{
|
||||||
- Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
|
|
||||||
- int code;
|
|
||||||
- unsigned fnKeysym = 0;
|
|
||||||
+ Lisp_Object cs = ns_ax_text_prop_at (start, intern ("completion--string"),
|
+ Lisp_Object cs = ns_ax_text_prop_at (start, intern ("completion--string"),
|
||||||
+ buf_obj);
|
+ buf_obj);
|
||||||
+ if (STRINGP (cs))
|
+ if (STRINGP (cs))
|
||||||
@@ -1983,31 +1997,52 @@ index 932d209..603688d 100644
|
|||||||
+ && (direction == ns_ax_text_selection_direction_next
|
+ && (direction == ns_ax_text_selection_direction_next
|
||||||
+ || direction == ns_ax_text_selection_direction_previous))
|
+ || direction == ns_ax_text_selection_direction_previous))
|
||||||
+ {
|
+ {
|
||||||
+ NSUInteger point_idx = [self accessibilityIndexForCharpos:point];
|
+ /* In completion-list-mode, prefer the completion--string
|
||||||
+ if (point_idx <= [cachedText length])
|
+ text property over the raw line text. The horizontal
|
||||||
|
+ layout places two candidates per line; reading the whole
|
||||||
|
+ line would announce both at once. */
|
||||||
|
+ NSString *announceText = nil;
|
||||||
|
+
|
||||||
|
+ struct buffer *cb = XBUFFER (w->contents);
|
||||||
|
+ Lisp_Object cs_sym = intern ("completion--string");
|
||||||
|
+ Lisp_Object cstr = Fget_char_property (make_fixnum (point),
|
||||||
|
+ cs_sym, Qnil);
|
||||||
|
+ announceText = ns_ax_completion_string_from_prop (cstr);
|
||||||
|
+
|
||||||
|
+ if (!announceText)
|
||||||
+ {
|
+ {
|
||||||
+ NSInteger lineNum = [self accessibilityLineForIndex:point_idx];
|
+ /* Fallback: read full line (non-completion buffers). */
|
||||||
+ NSRange lineRange = [self accessibilityRangeForLine:lineNum];
|
+ NSUInteger point_idx = [self accessibilityIndexForCharpos:point];
|
||||||
+ if (lineRange.location != NSNotFound
|
+ if (point_idx <= [cachedText length])
|
||||||
+ && lineRange.length > 0
|
|
||||||
+ && lineRange.location + lineRange.length <= [cachedText length])
|
|
||||||
+ {
|
+ {
|
||||||
+ NSString *lineText = [cachedText substringWithRange:lineRange];
|
+ NSInteger lineNum = [self accessibilityLineForIndex:point_idx];
|
||||||
+ lineText = [lineText stringByTrimmingCharactersInSet:
|
+ NSRange lineRange = [self accessibilityRangeForLine:lineNum];
|
||||||
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
+ if (lineRange.location != NSNotFound
|
||||||
+ if ([lineText length] > 0)
|
+ && lineRange.length > 0
|
||||||
|
+ && lineRange.location + lineRange.length <= [cachedText length])
|
||||||
+ {
|
+ {
|
||||||
+ NSDictionary *annInfo = @{
|
+ announceText = [cachedText substringWithRange:lineRange];
|
||||||
+ NSAccessibilityAnnouncementKey: lineText,
|
|
||||||
+ NSAccessibilityPriorityKey: @(NSAccessibilityPriorityHigh)
|
|
||||||
+ };
|
|
||||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
|
||||||
+ NSApp,
|
|
||||||
+ NSAccessibilityAnnouncementRequestedNotification,
|
|
||||||
+ annInfo);
|
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
|
+
|
||||||
|
+ if (announceText)
|
||||||
|
+ {
|
||||||
|
+ announceText = [announceText stringByTrimmingCharactersInSet:
|
||||||
|
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
+ if ([announceText length] > 0)
|
||||||
|
+ {
|
||||||
|
+ NSDictionary *annInfo = @{
|
||||||
|
+ NSAccessibilityAnnouncementKey: announceText,
|
||||||
|
+ NSAccessibilityPriorityKey: @(NSAccessibilityPriorityHigh)
|
||||||
|
+ };
|
||||||
|
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||||
|
+ NSApp,
|
||||||
|
+ NSAccessibilityAnnouncementRequestedNotification,
|
||||||
|
+ annInfo);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ (void) cb;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ /* --- Completions announcement ---
|
+ /* --- Completions announcement ---
|
||||||
@@ -2029,12 +2064,14 @@ index 932d209..603688d 100644
|
|||||||
+ if (b != current_buffer)
|
+ if (b != current_buffer)
|
||||||
+ set_buffer_internal_1 (b);
|
+ set_buffer_internal_1 (b);
|
||||||
+
|
+
|
||||||
+ /* 1) Prefer explicit completion candidate property when present. */
|
+ /* 1) Prefer explicit completion candidate property when present.
|
||||||
|
+ completion--string can be a plain string (simple completion)
|
||||||
|
+ or a list ("candidate" "annotation") for annotated completions.
|
||||||
|
+ In the list case, use car (the completion itself). */
|
||||||
+ Lisp_Object cstr = Fget_char_property (make_fixnum (point),
|
+ Lisp_Object cstr = Fget_char_property (make_fixnum (point),
|
||||||
+ intern ("completion--string"),
|
+ intern ("completion--string"),
|
||||||
+ Qnil);
|
+ Qnil);
|
||||||
+ if (STRINGP (cstr))
|
+ announceText = ns_ax_completion_string_from_prop (cstr);
|
||||||
+ announceText = [NSString stringWithLispString:cstr];
|
|
||||||
+
|
+
|
||||||
+ /* 2) Fallback: announce the mouse-face span at point.
|
+ /* 2) Fallback: announce the mouse-face span at point.
|
||||||
+ completion-list-mode often marks the active candidate this way. */
|
+ completion-list-mode often marks the active candidate this way. */
|
||||||
@@ -2562,7 +2599,7 @@ index 932d209..603688d 100644
|
|||||||
static NSMutableArray *nsEvArray;
|
static NSMutableArray *nsEvArray;
|
||||||
unsigned int flags = [theEvent modifierFlags];
|
unsigned int flags = [theEvent modifierFlags];
|
||||||
|
|
||||||
@@ -8237,6 +10260,28 @@ - (void)windowDidBecomeKey /* for direct calls */
|
@@ -8237,6 +10297,28 @@ - (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
|
||||||
@@ -2591,7 +2628,7 @@ index 932d209..603688d 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -9474,6 +11519,307 @@ - (int) fullscreenState
|
@@ -9474,6 +11556,307 @@ - (int) fullscreenState
|
||||||
return fs_state;
|
return fs_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user