From 0cef810504d6a2d573fc92bfb2df3b9ea7f62865 Mon Sep 17 00:00:00 2001 From: Builder Date: Mon, 19 Jan 2026 12:29:36 +0700 Subject: [PATCH 1/2] Fix: Prevent cursor position loss when switching keyboard layout (Shift+Alt) Fix editor losing focus and cursor position when switching keyboard layout ## Problem Non-English speaking users who work with multiple keyboard layouts (e.g., English + Russian, English + German, English + Chinese, etc.) experienced a frustrating issue when switching between languages using the standard Windows keyboard shortcut Shift+Alt (or alternative combinations). Every time users switched their input language while typing in the editor, the text cursor would jump away from the current line, losing its position. This forced users to: 1. Stop their workflow 2. Manually click back into the editor 3. Navigate back to where they were typing 4. Continue working For users who frequently switch between languages (which is very common when writing code with comments in native language, or working with multilingual content), this could happen dozens of times per session, severely impacting productivity and causing significant frustration. ## Root Cause When pressing Shift+Alt to switch keyboard layout in Windows, the OS generates a KeyUp event for the Alt key after the layout switch completes. In Avalonia (and WPF), a standalone Alt key press activates the application's main menu bar, which steals keyboard focus from the TextEditor control. ## Solution Added a KeyUp event handler with tunnel routing strategy in MainWindow that intercepts the Alt key release event before it reaches the menu. When the editor has focus, the event is marked as handled, preventing menu activation and preserving cursor position. This fix specifically targets the keyboard layout switching scenario while maintaining normal Alt key functionality for menu access when the editor is not focused. Affects all Windows users with multiple keyboard layouts configured. --- SkEditor/Views/Windows/MainWindow.axaml.cs | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/SkEditor/Views/Windows/MainWindow.axaml.cs b/SkEditor/Views/Windows/MainWindow.axaml.cs index 2ee74a81..45ee86b2 100644 --- a/SkEditor/Views/Windows/MainWindow.axaml.cs +++ b/SkEditor/Views/Windows/MainWindow.axaml.cs @@ -6,6 +6,7 @@ using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Threading; +using AvaloniaEdit; using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Windowing; @@ -103,6 +104,28 @@ private void AddEvents() }; AddHandler(DragDrop.DropEvent, FileHandler.FileDropAction); + + AddHandler(KeyUpEvent, (_, e) => + { + if (e.Key is not (Key.LeftAlt or Key.RightAlt)) + { + return; + } + + TextEditor? editor = SkEditorAPI.Files.GetCurrentOpenedFile()?.Editor; + if (editor == null) + { + return; + } + + + if (editor.TextArea.IsFocused || editor.IsFocused) + { + e.Handled = true; + + Dispatcher.UIThread.Post(() => editor.TextArea.Focus()); + } + }, RoutingStrategies.Tunnel); } public void ReloadUiOfAddons() From da4cfedfb02e0fdc5b50e262ffa915fdd2f2af62 Mon Sep 17 00:00:00 2001 From: Radomir-Aksenenko Date: Tue, 20 Jan 2026 09:23:30 +0700 Subject: [PATCH 2/2] Fix cursor jumping away when switching keyboard layout Hey! Taking another shot at this after your feedback on the previous PR. You were right - blocking all Alt keypresses was a bad idea. So I rewrote it completely. Now it only kicks in when you actually use a layout switch combo (Alt+Shift or Ctrl+Shift), not when you're just trying to open the menu. Basically I track when these combos are pressed (KeyDown) and only then block the menu activation on KeyUp. Regular Alt still works fine for accessing menus. I use Russian + English daily so this bug was driving me crazy, had to fix it myself instead of waiting. Tested it pretty thoroughly - layout switching works, menu shortcuts work, everything seems good. Let me know if there's anything else to tweak! --- SkEditor/Views/Windows/MainWindow.axaml.cs | 43 +++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/SkEditor/Views/Windows/MainWindow.axaml.cs b/SkEditor/Views/Windows/MainWindow.axaml.cs index 45ee86b2..2bf76266 100644 --- a/SkEditor/Views/Windows/MainWindow.axaml.cs +++ b/SkEditor/Views/Windows/MainWindow.axaml.cs @@ -26,6 +26,7 @@ public partial class MainWindow : AppWindow private readonly SplashScreen? _splashScreen; private bool _isFullyLoaded; private WindowState _preFullScreenState; + private bool _layoutSwitchDetected; public MainWindow(SplashScreen? splashScreen = null) { @@ -104,26 +105,50 @@ private void AddEvents() }; AddHandler(DragDrop.DropEvent, FileHandler.FileDropAction); - + + AddHandler(KeyDownEvent, (_, e) => + { + if (e.Key is Key.LeftAlt or Key.RightAlt && e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + _layoutSwitchDetected = true; + } + else if (e.Key is Key.LeftShift or Key.RightShift && e.KeyModifiers.HasFlag(KeyModifiers.Alt)) + { + _layoutSwitchDetected = true; + } + else if (e.Key is Key.LeftShift or Key.RightShift && e.KeyModifiers.HasFlag(KeyModifiers.Control)) + { + _layoutSwitchDetected = true; + } + else if (e.Key is Key.LeftCtrl or Key.RightCtrl && e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + _layoutSwitchDetected = true; + } + }, RoutingStrategies.Tunnel); + AddHandler(KeyUpEvent, (_, e) => { - if (e.Key is not (Key.LeftAlt or Key.RightAlt)) + bool isLayoutSwitchKey = e.Key is Key.LeftAlt or Key.RightAlt + or Key.LeftShift or Key.RightShift + or Key.LeftCtrl or Key.RightCtrl; + + if (!isLayoutSwitchKey || !_layoutSwitchDetected) { return; } - - TextEditor? editor = SkEditorAPI.Files.GetCurrentOpenedFile()?.Editor; - if (editor == null) + + _layoutSwitchDetected = false; + + OpenedFile? currentFile = SkEditorAPI.Files.GetCurrentOpenedFile(); + if (currentFile?.Editor?.TextArea is not { } textArea) { return; } - - if (editor.TextArea.IsFocused || editor.IsFocused) + if (textArea.IsFocused) { e.Handled = true; - - Dispatcher.UIThread.Post(() => editor.TextArea.Focus()); + Dispatcher.UIThread.Post(() => textArea.Focus()); } }, RoutingStrategies.Tunnel); }