patches: 0008 - Zoom via parent overlayZoomRect + suppress window announcement

This commit is contained in:
2026-02-28 16:29:26 +01:00
parent 3e5fe814b8
commit 2dc4182856

View File

@@ -1,4 +1,4 @@
From e11d0688f119046827cd1895008d3a93e22f6d0d Mon Sep 17 00:00:00 2001 From a06955c951aaad2330add250f815c34d60128948 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100 Date: Sat, 28 Feb 2026 16:01:29 +0100
Subject: [PATCH 2/2] ns: announce child frame completion candidates for Subject: [PATCH 2/2] ns: announce child frame completion candidates for
@@ -31,11 +31,14 @@ Safety measures:
- Buffer size limit (10000 chars) skips non-completion child frames - Buffer size limit (10000 chars) skips non-completion child frames
such as eldoc documentation or which-key popups. such as eldoc documentation or which-key popups.
Announce via AnnouncementRequested to NSApp with High priority. Suppress VoiceOver's automatic "X window" announcement by setting
Use direct UAZoomChangeFocus (not the overlayZoomRect flag used the child frame's accessibility subrole to FloatingWindow (popovers
for minibuffer overlay completion) because the child frame renders and floating windows don't trigger window-appeared announcements).
independently --- its ns_update_end runs after the parent frame's
draw_window_cursor, so the last Zoom call wins. Zoom tracking stores the candidate rect in the PARENT frame's
overlayZoomRect (via screen coordinate conversion), so that the
parent's draw_window_cursor focuses Zoom on the candidate instead
of the text cursor. Cleared when no candidate is found.
* src/nsterm.h (EmacsView): Add announceChildFrameCompletion. * src/nsterm.h (EmacsView): Add announceChildFrameCompletion.
* src/nsterm.m (ns_ax_selected_child_frame_text): New function. * src/nsterm.m (ns_ax_selected_child_frame_text): New function.
@@ -44,8 +47,8 @@ draw_window_cursor, so the last Zoom call wins.
handler for FRAME_PARENT_FRAME frames, under re-entrance guard. handler for FRAME_PARENT_FRAME frames, under re-entrance guard.
--- ---
src/nsterm.h | 1 + src/nsterm.h | 1 +
src/nsterm.m | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/nsterm.m | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 205 insertions(+), 1 deletion(-) 2 files changed, 231 insertions(+), 1 deletion(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 5c15639..21b2823 100644 index 5c15639..21b2823 100644
@@ -60,7 +63,7 @@ index 5c15639..21b2823 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index d13c5c7..092bc11 100644 index d13c5c7..ea2a914 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7066,6 +7066,110 @@ ns_ax_selected_overlay_text (struct buffer *b, @@ -7066,6 +7066,110 @@ ns_ax_selected_overlay_text (struct buffer *b,
@@ -174,7 +177,7 @@ index d13c5c7..092bc11 100644
/* 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
@@ -12299,6 +12403,93 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12299,6 +12403,119 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -220,8 +223,19 @@ index d13c5c7..092bc11 100644
+ NSString *candidate + NSString *candidate
+ = ns_ax_selected_child_frame_text (b, w->contents, &selected_line); + = ns_ax_selected_child_frame_text (b, w->contents, &selected_line);
+ +
+ struct frame *parent = FRAME_PARENT_FRAME (emacsframe);
+
+ if (!candidate) + if (!candidate)
+ {
+ /* No selected candidate --- clear parent Zoom override. */
+ if (parent)
+ {
+ EmacsView *parentView = FRAME_NS_VIEW (parent);
+ if (parentView)
+ parentView->overlayZoomActive = NO;
+ }
+ return; + return;
+ }
+ +
+ /* Deduplicate --- avoid re-announcing the same candidate. */ + /* Deduplicate --- avoid re-announcing the same candidate. */
+ const char *cstr = [candidate UTF8String]; + const char *cstr = [candidate UTF8String];
@@ -230,6 +244,12 @@ index d13c5c7..092bc11 100644
+ xfree (lastCandidate); + xfree (lastCandidate);
+ lastCandidate = xstrdup (cstr); + lastCandidate = xstrdup (cstr);
+ +
+ /* Suppress VoiceOver's automatic "X window" announcement for
+ the child frame. Completion popups are semantically popovers,
+ not windows. Setting the subrole once is idempotent. */
+ [[self window]
+ setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole];
+
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{
+ NSAccessibilityAnnouncementKey: candidate, + NSAccessibilityAnnouncementKey: candidate,
+ NSAccessibilityPriorityKey: + NSAccessibilityPriorityKey:
@@ -240,35 +260,44 @@ index d13c5c7..092bc11 100644
+ NSAccessibilityAnnouncementRequestedNotification, + NSAccessibilityAnnouncementRequestedNotification,
+ annInfo); + annInfo);
+ +
+ /* Zoom tracking: focus on the selected row in the child frame. + /* Zoom tracking: store the candidate rect in the PARENT frame's
+ Use direct UAZoomChangeFocus rather than overlayZoomRect because + overlayZoomRect so that draw_window_cursor focuses Zoom on the
+ the child frame renders independently of the parent. */ + candidate instead of the text cursor. Convert through screen
+ if (selected_line >= 0 && UAZoomEnabled ()) + coordinates to handle arbitrary child frame positioning. */
+ if (selected_line >= 0 && parent)
+ {
+ EmacsView *parentView = FRAME_NS_VIEW (parent);
+ if (parentView)
+ { + {
+ int line_h = FRAME_LINE_HEIGHT (emacsframe); + int line_h = FRAME_LINE_HEIGHT (emacsframe);
+ int y_off = selected_line * line_h; + int y_off = selected_line * line_h;
+ NSRect r = NSMakeRect ( + NSRect childRect = NSMakeRect (
+ WINDOW_TEXT_TO_FRAME_PIXEL_X (w, 0), + WINDOW_TEXT_TO_FRAME_PIXEL_X (w, 0),
+ WINDOW_TO_FRAME_PIXEL_Y (w, y_off), + WINDOW_TO_FRAME_PIXEL_Y (w, y_off),
+ FRAME_COLUMN_WIDTH (emacsframe), + FRAME_COLUMN_WIDTH (emacsframe),
+ line_h); + line_h);
+ NSRect winRect = [self convertRect:r toView:nil]; +
+ NSRect screenRect + /* Child view → screen → parent view. */
+ = [[self window] convertRectToScreen:winRect]; + NSRect childWinR
+ CGRect cgRect = NSRectToCGRect (screenRect); + = [self convertRect:childRect toView:nil];
+ CGFloat primaryH + NSRect screenR
+ = [[[NSScreen screens] firstObject] frame].size.height; + = [[self window] convertRectToScreen:childWinR];
+ cgRect.origin.y + NSRect parentWinR
+ = primaryH - cgRect.origin.y - cgRect.size.height; + = [[parentView window]
+ UAZoomChangeFocus (&cgRect, &cgRect, + convertRectFromScreen:screenR];
+ kUAZoomFocusTypeInsertionPoint); + NSRect parentViewR
+ = [parentView convertRect:parentWinR fromView:nil];
+
+ parentView->overlayZoomRect = parentViewR;
+ parentView->overlayZoomActive = YES;
+ }
+ } + }
+} +}
+ +
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12309,11 +12500,23 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12309,11 +12526,23 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
/* Re-entrance guard: VoiceOver callbacks during notification posting /* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us can trigger redisplay, which calls ns_update_end, which calls us