Merge remote-tracking branch 'refs/remotes/origin/master'

This commit is contained in:
Martin Sukany
2026-02-26 17:31:27 +01:00

View File

@@ -1,8 +1,19 @@
diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..2e2c80f 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -453,6 +453,58 @@ enum ns_return_frame_mode
From 0084027a7680cbfb1449f091312ac21ed3794a6c Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz>
Date: Thu, 26 Feb 2026 17:30:09 +0100
Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction
fixes
---
nsterm.h | 68 ++
nsterm.m | 1968 +++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 1855 insertions(+), 181 deletions(-)
diff --git a/nsterm.h b/nsterm.h
index 7c1ee4c..719eeba 100644
--- a/nsterm.h
+++ b/nsterm.h
@@ -453,6 +453,59 @@ enum ns_return_frame_mode
@end
@@ -46,6 +57,7 @@ index 7c1ee4c..2e2c80f 100644
+@property (nonatomic, assign) ptrdiff_t cachedModiff;
+@property (nonatomic, assign) ptrdiff_t cachedPoint;
+@property (nonatomic, assign) BOOL cachedMarkActive;
+@property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
+- (void)invalidateTextCache;
+- (void)postAccessibilityNotificationsForFrame:(struct frame *)f;
+- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx;
@@ -61,7 +73,7 @@ index 7c1ee4c..2e2c80f 100644
/* ==========================================================================
The main Emacs view
@@ -471,6 +523,14 @@ enum ns_return_frame_mode
@@ -471,6 +524,14 @@ enum ns_return_frame_mode
#ifdef NS_IMPL_COCOA
char *old_title;
BOOL maximizing_resize;
@@ -76,7 +88,7 @@ index 7c1ee4c..2e2c80f 100644
#endif
BOOL font_panel_active;
NSFont *font_panel_result;
@@ -528,6 +588,13 @@ enum ns_return_frame_mode
@@ -528,6 +589,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey;
@@ -90,10 +102,10 @@ index 7c1ee4c..2e2c80f 100644
@end
diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..416e5a4 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
diff --git a/nsterm.m b/nsterm.m
index 932d209..336150a 100644
--- a/nsterm.m
+++ b/nsterm.m
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
unblock_input ();
@@ -144,7 +156,7 @@ index 932d209..416e5a4 100644
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6849,261 +6885,1380 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
@@ -6849,261 +6885,1511 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
/* ==========================================================================
@@ -568,10 +580,12 @@ index 932d209..416e5a4 100644
+@synthesize cachedModiff;
+@synthesize cachedPoint;
+@synthesize cachedMarkActive;
+@synthesize cachedCompletionAnnouncement;
+- (void)dealloc
+{
+ [cachedText release];
+ [cachedCompletionAnnouncement release];
+ if (visibleRuns)
+ xfree (visibleRuns);
+ [super dealloc];
@@ -1199,8 +1213,8 @@ index 932d209..416e5a4 100644
+ direction = 3;
+
+ /* Compute granularity from movement distance.
+ Check if we crossed a newline → line movement (3).
+ Otherwise single char (1) or unknown (0). */
+ Prefer robust line-range comparison for vertical movement,
+ otherwise single char (1) or unknown (0). */
+ NSInteger granularity = 0;
+ [self ensureTextCache];
+ if (cachedText && oldPoint > 0)
@@ -1210,22 +1224,21 @@ index 932d209..416e5a4 100644
+ granularity = 1; /* Character. */
+ else
+ {
+ /* Check for line crossing by looking for newlines
+ between old and new position. */
+ NSUInteger lo = [self accessibilityIndexForCharpos:
+ MIN (oldPoint, point)];
+ NSUInteger hi = [self accessibilityIndexForCharpos:
+ MAX (oldPoint, point)];
+ NSUInteger tlen = [cachedText length];
+ if (lo < tlen && hi <= tlen)
+ {
+ NSRange searchRange = NSMakeRange (lo, hi - lo);
+ NSRange nl = [cachedText rangeOfString:@"\n"
+ options:0
+ range:searchRange];
+ if (nl.location != NSNotFound)
+ NSUInteger oldIdx = [self accessibilityIndexForCharpos:oldPoint];
+ NSUInteger newIdx = [self accessibilityIndexForCharpos:point];
+ if (oldIdx > tlen)
+ oldIdx = tlen;
+ if (newIdx > tlen)
+ newIdx = tlen;
+
+ NSRange oldLine = [cachedText lineRangeForRange:
+ NSMakeRange (oldIdx, 0)];
+ NSRange newLine = [cachedText lineRangeForRange:
+ NSMakeRange (newIdx, 0)];
+ if (oldLine.location != newLine.location)
+ granularity = 3; /* Line. */
+ }
+
+ }
+ }
+
@@ -1307,19 +1320,19 @@ index 932d209..416e5a4 100644
+ }
+ }
+
+ /* 3) Fallback: check completions-highlight overlay span. */
+ /* 3) Fallback: check completions-highlight overlay span at point. */
+ if (!announceText)
+ {
+ Lisp_Object faceSym = intern ("completions-highlight");
+ Lisp_Object overlays = Foverlays_at (make_fixnum (point), 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, intern ("completions-highlight"))
+ if (EQ (face, faceSym)
+ || (CONSP (face)
+ && !NILP (Fmemq (intern ("completions-highlight"),
+ face))))
+ && !NILP (Fmemq (faceSym, face))))
+ {
+ ptrdiff_t ov_start = OVERLAY_START (ov);
+ ptrdiff_t ov_end = OVERLAY_END (ov);
@@ -1338,6 +1351,47 @@ index 932d209..416e5a4 100644
+ }
+ }
+
+ /* 4) Fallback: scan for completions-highlight anywhere in buffer.
+ TAB cycling can move highlight without moving point. */
+ if (!announceText)
+ {
+ Lisp_Object faceSym = intern ("completions-highlight");
+ ptrdiff_t begv2 = BUF_BEGV (b);
+ ptrdiff_t zv2 = BUF_ZV (b);
+ 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:
+ ov_start];
+ NSUInteger ax_e = [self accessibilityIndexForCharpos:
+ ov_end];
+ if (ax_e > ax_s && ax_e <= [cachedText length])
+ announceText = [cachedText substringWithRange:
+ NSMakeRange (ax_s, ax_e - ax_s)];
+ }
+ found = YES;
+ break;
+ }
+ }
+ }
+ }
+
+ if (b != oldb2)
+ set_buffer_internal_1 (oldb2);
+
@@ -1364,6 +1418,8 @@ index 932d209..416e5a4 100644
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([announceText length] > 0)
+ {
+ if (![announceText isEqualToString:self.cachedCompletionAnnouncement])
+ {
+ NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText,
+ NSAccessibilityPriorityKey:
@@ -1374,9 +1430,96 @@ index 932d209..416e5a4 100644
+ NSAccessibilityAnnouncementRequestedNotification,
+ annInfo);
+ }
+ self.cachedCompletionAnnouncement = announceText;
+ }
+ else
+ self.cachedCompletionAnnouncement = nil;
+ }
+ else
+ self.cachedCompletionAnnouncement = nil;
+ }
+
+ }
+ else
+ {
+ if ([self isAccessibilityFocused])
+ self.cachedCompletionAnnouncement = nil;
+ else
+ {
+ [self ensureTextCache];
+ if (cachedText)
+ {
+ NSString *announceText = nil;
+ struct buffer *oldb2 = current_buffer;
+ if (b != current_buffer)
+ set_buffer_internal_1 (b);
+
+ Lisp_Object faceSym = intern ("completions-highlight");
+ ptrdiff_t begv2 = BUF_BEGV (b);
+ ptrdiff_t zv2 = BUF_ZV (b);
+ 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:
+ ov_start];
+ NSUInteger ax_e = [self accessibilityIndexForCharpos:
+ ov_end];
+ if (ax_e > ax_s && ax_e <= [cachedText length])
+ announceText = [cachedText substringWithRange:
+ NSMakeRange (ax_s, ax_e - ax_s)];
+ }
+ found = YES;
+ break;
+ }
+ }
+ }
+
+ if (b != oldb2)
+ set_buffer_internal_1 (oldb2);
+
+ if (announceText)
+ {
+ announceText = [announceText stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([announceText length] > 0)
+ {
+ if (![announceText isEqualToString:self.cachedCompletionAnnouncement])
+ {
+ NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: announceText,
+ NSAccessibilityPriorityKey:
+ @(NSAccessibilityPriorityHigh)
+ };
+ NSAccessibilityPostNotificationWithUserInfo (
+ NSApp,
+ NSAccessibilityAnnouncementRequestedNotification,
+ annInfo);
+ }
+ self.cachedCompletionAnnouncement = announceText;
+ }
+ else
+ self.cachedCompletionAnnouncement = nil;
+ }
+ else
+ self.cachedCompletionAnnouncement = nil;
+ }
+ }
+ }
+}
+
@@ -1706,7 +1849,7 @@ index 932d209..416e5a4 100644
{
/* FIXME: What should happen for key sequences with more than
one character? */
@@ -8237,6 +9392,27 @@ ns_in_echo_area (void)
@@ -8237,6 +9523,27 @@ ns_in_echo_area (void)
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -1734,7 +1877,7 @@ index 932d209..416e5a4 100644
}
@@ -9474,6 +10650,297 @@ ns_in_echo_area (void)
@@ -9474,6 +10781,297 @@ ns_in_echo_area (void)
return fs_state;
}
@@ -2032,7 +2175,7 @@ index 932d209..416e5a4 100644
@end /* EmacsView */
@@ -9941,6 +11408,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
@@ -9941,6 +11539,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
return [super accessibilityAttributeValue:attribute];
}
@@ -2047,3 +2190,6 @@ index 932d209..416e5a4 100644
#endif /* NS_IMPL_COCOA */
/* Constrain size and placement of a frame.
--
2.43.0