Skip to content

fix(ui): follow-up fixes for the ascii_mode status icon#1159

Open
metalbreeze wants to merge 3 commits into
rime:masterfrom
metalbreeze:fix/status-icon-followups
Open

fix(ui): follow-up fixes for the ascii_mode status icon#1159
metalbreeze wants to merge 3 commits into
rime:masterfrom
metalbreeze:fix/status-icon-followups

Conversation

@metalbreeze

Copy link
Copy Markdown
Contributor

Summary

Two follow-up fixes for the menu-bar status icon introduced in #1122, found while testing across input-source switches.

1. Keep status icon and modifier state in sync on activation

Switching to Squirrel produces no flagsChanged event, so state established while another input source was active goes unnoticed:

  • lastModifiers may hold a stale Caps Lock bit; the user's first Caps Lock press after switching in then compares equal to it and is swallowed by the early-return in handle(). Seed the bit from CGEventSource.flagsState(.combinedSessionState)NSEvent.modifierFlags only reflects this process's own event stream and is stale at activation time.
  • Sessions are torn down and rebuilt around input-source switches, and a fresh session initializes its options from the schema's switches/@N/reset without firing any option notification — the icon keeps showing the pre-switch state while the session actually starts in the reset state (the icon says 中 while typing produces English). Repaint the icon silently from the actual state on every activation. set_option is deliberately not used for this: re-asserting an option fires the notification and would flash the status bubble on every app switch. Nor is ascii_mode ever written from an observed Caps Lock — macOS clears Caps Lock whenever the input source changes, so a Caps Lock seen at activation is transient and acting on it would race that clearing.

2. Hide the status icon reliably when switching away from Squirrel

The block-based addObserver(forName:object:queue:using:) on DistributedNotificationCenter registers with a suspension behavior that holds notifications back while the process is not active — which is precisely Squirrel's state right after the user switches to another input source. The "hide now" notification sat undelivered until the next activation, so the icon lingered in the menu bar while ABC was selected. Register selector-based with suspensionBehavior: .deliverImmediately instead; the handler also keeps performing the #1140 stranded-composition cleanup.

Testing

  • Toggling Shift / Caps Lock within Squirrel updates the icon as before.
  • Switching Squirrel → ABC hides the icon immediately; ABC → Squirrel shows it with the session's true mode and schema label.
  • Fresh sessions (relaunch, input-source round-trips) show the schema's reset state truthfully — no more 中-icon-but-English-typing.
  • With show_notifications_when: appropriate, no status-bubble flashes on app switches.

🤖 Generated with Claude Code

shu.nie and others added 3 commits July 24, 2026 20:15
Switching to Squirrel produces no flagsChanged event, so state
established while another input source was active goes unnoticed. Two
consequences fixed in activateServer:

- lastModifiers may hold a stale Caps Lock bit, so the user's first
  Caps Lock press after switching in compares equal to lastModifiers
  and is swallowed by the early-return in handle(). Seed the bit from
  CGEventSource.flagsState(.combinedSessionState) — NSEvent's
  modifierFlags only reflects this process's own event stream and is
  stale at activation time.

- The icon may not reflect the session's actual ascii_mode: sessions
  are torn down and rebuilt around input-source switches, and a fresh
  session initializes its options from the schema (switches/@N/reset)
  without firing any option notification — the icon keeps showing the
  pre-switch state while the session actually starts in the schema's
  reset state. Repaint it silently from the actual state on every
  activation. set_option is deliberately not used for this:
  re-asserting an option fires the notification and would flash the
  status bubble on every app switch. Nor is ascii_mode ever written
  from the observed Caps Lock: macOS clears Caps Lock when the input
  source changes, so a Caps Lock seen at activation is transient and
  acting on it would race that clearing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The status icon's visibility follows the
kTISNotifySelectedKeyboardInputSourceChanged distributed notification.
The block-based addObserver(forName:object:queue:using:) registers with
a suspension behavior that holds notifications back while the process
is not active — which is precisely Squirrel's state right after the
user switches to another input source. The "hide now" notification then
sat undelivered until the next activation, so the icon lingered in the
menu bar while ABC was selected.

Register with suspensionBehavior: .deliverImmediately (selector-based
API) so the notification arrives regardless of activation state, and
hop to the main queue before touching NSStatusItem. The observer is
already removed in applicationWillTerminate via
DistributedNotificationCenter.removeObserver(self).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Silences the SwiftLint unused_parameter warning; #selector resolution
is unaffected by the anonymous parameter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant