[feat] add support for minimizing virtual mouse to status bar

This commit is contained in:
dijunkun
2025-11-09 18:05:07 +08:00
parent 396992526f
commit 697ef41fe8
3 changed files with 170 additions and 1 deletions
+80 -1
View File
@@ -51,6 +51,8 @@
keyboardToggleMouse: document.getElementById("keyboard-toggle-mouse"),
keyboardToggle: document.getElementById("keyboard-toggle"),
keyboardClose: document.getElementById("keyboard-close"),
virtualMouseMinimize: document.getElementById("virtual-mouse-minimize"),
virtualMouseRestore: document.getElementById("virtual-mouse-restore"),
};
this.virtualKeyTimers = new Map(); // Store timers for each key element
@@ -89,6 +91,7 @@
initialTranslateY: 0,
currentTranslateX: 0,
currentTranslateY: 0,
virtualMouseMinimized: false,
};
this.onPointerLockChange = this.onPointerLockChange.bind(this);
@@ -672,9 +675,18 @@
if (this.elements.mobileModeSelector) {
this.elements.mobileModeSelector.style.display = "none";
}
// Hide minimize button on desktop
if (this.elements.virtualMouseMinimize) {
this.elements.virtualMouseMinimize.style.display = "none";
}
return;
}
// Show minimize button on mobile
if (this.elements.virtualMouseMinimize) {
this.elements.virtualMouseMinimize.style.display = "flex";
}
// 显示移动端模式选择器
if (this.elements.mobileModeSelector) {
this.elements.mobileModeSelector.style.display = "flex";
@@ -735,6 +747,21 @@
this.bindVirtualMouseDragging();
this.bindVirtualKeyboardDragging();
// Bind minimize/restore buttons
if (this.elements.virtualMouseMinimize) {
this.elements.virtualMouseMinimize.addEventListener("click", (e) => {
e.stopPropagation();
this.minimizeVirtualMouse();
});
}
if (this.elements.virtualMouseRestore) {
this.elements.virtualMouseRestore.addEventListener("click", (e) => {
e.stopPropagation();
this.restoreVirtualMouse();
});
}
// Add pinch zoom support for mobile devices (after isMobile is set)
if (this.state.isMobile && this.elements.video) {
const video = this.elements.video;
@@ -1114,6 +1141,16 @@
e.stopPropagation();
});
}
// 确保缩小按钮点击时不会触发拖动
if (this.elements.virtualMouseMinimize) {
this.elements.virtualMouseMinimize.addEventListener("touchstart", (e) => {
e.stopPropagation();
});
this.elements.virtualMouseMinimize.addEventListener("click", (e) => {
e.stopPropagation();
});
}
}
bindVirtualKeyboardDragging() {
@@ -1150,7 +1187,12 @@
// 检查是否点击在按钮上
const target = event.target;
if (target && (target.id === "keyboard-toggle-mouse" || target.closest("#keyboard-toggle-mouse"))) {
if (target && (
target.id === "keyboard-toggle-mouse" ||
target.closest("#keyboard-toggle-mouse") ||
target.id === "virtual-mouse-minimize" ||
target.closest("#virtual-mouse-minimize")
)) {
return; // 不触发拖动
}
@@ -1478,6 +1520,43 @@
}
}
minimizeVirtualMouse() {
if (!this.elements.virtualMouse || this.state.virtualMouseMinimized) return;
this.state.virtualMouseMinimized = true;
this.elements.virtualMouse.classList.add("minimized-to-statusbar");
this.elements.virtualMouse.style.display = "none";
// Show restore button in status bar
if (this.elements.virtualMouseRestore) {
this.elements.virtualMouseRestore.style.display = "inline-flex";
// Position relative to connection status group
const statusGroup = document.querySelector(".connection-status-group");
if (statusGroup && this.elements.virtualMouseRestore.parentElement) {
const statusGroupRect = statusGroup.getBoundingClientRect();
const parentRect = this.elements.virtualMouseRestore.parentElement.getBoundingClientRect();
const leftOffset = statusGroupRect.left - parentRect.left - 48; // 36px button + 12px gap
this.elements.virtualMouseRestore.style.left = `${leftOffset}px`;
this.elements.virtualMouseRestore.style.right = "auto";
this.elements.virtualMouseRestore.style.top = "0";
this.elements.virtualMouseRestore.style.transform = "none";
}
}
}
restoreVirtualMouse() {
if (!this.elements.virtualMouse || !this.state.virtualMouseMinimized) return;
this.state.virtualMouseMinimized = false;
this.elements.virtualMouse.classList.remove("minimized-to-statusbar");
this.elements.virtualMouse.style.display = "flex";
// Hide restore button in status bar
if (this.elements.virtualMouseRestore) {
this.elements.virtualMouseRestore.style.display = "none";
}
}
resetZoom() {
this.state.currentScale = 1.0;
this.state.initialScale = 1.0;
+10
View File
@@ -121,6 +121,15 @@
</div>
<!-- Right side group: status and buttons -->
<div class="controls-right-group">
<!-- Virtual mouse restore button (shown when minimized) -->
<button
id="virtual-mouse-restore"
class="virtual-mouse-restore-btn"
style="display: none"
title="显示虚拟鼠标"
>
🖱
</button>
<!-- Connection status -->
<div class="connection-status-group">
<span class="connection-status-label">连接状态</span>
@@ -149,6 +158,7 @@
<div id="virtual-mouse">
<div id="virtual-mouse-header" class="virtual-mouse-header">
<button id="keyboard-toggle-mouse" class="keyboard-toggle-btn"></button>
<button id="virtual-mouse-minimize" class="virtual-mouse-minimize-btn"></button>
</div>
<div class="virtual-mouse-row">
<button id="virtual-left-btn" class="virtual-mouse-btn">
+80
View File
@@ -685,6 +685,7 @@ video {
.connected-controls-row .connection-status-group {
flex-shrink: 0;
margin: 0;
position: relative;
}
.connected-controls-row .disconnect-btn {
@@ -733,6 +734,7 @@ video {
box-sizing: border-box;
min-width: auto;
justify-content: center;
position: relative;
}
.connection-status-label {
@@ -855,6 +857,84 @@ video {
gap: max(4px, 0.6vw);
}
.virtual-mouse-minimize-btn {
padding: 0;
background-color: rgba(255, 255, 255, 0.48);
color: #222;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: max(4px, 0.6vw);
font-size: 20px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
user-select: none;
touch-action: manipulation;
transition: all 0.1s;
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
box-sizing: border-box;
outline: none !important;
-webkit-tap-highlight-color: transparent;
flex-shrink: 0;
margin-left: auto;
}
.virtual-mouse-minimize-btn:active {
background-color: rgba(200, 200, 200, 0.65) !important;
transform: scale(0.92);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.virtual-mouse-minimize-btn:focus {
outline: none !important;
background-color: rgba(255, 255, 255, 0.48) !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08) !important;
}
.virtual-mouse-restore-btn {
padding: 0;
background-color: rgba(255, 255, 255, 0.6);
color: #222;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
cursor: pointer;
font-size: 18px;
font-weight: 500;
height: 36px;
width: 36px;
min-width: 36px;
transition: background-color 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
white-space: nowrap;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0;
position: absolute;
top: 0;
flex-shrink: 0;
z-index: 1;
}
.virtual-mouse-restore-btn:hover {
background-color: rgba(255, 255, 255, 0.8);
}
.virtual-mouse-restore-btn:active {
background-color: rgba(200, 200, 200, 0.8);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
#virtual-mouse.minimized-to-statusbar {
display: none !important;
}
.virtual-mouse-row {
display: flex;
gap: max(4px, 0.6vw);