patches: completion candidate-aware VoiceOver announce fix

This commit is contained in:
2026-02-26 18:25:04 +01:00
parent 74b9691856
commit 1b42d553a2

View File

@@ -1,19 +1,19 @@
From 5fb855925912142401db0c732fff2014d21c3362 Mon Sep 17 00:00:00 2001 From f88649b2e09520d4c6e7ebc041e9e1a6e7ed616b Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Thu, 26 Feb 2026 18:06:10 +0100 Date: Thu, 26 Feb 2026 18:24:58 +0100
Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction
fixes fixes
--- ---
nsterm.h | 70 ++ nsterm.h | 71 ++
nsterm.m | 2090 +++++++++++++++++++++++++++++++++++++++++++++++++----- nsterm.m | 2126 ++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 1997 insertions(+), 163 deletions(-) 2 files changed, 2051 insertions(+), 146 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..97da979 100644 index 7c1ee4c..22828f2 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -453,6 +453,61 @@ enum ns_return_frame_mode @@ -453,6 +453,62 @@ enum ns_return_frame_mode
@end @end
@@ -60,6 +60,7 @@ index 7c1ee4c..97da979 100644
+@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; +@property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
+@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayStart; +@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayStart;
+@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayEnd; +@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayEnd;
+@property (nonatomic, assign) ptrdiff_t cachedCompletionPoint;
+- (void)invalidateTextCache; +- (void)invalidateTextCache;
+- (void)postAccessibilityNotificationsForFrame:(struct frame *)f; +- (void)postAccessibilityNotificationsForFrame:(struct frame *)f;
+- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx; +- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx;
@@ -75,7 +76,7 @@ index 7c1ee4c..97da979 100644
/* ========================================================================== /* ==========================================================================
The main Emacs view The main Emacs view
@@ -471,6 +526,14 @@ enum ns_return_frame_mode @@ -471,6 +527,14 @@ 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;
@@ -90,7 +91,7 @@ index 7c1ee4c..97da979 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -528,6 +591,13 @@ enum ns_return_frame_mode @@ -528,6 +592,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen; - (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen; - (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey; - (void)windowDidBecomeKey;
@@ -105,7 +106,7 @@ index 7c1ee4c..97da979 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..e7af9a3 100644 index 932d209..add827f 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
@@ -158,7 +159,7 @@ index 932d209..e7af9a3 100644
ns_focus (f, NULL, 0); ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6849,240 +6885,1646 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) @@ -6849,220 +6885,1696 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
/* ========================================================================== /* ==========================================================================
@@ -173,93 +174,57 @@ index 932d209..e7af9a3 100644
+/* ---- Helper: extract buffer text for accessibility ---- */ +/* ---- Helper: extract buffer text for accessibility ---- */
-- (void)windowDidEndLiveResize:(NSNotification *)notification -- (void)windowDidEndLiveResize:(NSNotification *)notification
-{
- [self updateFramePosition];
-}
+/* Maximum characters exposed via accessibilityValue. */ +/* Maximum characters exposed via accessibilityValue. */
+#define NS_AX_TEXT_CAP 100000 +#define NS_AX_TEXT_CAP 100000
+
-/* Needed to inform when window closed from lisp. */
-- (void) setWindowClosing: (BOOL)closing
-{
- NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
+/* Build accessibility text for window W, skipping invisible text. +/* Build accessibility text for window W, skipping invisible text.
+ Populates *OUT_START with the buffer start charpos. + Populates *OUT_START with the buffer start charpos.
+ Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS + Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS
+ with the count. Caller must free *OUT_RUNS with xfree(). */ + with the count. Caller must free *OUT_RUNS with xfree(). */
+
- windowClosing = closing;
-}
+static NSString * +static NSString *
+ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start, +ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start,
+ ns_ax_visible_run **out_runs, NSUInteger *out_nruns) + ns_ax_visible_run **out_runs, NSUInteger *out_nruns)
+{ {
- [self updateFramePosition];
+ *out_runs = NULL; + *out_runs = NULL;
+ *out_nruns = 0; + *out_nruns = 0;
+
+ if (!w || !WINDOW_LEAF_P (w)) + if (!w || !WINDOW_LEAF_P (w))
+ { + {
+ *out_start = 0; + *out_start = 0;
+ return @""; + return @"";
+ } + }
+
-- (void)dealloc
-{
- NSTRACE ("[EmacsView dealloc]");
+ struct buffer *b = XBUFFER (w->contents); + struct buffer *b = XBUFFER (w->contents);
+ if (!b) + if (!b)
+ { + {
+ *out_start = 0; + *out_start = 0;
+ return @""; + return @"";
+ } + }
+
- /* Clear the view resize notification. */
- [[NSNotificationCenter defaultCenter]
- removeObserver:self
- name:NSViewFrameDidChangeNotification
- object:nil];
+ ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t begv = BUF_BEGV (b);
+ ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t zv = BUF_ZV (b);
+
- if (fs_state == FULLSCREEN_BOTH)
- [nonfs_window release];
+ *out_start = begv; + *out_start = begv;
+
-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
- /* Release layer and menu */
- EmacsLayer *layer = (EmacsLayer *)[self layer];
- [layer release];
-#endif
+ if (zv <= begv) + if (zv <= begv)
+ return @""; + return @"";
+
- [[self menu] release];
- [super dealloc];
-}
+ struct buffer *oldb = current_buffer; + struct buffer *oldb = current_buffer;
+ if (b != current_buffer) + if (b != current_buffer)
+ set_buffer_internal_1 (b); + set_buffer_internal_1 (b);
+
+ /* First pass: count visible runs to allocate the mapping array. */ + /* First pass: count visible runs to allocate the mapping array. */
+ NSUInteger run_capacity = 64; + NSUInteger run_capacity = 64;
+ ns_ax_visible_run *runs = xmalloc (run_capacity + ns_ax_visible_run *runs = xmalloc (run_capacity
+ * sizeof (ns_ax_visible_run)); + * sizeof (ns_ax_visible_run));
+ NSUInteger nruns = 0; + NSUInteger nruns = 0;
+ NSUInteger ax_offset = 0; + NSUInteger ax_offset = 0;
+
-/* Called on font panel selection. */
-- (void) changeFont: (id) sender
-{
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
- NSFont *nsfont;
+ NSMutableString *result = [NSMutableString string]; + NSMutableString *result = [NSMutableString string];
+ ptrdiff_t pos = begv; + ptrdiff_t pos = begv;
+
-#ifdef NS_IMPL_GNUSTEP
- nsfont = ((struct nsfont_info *) font)->nsfont;
-#else
- nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif
+ while (pos < zv) + while (pos < zv)
+ { + {
+ /* Check invisible property (text properties + overlays). */ + /* Check invisible property (text properties + overlays). */
@@ -279,16 +244,12 @@ index 932d209..e7af9a3 100644
+ pos = FIXNUMP (next) ? XFIXNUM (next) : zv; + pos = FIXNUMP (next) ? XFIXNUM (next) : zv;
+ continue; + continue;
+ } + }
+
- if (!font_panel_active)
- return;
+ /* Find end of this visible run: where invisible property changes. */ + /* Find end of this visible run: where invisible property changes. */
+ Lisp_Object next = Fnext_single_char_property_change ( + Lisp_Object next = Fnext_single_char_property_change (
+ make_fixnum (pos), Qinvisible, Qnil, make_fixnum (zv)); + make_fixnum (pos), Qinvisible, Qnil, make_fixnum (zv));
+ ptrdiff_t run_end = FIXNUMP (next) ? XFIXNUM (next) : zv; + ptrdiff_t run_end = FIXNUMP (next) ? XFIXNUM (next) : zv;
+
- if (font_panel_result)
- [font_panel_result release];
+ /* Cap total text at NS_AX_TEXT_CAP. */ + /* Cap total text at NS_AX_TEXT_CAP. */
+ ptrdiff_t run_len = run_end - pos; + ptrdiff_t run_len = run_end - pos;
+ if (ax_offset + (NSUInteger) run_len > NS_AX_TEXT_CAP) + if (ax_offset + (NSUInteger) run_len > NS_AX_TEXT_CAP)
@@ -319,54 +280,41 @@ index 932d209..e7af9a3 100644
+ runs[nruns].ax_start = ax_offset; + runs[nruns].ax_start = ax_offset;
+ runs[nruns].ax_length = ns_len; + runs[nruns].ax_length = ns_len;
+ nruns++; + nruns++;
+
- font_panel_result = (NSFont *) [sender convertFont: nsfont];
+ ax_offset += ns_len; + ax_offset += ns_len;
+ pos = run_end; + pos = run_end;
+ } + }
+
- if (font_panel_result)
- [font_panel_result retain];
+ if (b != oldb) + if (b != oldb)
+ set_buffer_internal_1 (oldb); + set_buffer_internal_1 (oldb);
+
-#ifndef NS_IMPL_COCOA
- font_panel_active = NO;
- [NSApp stop: self];
-#endif
+ *out_runs = runs; + *out_runs = runs;
+ *out_nruns = nruns; + *out_nruns = nruns;
+ return result; + return result;
} }
-#ifdef NS_IMPL_COCOA -/* Needed to inform when window closed from lisp. */
-- (void) noteUserSelectedFont -- (void) setWindowClosing: (BOOL)closing
+ +
+/* ---- Helper: extract mode line text from glyph rows ---- */ +/* ---- Helper: extract mode line text from glyph rows ---- */
+ +
+static NSString * +static NSString *
+ns_ax_mode_line_text (struct window *w) +ns_ax_mode_line_text (struct window *w)
{ {
- font_panel_active = NO; - NSTRACE ("[EmacsView setWindowClosing:%d]", closing);
+ if (!w || !w->current_matrix) + if (!w || !w->current_matrix)
+ return @""; + return @"";
- /* If no font was previously selected, use the currently selected - windowClosing = closing;
- font. */
+ struct glyph_matrix *matrix = w->current_matrix; + struct glyph_matrix *matrix = w->current_matrix;
+ NSMutableString *text = [NSMutableString string]; + NSMutableString *text = [NSMutableString string];
+
- if (!font_panel_result && FRAME_FONT (emacsframe))
+ for (int i = 0; i < matrix->nrows; i++) + for (int i = 0; i < matrix->nrows; i++)
{ + {
- font_panel_result
- = macfont_get_nsctfont (FRAME_FONT (emacsframe));
+ struct glyph_row *row = matrix->rows + i; + struct glyph_row *row = matrix->rows + i;
+ if (!row->enabled_p || !row->mode_line_p) + if (!row->enabled_p || !row->mode_line_p)
+ continue; + continue;
+
- if (font_panel_result)
- [font_panel_result retain];
+ struct glyph *g = row->glyphs[TEXT_AREA]; + struct glyph *g = row->glyphs[TEXT_AREA];
+ struct glyph *end = g + row->used[TEXT_AREA]; + struct glyph *end = g + row->used[TEXT_AREA];
+ for (; g < end; g++) + for (; g < end; g++)
@@ -378,36 +326,42 @@ index 932d209..e7af9a3 100644
+ length:1]]; + length:1]];
+ } + }
+ } + }
} + }
-
- [NSApp stop: self];
+ return text; + return text;
} }
-- (void) noteUserCancelledSelection
+ -- (void)dealloc
+/* ---- Helper: screen rect for a character range via glyph matrix ---- */ +/* ---- Helper: screen rect for a character range via glyph matrix ---- */
+ +
+static NSRect +static NSRect
+ns_ax_frame_for_range (struct window *w, EmacsView *view, +ns_ax_frame_for_range (struct window *w, EmacsView *view,
+ ptrdiff_t text_start, NSRange range) + ptrdiff_t text_start, NSRange range)
{ {
- font_panel_active = NO; - NSTRACE ("[EmacsView dealloc]");
+ if (!w || !w->current_matrix || !view) + if (!w || !w->current_matrix || !view)
+ return NSZeroRect; + return NSZeroRect;
- if (font_panel_result) - /* Clear the view resize notification. */
- [font_panel_result release]; - [[NSNotificationCenter defaultCenter]
- font_panel_result = nil; - removeObserver:self
- name:NSViewFrameDidChangeNotification
- object:nil];
+ /* Convert range indices back to buffer charpos. */ + /* Convert range indices back to buffer charpos. */
+ ptrdiff_t cp_start = text_start + (ptrdiff_t) range.location; + ptrdiff_t cp_start = text_start + (ptrdiff_t) range.location;
+ ptrdiff_t cp_end = cp_start + (ptrdiff_t) range.length; + ptrdiff_t cp_end = cp_start + (ptrdiff_t) range.length;
- [NSApp stop: self]; - if (fs_state == FULLSCREEN_BOTH)
- [nonfs_window release];
+ struct glyph_matrix *matrix = w->current_matrix; + struct glyph_matrix *matrix = w->current_matrix;
+ NSRect result = NSZeroRect; + NSRect result = NSZeroRect;
+ BOOL found = NO; + BOOL found = NO;
+
-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
- /* Release layer and menu */
- EmacsLayer *layer = (EmacsLayer *)[self layer];
- [layer release];
-#endif
+ for (int i = 0; i < matrix->nrows; i++) + for (int i = 0; i < matrix->nrows; i++)
+ { + {
+ struct glyph_row *row = matrix->rows + i; + struct glyph_row *row = matrix->rows + i;
@@ -415,7 +369,9 @@ index 932d209..e7af9a3 100644
+ continue; + continue;
+ if (!row->displays_text_p && !row->ends_at_zv_p) + if (!row->displays_text_p && !row->ends_at_zv_p)
+ continue; + continue;
+
- [[self menu] release];
- [super dealloc];
+ 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);
+ +
@@ -459,9 +415,7 @@ index 932d209..e7af9a3 100644
+ NSRect winRect = [view convertRect:result toView:nil]; + NSRect winRect = [view convertRect:result toView:nil];
+ return [[view window] convertRectToScreen:winRect]; + return [[view window] convertRectToScreen:winRect];
} }
-#endif
-- (Lisp_Object) showFontPanel
+/* AX enum numeric compatibility for NSAccessibility notifications. +/* AX enum numeric compatibility for NSAccessibility notifications.
+ Values match WebKit AXObjectCacheMac fallback enums + Values match WebKit AXObjectCacheMac fallback enums
+ (AXTextStateChangeType / AXTextEditType / AXTextSelectionDirection / + (AXTextStateChangeType / AXTextEditType / AXTextSelectionDirection /
@@ -470,86 +424,75 @@ index 932d209..e7af9a3 100644
+ ns_ax_text_state_change_unknown = 0, + ns_ax_text_state_change_unknown = 0,
+ ns_ax_text_state_change_edit = 1, + ns_ax_text_state_change_edit = 1,
+ ns_ax_text_state_change_selection_move = 2, + ns_ax_text_state_change_selection_move = 2,
+
+ ns_ax_text_edit_type_typing = 3, -/* Called on font panel selection. */
+ -- (void) changeFont: (id) sender
+ ns_ax_text_selection_direction_unknown = 0, -{
+ ns_ax_text_selection_direction_previous = 3,
+ ns_ax_text_selection_direction_next = 4,
+ ns_ax_text_selection_direction_discontiguous = 5,
+
+ ns_ax_text_selection_granularity_unknown = 0,
+ ns_ax_text_selection_granularity_character = 1,
+ ns_ax_text_selection_granularity_line = 3,
+};
+
+static NSUInteger
+ns_ax_utf16_length_for_buffer_range (struct buffer *b, ptrdiff_t start,
+ ptrdiff_t end)
{
- id fm = [NSFontManager sharedFontManager];
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; - struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
- NSFont *nsfont, *result; - NSFont *nsfont;
- struct timespec timeout; + ns_ax_text_edit_type_typing = 3,
-#ifdef NS_IMPL_COCOA
- NSView *buttons;
- BOOL canceled;
-#endif
+ if (!b || end <= start)
+ return 0;
-#ifdef NS_IMPL_GNUSTEP -#ifdef NS_IMPL_GNUSTEP
- nsfont = ((struct nsfont_info *) font)->nsfont; - nsfont = ((struct nsfont_info *) font)->nsfont;
-#else -#else
- nsfont = (NSFont *) macfont_get_nsctfont (font); - nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif -#endif
+ ns_ax_text_selection_direction_unknown = 0,
+ ns_ax_text_selection_direction_previous = 3,
+ ns_ax_text_selection_direction_next = 4,
+ ns_ax_text_selection_direction_discontiguous = 5,
- if (!font_panel_active)
- return;
+ ns_ax_text_selection_granularity_unknown = 0,
+ ns_ax_text_selection_granularity_character = 1,
+ ns_ax_text_selection_granularity_line = 3,
+};
- if (font_panel_result)
- [font_panel_result release];
+static NSUInteger
+ns_ax_utf16_length_for_buffer_range (struct buffer *b, ptrdiff_t start,
+ ptrdiff_t end)
+{
+ if (!b || end <= start)
+ return 0;
- font_panel_result = (NSFont *) [sender convertFont: nsfont];
+ struct buffer *oldb = current_buffer; + struct buffer *oldb = current_buffer;
+ if (b != current_buffer) + if (b != current_buffer)
+ set_buffer_internal_1 (b); + set_buffer_internal_1 (b);
-#ifdef NS_IMPL_COCOA - if (font_panel_result)
- buttons - [font_panel_result retain];
- = ns_create_font_panel_buttons (self,
- @selector (noteUserSelectedFont),
- @selector (noteUserCancelledSelection));
- [[fm fontPanel: YES] setAccessoryView: buttons];
- [buttons release];
-#endif
+ Lisp_Object lstr = Fbuffer_substring_no_properties (make_fixnum (start), + Lisp_Object lstr = Fbuffer_substring_no_properties (make_fixnum (start),
+ make_fixnum (end)); + make_fixnum (end));
+ NSString *nsstr = [NSString stringWithLispString:lstr]; + NSString *nsstr = [NSString stringWithLispString:lstr];
+ NSUInteger len = [nsstr length]; + NSUInteger len = [nsstr length];
- [fm setSelectedFont: nsfont isMultiple: NO]; -#ifndef NS_IMPL_COCOA
- [fm orderFrontFontPanel: NSApp]; - font_panel_active = NO;
- [NSApp stop: self];
-#endif
+ if (b != oldb) + if (b != oldb)
+ set_buffer_internal_1 (oldb); + set_buffer_internal_1 (oldb);
+
- font_panel_active = YES;
- timeout = make_timespec (0, 100000000);
+ return len; + return len;
+} }
- block_input ();
- while (font_panel_active
-#ifdef NS_IMPL_COCOA -#ifdef NS_IMPL_COCOA
- && (canceled = [[fm fontPanel: YES] isVisible]) -- (void) noteUserSelectedFont
-#else
- && [[fm fontPanel: YES] isVisible]
-#endif
- )
- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES);
- unblock_input ();
+static BOOL +static BOOL
+ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point, +ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
+ ptrdiff_t *out_start, + ptrdiff_t *out_start,
+ ptrdiff_t *out_end) + ptrdiff_t *out_end)
+{ {
- font_panel_active = NO;
+ if (!b || !out_start || !out_end) + if (!b || !out_start || !out_end)
+ return NO; + return NO;
- if (font_panel_result) - /* If no font was previously selected, use the currently selected
- [font_panel_result autorelease]; - font. */
+ Lisp_Object faceSym = intern ("completions-highlight"); + Lisp_Object faceSym = intern ("completions-highlight");
+ ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t begv = BUF_BEGV (b);
+ ptrdiff_t zv = BUF_ZV (b); + ptrdiff_t zv = BUF_ZV (b);
@@ -558,10 +501,50 @@ index 932d209..e7af9a3 100644
+ ptrdiff_t best_dist = PTRDIFF_MAX; + ptrdiff_t best_dist = PTRDIFF_MAX;
+ BOOL found = NO; + BOOL found = NO;
-#ifdef NS_IMPL_COCOA - if (!font_panel_result && FRAME_FONT (emacsframe))
- if (!canceled) + /* Fast path: look at point and immediate neighbors first. */
- font_panel_result = nil; + ptrdiff_t probes[3] = { point, point - 1, point + 1 };
-#endif + for (int i = 0; i < 3 && !found; i++)
{
- font_panel_result
- = macfont_get_nsctfont (FRAME_FONT (emacsframe));
+ ptrdiff_t p = probes[i];
+ if (p < begv || p > zv)
+ continue;
- if (font_panel_result)
- [font_panel_result retain];
+ Lisp_Object overlays = Foverlays_at (make_fixnum (p), Qnil);
+ Lisp_Object tail;
+ for (tail = overlays; CONSP (tail); tail = XCDR (tail))
+ {
+ Lisp_Object ov = XCAR (tail);
+ Lisp_Object face = Foverlay_get (ov, Qface);
+ if (!(EQ (face, faceSym)
+ || (CONSP (face) && !NILP (Fmemq (faceSym, face)))))
+ continue;
+
+ ptrdiff_t ov_start = OVERLAY_START (ov);
+ ptrdiff_t ov_end = OVERLAY_END (ov);
+ if (ov_end <= ov_start)
+ continue;
+
+ best_start = ov_start;
+ best_end = ov_end;
+ best_dist = 0;
+ found = YES;
+ break;
+ }
}
- [NSApp stop: self];
-}
-
-- (void) noteUserCancelledSelection
-{
- font_panel_active = NO;
+ if (!found)
+ {
+ for (ptrdiff_t scan = begv; scan < zv; scan++) + for (ptrdiff_t scan = begv; scan < zv; scan++)
+ { + {
+ Lisp_Object overlays = Foverlays_at (make_fixnum (scan), Qnil); + Lisp_Object overlays = Foverlays_at (make_fixnum (scan), Qnil);
@@ -596,32 +579,130 @@ index 932d209..e7af9a3 100644
+ } + }
+ } + }
+ } + }
+ }
- result = font_panel_result; - if (font_panel_result)
- [font_panel_result release];
- font_panel_result = nil; - font_panel_result = nil;
+ if (!found) + if (!found)
+ return NO; + return NO;
- [[fm fontPanel: YES] setIsVisible: NO]; - [NSApp stop: self];
- font_panel_active = NO;
+ *out_start = best_start; + *out_start = best_start;
+ *out_end = best_end; + *out_end = best_end;
+ return YES; + return YES;
}
-#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)
{
- id fm = [NSFontManager sharedFontManager];
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
- NSFont *nsfont, *result;
- struct timespec timeout;
-#ifdef NS_IMPL_COCOA
- NSView *buttons;
- BOOL canceled;
-#endif
-
-#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;
-#ifdef NS_IMPL_COCOA
- buttons
- = ns_create_font_panel_buttons (self,
- @selector (noteUserSelectedFont),
- @selector (noteUserCancelledSelection));
- [[fm fontPanel: YES] setAccessoryView: buttons];
- [buttons release];
-#endif
+ 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];
+ /* Prefer canonical completion candidate string from text property. */
+ ptrdiff_t probes[2] = { start, end - 1 };
+ for (int i = 0; i < 2 && !text; i++)
+ {
+ ptrdiff_t p = probes[i];
+ Lisp_Object cstr = Fget_char_property (make_fixnum (p),
+ intern ("completion--string"),
+ Qnil);
+ if (STRINGP (cstr))
+ 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
- && (canceled = [[fm fontPanel: YES] isVisible])
-#else
- && [[fm fontPanel: YES] isVisible]
-#endif
- )
- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES);
- unblock_input ();
+ if (b != oldb)
+ set_buffer_internal_1 (oldb);
- if (font_panel_result)
- [font_panel_result autorelease];
+ if (text)
+ {
+ text = [text stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([text length] == 0)
+ text = nil;
+ }
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- 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) - if (result)
- return ns_font_desc_to_font_spec ([result fontDescriptor], - return ns_font_desc_to_font_spec ([result fontDescriptor],
- result); - result);
- return Qnil;
+@implementation EmacsAccessibilityElement
+
+- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh +- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh
+{ +{
+ EmacsView *view = self.emacsView; + EmacsView *view = self.emacsView;
+ if (!view || ![view window]) + if (!view || ![view window])
+ return NSZeroRect; + return NSZeroRect;
+
- return Qnil;
+ NSRect r = NSMakeRect (x, y, ew, eh); + NSRect r = NSMakeRect (x, y, ew, eh);
+ NSRect winRect = [view convertRect:r toView:nil]; + NSRect winRect = [view convertRect:r toView:nil];
+ return [[view window] convertRectToScreen:winRect]; + return [[view window] convertRectToScreen:winRect];
@@ -684,6 +765,7 @@ index 932d209..e7af9a3 100644
+@synthesize cachedCompletionAnnouncement; +@synthesize cachedCompletionAnnouncement;
+@synthesize cachedCompletionOverlayStart; +@synthesize cachedCompletionOverlayStart;
+@synthesize cachedCompletionOverlayEnd; +@synthesize cachedCompletionOverlayEnd;
+@synthesize cachedCompletionPoint;
+- (void)dealloc +- (void)dealloc
+{ +{
@@ -705,8 +787,6 @@ index 932d209..e7af9a3 100644
- Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe); - Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
- int code; - int code;
- unsigned fnKeysym = 0; - unsigned fnKeysym = 0;
- static NSMutableArray *nsEvArray;
- unsigned int flags = [theEvent modifierFlags];
+ [cachedText release]; + [cachedText release];
+ cachedText = nil; + cachedText = nil;
+ if (visibleRuns) + if (visibleRuns)
@@ -716,21 +796,17 @@ index 932d209..e7af9a3 100644
+ } + }
+ visibleRunCount = 0; + visibleRunCount = 0;
+} +}
+
- NSTRACE ("[EmacsView keyDown:]");
+- (void)ensureTextCache +- (void)ensureTextCache
+{ +{
+ struct window *w = self.emacsWindow; + struct window *w = self.emacsWindow;
+ if (!w || !WINDOW_LEAF_P (w)) + if (!w || !WINDOW_LEAF_P (w))
+ return; + return;
+
- /* Rhapsody and macOS give up and down events for the arrow keys. */
- if ([theEvent type] != NSEventTypeKeyDown)
+ struct buffer *b = XBUFFER (w->contents); + struct buffer *b = XBUFFER (w->contents);
+ if (!b) + if (!b)
return; + return;
+
- if (!emacs_event)
+ ptrdiff_t modiff = BUF_MODIFF (b); + ptrdiff_t modiff = BUF_MODIFF (b);
+ ptrdiff_t pt = BUF_PT (b); + ptrdiff_t pt = BUF_PT (b);
+ NSUInteger textLen = cachedText ? [cachedText length] : 0; + NSUInteger textLen = cachedText ? [cachedText length] : 0;
@@ -738,16 +814,8 @@ index 932d209..e7af9a3 100644
+ && pt >= cachedTextStart + && pt >= cachedTextStart
+ && (textLen == 0 + && (textLen == 0
+ || [self accessibilityIndexForCharpos:pt] <= textLen)) + || [self accessibilityIndexForCharpos:pt] <= textLen))
return; + return;
+
- if (![[self window] isKeyWindow]
- && [[theEvent window] isKindOfClass: [EmacsWindow class]]
- /* We must avoid an infinite loop here. */
- && (EmacsView *)[[theEvent window] delegate] != self)
- {
- /* XXX: There is an occasional condition in which, when Emacs display
- updates a different frame from the current one, and temporarily
- selects it, then processes some interrupt-driven input
+ ptrdiff_t start; + ptrdiff_t start;
+ ns_ax_visible_run *runs = NULL; + ns_ax_visible_run *runs = NULL;
+ NSUInteger nruns = 0; + NSUInteger nruns = 0;
@@ -1470,18 +1538,10 @@ index 932d209..e7af9a3 100644
+ ptrdiff_t ov_end = OVERLAY_END (ov); + ptrdiff_t ov_end = OVERLAY_END (ov);
+ if (ov_end > ov_start) + if (ov_end > ov_start)
+ { + {
+ NSUInteger ax_idx = [self accessibilityIndexForCharpos: + announceText = ns_ax_completion_text_for_span (self, b,
+ ov_start]; + ov_start,
+ if (ax_idx <= [cachedText length]) + ov_end,
+ { + cachedText);
+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx];
+ NSRange lineRange = [self accessibilityRangeForLine:lineNum];
+ if (lineRange.location != NSNotFound
+ && lineRange.length > 0
+ && lineRange.location + lineRange.length
+ <= [cachedText length])
+ announceText = [cachedText substringWithRange:lineRange];
+ }
+ currentOverlayStart = ov_start; + currentOverlayStart = ov_start;
+ currentOverlayEnd = ov_end; + currentOverlayEnd = ov_end;
+ } + }
@@ -1498,17 +1558,10 @@ index 932d209..e7af9a3 100644
+ ptrdiff_t ov_end = 0; + ptrdiff_t ov_end = 0;
+ if (ns_ax_find_completion_overlay_range (b, point, &ov_start, &ov_end)) + if (ns_ax_find_completion_overlay_range (b, point, &ov_start, &ov_end))
+ { + {
+ NSUInteger ax_idx = [self accessibilityIndexForCharpos:ov_start]; + announceText = ns_ax_completion_text_for_span (self, b,
+ if (ax_idx <= [cachedText length]) + ov_start,
+ { + ov_end,
+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx]; + cachedText);
+ NSRange lineRange = [self accessibilityRangeForLine:lineNum];
+ if (lineRange.location != NSNotFound
+ && lineRange.length > 0
+ && lineRange.location + lineRange.length
+ <= [cachedText length])
+ announceText = [cachedText substringWithRange:lineRange];
+ }
+ currentOverlayStart = ov_start; + currentOverlayStart = ov_start;
+ currentOverlayEnd = ov_end; + currentOverlayEnd = ov_end;
+ } + }
@@ -1545,7 +1598,8 @@ index 932d209..e7af9a3 100644
+ BOOL overlayChanged = + BOOL overlayChanged =
+ (currentOverlayStart != self.cachedCompletionOverlayStart + (currentOverlayStart != self.cachedCompletionOverlayStart
+ || currentOverlayEnd != self.cachedCompletionOverlayEnd); + || currentOverlayEnd != self.cachedCompletionOverlayEnd);
+ if (textChanged || overlayChanged) + BOOL pointChanged = (point != self.cachedCompletionPoint);
+ if (textChanged || overlayChanged || pointChanged)
+ { + {
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText, + NSAccessibilityAnnouncementKey: announceText,
@@ -1560,12 +1614,14 @@ index 932d209..e7af9a3 100644
+ self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionAnnouncement = announceText;
+ self.cachedCompletionOverlayStart = currentOverlayStart; + self.cachedCompletionOverlayStart = currentOverlayStart;
+ self.cachedCompletionOverlayEnd = currentOverlayEnd; + self.cachedCompletionOverlayEnd = currentOverlayEnd;
+ self.cachedCompletionPoint = point;
+ } + }
+ else + else
+ { + {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0; + self.cachedCompletionOverlayEnd = 0;
+ self.cachedCompletionPoint = 0;
+ } + }
+ } + }
+ else + else
@@ -1573,6 +1629,7 @@ index 932d209..e7af9a3 100644
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0; + self.cachedCompletionOverlayEnd = 0;
+ self.cachedCompletionPoint = 0;
+ } + }
+ } + }
+ +
@@ -1584,6 +1641,7 @@ index 932d209..e7af9a3 100644
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0; + self.cachedCompletionOverlayEnd = 0;
+ self.cachedCompletionPoint = 0;
+ } + }
+ else + else
+ { + {
@@ -1601,18 +1659,10 @@ index 932d209..e7af9a3 100644
+ &currentOverlayStart, + &currentOverlayStart,
+ &currentOverlayEnd)) + &currentOverlayEnd))
+ { + {
+ NSUInteger ax_idx = [self accessibilityIndexForCharpos: + announceText = ns_ax_completion_text_for_span (self, b,
+ currentOverlayStart]; + currentOverlayStart,
+ if (ax_idx <= [cachedText length]) + currentOverlayEnd,
+ { + cachedText);
+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx];
+ NSRange lineRange = [self accessibilityRangeForLine:lineNum];
+ if (lineRange.location != NSNotFound
+ && lineRange.length > 0
+ && lineRange.location + lineRange.length
+ <= [cachedText length])
+ announceText = [cachedText substringWithRange:lineRange];
+ }
+ } + }
+ +
+ if (b != oldb2) + if (b != oldb2)
@@ -1629,7 +1679,8 @@ index 932d209..e7af9a3 100644
+ BOOL overlayChanged = + BOOL overlayChanged =
+ (currentOverlayStart != self.cachedCompletionOverlayStart + (currentOverlayStart != self.cachedCompletionOverlayStart
+ || currentOverlayEnd != self.cachedCompletionOverlayEnd); + || currentOverlayEnd != self.cachedCompletionOverlayEnd);
+ if (textChanged || overlayChanged) + BOOL pointChanged = (point != self.cachedCompletionPoint);
+ if (textChanged || overlayChanged || pointChanged)
+ { + {
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText, + NSAccessibilityAnnouncementKey: announceText,
@@ -1644,12 +1695,14 @@ index 932d209..e7af9a3 100644
+ self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionAnnouncement = announceText;
+ self.cachedCompletionOverlayStart = currentOverlayStart; + self.cachedCompletionOverlayStart = currentOverlayStart;
+ self.cachedCompletionOverlayEnd = currentOverlayEnd; + self.cachedCompletionOverlayEnd = currentOverlayEnd;
+ self.cachedCompletionPoint = point;
+ } + }
+ else + else
+ { + {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0; + self.cachedCompletionOverlayEnd = 0;
+ self.cachedCompletionPoint = 0;
+ } + }
+ } + }
+ else + else
@@ -1657,6 +1710,7 @@ index 932d209..e7af9a3 100644
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0; + self.cachedCompletionOverlayEnd = 0;
+ self.cachedCompletionPoint = 0;
+ } + }
+ } + }
+ } + }
@@ -1945,30 +1999,10 @@ index 932d209..e7af9a3 100644
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe); + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
+ int code; + int code;
+ unsigned fnKeysym = 0; + unsigned fnKeysym = 0;
+ static NSMutableArray *nsEvArray; static NSMutableArray *nsEvArray;
+ unsigned int flags = [theEvent modifierFlags]; unsigned int flags = [theEvent modifierFlags];
+
+ NSTRACE ("[EmacsView keyDown:]"); @@ -8237,6 +9749,28 @@ ns_in_echo_area (void)
+
+ /* Rhapsody and macOS give up and down events for the arrow keys. */
+ if ([theEvent type] != NSEventTypeKeyDown)
+ return;
+
+ if (!emacs_event)
+ return;
+
+ if (![[self window] isKeyWindow]
+ && [[theEvent window] isKindOfClass: [EmacsWindow class]]
+ /* We must avoid an infinite loop here. */
+ && (EmacsView *)[[theEvent window] delegate] != self)
+ {
+ /* XXX: There is an occasional condition in which, when Emacs display
+ updates a different frame from the current one, and temporarily
+ selects it, then processes some interrupt-driven input
(dispnew.c:3878), OS will send the event to the correct NSWindow, but
for some reason that window has its first responder set to the NSView
most recently updated (I guess), which is not the correct one. */
@@ -8237,6 +9679,28 @@ ns_in_echo_area (void)
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
@@ -1997,7 +2031,7 @@ index 932d209..e7af9a3 100644
} }
@@ -9474,6 +10938,298 @@ ns_in_echo_area (void) @@ -9474,6 +11008,298 @@ ns_in_echo_area (void)
return fs_state; return fs_state;
} }
@@ -2296,7 +2330,7 @@ index 932d209..e7af9a3 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -9941,6 +11697,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) @@ -9941,6 +11767,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
return [super accessibilityAttributeValue:attribute]; return [super accessibilityAttributeValue:attribute];
} }