v15.2: fix all AXTextStateChange enum values (off-by-one from Unknown=0)

Root cause: Apple's AXTextStateChange enums start with Unknown=0,
shifting all named constants by +1 vs our values. WebKit source
(AXObjectCacheMac.mm) confirms:
- kAXTextStateChangeTypeEdit = 1 (was 0)
- kAXTextStateChangeTypeSelectionMove = 2 (was 1)
- kAXTextSelectionDirectionPrevious = 3 (was 2)
- kAXTextSelectionDirectionNext = 4 (was 3)
- kAXTextSelectionDirectionDiscontiguous = 5 (was 4)
- kAXTextSelectionGranularityCharacter = 1 (was 0)
- kAXTextSelectionGranularityLine = 3 (was 2)

Typing echo worked by coincidence (kAXTextEditTypeTyping=3 unchanged).
SelectionMove=1 mapped to Edit, so VoiceOver ignored cursor movement.

Completions two-line reading is expected (columnar buffer layout).
This commit is contained in:
2026-02-26 14:11:16 +01:00
parent cd48418a3c
commit 9d963a6ab1

View File

@@ -73,7 +73,7 @@ index 7c1ee4c..4abeafe 100644
diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..e830f54 100644
index 932d209..792f7c5 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
@@ -718,7 +718,7 @@ index 932d209..e830f54 100644
+ BOOL markActive = !NILP (BVAR (b, mark_active));
+
+ /* --- Text changed → typing echo ---
+ kAXTextStateChangeTypeEdit = 0, kAXTextEditTypeTyping = 3. */
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */
+ if (modiff != self.cachedModiff)
+ {
+ /* Capture changed char before invalidating cache. */
@@ -750,7 +750,7 @@ index 932d209..e830f54 100644
+ @"AXTextChangeValueLength": @([changedChar length])
+ };
+ NSDictionary *userInfo = @{
+ @"AXTextStateChangeType": @0,
+ @"AXTextStateChangeType": @1,
+ @"AXTextChangeValues": @[change]
+ };
+ NSAccessibilityPostNotificationWithUserInfo (
@@ -758,30 +758,30 @@ index 932d209..e830f54 100644
+ }
+
+ /* --- Cursor moved or selection changed → line reading ---
+ kAXTextStateChangeTypeSelectionMove = 1. */
+ kAXTextStateChangeTypeSelectionMove = 2. */
+ if (point != self.cachedPoint || markActive != self.cachedMarkActive)
+ {
+ ptrdiff_t oldPoint = self.cachedPoint;
+ self.cachedPoint = point;
+ self.cachedMarkActive = markActive;
+
+ /* Compute direction: 2=Previous, 3=Next, 4=Discontiguous. */
+ NSInteger direction = 4;
+ /* Compute direction: 3=Previous, 4=Next, 5=Discontiguous. */
+ NSInteger direction = 5;
+ if (point > oldPoint)
+ direction = 3;
+ direction = 4;
+ else if (point < oldPoint)
+ direction = 2;
+ direction = 3;
+
+ /* Compute granularity from movement distance.
+ Check if we crossed a newline → line movement (2).
+ Otherwise single char (0) or discontiguous (5=unknown). */
+ NSInteger granularity = 5;
+ Check if we crossed a newline → line movement (3).
+ Otherwise single char (1) or unknown (0). */
+ NSInteger granularity = 0;
+ [self ensureTextCache];
+ if (cachedText && oldPoint > 0)
+ {
+ ptrdiff_t delta = point - oldPoint;
+ if (delta == 1 || delta == -1)
+ granularity = 0; /* Character. */
+ granularity = 1; /* Character. */
+ else
+ {
+ /* Check for line crossing by looking for newlines
@@ -798,13 +798,13 @@ index 932d209..e830f54 100644
+ options:0
+ range:searchRange];
+ if (nl.location != NSNotFound)
+ granularity = 2; /* Line. */
+ granularity = 3; /* Line. */
+ }
+ }
+ }
+
+ NSDictionary *moveInfo = @{
+ @"AXTextStateChangeType": @1,
+ @"AXTextStateChangeType": @2,
+ @"AXTextSelectionDirection": @(direction),
+ @"AXTextSelectionGranularity": @(granularity)
+ };