v15.3: fix VoiceOver cursor not following during typing
Root cause (confirmed via WebKit/Chromium source): ValueChanged (edit) and SelectedTextChanged (cursor move) are MUTUALLY EXCLUSIVE — apps must never send both for the same user action. VoiceOver enters 'typing mode' on Edit notifications and suppresses/ignores concurrent SelectionMove notifications, causing the cursor to appear stuck. Fix: (1) Update cachedPoint inside the modiff branch so the selection-move check doesn't trigger for edit-caused point changes. (2) Change 'if' to 'else if' for explicit mutual exclusion. Source: WebKit AXObjectCacheMac.mm — postTextStateChangePlatformNotification vs postTextSelectionChangePlatformNotification are separate code paths that never fire for the same event.
This commit is contained in:
@@ -73,7 +73,7 @@ index 7c1ee4c..4abeafe 100644
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||||
index 932d209..792f7c5 100644
|
index 932d209..5252e6d 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)
|
||||||
@@ -126,7 +126,7 @@ index 932d209..792f7c5 100644
|
|||||||
ns_focus (f, NULL, 0);
|
ns_focus (f, NULL, 0);
|
||||||
|
|
||||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||||
@@ -6847,6 +6883,756 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
@@ -6847,6 +6883,764 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -743,6 +743,11 @@ index 932d209..792f7c5 100644
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ self.cachedModiff = modiff;
|
+ self.cachedModiff = modiff;
|
||||||
|
+ /* Update cachedPoint here so the selection-move branch below
|
||||||
|
+ does NOT fire for point changes caused by edits. WebKit and
|
||||||
|
+ Chromium never send both ValueChanged and SelectedTextChanged
|
||||||
|
+ for the same user action — they are mutually exclusive. */
|
||||||
|
+ self.cachedPoint = point;
|
||||||
+
|
+
|
||||||
+ NSDictionary *change = @{
|
+ NSDictionary *change = @{
|
||||||
+ @"AXTextEditType": @3,
|
+ @"AXTextEditType": @3,
|
||||||
@@ -758,8 +763,11 @@ index 932d209..792f7c5 100644
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ /* --- Cursor moved or selection changed → line reading ---
|
+ /* --- Cursor moved or selection changed → line reading ---
|
||||||
+ kAXTextStateChangeTypeSelectionMove = 2. */
|
+ kAXTextStateChangeTypeSelectionMove = 2.
|
||||||
+ if (point != self.cachedPoint || markActive != self.cachedMarkActive)
|
+ Use 'else if' — edits and selection moves are mutually exclusive
|
||||||
|
+ per the WebKit/Chromium pattern. VoiceOver gets confused if
|
||||||
|
+ both notifications arrive in the same runloop iteration. */
|
||||||
|
+ else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
|
||||||
+ {
|
+ {
|
||||||
+ ptrdiff_t oldPoint = self.cachedPoint;
|
+ ptrdiff_t oldPoint = self.cachedPoint;
|
||||||
+ self.cachedPoint = point;
|
+ self.cachedPoint = point;
|
||||||
@@ -883,7 +891,7 @@ index 932d209..792f7c5 100644
|
|||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
|
|
||||||
EmacsView implementation
|
EmacsView implementation
|
||||||
@@ -6889,6 +7675,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
@@ -6889,6 +7683,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
|
||||||
[layer release];
|
[layer release];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -891,7 +899,7 @@ index 932d209..792f7c5 100644
|
|||||||
[[self menu] release];
|
[[self menu] release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
@@ -8237,6 +9024,18 @@ ns_in_echo_area (void)
|
@@ -8237,6 +9032,18 @@ 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
|
||||||
@@ -910,7 +918,7 @@ index 932d209..792f7c5 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -9474,6 +10273,290 @@ ns_in_echo_area (void)
|
@@ -9474,6 +10281,290 @@ ns_in_echo_area (void)
|
||||||
return fs_state;
|
return fs_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,7 +1209,7 @@ index 932d209..792f7c5 100644
|
|||||||
@end /* EmacsView */
|
@end /* EmacsView */
|
||||||
|
|
||||||
|
|
||||||
@@ -9941,6 +11024,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
|
@@ -9941,6 +11032,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
|
||||||
|
|
||||||
return [super accessibilityAttributeValue:attribute];
|
return [super accessibilityAttributeValue:attribute];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user