patches: v6 0007 - fix Zoom Y offset (line_height arithmetic)

This commit is contained in:
2026-02-28 15:39:44 +01:00
parent 9129f032cf
commit 4e5596d9de

View File

@@ -1,4 +1,4 @@
From 3616ffd4289b0a73da93d83fa8dfc7be9d6554b9 Mon Sep 17 00:00:00 2001 From b37888bd77b77009e40b564b05164c584c9305ae Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100 Date: Sat, 28 Feb 2026 14:46:25 +0100
Subject: [PATCH] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH] ns: announce overlay completion candidates for VoiceOver
@@ -31,7 +31,8 @@ Key implementation details:
Do not post SelectedTextChanged (that reads the AX text at cursor Do not post SelectedTextChanged (that reads the AX text at cursor
position, which is the minibuffer input, not the candidate). position, which is the minibuffer input, not the candidate).
- Zoom tracking: store the selected candidate's glyph row rect in - Zoom tracking: store the selected candidate's rect (computed from
FRAME_LINE_HEIGHT and the candidate's visual line index) in
overlayZoomRect. ns_draw_window_cursor checks overlayZoomActive overlayZoomRect. ns_draw_window_cursor checks overlayZoomActive
and uses the stored rect instead of the text cursor rect, keeping and uses the stored rect instead of the text cursor rect, keeping
Zoom focused on the candidate. The flag is cleared when the user Zoom focused on the candidate. The flag is cleared when the user
@@ -49,8 +50,8 @@ Independent overlay branch, BUF_CHARS_MODIFF gating, candidate
announcement with overlay Zoom rect storage. announcement with overlay Zoom rect storage.
--- ---
src/nsterm.h | 3 + src/nsterm.h | 3 +
src/nsterm.m | 314 +++++++++++++++++++++++++++++++++++++++++++++------ src/nsterm.m | 316 +++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 281 insertions(+), 36 deletions(-) 2 files changed, 283 insertions(+), 36 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 51c30ca..5c15639 100644 index 51c30ca..5c15639 100644
@@ -74,7 +75,7 @@ index 51c30ca..5c15639 100644
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 1780194..31b2c33 100644 index 1780194..2fdea91 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -3258,7 +3258,12 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, @@ -3258,7 +3258,12 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
@@ -397,7 +398,7 @@ index 1780194..31b2c33 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -8789,14 +8935,110 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem, @@ -8789,14 +8935,112 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
BOOL markActive = !NILP (BVAR (b, mark_active)); BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */ /* --- Text changed (edit) --- */
@@ -466,34 +467,36 @@ index 1780194..31b2c33 100644
+ /* --- Zoom tracking for overlay candidates --- + /* --- Zoom tracking for overlay candidates ---
+ Store the candidate row rect so draw_window_cursor + Store the candidate row rect so draw_window_cursor
+ focuses Zoom there instead of on the text cursor. + focuses Zoom there instead of on the text cursor.
+ Cleared when the user types (chars_modiff change). */ + Cleared when the user types (chars_modiff change).
+
+ Use default line height to compute the Y offset:
+ row 0 is the input line, overlay candidates start
+ from row 1. This avoids fragile glyph matrix row
+ index mapping which can be off when group titles
+ or wrapped lines shift row numbering. */
+ if (selected_line >= 0) + if (selected_line >= 0)
+ { + {
+ struct window *w2 = [self validWindow]; + struct window *w2 = [self validWindow];
+ if (w2 && w2->current_matrix) + if (w2)
+ { + {
+ EmacsView *view = self.emacsView; + EmacsView *view = self.emacsView;
+ int target_vrow = selected_line + 1; + struct frame *f2 = XFRAME (w2->frame);
+ int nrows = w2->current_matrix->nrows; + int line_h = FRAME_LINE_HEIGHT (f2);
+ if (target_vrow < nrows) + int y_off = (selected_line + 1) * line_h;
+ { +
+ struct glyph_row *row + if (y_off < w2->pixel_height)
+ = w2->current_matrix->rows + target_vrow;
+ if (row->enabled_p
+ && row->visible_height > 0)
+ { + {
+ view->overlayZoomRect = NSMakeRect ( + view->overlayZoomRect = NSMakeRect (
+ w2->pixel_left, + w2->pixel_left,
+ WINDOW_TOP_EDGE_Y (w2) + row->y, + WINDOW_TOP_EDGE_Y (w2) + y_off,
+ w2->pixel_width, + w2->pixel_width,
+ row->visible_height); + line_h);
+ view->overlayZoomActive = YES; + view->overlayZoomActive = YES;
+ } + }
+ } + }
+ } + }
+ } + }
+ } + }
+ }
+ else + else
+ { + {
+ /* No selected candidate --- overlay completion ended + /* No selected candidate --- overlay completion ended
@@ -510,7 +513,7 @@ index 1780194..31b2c33 100644
per the WebKit/Chromium pattern. */ per the WebKit/Chromium pattern. */
else if (point != self.cachedPoint || markActive != self.cachedMarkActive) else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
{ {
@@ -8966,7 +9208,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem, @@ -8966,7 +9210,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
/* =================================================================== /* ===================================================================
@@ -519,7 +522,7 @@ index 1780194..31b2c33 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9157,7 +9399,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9157,7 +9401,7 @@ ns_ax_scan_interactive_spans (struct window *w,
- (BOOL) isAccessibilityFocused - (BOOL) isAccessibilityFocused
{ {
/* Read the cached point stored by EmacsAccessibilityBuffer on the main /* Read the cached point stored by EmacsAccessibilityBuffer on the main
@@ -528,7 +531,7 @@ index 1780194..31b2c33 100644
EmacsAccessibilityBuffer *pb = self.parentBuffer; EmacsAccessibilityBuffer *pb = self.parentBuffer;
if (!pb) if (!pb)
return NO; return NO;
@@ -9174,7 +9416,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9174,7 +9418,7 @@ ns_ax_scan_interactive_spans (struct window *w,
dispatch_async (dispatch_get_main_queue (), ^{ dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe /* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that because Lisp_Objects are tagged integers/pointers that
@@ -537,7 +540,7 @@ index 1780194..31b2c33 100644
Emacs. The WINDOW_LIVE_P check below guards against the Emacs. The WINDOW_LIVE_P check below guards against the
window being deleted between capture and execution. */ window being deleted between capture and execution. */
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin))) if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
@@ -9200,7 +9442,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9200,7 +9444,7 @@ ns_ax_scan_interactive_spans (struct window *w,
@end @end
@@ -546,7 +549,7 @@ index 1780194..31b2c33 100644
Methods are kept here (same .m file) so they access the ivars Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */ declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans) @implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -10520,13 +10762,13 @@ ns_in_echo_area (void) @@ -10520,13 +10764,13 @@ ns_in_echo_area (void)
if (old_title == 0) if (old_title == 0)
{ {
char *t = strdup ([[[self window] title] UTF8String]); char *t = strdup ([[[self window] title] UTF8String]);
@@ -562,7 +565,7 @@ index 1780194..31b2c33 100644
[window setTitle: [NSString stringWithUTF8String: size_title]]; [window setTitle: [NSString stringWithUTF8String: size_title]];
[window display]; [window display];
xfree (size_title); xfree (size_title);
@@ -11922,7 +12164,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -11922,7 +12166,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -571,7 +574,7 @@ index 1780194..31b2c33 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -11956,7 +12198,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -11956,7 +12200,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
} }
else else
{ {
@@ -580,7 +583,7 @@ index 1780194..31b2c33 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12068,7 +12310,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12068,7 +12312,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
accessibilityUpdating = YES; accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
@@ -589,7 +592,7 @@ index 1780194..31b2c33 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12077,12 +12319,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12077,12 +12321,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
} }
/* If tree is stale, rebuild FIRST so we don't iterate freed /* If tree is stale, rebuild FIRST so we don't iterate freed