Skip to content

Add press-and-hold activation mode to Quick Accent - #48937

Merged
Clint Rutkas (crutkas) merged 4 commits into
mainfrom
user/crutkas/quickaccent-press-and-hold
Jul 3, 2026
Merged

Clint Rutkas (crutkas) merged 4 commits into
mainfrom
user/crutkas/quickaccent-press-and-hold

Conversation

@crutkas

@crutkas Clint Rutkas (crutkas) commented Jun 27, 2026 •

Copy link
Copy Markdown
Member

Summary of the Pull Request

Adds an opt-in press-and-hold activation mode to Quick Accent, like iOS / macOS: hold an accent-capable letter (e.g. a) and after a short, configurable delay the accent picker opens automatically — no separate trigger key (Space/arrows) required.

This is purely additive. The existing trigger-key modes (Left/Right arrow, Space, Both) and all serialized settings values are unchanged.

Recording.2026-06-26.175015.mp4

What it does

  • Holding a letter types the base letter immediately, then arms the picker. After the Hold duration (default 500 ms) the toolbar appears.
  • Navigate the options with the arrow keys / Space, then release the letter to insert the selected accent (it replaces the base letter).
  • A quick tap types just the letter. Holding and releasing without selecting leaves the base letter as-is.
  • Ctrl / Alt / AltGr / Win + letter combinations are left untouched, so shortcuts like Ctrl+A still work.

PR Checklist

  • Closes: N/A — feature enhancement (happy to link a tracking issue if one is preferred)
  • Communication: Discussed direction with maintainers; coordinated with [Quick Accent] Migrate UI to WinUI 3 #48891 (see below)
  • Tests: No automated tests added — the activation decision lives in the C++ low-level keyboard hook and isn't reachable from the existing managed unit-test project. Validated manually (steps below). Open to guidance on the preferred test surface.
  • Localization: All new end-user strings are in Settings.UI/Strings/en-us/Resources.resw with translator comments.
  • Dev docs: doc/devdocs/modules/quickaccent.md updated with the new mode.
  • New binaries: None.
  • Documentation updated: Dev docs updated; public Learn docs can follow once shipped.

