Skip to content

Commit f37b30c

Browse files
committed
fix: fullscreen activation, stuck modifiers, and toolbar behavior
- WM_ACTIVATE: call DefWindowProc + SetFocus on activation to restore keyboard focus; skip toolbar hide when the toolbar itself is clicked - ReleaseAllModifiers when capture is released or DisplayPanel loses focus, preventing stuck modifier keys after system window switching - Toolbar always hides on fullscreen deactivation, restores on activation - All toolbar SetWindowPos calls use SWP_NOACTIVATE to prevent focus steal - Drag handle: larger 54x54 icon, tooltip, SS_NOTIFY for mouse tracking - Auto-hide timer reduced to 1.5s; safe zone skipped during drag
1 parent 40fb05c commit f37b30c

4 files changed

Lines changed: 46 additions & 20 deletions

File tree

src/manager/ui/win32_display_panel.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ void DisplayPanel::SetCaptured(bool captured) {
494494
if (hwnd_) SetTimer(hwnd_, kHintTimerId, kHintDurationMs, nullptr);
495495
}
496496
} else {
497+
ReleaseAllModifiers();
497498
UninstallKeyboardHook();
498499
capture_hint_visible_ = false;
499500
if (hwnd_) KillTimer(hwnd_, kHintTimerId);
@@ -612,6 +613,7 @@ LRESULT CALLBACK DisplayPanel::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp
612613
return 0;
613614

614615
case WM_KILLFOCUS:
616+
self->ReleaseAllModifiers();
615617
self->SetCaptured(false);
616618
self->mouse_buttons_ = 0;
617619
return 0;

src/manager/ui/win32_floating_toolbar.cpp

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99
namespace {
1010
constexpr const wchar_t* kWndClass = L"TenBoxFloatingToolbar";
1111

12-
constexpr int kBarHeight = 56;
12+
constexpr int kBarHeight = 62;
1313
constexpr int kBtnH = 38;
14-
constexpr int kPad = 12;
15-
constexpr int kGripW = 52;
14+
constexpr int kPad = 10;
15+
constexpr int kGripW = 56;
1616
constexpr int kVmBtnMinW = 280;
1717
constexpr int kIconBtnW = 40;
1818
constexpr int kRightPadding = 14;
1919
constexpr UINT_PTR kHideTimerId = 1;
20-
constexpr DWORD kAutoHideMs = 3000;
20+
constexpr DWORD kAutoHideMs = 1500;
2121

22-
enum BtnId { kBtnVm = 301, kBtnDpi = 302, kBtnPin = 303, kBtnExit = 304 };
22+
enum BtnId { kBtnDrag = 300, kBtnVm = 301, kBtnDpi = 302, kBtnPin = 303, kBtnExit = 304 };
2323
}
2424

2525
HWND FloatingToolbar::Create(HINSTANCE hinst, HWND fullscreen_hwnd) {
@@ -55,11 +55,11 @@ HWND FloatingToolbar::Create(HINSTANCE hinst, HWND fullscreen_hwnd) {
5555

5656
// Drag handle — TenBox icon
5757
st->drag_handle = CreateWindowExW(0, WC_STATICW, L"",
58-
WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
59-
0, 0, 0, 0, hwnd, nullptr, hinst, nullptr);
58+
WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE | SS_NOTIFY,
59+
0, 0, 0, 0, hwnd, reinterpret_cast<HMENU>(kBtnDrag), hinst, nullptr);
6060
{
6161
HICON icon = static_cast<HICON>(LoadImageW(hinst, MAKEINTRESOURCEW(IDI_APP_ICON),
62-
IMAGE_ICON, 48, 48, LR_SHARED));
62+
IMAGE_ICON, 54, 54, LR_SHARED));
6363
SendMessageW(st->drag_handle, STM_SETICON, reinterpret_cast<WPARAM>(icon), 0);
6464
}
6565

@@ -121,6 +121,8 @@ HWND FloatingToolbar::Create(HINSTANCE hinst, HWND fullscreen_hwnd) {
121121
AddTool(st->btn_exit, L"✕ 退出全屏 (或长按 ESC 2 秒)");
122122
AddTool(st->btn_vm, L"切换虚拟机");
123123

124+
AddTool(st->drag_handle, L"可拖拽至窗口边缘以吸附");
125+
124126
return hwnd;
125127
}
126128

