From a25c90e991c8e316e30920b97153c02b89155248 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 04:06:01 +0000 Subject: [PATCH 1/4] Initial plan From 02276740ced0fb1f4e0d57ed2515ca5148947432 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 04:08:24 +0000 Subject: [PATCH 2/4] Fix Quick Send titlebar styling and focus dismissal behavior Agent-Logs-Url: https://github.com/openclaw/openclaw-windows-node/sessions/326ca749-385e-45f0-a02f-688540edf7de Co-authored-by: shanselman <2892+shanselman@users.noreply.github.com> --- .../Dialogs/QuickSendDialog.cs | 92 ++++++++++--------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs index 1c54f202..ad7cd1a9 100644 --- a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs +++ b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs @@ -31,21 +31,8 @@ public sealed class QuickSendDialog : WindowEx [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - [DllImport("user32.dll")] - private static extern bool SetWindowPos( - IntPtr hWnd, - IntPtr hWndInsertAfter, - int X, - int Y, - int cx, - int cy, - uint uFlags); - - private static readonly IntPtr HWND_TOPMOST = new(-1); + private const int TitleBarHeight = 48; private const int SW_SHOWNORMAL = 1; - private const uint SWP_NOMOVE = 0x0002; - private const uint SWP_NOSIZE = 0x0001; - private const uint SWP_SHOWWINDOW = 0x0040; public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = null) { @@ -53,7 +40,8 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu // Window setup Title = LocalizationHelper.GetString("WindowTitle_QuickSend"); - this.SetWindowSize(420, 260); + ExtendsContentIntoTitleBar = true; + this.SetWindowSize(420, 260 + TitleBarHeight); this.CenterOnScreen(); this.SetIcon(IconHelper.GetStatusIconPath(ConnectionStatus.Connected)); @@ -61,10 +49,6 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu // This avoids focus/activation oddities on Windows 10 for hotkey-launched windows. BackdropHelper.TrySetAcrylicBackdrop((Microsoft.UI.Xaml.Window)this); - // Hotkey-launched windows can fail to foreground on Windows 10 due to - // foreground activation restrictions. Ensure the window is topmost. - this.IsAlwaysOnTop = true; - // Build UI programmatically (simple dialog) var root = new Grid { @@ -130,18 +114,44 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu Grid.SetRow(buttonPanel, 3); root.Children.Add(buttonPanel); - Content = new Border + var body = new Border { Padding = new Thickness(24), Child = root }; - // Focus the text box when shown - Activated += (s, e) => + var outerGrid = new Grid(); + outerGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(TitleBarHeight) }); + outerGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + + var titleBar = new Grid { Padding = new Thickness(16, 0, 140, 0) }; + var titleStack = new StackPanel { Orientation = Orientation.Horizontal }; + titleStack.Children.Add(new TextBlock { - TryBringToFront(); - RequestInputFocus(); - }; + Text = "🦞", + FontSize = 20, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(0, 0, 10, 0) + }); + titleStack.Children.Add(new TextBlock + { + Text = LocalizationHelper.GetString("WindowTitle_QuickSend"), + FontSize = 13, + Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"], + VerticalAlignment = VerticalAlignment.Center + }); + titleBar.Children.Add(titleStack); + Grid.SetRow(titleBar, 0); + outerGrid.Children.Add(titleBar); + + Grid.SetRow(body, 1); + outerGrid.Children.Add(body); + + Content = outerGrid; + SetTitleBar(titleBar); + + // Focus the text box when shown + Activated += OnActivated; Closed += (s, e) => Logger.Info("[QuickSend] Dialog closed"); @@ -155,9 +165,8 @@ private void TryBringToFront() var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); if (hwnd == IntPtr.Zero) return; - // Make sure it's actually shown and promoted. + // Make sure it's shown and activated. ShowWindow(hwnd, SW_SHOWNORMAL); - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); SetForegroundWindow(hwnd); } catch (Exception ex) @@ -166,6 +175,17 @@ private void TryBringToFront() } } + private void OnActivated(object sender, WindowActivatedEventArgs args) + { + if (args.WindowActivationState == WindowActivationState.Deactivated) + { + Close(); + return; + } + + RequestInputFocus(); + } + private async void OnKeyDown(object sender, KeyRoutedEventArgs e) { if (e.Key == global::Windows.System.VirtualKey.Enter && !_isSending) @@ -191,7 +211,7 @@ private async Task SendMessageAsync() _errorDetailsTextBox.Visibility = Visibility.Collapsed; _errorDetailsTextBox.Text = string.Empty; - this.SetWindowSize(420, 260); + this.SetWindowSize(420, 260 + TitleBarHeight); _isSending = true; _sendButton.IsEnabled = false; @@ -257,7 +277,7 @@ private void ShowErrorDetails(string details) _errorDetailsTextBox.MinHeight = 140; _errorDetailsTextBox.Text = details; _errorDetailsTextBox.Visibility = Visibility.Visible; - this.SetWindowSize(520, 400); + this.SetWindowSize(520, 400 + TitleBarHeight); // Move focus to the details box so users can immediately select/copy text. _errorDetailsTextBox.Focus(FocusState.Programmatic); @@ -269,7 +289,7 @@ private void ShowDetails(string details) _errorDetailsTextBox.MinHeight = 80; _errorDetailsTextBox.Text = details; _errorDetailsTextBox.Visibility = Visibility.Visible; - this.SetWindowSize(500, 320); + this.SetWindowSize(500, 320 + TitleBarHeight); } private static bool TryExtractMissingScope(string? message, out string scope) @@ -317,18 +337,6 @@ private void QueueFocusMessageInput() private void RequestInputFocus() { QueueFocusMessageInput(); - _ = RetryFocusMessageInputAsync(); - } - - private async Task RetryFocusMessageInputAsync() - { - var delaysMs = new[] { 60, 160, 320 }; - foreach (var delay in delaysMs) - { - await Task.Delay(delay); - TryBringToFront(); - QueueFocusMessageInput(); - } } private async Task EnsureGatewayConnectedAsync(int timeoutMs = 3000) From 9dc605f48380c801d088caf59a606f4b5ef56f30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 04:12:12 +0000 Subject: [PATCH 3/4] Polish Quick Send activation dismissal safeguards Agent-Logs-Url: https://github.com/openclaw/openclaw-windows-node/sessions/326ca749-385e-45f0-a02f-688540edf7de Co-authored-by: shanselman <2892+shanselman@users.noreply.github.com> --- src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs index ad7cd1a9..084ad144 100644 --- a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs +++ b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs @@ -25,6 +25,8 @@ public sealed class QuickSendDialog : WindowEx private readonly Button _sendButton; private bool _isSending; + private const string TitleIcon = "🦞"; + private const double WindowControlsReservedWidth = 140; [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); @@ -124,11 +126,11 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu outerGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(TitleBarHeight) }); outerGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); - var titleBar = new Grid { Padding = new Thickness(16, 0, 140, 0) }; + var titleBar = new Grid { Padding = new Thickness(16, 0, WindowControlsReservedWidth, 0) }; var titleStack = new StackPanel { Orientation = Orientation.Horizontal }; titleStack.Children.Add(new TextBlock { - Text = "🦞", + Text = TitleIcon, FontSize = 20, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 10, 0) @@ -179,6 +181,11 @@ private void OnActivated(object sender, WindowActivatedEventArgs args) { if (args.WindowActivationState == WindowActivationState.Deactivated) { + if (_isSending) + { + return; + } + Close(); return; } From 5051f087c456bfa75fd2fcc4b02c8eb173bfbb59 Mon Sep 17 00:00:00 2001 From: Scott Hanselman Date: Thu, 7 May 2026 14:02:53 -0400 Subject: [PATCH 4/4] fix(quicksend): preserve focus workaround with custom titlebar Keep the titlebar update while removing close-on-deactivation and restoring the Windows 10 hotkey foreground retry/topmost promotion path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Dialogs/QuickSendDialog.cs | 88 ++++++++++++++----- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs index 084ad144..33d02c8c 100644 --- a/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs +++ b/src/OpenClaw.Tray.WinUI/Dialogs/QuickSendDialog.cs @@ -24,6 +24,8 @@ public sealed class QuickSendDialog : WindowEx private readonly TextBox _errorDetailsTextBox; private readonly Button _sendButton; private bool _isSending; + private bool _isClosed; + private bool _focusRetryRunning; private const string TitleIcon = "🦞"; private const double WindowControlsReservedWidth = 140; @@ -33,8 +35,22 @@ public sealed class QuickSendDialog : WindowEx [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + [DllImport("user32.dll")] + private static extern bool SetWindowPos( + IntPtr hWnd, + IntPtr hWndInsertAfter, + int X, + int Y, + int cx, + int cy, + uint uFlags); + + private static readonly IntPtr HWND_TOPMOST = new(-1); private const int TitleBarHeight = 48; private const int SW_SHOWNORMAL = 1; + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_SHOWWINDOW = 0x0040; public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = null) { @@ -51,6 +67,10 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu // This avoids focus/activation oddities on Windows 10 for hotkey-launched windows. BackdropHelper.TrySetAcrylicBackdrop((Microsoft.UI.Xaml.Window)this); + // Hotkey-launched windows can fail to foreground on Windows 10 due to + // foreground activation restrictions. Keep the existing topmost promotion. + this.IsAlwaysOnTop = true; + // Build UI programmatically (simple dialog) var root = new Grid { @@ -152,10 +172,21 @@ public QuickSendDialog(OpenClawGatewayClient client, string? prefillMessage = nu Content = outerGrid; SetTitleBar(titleBar); - // Focus the text box when shown - Activated += OnActivated; + // Focus the text box when shown without closing on transient deactivation. + Activated += (s, e) => + { + if (e.WindowActivationState != WindowActivationState.Deactivated) + { + TryBringToFront(); + RequestInputFocus(); + } + }; - Closed += (s, e) => Logger.Info("[QuickSend] Dialog closed"); + Closed += (s, e) => + { + _isClosed = true; + Logger.Info("[QuickSend] Dialog closed"); + }; Logger.Info($"[QuickSend] Dialog opened (prefill={!string.IsNullOrEmpty(prefillMessage)})"); } @@ -164,11 +195,15 @@ private void TryBringToFront() { try { + if (_isClosed) + return; + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); if (hwnd == IntPtr.Zero) return; - // Make sure it's shown and activated. + // Make sure it's actually shown and promoted. ShowWindow(hwnd, SW_SHOWNORMAL); + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); SetForegroundWindow(hwnd); } catch (Exception ex) @@ -177,22 +212,6 @@ private void TryBringToFront() } } - private void OnActivated(object sender, WindowActivatedEventArgs args) - { - if (args.WindowActivationState == WindowActivationState.Deactivated) - { - if (_isSending) - { - return; - } - - Close(); - return; - } - - RequestInputFocus(); - } - private async void OnKeyDown(object sender, KeyRoutedEventArgs e) { if (e.Key == global::Windows.System.VirtualKey.Enter && !_isSending) @@ -338,12 +357,41 @@ private static void CopyTextToClipboard(string text) private void QueueFocusMessageInput() { + if (_isClosed) + return; + DispatcherQueue?.TryEnqueue(FocusMessageInput); } private void RequestInputFocus() { QueueFocusMessageInput(); + if (!_focusRetryRunning) + { + _focusRetryRunning = true; + _ = RetryFocusMessageInputAsync(); + } + } + + private async Task RetryFocusMessageInputAsync() + { + try + { + var delaysMs = new[] { 60, 160, 320 }; + foreach (var delay in delaysMs) + { + await Task.Delay(delay); + if (_isClosed) + return; + + TryBringToFront(); + QueueFocusMessageInput(); + } + } + finally + { + _focusRetryRunning = false; + } } private async Task EnsureGatewayConnectedAsync(int timeoutMs = 3000)