Detailed Description of the Pull Request / Additional comments

  • PowerAccentKeyboardService (C++ hook):
    • Append PressAndHold to the internal PowerAccentActivationKey enum (value 3, appended to preserve serialized 0/1/2).
    • Add a holdDuration setting and UpdateHoldDuration(Int32) to the WinRT projection (.idl).
    • In OnKeyDown, arm the picker on the held letter itself; the base letter still types on first press and auto-repeat is swallowed (reuses the existing m_toolbarVisible repeat guard from Quick Accent not properly working when using on-screen keyboard #36853).
    • In OnKeyUp, use the hold duration as the minimum-hold release threshold for this mode (trigger modes keep using inputTime).
    • Modifier guard: Ctrl/Alt/AltGr/Win do not arm the mode.
  • Settings model (Settings.UI.Library): append PressAndHold to PowerAccentActivationKey; add hold_duration_ms (IntProperty, default 500). Existing settings.json without the field falls back to the 500 ms default.
  • PowerAccent.Core: read and forward the hold duration to the hook, and use it as the popup delay when PressAndHold is active.
  • Settings UI: add the "Press and hold the letter" activation option and a "Hold duration (ms)" control that is shown only when that mode is selected.

Enum sync note

PowerAccentActivationKey exists in both the C++ hook and the managed settings library and is kept in sync by integer value. PressAndHold was appended (never reordered) so existing serialized settings (0/1/2) keep their meaning.

Coordination with #48891 (Quick Accent WinUI migration)

This lands as its own atomic change on main. The overlap with the in-progress WinUI migration (#48891) is tiny: only PowerAccent.cs's mode-aware popup delay (a single Task.Delay line). The C++ hook, settings enum/model, and Settings UI are not touched by #48891, so it can rebase onto this with minimal effort.

Validation Steps Performed

  • Built the full chain in Debug|x64:
    • PowerAccent.UI.csproj → rebuilds the C++ PowerAccentKeyboardService projection (incl. UpdateHoldDuration) + PowerAccent.Core + Settings.UI.Library. 0 errors.
    • PowerToys.Settings.csproj → Settings UI XAML / ViewModel / .resw (XamlIndexBuilder search index regenerated). 0 errors.
  • Manual trial of the running module (PowerToys.PowerAccent.exe) with activation_key = 3:
    • Hold a → base letter types immediately; picker opens after ~500 ms; arrows/Space navigate; releasing inserts the accent (replacing the base letter).
    • Quick tap → base letter only. Hold + release without selecting → base letter remains.
    • Ctrl+A / Alt+letter unaffected.

Add an opt-in iOS/macOS-style activation mode where holding an
accent-capable letter (e.g. "a") opens the accent picker automatically
after a configurable hold duration, with no separate trigger key.

- KeyboardListener (C++): append PressAndHold to the activation enum, add
  a holdDuration setting plus an UpdateHoldDuration projection, arm the
  picker in OnKeyDown on the held letter (typing the base letter
  immediately and swallowing auto-repeat), and use the hold duration as
  the release threshold in OnKeyUp. Ctrl/Alt/AltGr/Win Budugs do not arm.
- Settings model: append PressAndHold to PowerAccentActivationKey and add
  a hold_duration_ms property (default 500 ms).
- PowerAccent.Core: read and forward the hold duration, and use it as the
  popup delay when PressAndHold is active.
- Settings UI: add the "Press and hold the letter" option and a
  "Hold duration (ms)" control shown only for that mode.
- Docs: document the new activation mode.

The base letter types immediately and is replaced when an accent is
picked; a quick tap types just the letter. Existing trigger-key modes
(Space / arrows / both) and serialized settings values are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the Product-Quick Accent Refers to the Quick Accent PowerToy label Jun 27, 2026
Fixes from a 3-model code review of the press-and-hold activation mode:

- Don't treat the picker as interactive until the hold duration elapses, so
  Space/arrow pass through during the pre-popup window instead of being
  swallowed and silently navigating an invisible menu.
- Let the letter key-up pass through on a press-and-hold false start to avoid
  a perceived stuck key, and only react to the key-up of the letter that owns
  the toolbar so a second held letter can't cancel or commit it.
- Gate the legacy trigger-key show path off in press-and-hold mode (it could
  still open the picker after a modifier-blocked press).
- Show the press-and-hold activation label on the Settings Dashboard; the
  switch was missing the case (and a default), leaving it blank.
- Hide the now-unused "Input delay" card while press-and-hold is active.
- Defer the character/description build until the popup actually renders so
  quick taps stay off the keystroke hot path.
- Drop redundant explicit OnPropertyChanged calls (keeping RaisePropertyChanged,
  which also persists settings over IPC), reuse the cached letter-key cast, and
  trim verbose comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@daverayment

Copy link
Copy Markdown
Collaborator

In terms of related issues, this would partly fulfil #20429.

Also #24780 asks for Quick Accent to support Space to trigger whitespace character mappings. We've traditionally used other keys for these mappings, as Space is used to activate the application, but an alternative hold-to-activate mode like this would give us the opportunity to duplicate those whitespace mappings to Space. It would have to be additive to accommodate both activation methods, but sounds useful.

Apply two remaining Quick Accent press-and-hold review items:
- Convert SettingsService.HoldDuration to an auto-property, dropping
  the redundant backing field and trivial get/set wrapper.
- Add iOS to the spell-check expect list; macOS is already recognized
  by the software-terms dictionary but iOS is not, so the new
  doc/resource references would otherwise fail check-spelling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@niels9001

Copy link
Copy Markdown
Collaborator

Yu Leng (@moooyo) fyi

Yu Leng (moooyo) pushed a commit that referenced this pull request Jul 1, 2026
## Summary

Quick Accent's ShowToolbar queues a delayed render of the accent menu
through Task.Delay(...).ContinueWith(...). The continuation only checked
_visible before rendering, so a timer started by an earlier key press
could still fire for a **newer** summon — or one that had already been
hidden — popping the accent menu earlier than the configured delay
intended.

This was surfaced while reviewing the press-and-hold work in #48937, but
it is a pre-existing race independent of that feature, so it ships on
its own.

## Fix

- Tag each `ShowToolbar` summon with an incrementing `_showGeneration`
id and capture it in the local closure.
- The delayed continuation now renders only when it is still the most
recent summon (`generation == _showGeneration`) **and** `_visible`.
- Bump the generation when the toolbar hides, so any in-flight timer
queued before the hide is cancelled.

Everything runs on the UI thread (the dispatcher marshals
`ShowToolbar`/hide and the continuation uses
`FromCurrentSynchronizationContext`), so the counter needs no locking.

## Validation

- Built `PowerAccent.UI` (C++ hook + Core) Debug|x64 — 0 warnings, 0
errors.
- Manual: rapid repeated taps of an accent-capable letter no longer
flash the menu early from a leftover timer; normal hold-to-open and
navigation/commit are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… guard

main's #48944 added the _showGeneration race guard to ShowToolbar; this
branch refactored ShowToolbar to defer character building into
PrepareCharacters() off the keystroke hot path. Resolved the overlap by
keeping both the isPressAndHold branch and the generation id, and dropping
main's now-redundant inline _characters/_characterDescriptions/
_showUnicodeDescription assignments (superseded by PrepareCharacters()).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@moooyo Yu Leng (moooyo) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@crutkas
Clint Rutkas (crutkas) enabled auto-merge (squash) July 3, 2026 03:28
@crutkas
Clint Rutkas (crutkas) merged commit af45c3e into main Jul 3, 2026
8 checks passed
@crutkas
Clint Rutkas (crutkas) deleted the user/crutkas/quickaccent-press-and-hold branch July 3, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs-Review This Pull Request awaits the review of a maintainer. Product-Quick Accent Refers to the Quick Accent PowerToy

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants