Skip to content

fix(skia): Support Windows Alt codes in TextBox via CharacterReceived - #23901

Open
ramezgerges wants to merge 7 commits into
unoplatform:masterfrom
ramezgerges:dev/rara/win32-alt-codes
Open

fix(skia): Support Windows Alt codes in TextBox via CharacterReceived#23901
ramezgerges wants to merge 7 commits into
unoplatform:masterfrom
ramezgerges:dev/rara/win32-alt-codes

Conversation

@ramezgerges

Copy link
Copy Markdown
Contributor

GitHub Issue: closes #22254

PR Type:

🐞 Bugfix

What changed? 🚀

On the Skia Desktop (Windows) target, Windows Alt codes (hold Alt, type numpad digits, release Alt — e.g. Alt+0154š) never inserted anything into TextBox. The composed WM_CHAR is synthesized by TranslateMessage while translating the Alt key-up, so it was attached to the KeyUp KeyEventArgs, which nothing consumes (TextBox inserts on KeyDown only) — the character was silently destroyed.

This PR implements UIElement.CharacterReceived as a real routed event on Skia (it was a [NotImplemented] stub) and delivers Alt codes through it:

  • UIElement.CharacterReceived + Control.OnCharacterReceived are implemented following the ContextRequested precedent (flag, routed event registration, InvokeHandler case, generated-stub removal, ImplementedRoutedEventsGenerator entry, Control class-handler wiring).
  • Characters produced by a key press (regular typing) are raised after KeyDown from InputManager, matching the WM_KEYDOWNWM_CHAR ordering on Windows.
  • Characters composed on a key release (Alt+numpad codes) are delivered by the Win32 host through a new internal IUnoKeyboardInputSource.CharacterReceived channel, with KeyStatus.IsKeyReleased set — mirroring the real WM_CHAR's lParam transition bit. Windows.UI.Core.CharacterReceivedEventArgs is implemented as the channel payload.
  • TextBox inserts key-release characters from OnCharacterReceived. The path doesn't depend on an IME session, so Alt codes now also work in PasswordBox.
  • No IME/composition machinery is involved: TextCompositionStarted/Changed/Ended do not fire for Alt codes (the end-to-end test asserts this).

The WASM half of the issue (digits being typed alongside the character) was already fixed on master by aa9f15b and is unreleased; the composed-character delivery on WASM was verified working via a CDP-driven emulation of the exact browser event stream.

Also includes a one-line fix to the browser keyboard debug log (missing alt modifier and a key=$ interpolation typo).

Validation (runtime): full Given_TextBox + Given_ComboBox suites pass on Skia Desktop (263/263); new runtime tests cover the typing path (raised once, after KeyDown), key-release insertion incl. selection replacement, read-only/key-press no-ops, PasswordBox, and a Win32-only end-to-end test that posts the real WM_KEYUP(VK_MENU)+WM_CHAR pair to the app's own HWND. Manually validated with physical keyboard Alt codes on Windows.

Notes / known limitations:

  • CoreWindow.CharacterReceived and InputKeyboardSource.CharacterReceived remain unimplemented; native (non-Skia) targets declare the routed event but don't raise it yet.
  • ComboBox's #if HAS_UNO typeahead workaround (synthesized CharacterReceived) is left in place and can migrate to the real event as a follow-up; its popup-child subscription now activates with the real event (no double-fire with the workaround — verified by the ComboBox suite).
  • Characters beyond the BMP entered via hex-numpad (EnableHexNumpad) arrive as two WM_CHARs and only the first is delivered — pre-existing limitation shared with the key-press path.

PR Checklist ✅

🤖 Generated with Claude Code

ramezgerges and others added 3 commits July 28, 2026 18:01
Implements the CharacterReceived routed event and the matching
Control.OnCharacterReceived virtual on Skia targets. Characters produced
by a key press are raised after KeyDown from InputManager (mirroring the
WM_KEYDOWN → WM_CHAR ordering); characters composed on a key release —
Windows Alt+numpad codes, which TranslateMessage synthesizes when Alt is
released — are delivered by the Win32 host through a new
IUnoKeyboardInputSource.CharacterReceived channel with
KeyStatus.IsKeyReleased set, mirroring the real WM_CHAR lParam
transition bit. Windows.UI.Core.CharacterReceivedEventArgs is
implemented as the channel's payload.

Also fixes the browser keyboard debug log (missing alt modifier and a
key=$ interpolation typo).

Part of unoplatform#22254.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An Alt+numpad code's character arrives with the Alt key release, so the
KeyDown-based insertion path never sees it and the character was
silently dropped on Win32. TextBox now inserts key-release characters
from CharacterReceived; the path doesn't depend on an IME session, so
this also fixes Alt codes in PasswordBox.

Fixes unoplatform#22254 for the Skia Desktop (Windows) target; the WASM half was
fixed by aa9f15b.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers the typing path (CharacterReceived raised once, after KeyDown),
key-release insertion incl. selection replacement, read-only and
key-press no-ops, PasswordBox, direct IME commits, and a Win32-only
end-to-end test posting the real WM_KEYUP(VK_MENU)+WM_CHAR pair to the
app's own HWND, asserting no composition events are raised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 22:28
@github-actions github-actions Bot added platform/wasm 🌐 Categorizes an issue or PR as relevant to the WebAssembly platform platform/android 🤖 Categorizes an issue or PR as relevant to the Android platform platform/macos 🍏 Categorizes an issue or PR as relevant to the macOS platform platform/ios 🍎 Categorizes an issue or PR as relevant to the iOS platform area/skia ✏️ Categorizes an issue or PR as relevant to Skia area/code-generation Categorizes an issue or PR as relevant to code generation platform/x11 🐧 Categorizes an issue or PR as relevant to X11 labels Jul 28, 2026

Copilot AI 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.

Pull request overview

This PR fixes Skia Desktop (Win32) TextBox input so Windows Alt+numpad composed characters are delivered and inserted correctly by implementing UIElement.CharacterReceived as a real routed event on Skia and wiring a Win32-only keyboard-input channel for characters that arrive on key release (e.g., Alt+0154š). This aligns the Skia input pipeline more closely with Win32’s WM_KEYDOWNWM_CHAR ordering and avoids losing characters that are synthesized during Alt key-up.

Changes:

  • Implement UIElement.CharacterReceived routed event on Skia, plus Control.OnCharacterReceived override plumbing and generator support.
  • Deliver Win32 key-release composed characters via IUnoKeyboardInputSource.CharacterReceived and handle them in TextBox insertion logic.
  • Add runtime tests covering key-press ordering, key-release insertion behavior (including selection replacement), read-only behavior, and a Win32 end-to-end PostMessage path; includes a small browser keyboard debug-log fix.

Reviewed changes

Copilot reviewed 18 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Uno.UWP/UI/Core/Internal/IUnoKeyboardInputSource.cs Adds CharacterReceived to the internal keyboard input source contract.
src/Uno.UWP/UI/Core/CharacterReceivedEventArgs.cs Introduces a real CharacterReceivedEventArgs payload type (Skia path).
src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Core/CharacterReceivedEventArgs.cs Removes generated [NotImplemented] stubs in favor of the real implementation.
src/Uno.UI/UI/Xaml/UIElement.RoutedEvents.cs Declares CharacterReceivedEvent and exposes UIElement.CharacterReceived event; adds invoke support.
src/Uno.UI/UI/Xaml/RoutedEventFlag.cs Enables RoutedEventFlag.CharacterReceived and keeps it out of _isKey.
src/Uno.UI/UI/Xaml/Internal/InputManager.Keyboard.skia.cs Raises CharacterReceived after KeyDown (key press) and from the new key-release channel.
src/Uno.UI/UI/Xaml/Input/CharacterReceivedRoutedEventArgs.cs Adds an internal ctor that supports setting OriginalSource for raised events/tests.
src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.skia.cs Inserts key-release composed characters via OnCharacterReceived (Alt codes).
src/Uno.UI/UI/Xaml/Controls/Control/Control.cs Adds OnCharacterReceived virtual and class-handler subscription via implemented routed events.
src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml/UIElement.cs Removes generated [NotImplemented] CharacterReceived members due to real declarations.
src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml.Controls/Control.cs Removes generated [NotImplemented] OnCharacterReceived stub due to real override point.
src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.skia.cs Adds runtime test coverage for direct commits and Win32 Alt-code delivery behavior.
src/Uno.UI.Runtime.Skia.X11/Devices/Input/X11KeyboardInputSource.cs Implements the new interface event as a no-op for X11.
src/Uno.UI.Runtime.Skia.Win32/Devices/Input/Win32WindowWrapper.Keyboard.cs Delivers key-release composed characters via CharacterReceived instead of attaching them to KeyUp.
src/Uno.UI.Runtime.Skia.WebAssembly.Browser/Devices/Input/BrowserKeyboardInputSource.cs Implements the new interface event as a no-op for browser Skia and fixes keyboard debug log.
src/Uno.UI.Runtime.Skia.MacOS/UI/Xaml/Window/MacOSWindowHost.cs Implements the new interface event as a no-op for macOS Skia.
src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/Devices/Input/FrameBufferKeyboardInputSource.cs Implements the new interface event as a no-op for framebuffer.
src/Uno.UI.Runtime.Skia.AppleUIKit/Devices/Input/UnoKeyboardInputSource.cs Implements the new interface event as a no-op for AppleUIKit Skia.
src/Uno.UI.Runtime.Skia.Android/Devices/Input/AndroidKeyboardInputSource.cs Implements the new interface event as a no-op for Android Skia.
src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlConstants.cs Adds CharacterReceivedRoutedEventArgs type constant for generators.
src/SourceGenerators/Uno.UI.SourceGenerators/ImplementedRoutedEvents/ImplementedRoutedEventsGenerator.cs Registers OnCharacterReceived as an implemented routed event for Control override detection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The real WM_CHAR inherits the Alt key-up's lParam, whose
previous-key-state bit is set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 22:38
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 18 out of 21 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 28, 2026 22:45

Copilot AI 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.

Pull request overview

Copilot reviewed 18 out of 21 changed files in this pull request and generated 1 comment.

Comment thread src/Uno.UWP/UI/Core/CharacterReceivedEventArgs.cs Outdated
Matches the convention of the sibling Windows.UI.Core partials
(PointerEventArgs, KeyEventArgs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 22:50

Copilot AI 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.

Pull request overview

Copilot reviewed 18 out of 21 changed files in this pull request and generated no new comments.

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23901/wasm-skia-net9/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 224634 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ContextRequested.png-dark
    • ContextRequested.png
    • ClipboardTests.png-dark
    • ImageIconPage.png-dark
    • Focus_FocusVisual_Properties.png-dark
    • Buttons.png-dark
    • Buttons.png
    • DisplayInformation.png-dark
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • Examples.png-dark
    • Examples.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
  • skia-windows-screenshots: 2348 changed over 2392

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • AppBar_KeyBoard.png-dark
    • AppBar_KeyBoard.png
    • ApplicationViewSizing.png
    • ApplicationViewSpanningRectsPage.png
    • Arrange_Performance01.png-dark
    • AutoBorderStretchwithrightmargin.png-dark
    • AutoBorderStretchwithbottommargin.png-dark
    • AutoSuggestBox_BitmapIcon.png-dark
    • AutoSizedPathCentered.png-dark
    • BasicAutoSuggestBox.png-dark
    • AutoSuggestBox_SoftKeboard.png
    • Battery.png-dark
    • BasicThemeResources.png
    • BezierSegment.png
    • BorderBottomwithmargins.png
    • BorderBottomwithmargins.png-dark
    • BorderRightwithmargins.png
    • BorderLeftwithmargins.png-dark
    • BorderTopwithmargins.png-dark
    • BorderLeftwithmargins.png
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • UITests.Uno_Web.Http.CookieManagerTests
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension

The Skia-only override made the TextBox API surface diverge from the
Reference assembly, failing the ReferenceImplComparer package check.
The override now lives in the shared TextBox partial and delegates to a
Skia-implemented partial method, keeping the surface identical across
targets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 13:46

Copilot AI 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.

Pull request overview

Copilot reviewed 19 out of 22 changed files in this pull request and generated no new comments.

@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23901/wasm-skia-net9/index.html

@nventive-devops

Copy link
Copy Markdown
Contributor

The build 224727 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Buttons.png-dark
    • Buttons.png
    • ClipboardTests.png-dark
    • Focus_FocusVisual_Properties.png-dark
    • DisplayInformation.png-dark
    • ImageIconPage.png-dark
    • ContextRequested.png-dark
    • ContextRequested.png
    • Examples.png-dark
    • Examples.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • Image_AnimatedWebP.png-dark
    • Image_AnimatedWebP.png
  • skia-windows-screenshots: 2348 changed over 2392

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ApplicationViewMode.png
    • Attributed_text_Supserscript.png
    • AutoBorderStretchwithtopmargin.png-dark
    • AutoSuggestBox_Description.png-dark
    • Basics_Automated.png
    • BitmapIcon_Foreground.png
    • BorderVisualClipping.png
    • Button_Opacity_Automated.png
    • CenteredGridinGridwiththreefixedsizechildren.png-dark
    • CenteredGridinGridwiththreefixedsizechildren.png
    • ClippingRoundedCorners.png-dark
    • ClippingRoundedCorners.png
    • ComboBox_DropDownWidth.png-dark
    • ComboBox_DropDownWidth.png
    • ComboBox_Picker.png-dark
    • ComboBox_Picker.png
    • CommandBar_BackButtonImage.png-dark
    • CommandBar_BackButtonImage.png
    • Contacts_Pick.png-dark
    • Contacts_Pick.png
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • UITests.Uno_Web.Http.CookieManagerTests
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/code-generation Categorizes an issue or PR as relevant to code generation area/skia ✏️ Categorizes an issue or PR as relevant to Skia platform/android 🤖 Categorizes an issue or PR as relevant to the Android platform platform/ios 🍎 Categorizes an issue or PR as relevant to the iOS platform platform/macos 🍏 Categorizes an issue or PR as relevant to the macOS platform platform/wasm 🌐 Categorizes an issue or PR as relevant to the WebAssembly platform platform/x11 🐧 Categorizes an issue or PR as relevant to X11

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Skia] TextBox ALT character code problems

4 participants