[feat] add support for minimizing virtual mouse to status bar
This commit is contained in:
+80
-1
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user