@@ -181,7 +183,7 @@ void FloatingToolbar::SetVmInfo(HWND hwnd, const std::string& current_id, const
181183
if (new_w < 420) new_w = 420;
182184
st->tb_width = new_w;
183185

184-
SetWindowPos(hwnd, nullptr, 0, 0, st->tb_width, kBarHeight, SWP_NOMOVE | SWP_NOZORDER);
186+
SetWindowPos(hwnd, nullptr, 0, 0, st->tb_width, kBarHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
185187
LayoutButtons(hwnd);
186188
UpdatePosition(hwnd);
187189
InvalidateRect(hwnd, nullptr, TRUE); // force full redraw, erase background
@@ -196,9 +198,11 @@ void FloatingToolbar::SetDpiZoomState(HWND hwnd, bool enabled) {
196198
}
197199

198200
void FloatingToolbar::OnFullscreenDeactivated(HWND hwnd) {
199-
auto* st = GetState(hwnd);
200-
if (!st) return;
201-
if (!st->pinned) HideBar(hwnd);
201+
ShowWindow(hwnd, SW_HIDE);
202+
}
203+
204+
void FloatingToolbar::OnFullscreenActivated(HWND hwnd) {
205+
ShowBar(hwnd);
202206
}
203207

204208
FloatingToolbar::ToolbarState* FloatingToolbar::GetState(HWND hwnd) {
@@ -226,7 +230,7 @@ void FloatingToolbar::ShowBar(HWND hwnd) {
226230
st->tab_mode = false;
227231
// Restore full size
228232
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, st->tb_width, st->tb_height,
229-
SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
233+
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
230234
InvalidateRect(hwnd, nullptr, TRUE);
231235
// Show all children
232236
ShowWindow(st->drag_handle, SW_SHOW);
@@ -293,9 +297,10 @@ void FloatingToolbar::LayoutButtons(HWND hwnd) {
293297
rx -= kIconBtnW + 2; MoveWindow(st->btn_pin, rx, y, kIconBtnW, kBtnH, FALSE);
294298
rx -= kIconBtnW + 2; MoveWindow(st->btn_dpi, rx, y, kIconBtnW, kBtnH, FALSE);
295299

296-
// Drag handle at left
300+
// Drag handle at left — taller to fill more vertical space
297301
int lx = kPad;
298-
MoveWindow(st->drag_handle, lx, y, kGripW, kBtnH, FALSE);
302+
int grip_h = kBarHeight - 10;
303+
MoveWindow(st->drag_handle, lx, 5, kGripW, grip_h, FALSE);
299304
lx += kGripW + 2;
300305

301306
// VM button fills space between drag handle and icon buttons
@@ -304,7 +309,7 @@ void FloatingToolbar::LayoutButtons(HWND hwnd) {
304309
MoveWindow(st->btn_vm, lx, y, vm_w, kBtnH, FALSE);
305310

306311
st->tb_height = kBarHeight;
307-
SetWindowPos(hwnd, nullptr, 0, 0, st->tb_width, kBarHeight, SWP_NOMOVE | SWP_NOZORDER);
312+
SetWindowPos(hwnd, nullptr, 0, 0, st->tb_width, kBarHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
308313
}
309314

310315
void FloatingToolbar::UpdatePosition(HWND hwnd) {
@@ -396,7 +401,7 @@ void FloatingToolbar::SnapToNearestEdge(HWND hwnd) {
396401

397402
void FloatingToolbar::CheckMouseNearEdge(HWND hwnd, POINT cursor) {
398403
auto* st = GetState(hwnd);
399-
if (!st || !st->fullscreen_parent) return;
404+
if (!st || !st->fullscreen_parent || st->dragging) return;
400405
RECT pr;
401406
GetWindowRect(st->fullscreen_parent, &pr);
402407
int screen_w = pr.right - pr.left;
@@ -498,7 +503,7 @@ LRESULT CALLBACK FloatingToolbar::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM
498503
ClientToScreen(hwnd, &pt);
499504
int dx = pt.x - st->drag_start.x, dy = pt.y - st->drag_start.y;
500505
RECT rc; GetWindowRect(hwnd, &rc);
501-
SetWindowPos(hwnd, nullptr, rc.left + dx, rc.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
506+
SetWindowPos(hwnd, nullptr, rc.left + dx, rc.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
502507
st->drag_start = pt;
503508
}
504509
return 0;
@@ -520,6 +525,13 @@ LRESULT CALLBACK FloatingToolbar::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM
520525
if (st && !st->pinned) { KillAutoHideTimer(hwnd); StartAutoHideTimer(hwnd); }
521526
UINT id = LOWORD(wp);
522527
switch (id) {
528+
case kBtnDrag:
529+
if (st && !st->dragging) {
530+
st->dragging = true;
531+
GetCursorPos(&st->drag_start);
532+
SetCapture(hwnd);
533+
}
534+
return 0;
523535
case kBtnVm: {
524536
if (st->running_vm_ids.empty()) break;
525537
HMENU menu = CreatePopupMenu();

src/manager/ui/win32_floating_toolbar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class FloatingToolbar {
3636
static void SetDpiZoomState(HWND hwnd, bool enabled);
3737

3838
static void OnFullscreenDeactivated(HWND hwnd);
39+
static void OnFullscreenActivated(HWND hwnd);
3940
static void CheckMouseNearEdge(HWND hwnd, POINT cursor_screen);
4041

4142
private:

src/manager/ui/win32_fullscreen_window.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,22 @@ LRESULT CALLBACK FullscreenWindow::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARA
218218

219219
case WM_ACTIVATE:
220220
if (LOWORD(wp) == WA_INACTIVE) {
221-
if (self && self->toolbar_hwnd_) {
221+
// Don't hide toolbar if it's the window gaining activation (e.g. user clicked it)
222+
if (self && self->toolbar_hwnd_ && reinterpret_cast<HWND>(lp) != self->toolbar_hwnd_) {
222223
FloatingToolbar::OnFullscreenDeactivated(self->toolbar_hwnd_);
223224
}
225+
return 0;
226+
}
227+
{
228+
LRESULT lr = DefWindowProcW(hwnd, msg, wp, lp);
229+
if (self && self->display_panel_) {
230+
SetFocus(self->display_panel_->Handle());
231+
}
232+
if (self && self->toolbar_hwnd_) {
233+
FloatingToolbar::OnFullscreenActivated(self->toolbar_hwnd_);
234+
}
235+
return lr;
224236
}
225-
return 0;
226237

227238
case WM_DISPLAYCHANGE:
228239
// Re-anchor to current monitor on display config changes

0 commit comments

Comments
 (0)