patches: 0007 fix hl-line-mode blocking SelectedTextChanged
hl-line-mode (and similar) bumps BUF_MODIFF via text property changes on every cursor movement. The else-if structure caused the modiff branch to fire (skipping ValueChanged correctly) but also blocked the cursor-move branch (SelectedTextChanged). Fix: use textDidChange flag to decouple the two branches. ValueChanged and SelectedTextChanged remain mutually exclusive for real edits, but SelectedTextChanged now fires when only text properties changed.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
From 8157451dedda9b43de47f82d1deb85c9d2853a35 Mon Sep 17 00:00:00 2001
|
||||
From 7d5fe56a0e86aee931787eda2e4988a6b4815291 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Sukany <martin@sukany.cz>
|
||||
Date: Sat, 28 Feb 2026 14:46:25 +0100
|
||||
Subject: [PATCH] ns: announce overlay completion candidates for VoiceOver
|
||||
Subject: [PATCH 1/2] ns: announce overlay completion candidates for VoiceOver
|
||||
|
||||
Completion frameworks such as Vertico, Ivy, and Icomplete render
|
||||
candidates via overlay before-string/after-string properties rather
|
||||
@@ -52,8 +52,8 @@ Independent overlay branch, BUF_CHARS_MODIFF gating, candidate
|
||||
announcement with overlay Zoom rect storage.
|
||||
---
|
||||
src/nsterm.h | 3 +
|
||||
src/nsterm.m | 319 +++++++++++++++++++++++++++++++++++++++++++++------
|
||||
2 files changed, 286 insertions(+), 36 deletions(-)
|
||||
src/nsterm.m | 335 +++++++++++++++++++++++++++++++++++++++++++++------
|
||||
2 files changed, 300 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/src/nsterm.h b/src/nsterm.h
|
||||
index 51c30ca..5c15639 100644
|
||||
@@ -77,7 +77,7 @@ index 51c30ca..5c15639 100644
|
||||
BOOL font_panel_active;
|
||||
NSFont *font_panel_result;
|
||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||
index 1780194..d13c5c7 100644
|
||||
index 1780194..6efeb1d 100644
|
||||
--- a/src/nsterm.m
|
||||
+++ b/src/nsterm.m
|
||||
@@ -3258,7 +3258,12 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
|
||||
@@ -403,26 +403,34 @@ index 1780194..d13c5c7 100644
|
||||
self.cachedPoint = point;
|
||||
|
||||
NSDictionary *change = @{
|
||||
@@ -8789,14 +8938,112 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
@@ -8789,16 +8938,126 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
BOOL markActive = !NILP (BVAR (b, mark_active));
|
||||
|
||||
/* --- Text changed (edit) --- */
|
||||
+ ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b);
|
||||
+ BOOL textDidChange = NO;
|
||||
if (modiff != self.cachedModiff)
|
||||
{
|
||||
self.cachedModiff = modiff;
|
||||
- [self postTextChangedNotification:point];
|
||||
+ /* Only post ValueChanged when actual characters changed.
|
||||
+ Text property changes (e.g. face updates from
|
||||
+ Text property changes (e.g. face updates from hl-line-mode,
|
||||
+ vertico--prompt-selection) bump BUF_MODIFF but not
|
||||
+ BUF_CHARS_MODIFF. Posting ValueChanged for property-only
|
||||
+ changes causes VoiceOver to say "new line" when the diff
|
||||
+ is non-empty due to overlay content changes. */
|
||||
+ is non-empty due to overlay content changes.
|
||||
+
|
||||
+ Use textDidChange to avoid blocking the cursor-move branch
|
||||
+ below: property-only changes must not prevent
|
||||
+ SelectedTextChanged from firing when point also moved
|
||||
+ (e.g. hl-line-mode updates face properties on every cursor
|
||||
+ movement in dired and other read-only buffers). */
|
||||
+ if (chars_modiff != self.cachedCharsModiff)
|
||||
+ {
|
||||
+ self.cachedCharsModiff = chars_modiff;
|
||||
+ self.emacsView->overlayZoomActive = NO;
|
||||
+ [self postTextChangedNotification:point];
|
||||
+ textDidChange = YES;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -514,11 +522,19 @@ index 1780194..d13c5c7 100644
|
||||
|
||||
/* --- Cursor moved or selection changed ---
|
||||
- Use 'else if' — edits and selection moves are mutually exclusive
|
||||
+ Use 'else if' --- edits and selection moves are mutually exclusive
|
||||
per the WebKit/Chromium pattern. */
|
||||
else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
|
||||
- per the WebKit/Chromium pattern. */
|
||||
- else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
|
||||
+ Skip when ValueChanged was already posted (edits and selection
|
||||
+ moves are mutually exclusive per the WebKit/Chromium pattern).
|
||||
+ But DO fire when only text properties changed (BUF_MODIFF bumped
|
||||
+ without BUF_CHARS_MODIFF) --- hl-line-mode and similar packages
|
||||
+ update face properties on every cursor movement. */
|
||||
+ if (!textDidChange
|
||||
+ && (point != self.cachedPoint || markActive != self.cachedMarkActive))
|
||||
{
|
||||
@@ -8966,7 +9213,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
ptrdiff_t oldPoint = self.cachedPoint;
|
||||
BOOL oldMarkActive = self.cachedMarkActive;
|
||||
@@ -8966,7 +9225,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
@@ -527,7 +543,7 @@ index 1780194..d13c5c7 100644
|
||||
=================================================================== */
|
||||
|
||||
/* Scan visible range of window W for interactive spans.
|
||||
@@ -9157,7 +9404,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
@@ -9157,7 +9416,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
- (BOOL) isAccessibilityFocused
|
||||
{
|
||||
/* Read the cached point stored by EmacsAccessibilityBuffer on the main
|
||||
@@ -536,7 +552,7 @@ index 1780194..d13c5c7 100644
|
||||
EmacsAccessibilityBuffer *pb = self.parentBuffer;
|
||||
if (!pb)
|
||||
return NO;
|
||||
@@ -9174,7 +9421,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
@@ -9174,7 +9433,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
dispatch_async (dispatch_get_main_queue (), ^{
|
||||
/* lwin is a Lisp_Object captured by value. This is GC-safe
|
||||
because Lisp_Objects are tagged integers/pointers that
|
||||
@@ -545,7 +561,7 @@ index 1780194..d13c5c7 100644
|
||||
Emacs. The WINDOW_LIVE_P check below guards against the
|
||||
window being deleted between capture and execution. */
|
||||
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
|
||||
@@ -9200,7 +9447,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
@@ -9200,7 +9459,7 @@ ns_ax_scan_interactive_spans (struct window *w,
|
||||
|
||||
@end
|
||||
|
||||
@@ -554,7 +570,7 @@ index 1780194..d13c5c7 100644
|
||||
Methods are kept here (same .m file) so they access the ivars
|
||||
declared in the @interface ivar block. */
|
||||
@implementation EmacsAccessibilityBuffer (InteractiveSpans)
|
||||
@@ -10520,13 +10767,13 @@ ns_in_echo_area (void)
|
||||
@@ -10520,13 +10779,13 @@ ns_in_echo_area (void)
|
||||
if (old_title == 0)
|
||||
{
|
||||
char *t = strdup ([[[self window] title] UTF8String]);
|
||||
@@ -570,7 +586,7 @@ index 1780194..d13c5c7 100644
|
||||
[window setTitle: [NSString stringWithUTF8String: size_title]];
|
||||
[window display];
|
||||
xfree (size_title);
|
||||
@@ -11922,7 +12169,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
@@ -11922,7 +12181,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
|
||||
if (WINDOW_LEAF_P (w))
|
||||
{
|
||||
@@ -579,7 +595,7 @@ index 1780194..d13c5c7 100644
|
||||
EmacsAccessibilityBuffer *elem
|
||||
= [existing objectForKey:[NSValue valueWithPointer:w]];
|
||||
if (!elem)
|
||||
@@ -11956,7 +12203,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
@@ -11956,7 +12215,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -588,7 +604,7 @@ index 1780194..d13c5c7 100644
|
||||
Lisp_Object child = w->contents;
|
||||
while (!NILP (child))
|
||||
{
|
||||
@@ -12068,7 +12315,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
@@ -12068,7 +12327,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
accessibilityUpdating = YES;
|
||||
|
||||
/* Detect window tree change (split, delete, new buffer). Compare
|
||||
@@ -597,7 +613,7 @@ index 1780194..d13c5c7 100644
|
||||
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
|
||||
if (!EQ (curRoot, lastRootWindow))
|
||||
{
|
||||
@@ -12077,12 +12324,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
@@ -12077,12 +12336,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
|
||||
}
|
||||
|
||||
/* If tree is stale, rebuild FIRST so we don't iterate freed
|
||||
|
||||
Reference in New Issue
Block a user