feat(desktop): always-on-top for main and detached windows

This commit is contained in:
Mo-Moore authored and GitHub committed 2026-09-22 16:30:19 +08:00
1 parent 933ed6a670
commit 78d8076c53
18 files changed
+262 -11

No files matched your search

@@ -2,6 +2,7 @@ import { ref, onMounted, onUnmounted } from "vue";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import { isMacOS } from "@/lib/backend/platform";
import * as api from "@/lib/backend/api";
import { raiseDetachedWindowsAboveMain, WINDOW_ALWAYS_ON_TOP_CHANGED_EVENT, type WindowAlwaysOnTopChangedPayload } from "@/lib/app/windowAlwaysOnTop";
const MIN_UI_SCALE = 0.75;
const MAX_UI_SCALE = 2;
@@ -44,19 +45,29 @@ export function shouldDrawDesktopWindowFrame(isMac: boolean, isDesktop = true, i
export function useWindowControls() {
const isMaximized = ref(false);
const isFullscreen = ref(false);
const isAlwaysOnTop = ref(false);
const isMac = isMacOS();
const isDesktop = isTauriRuntime();
const showControls = shouldShowWindowControls(isMac, isDesktop);
let unlisten: (() => void) | null = null;
let unlistenResize: (() => void) | null = null;
let unlistenFocus: (() => void) | null = null;
let unlistenAlwaysOnTop: (() => void) | null = null;
async function updateWindowState() {
if (!isDesktop) return;
const { getCurrentWindow } = await import("@tauri-apps/api/window");
const currentWindow = getCurrentWindow();
const [maximized, fullscreen] = await Promise.all([currentWindow.isMaximized(), currentWindow.isFullscreen()]);
const [maximized, fullscreen, alwaysOnTop] = await Promise.all([currentWindow.isMaximized(), currentWindow.isFullscreen(), currentWindow.isAlwaysOnTop()]);
isMaximized.value = maximized;
isFullscreen.value = fullscreen;
isAlwaysOnTop.value = alwaysOnTop;
}
async function refreshAlwaysOnTopState() {
if (!isDesktop) return;
const { getCurrentWindow } = await import("@tauri-apps/api/window");
isAlwaysOnTop.value = await getCurrentWindow().isAlwaysOnTop();
}
async function minimize() {
@@ -70,6 +81,18 @@ export function useWindowControls() {
setTimeout(updateWindowState, 50);
}
async function toggleAlwaysOnTop() {
if (!isDesktop) return;
const { getCurrentWindow } = await import("@tauri-apps/api/window");
const currentWindow = getCurrentWindow();
const next = !isAlwaysOnTop.value;
await currentWindow.setAlwaysOnTop(next);
isAlwaysOnTop.value = await currentWindow.isAlwaysOnTop();
if (next && currentWindow.label === "main") {
await raiseDetachedWindowsAboveMain();
}
}
async function close() {
if (!isDesktop) return;
await api.requestAppClose();
@@ -79,14 +102,26 @@ export function useWindowControls() {
if (!isDesktop) return;
await updateWindowState();
const { getCurrentWindow } = await import("@tauri-apps/api/window");
const unlistenFn = await getCurrentWindow().onResized(() => {
const currentWindow = getCurrentWindow();
const windowLabel = currentWindow.label;
unlistenResize = await currentWindow.onResized(() => {
void updateWindowState();
});
unlisten = unlistenFn;
unlistenFocus = await currentWindow.onFocusChanged(({ payload: focused }) => {
if (focused) void refreshAlwaysOnTopState();
});
const { listen } = await import("@tauri-apps/api/event");
unlistenAlwaysOnTop = await listen<WindowAlwaysOnTopChangedPayload>(WINDOW_ALWAYS_ON_TOP_CHANGED_EVENT, (event) => {
const payload = event.payload;
if (!payload || payload.windowLabel !== windowLabel || typeof payload.alwaysOnTop !== "boolean") return;
isAlwaysOnTop.value = payload.alwaysOnTop;
});
});
onUnmounted(() => {
unlisten?.();
unlistenResize?.();
unlistenFocus?.();
unlistenAlwaysOnTop?.();
});
return {
@@ -95,8 +130,10 @@ export function useWindowControls() {
showControls,
isMaximized,
isFullscreen,
isAlwaysOnTop,
minimize,
toggleMaximize,
toggleAlwaysOnTop,
close,
};
}