patches: robust AX/VoiceOver fix after full audit pipeline

This commit is contained in:
2026-02-26 17:49:56 +01:00
parent 8720a43d04
commit b3a6141831

View File

@@ -1,19 +1,19 @@
From 0084027a7680cbfb1449f091312ac21ed3794a6c Mon Sep 17 00:00:00 2001 From 991ef1c9f04ccfdd71482ef3df54050f314e8ab5 Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Thu, 26 Feb 2026 17:30:09 +0100 Date: Thu, 26 Feb 2026 17:49:52 +0100
Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction
fixes fixes
--- ---
nsterm.h | 68 ++ nsterm.h | 70 ++
nsterm.m | 1968 +++++++++++++++++++++++++++++++++++++++++++++++++----- nsterm.m | 2058 +++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 1855 insertions(+), 181 deletions(-) 2 files changed, 1960 insertions(+), 168 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..719eeba 100644 index 7c1ee4c..97da979 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -453,6 +453,59 @@ enum ns_return_frame_mode @@ -453,6 +453,61 @@ enum ns_return_frame_mode
@end @end
@@ -58,6 +58,8 @@ index 7c1ee4c..719eeba 100644
+@property (nonatomic, assign) ptrdiff_t cachedPoint; +@property (nonatomic, assign) ptrdiff_t cachedPoint;
+@property (nonatomic, assign) BOOL cachedMarkActive; +@property (nonatomic, assign) BOOL cachedMarkActive;
+@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; +@property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
+@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayStart;
+@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayEnd;
+- (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;
@@ -73,7 +75,7 @@ index 7c1ee4c..719eeba 100644
/* ========================================================================== /* ==========================================================================
The main Emacs view The main Emacs view
@@ -471,6 +524,14 @@ enum ns_return_frame_mode @@ -471,6 +526,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;
@@ -88,7 +90,7 @@ index 7c1ee4c..719eeba 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -528,6 +589,13 @@ enum ns_return_frame_mode @@ -528,6 +591,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen; - (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen; - (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey; - (void)windowDidBecomeKey;
@@ -103,7 +105,7 @@ index 7c1ee4c..719eeba 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..336150a 100644 index 932d209..8673194 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)
@@ -156,7 +158,7 @@ index 932d209..336150a 100644
ns_focus (f, NULL, 0); ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6849,261 +6885,1511 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) @@ -6849,245 +6885,1611 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
/* ========================================================================== /* ==========================================================================
@@ -383,57 +385,29 @@ index 932d209..336150a 100644
} }
-- (void) noteUserCancelledSelection -- (void) noteUserCancelledSelection
-{ +
- font_panel_active = NO;
-
- if (font_panel_result)
- [font_panel_result release];
- font_panel_result = nil;
- [NSApp stop: self];
-}
-#endif
+/* ---- Helper: screen rect for a character range via glyph matrix ---- */ +/* ---- Helper: screen rect for a character range via glyph matrix ---- */
+
-- (Lisp_Object) showFontPanel
+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)
{ {
- id fm = [NSFontManager sharedFontManager]; - font_panel_active = NO;
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
- NSFont *nsfont, *result;
- struct timespec timeout;
-#ifdef NS_IMPL_COCOA
- NSView *buttons;
- BOOL canceled;
-#endif
+ if (!w || !w->current_matrix || !view) + if (!w || !w->current_matrix || !view)
+ return NSZeroRect; + return NSZeroRect;
-#ifdef NS_IMPL_GNUSTEP - if (font_panel_result)
- nsfont = ((struct nsfont_info *) font)->nsfont; - [font_panel_result release];
-#else - font_panel_result = nil;
- nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif
+ /* 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;
-#ifdef NS_IMPL_COCOA - [NSApp stop: self];
- buttons
- = ns_create_font_panel_buttons (self,
- @selector (noteUserSelectedFont),
- @selector (noteUserCancelledSelection));
- [[fm fontPanel: YES] setAccessoryView: buttons];
- [buttons release];
-#endif
+ 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;
+
- [fm setSelectedFont: nsfont isMultiple: NO];
- [fm orderFrontFontPanel: NSApp];
+ 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;
@@ -441,22 +415,10 @@ index 932d209..336150a 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;
+
- font_panel_active = YES;
- timeout = make_timespec (0, 100000000);
+ 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);
+
- 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 (row_start < cp_end && row_end > cp_start) + if (row_start < cp_end && row_end > cp_start)
+ { + {
+ int window_x, window_y, window_width; + int window_x, window_y, window_width;
@@ -479,16 +441,10 @@ index 932d209..336150a 100644
+ result = NSUnionRect (result, rowRect); + result = NSUnionRect (result, rowRect);
+ } + }
+ } + }
+
- if (font_panel_result)
- [font_panel_result autorelease];
+ if (!found) + if (!found)
+ return NSZeroRect; + return NSZeroRect;
+
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- font_panel_result = nil;
-#endif
+ /* Clip result to text area bounds. */ + /* Clip result to text area bounds. */
+ { + {
+ int text_area_x, text_area_y, text_area_w, text_area_h; + int text_area_x, text_area_y, text_area_w, text_area_h;
@@ -498,23 +454,147 @@ index 932d209..336150a 100644
+ if (NSMaxY (result) > max_y) + if (NSMaxY (result) > max_y)
+ result.size.height = max_y - result.origin.y; + result.size.height = max_y - result.origin.y;
+ } + }
+
- result = font_panel_result;
- font_panel_result = nil;
+ /* Convert from EmacsView (flipped) coords to screen coords. */ + /* Convert from EmacsView (flipped) coords to screen coords. */
+ 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
+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;
- NSFont *nsfont, *result;
- struct timespec timeout;
-#ifdef NS_IMPL_COCOA
- NSView *buttons;
- BOOL canceled;
-#endif
+ if (!b || end <= start)
+ return 0;
-#ifdef NS_IMPL_GNUSTEP
- nsfont = ((struct nsfont_info *) font)->nsfont;
-#else
- nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif
+ struct buffer *oldb = current_buffer;
+ if (b != current_buffer)
+ set_buffer_internal_1 (b);
-#ifdef NS_IMPL_COCOA
- buttons
- = 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),
+ make_fixnum (end));
+ NSString *nsstr = [NSString stringWithLispString:lstr];
+ NSUInteger len = [nsstr length];
- [fm setSelectedFont: nsfont isMultiple: NO];
- [fm orderFrontFontPanel: NSApp];
+ if (b != oldb)
+ set_buffer_internal_1 (oldb);
- font_panel_active = YES;
- timeout = make_timespec (0, 100000000);
+ return len;
+} +}
- 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 ();
+static BOOL
+ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
+ ptrdiff_t *out_start,
+ ptrdiff_t *out_end)
+{
+ if (!b || !out_start || !out_end)
+ return NO;
- if (font_panel_result)
- [font_panel_result autorelease];
+ Lisp_Object faceSym = intern ("completions-highlight");
+ ptrdiff_t begv = BUF_BEGV (b);
+ ptrdiff_t zv = BUF_ZV (b);
+ ptrdiff_t best_start = 0;
+ ptrdiff_t best_end = 0;
+ ptrdiff_t best_dist = PTRDIFF_MAX;
+ BOOL found = NO;
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- font_panel_result = nil;
-#endif
+ for (ptrdiff_t scan = begv; scan < zv; scan++)
+ {
+ Lisp_Object overlays = Foverlays_at (make_fixnum (scan), 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;
+
+ ptrdiff_t dist = 0;
+ if (point < ov_start)
+ dist = ov_start - point;
+ else if (point > ov_end)
+ dist = point - ov_end;
+
+ if (!found || dist < best_dist
+ || (dist == best_dist
+ && (ov_start < point && best_start >= point)))
+ {
+ best_start = ov_start;
+ best_end = ov_end;
+ best_dist = dist;
+ found = YES;
+ }
+ }
+ }
- result = font_panel_result;
- font_panel_result = nil;
+ if (!found)
+ return NO;
- [[fm fontPanel: YES] setIsVisible: NO]; - [[fm fontPanel: YES] setIsVisible: NO];
- font_panel_active = NO; - font_panel_active = NO;
+ *out_start = best_start;
+ *out_end = best_end;
+ return YES;
+}
- if (result) - if (result)
- return ns_font_desc_to_font_spec ([result fontDescriptor], - return ns_font_desc_to_font_spec ([result fontDescriptor],
- result); - result);
+@implementation EmacsAccessibilityElement
- return Qnil; - 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;
@@ -581,6 +661,8 @@ index 932d209..336150a 100644
+@synthesize cachedPoint; +@synthesize cachedPoint;
+@synthesize cachedMarkActive; +@synthesize cachedMarkActive;
+@synthesize cachedCompletionAnnouncement; +@synthesize cachedCompletionAnnouncement;
+@synthesize cachedCompletionOverlayStart;
+@synthesize cachedCompletionOverlayEnd;
+- (void)dealloc +- (void)dealloc
+{ +{
@@ -650,41 +732,43 @@ index 932d209..336150a 100644
- most recently updated (I guess), which is not the correct one. */ - most recently updated (I guess), which is not the correct one. */
- [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent]; - [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
- return; - return;
- }
+ ptrdiff_t start; + ptrdiff_t start;
+ ns_ax_visible_run *runs = NULL; + ns_ax_visible_run *runs = NULL;
+ NSUInteger nruns = 0; + NSUInteger nruns = 0;
+ NSString *text = ns_ax_buffer_text (w, &start, &runs, &nruns); + NSString *text = ns_ax_buffer_text (w, &start, &runs, &nruns);
+
- if (nsEvArray == nil)
- nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
+ [cachedText release]; + [cachedText release];
+ cachedText = [text retain]; + cachedText = [text retain];
+ cachedTextModiff = modiff; + cachedTextModiff = modiff;
+ cachedTextStart = start; + cachedTextStart = start;
+
- [NSCursor setHiddenUntilMouseMoves:! NILP (Vmake_pointer_invisible)];
+ if (visibleRuns) + if (visibleRuns)
+ xfree (visibleRuns); + xfree (visibleRuns);
+ visibleRuns = runs; + visibleRuns = runs;
+ visibleRunCount = nruns; + visibleRunCount = nruns;
+} +}
+
- if (!hlinfo->mouse_face_hidden
- && FIXNUMP (Vmouse_highlight)
- && !EQ (emacsframe->tab_bar_window, hlinfo->mouse_face_window))
+/* ---- Index mapping ---- */ +/* ---- Index mapping ---- */
+ +
+/* Convert buffer charpos to accessibility string index. */ +/* Convert buffer charpos to accessibility string index. */
+- (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos +- (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
+{ +{
+ struct window *w = self.emacsWindow;
+ struct buffer *b = (w && WINDOW_LEAF_P (w)) ? XBUFFER (w->contents) : NULL;
+
+ for (NSUInteger i = 0; i < visibleRunCount; i++) + for (NSUInteger i = 0; i < visibleRunCount; i++)
{ + {
- clear_mouse_face (hlinfo);
- hlinfo->mouse_face_hidden = true;
+ ns_ax_visible_run *r = &visibleRuns[i]; + ns_ax_visible_run *r = &visibleRuns[i];
+ if (charpos >= r->charpos && charpos < r->charpos + r->length) + if (charpos >= r->charpos && charpos < r->charpos + r->length)
+ return r->ax_start + (NSUInteger) (charpos - r->charpos); + {
+ if (!b)
+ return r->ax_start;
+ NSUInteger delta = ns_ax_utf16_length_for_buffer_range (b, r->charpos,
+ charpos);
+ if (delta > r->ax_length)
+ delta = r->ax_length;
+ return r->ax_start + delta;
+ }
+ /* If charpos falls in an invisible gap before the next run, + /* If charpos falls in an invisible gap before the next run,
+ map it to the start of the next visible run. */ + map it to the start of the next visible run. */
+ if (charpos < r->charpos) + if (charpos < r->charpos)
@@ -695,20 +779,43 @@ index 932d209..336150a 100644
+ { + {
+ ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; + ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
+ return last->ax_start + last->ax_length; + return last->ax_start + last->ax_length;
} + }
+ return 0; + return 0;
+} +}
+
- if (!processingCompose)
+/* Convert accessibility string index to buffer charpos. */ +/* Convert accessibility string index to buffer charpos. */
+- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx +- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
+{ +{
+ struct window *w = self.emacsWindow;
+ struct buffer *b = (w && WINDOW_LEAF_P (w)) ? XBUFFER (w->contents) : NULL;
+
+ for (NSUInteger i = 0; i < visibleRunCount; i++) + for (NSUInteger i = 0; i < visibleRunCount; i++)
+ { + {
+ ns_ax_visible_run *r = &visibleRuns[i]; + ns_ax_visible_run *r = &visibleRuns[i];
+ if (ax_idx >= r->ax_start + if (ax_idx >= r->ax_start
+ && ax_idx < r->ax_start + r->ax_length) + && ax_idx < r->ax_start + r->ax_length)
+ return r->charpos + (ptrdiff_t) (ax_idx - r->ax_start); + {
+ if (!b)
+ return r->charpos;
+
+ NSUInteger target = ax_idx - r->ax_start;
+ ptrdiff_t lo = r->charpos;
+ ptrdiff_t hi = r->charpos + r->length;
+
+ while (lo < hi)
+ {
+ ptrdiff_t mid = lo + (hi - lo) / 2;
+ NSUInteger mid_len = ns_ax_utf16_length_for_buffer_range (b,
+ r->charpos,
+ mid);
+ if (mid_len < target)
+ lo = mid + 1;
+ else
+ hi = mid;
+ }
+
+ return lo;
+ }
+ } + }
+ /* Past end — return last charpos. */ + /* Past end — return last charpos. */
+ if (visibleRunCount > 0) + if (visibleRunCount > 0)
@@ -850,7 +957,7 @@ index 932d209..336150a 100644
+ +
+ SET_PT_BOTH (charpos, CHAR_TO_BYTE (charpos)); + SET_PT_BOTH (charpos, CHAR_TO_BYTE (charpos));
+ +
+ /* If range has nonzero length, activate the mark. */ + /* Keep mark state aligned with requested selection range. */
+ if (range.length > 0) + if (range.length > 0)
+ { + {
+ ptrdiff_t mark_charpos = [self charposForAccessibilityIndex: + ptrdiff_t mark_charpos = [self charposForAccessibilityIndex:
@@ -859,7 +966,10 @@ index 932d209..336150a 100644
+ mark_charpos = BUF_ZV (b); + mark_charpos = BUF_ZV (b);
+ Fset_marker (BVAR (b, mark), make_fixnum (mark_charpos), + Fset_marker (BVAR (b, mark), make_fixnum (mark_charpos),
+ Fcurrent_buffer ()); + Fcurrent_buffer ());
+ bset_mark_active (b, Qt);
+ } + }
+ else
+ bset_mark_active (b, Qnil);
+ +
+ if (b != oldb) + if (b != oldb)
+ set_buffer_internal_1 (oldb); + set_buffer_internal_1 (oldb);
@@ -869,6 +979,7 @@ index 932d209..336150a 100644
+ /* Update cached state so the next notification cycle doesn't + /* Update cached state so the next notification cycle doesn't
+ re-announce this movement. */ + re-announce this movement. */
+ self.cachedPoint = charpos; + self.cachedPoint = charpos;
+ self.cachedMarkActive = (range.length > 0);
+} +}
+ +
+- (void)setAccessibilityFocused:(BOOL)flag +- (void)setAccessibilityFocused:(BOOL)flag
@@ -1264,6 +1375,8 @@ index 932d209..336150a 100644
+ if (![self isAccessibilityFocused] && cachedText) + if (![self isAccessibilityFocused] && cachedText)
+ { + {
+ NSString *announceText = nil; + NSString *announceText = nil;
+ ptrdiff_t currentOverlayStart = 0;
+ ptrdiff_t currentOverlayEnd = 0;
+ +
+ struct buffer *oldb2 = current_buffer; + struct buffer *oldb2 = current_buffer;
+ if (b != current_buffer) + if (b != current_buffer)
@@ -1351,43 +1464,22 @@ index 932d209..336150a 100644
+ } + }
+ } + }
+ +
+ /* 4) Fallback: scan for completions-highlight anywhere in buffer. + /* 4) Fallback: select the best completions-highlight overlay.
+ TAB cycling can move highlight without moving point. */ + Prefer overlay nearest to point over first-found in buffer. */
+ if (!announceText) + if (!announceText)
+ { + {
+ Lisp_Object faceSym = intern ("completions-highlight"); + ptrdiff_t ov_start = 0;
+ ptrdiff_t begv2 = BUF_BEGV (b); + ptrdiff_t ov_end = 0;
+ ptrdiff_t zv2 = BUF_ZV (b); + if (ns_ax_find_completion_overlay_range (b, point, &ov_start, &ov_end))
+ ptrdiff_t scanPos;
+ BOOL found = NO;
+
+ for (scanPos = begv2; scanPos < zv2 && !found; scanPos++)
+ { + {
+ Lisp_Object overlays = Foverlays_at (make_fixnum (scanPos), Qnil); + NSUInteger ax_s = [self accessibilityIndexForCharpos:ov_start];
+ Lisp_Object tail; + NSUInteger ax_e = [self accessibilityIndexForCharpos:ov_end];
+ 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))))
+ {
+ ptrdiff_t ov_start = OVERLAY_START (ov);
+ ptrdiff_t ov_end = OVERLAY_END (ov);
+ if (ov_end > ov_start)
+ {
+ NSUInteger ax_s = [self accessibilityIndexForCharpos:
+ ov_start];
+ NSUInteger ax_e = [self accessibilityIndexForCharpos:
+ ov_end];
+ if (ax_e > ax_s && ax_e <= [cachedText length]) + if (ax_e > ax_s && ax_e <= [cachedText length])
+ {
+ announceText = [cachedText substringWithRange: + announceText = [cachedText substringWithRange:
+ NSMakeRange (ax_s, ax_e - ax_s)]; + NSMakeRange (ax_s, ax_e - ax_s)];
+ } + currentOverlayStart = ov_start;
+ found = YES; + currentOverlayEnd = ov_end;
+ break;
+ }
+ } + }
+ } + }
+ } + }
@@ -1418,7 +1510,12 @@ index 932d209..336150a 100644
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([announceText length] > 0) + if ([announceText length] > 0)
+ { + {
+ if (![announceText isEqualToString:self.cachedCompletionAnnouncement]) + BOOL textChanged = ![announceText isEqualToString:
+ self.cachedCompletionAnnouncement];
+ BOOL overlayChanged =
+ (currentOverlayStart != self.cachedCompletionOverlayStart
+ || currentOverlayEnd != self.cachedCompletionOverlayEnd);
+ if (textChanged || overlayChanged)
+ { + {
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText, + NSAccessibilityAnnouncementKey: announceText,
@@ -1431,64 +1528,57 @@ index 932d209..336150a 100644
+ annInfo); + annInfo);
+ } + }
+ self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionAnnouncement = announceText;
+ self.cachedCompletionOverlayStart = currentOverlayStart;
+ self.cachedCompletionOverlayEnd = currentOverlayEnd;
+ } + }
+ else + else
+ {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0;
+ }
+ } + }
+ else + else
+ {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0;
+ }
+ } + }
+ +
+ } + }
+ else + else
+ { + {
+ if ([self isAccessibilityFocused]) + if ([self isAccessibilityFocused])
+ {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0;
+ }
+ else + else
+ { + {
+ [self ensureTextCache]; + [self ensureTextCache];
+ if (cachedText) + if (cachedText)
+ { + {
+ NSString *announceText = nil; + NSString *announceText = nil;
+ ptrdiff_t currentOverlayStart = 0;
+ ptrdiff_t currentOverlayEnd = 0;
+ struct buffer *oldb2 = current_buffer; + struct buffer *oldb2 = current_buffer;
+ if (b != current_buffer) + if (b != current_buffer)
+ set_buffer_internal_1 (b); + set_buffer_internal_1 (b);
+ +
+ Lisp_Object faceSym = intern ("completions-highlight"); + if (ns_ax_find_completion_overlay_range (b, point,
+ ptrdiff_t begv2 = BUF_BEGV (b); + &currentOverlayStart,
+ ptrdiff_t zv2 = BUF_ZV (b); + &currentOverlayEnd))
+ ptrdiff_t scanPos;
+ BOOL found = NO;
+
+ for (scanPos = begv2; scanPos < zv2 && !found; scanPos++)
+ {
+ Lisp_Object overlays = Foverlays_at (make_fixnum (scanPos), 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))))
+ {
+ ptrdiff_t ov_start = OVERLAY_START (ov);
+ ptrdiff_t ov_end = OVERLAY_END (ov);
+ if (ov_end > ov_start)
+ { + {
+ NSUInteger ax_s = [self accessibilityIndexForCharpos: + NSUInteger ax_s = [self accessibilityIndexForCharpos:
+ ov_start]; + currentOverlayStart];
+ NSUInteger ax_e = [self accessibilityIndexForCharpos: + NSUInteger ax_e = [self accessibilityIndexForCharpos:
+ ov_end]; + currentOverlayEnd];
+ if (ax_e > ax_s && ax_e <= [cachedText length]) + if (ax_e > ax_s && ax_e <= [cachedText length])
+ announceText = [cachedText substringWithRange: + announceText = [cachedText substringWithRange:
+ NSMakeRange (ax_s, ax_e - ax_s)]; + NSMakeRange (ax_s, ax_e - ax_s)];
+ } + }
+ found = YES;
+ break;
+ }
+ }
+ }
+ +
+ if (b != oldb2) + if (b != oldb2)
+ set_buffer_internal_1 (oldb2); + set_buffer_internal_1 (oldb2);
@@ -1499,7 +1589,12 @@ index 932d209..336150a 100644
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([announceText length] > 0) + if ([announceText length] > 0)
+ { + {
+ if (![announceText isEqualToString:self.cachedCompletionAnnouncement]) + BOOL textChanged = ![announceText isEqualToString:
+ self.cachedCompletionAnnouncement];
+ BOOL overlayChanged =
+ (currentOverlayStart != self.cachedCompletionOverlayStart
+ || currentOverlayEnd != self.cachedCompletionOverlayEnd);
+ if (textChanged || overlayChanged)
+ { + {
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText, + NSAccessibilityAnnouncementKey: announceText,
@@ -1512,12 +1607,22 @@ index 932d209..336150a 100644
+ annInfo); + annInfo);
+ } + }
+ self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionAnnouncement = announceText;
+ self.cachedCompletionOverlayStart = currentOverlayStart;
+ self.cachedCompletionOverlayEnd = currentOverlayEnd;
+ } + }
+ else + else
+ {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0;
+ }
+ } + }
+ else + else
+ {
+ self.cachedCompletionAnnouncement = nil; + self.cachedCompletionAnnouncement = nil;
+ self.cachedCompletionOverlayStart = 0;
+ self.cachedCompletionOverlayEnd = 0;
+ }
+ } + }
+ } + }
+ } + }
@@ -1830,26 +1935,10 @@ index 932d209..336150a 100644
+ most recently updated (I guess), which is not the correct one. */ + most recently updated (I guess), which is not the correct one. */
+ [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent]; + [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent];
+ return; + return;
+ } }
+
+ if (nsEvArray == nil) if (nsEvArray == nil)
+ nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; @@ -8237,6 +9639,27 @@ ns_in_echo_area (void)
+
+ [NSCursor setHiddenUntilMouseMoves:! NILP (Vmake_pointer_invisible)];
+
+ if (!hlinfo->mouse_face_hidden
+ && FIXNUMP (Vmouse_highlight)
+ && !EQ (emacsframe->tab_bar_window, hlinfo->mouse_face_window))
+ {
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_hidden = true;
+ }
+
+ if (!processingCompose)
{
/* FIXME: What should happen for key sequences with more than
one character? */
@@ -8237,6 +9523,27 @@ 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
@@ -1877,7 +1966,7 @@ index 932d209..336150a 100644
} }
@@ -9474,6 +10781,297 @@ ns_in_echo_area (void) @@ -9474,6 +10897,297 @@ ns_in_echo_area (void)
return fs_state; return fs_state;
} }
@@ -2175,7 +2264,7 @@ index 932d209..336150a 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -9941,6 +11539,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) @@ -9941,6 +11655,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
return [super accessibilityAttributeValue:attribute]; return [super accessibilityAttributeValue:attribute];
} }