mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-25 15:59:30 +01:00
Compare commits
3 Commits
main
...
center-122
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dce440830 | ||
|
|
1614291f94 | ||
|
|
1806be2a99 |
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -72,6 +72,7 @@
|
||||
"unlisten",
|
||||
"unlistener",
|
||||
"unlisteners",
|
||||
"unmaximize",
|
||||
"unminimize",
|
||||
"uuidv",
|
||||
"VITE",
|
||||
|
||||
@@ -14,10 +14,11 @@ Information about release notes of Coco App is provided here.
|
||||
### 🚀 Features
|
||||
|
||||
- feat: support app search even if Spotlight is disabled #1028
|
||||
- feat: read localized names from root InfoPlist.strings #1029
|
||||
|
||||
### 🐛 Bug fix
|
||||
|
||||
- fix: avoid recentering when resizing to compact after leaving extension #1030
|
||||
|
||||
### ✈️ Improvements
|
||||
|
||||
- refactor: add a timeout to open() #1025
|
||||
@@ -525,4 +526,4 @@ Information about release notes of Coco App is provided here.
|
||||
|
||||
### Bug fix
|
||||
|
||||
### Improvements
|
||||
### Improvements
|
||||
|
||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -332,7 +332,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
[[package]]
|
||||
name = "applications"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/infinilabs/applications-rs?rev=0b086c036b13178048252cddba2c46868a55352e#0b086c036b13178048252cddba2c46868a55352e"
|
||||
source = "git+https://github.com/infinilabs/applications-rs?rev=ec174b7761bfa5eb7af0a93218b014e2d1505643#ec174b7761bfa5eb7af0a93218b014e2d1505643"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"core-foundation 0.9.4",
|
||||
|
||||
@@ -62,7 +62,7 @@ tauri-plugin-drag = "2"
|
||||
tauri-plugin-macos-permissions = "2"
|
||||
tauri-plugin-fs-pro = "2"
|
||||
tauri-plugin-screenshots = "2"
|
||||
applications = { git = "https://github.com/infinilabs/applications-rs", rev = "0b086c036b13178048252cddba2c46868a55352e" }
|
||||
applications = { git = "https://github.com/infinilabs/applications-rs", rev = "ec174b7761bfa5eb7af0a93218b014e2d1505643" }
|
||||
tokio-native-tls = "0.3" # For wss connections
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.20", features = ["native-tls"] }
|
||||
@@ -172,4 +172,4 @@ windows = { version = "0.61", features = ["Win32_Foundation", "Win32_System_Com"
|
||||
windows-sys = { version = "0.61", features = ["Win32", "Win32_System", "Win32_System_Com"] }
|
||||
|
||||
[target."cfg(target_os = \"windows\")".build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
bindgen = "0.72.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useState, useEffect, useMemo, useRef, useCallback } from "react";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Maximize2, Minimize2, Focus } from "lucide-react";
|
||||
|
||||
@@ -7,46 +7,18 @@ import { useSearchStore } from "@/stores/searchStore";
|
||||
import {
|
||||
ExtensionFileSystemPermission,
|
||||
FileSystemAccess,
|
||||
ViewExtensionUISettingsOrNull,
|
||||
} from "../Settings/Extensions";
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import { useShortcutsStore } from "@/stores/shortcutsStore";
|
||||
import { isMac } from "@/utils/platform";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { useViewExtensionWindow } from "@/hooks/useViewExtensionWindow";
|
||||
|
||||
const ViewExtension: React.FC = () => {
|
||||
const { viewExtensionOpened } = useSearchStore();
|
||||
|
||||
const isTauri = useAppStore((state) => state.isTauri);
|
||||
|
||||
// Complete list of the backend APIs, grouped by their category.
|
||||
const [apis, setApis] = useState<Map<string, string[]> | null>(null);
|
||||
const { setModifierKeyPressed } = useShortcutsStore();
|
||||
const { t } = useTranslation();
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const prevWindowRef = useRef<{
|
||||
width: number;
|
||||
height: number;
|
||||
resizable: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
} | null>(null);
|
||||
const fullscreenPrevRef = useRef<{
|
||||
width: number;
|
||||
height: number;
|
||||
resizable: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
} | null>(null);
|
||||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [fallbackViewSize, setFallbackViewSize] = useState<{
|
||||
width: number;
|
||||
height: number;
|
||||
} | null>(() => {
|
||||
if (typeof window === "undefined") return null;
|
||||
return { width: window.innerWidth, height: window.innerHeight };
|
||||
});
|
||||
|
||||
if (viewExtensionOpened == null) {
|
||||
// When this view gets loaded, this state should not be NULL.
|
||||
@@ -196,183 +168,15 @@ const ViewExtension: React.FC = () => {
|
||||
}, [reversedApis, permission]); // Add apiPermissions as dependency
|
||||
|
||||
const fileUrl = viewExtensionOpened[2];
|
||||
const ui: ViewExtensionUISettingsOrNull = useMemo(() => {
|
||||
return viewExtensionOpened[4] as ViewExtensionUISettingsOrNull;
|
||||
}, [viewExtensionOpened]);
|
||||
const resizable = ui?.resizable;
|
||||
|
||||
const uiWidth = ui && typeof ui.width === "number" ? ui.width : null;
|
||||
const uiHeight = ui && typeof ui.height === "number" ? ui.height : null;
|
||||
const hasExplicitWindowSize = uiWidth != null && uiHeight != null;
|
||||
|
||||
const baseWidth = useMemo(() => {
|
||||
if (uiWidth != null) return uiWidth;
|
||||
if (fallbackViewSize != null) return fallbackViewSize.width;
|
||||
return 0;
|
||||
}, [uiWidth, fallbackViewSize]);
|
||||
const baseHeight = useMemo(() => {
|
||||
if (uiHeight != null) return uiHeight;
|
||||
if (fallbackViewSize != null) return fallbackViewSize.height;
|
||||
return 0;
|
||||
}, [uiHeight, fallbackViewSize]);
|
||||
|
||||
const recomputeScale = useCallback(async () => {
|
||||
if (!hasExplicitWindowSize) {
|
||||
setScale(1);
|
||||
return;
|
||||
}
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const nextScale = Math.min(
|
||||
size.width / baseWidth,
|
||||
size.height / baseHeight
|
||||
);
|
||||
setScale(Math.max(nextScale, 0.1));
|
||||
}, [hasExplicitWindowSize, baseWidth, baseHeight]);
|
||||
|
||||
const applyFullscreen = useCallback(
|
||||
async (next: boolean) => {
|
||||
if (next) {
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const resizable = await platformAdapter.isWindowResizable();
|
||||
const pos = await platformAdapter.getWindowPosition();
|
||||
fullscreenPrevRef.current = {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
resizable,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
};
|
||||
|
||||
if (isMac && isTauri) {
|
||||
const monitor = await platformAdapter.getMonitorFromCursor();
|
||||
|
||||
if (!monitor) return;
|
||||
const window = await platformAdapter.getCurrentWebviewWindow();
|
||||
const factor = await window.scaleFactor();
|
||||
|
||||
const { size, position } = monitor;
|
||||
|
||||
const { width, height } = size.toLogical(factor);
|
||||
const { x, y } = position.toLogical(factor);
|
||||
|
||||
await platformAdapter.setWindowSize(width, height);
|
||||
await platformAdapter.setWindowPosition(x, y);
|
||||
await platformAdapter.setWindowResizable(true);
|
||||
await recomputeScale();
|
||||
} else {
|
||||
await platformAdapter.setWindowFullscreen(true);
|
||||
await recomputeScale();
|
||||
}
|
||||
} else {
|
||||
if (!isMac) {
|
||||
await platformAdapter.setWindowFullscreen(false);
|
||||
}
|
||||
if (fullscreenPrevRef.current) {
|
||||
const prev = fullscreenPrevRef.current;
|
||||
await platformAdapter.setWindowSize(prev.width, prev.height);
|
||||
await platformAdapter.setWindowResizable(prev.resizable);
|
||||
await platformAdapter.setWindowPosition(prev.x, prev.y);
|
||||
fullscreenPrevRef.current = null;
|
||||
await recomputeScale();
|
||||
} else if (hasExplicitWindowSize) {
|
||||
const nextResizable =
|
||||
ui && typeof ui.resizable === "boolean" ? ui.resizable : true;
|
||||
await platformAdapter.setWindowSize(uiWidth, uiHeight);
|
||||
await platformAdapter.setWindowResizable(nextResizable);
|
||||
await platformAdapter.centerOnCurrentMonitor();
|
||||
await recomputeScale();
|
||||
} else {
|
||||
await recomputeScale();
|
||||
}
|
||||
setTimeout(() => {
|
||||
iframeRef.current?.focus();
|
||||
try {
|
||||
iframeRef.current?.contentWindow?.focus();
|
||||
} catch {}
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
[ui, recomputeScale]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const applyWindowSettings = async () => {
|
||||
if (viewExtensionOpened != null) {
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const resizable = await platformAdapter.isWindowResizable();
|
||||
const pos = await platformAdapter.getWindowPosition();
|
||||
setFallbackViewSize({ width: size.width, height: size.height });
|
||||
prevWindowRef.current = {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
resizable,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
};
|
||||
|
||||
if (hasExplicitWindowSize) {
|
||||
const nextResizable =
|
||||
ui && typeof ui.resizable === "boolean" ? ui.resizable : true;
|
||||
await platformAdapter.setWindowSize(uiWidth, uiHeight);
|
||||
await platformAdapter.setWindowResizable(nextResizable);
|
||||
await platformAdapter.centerOnCurrentMonitor();
|
||||
await recomputeScale();
|
||||
} else {
|
||||
await recomputeScale();
|
||||
}
|
||||
setTimeout(() => {
|
||||
iframeRef.current?.focus();
|
||||
try {
|
||||
iframeRef.current?.contentWindow?.focus();
|
||||
} catch {}
|
||||
}, 0);
|
||||
} else {
|
||||
if (prevWindowRef.current) {
|
||||
const prev = prevWindowRef.current;
|
||||
await platformAdapter.setWindowSize(prev.width, prev.height);
|
||||
await platformAdapter.setWindowResizable(prev.resizable);
|
||||
await platformAdapter.centerOnCurrentMonitor();
|
||||
prevWindowRef.current = null;
|
||||
await recomputeScale();
|
||||
setTimeout(() => {
|
||||
iframeRef.current?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
applyWindowSettings();
|
||||
return () => {
|
||||
if (prevWindowRef.current) {
|
||||
const prev = prevWindowRef.current;
|
||||
platformAdapter.setWindowSize(prev.width, prev.height);
|
||||
platformAdapter.setWindowResizable(prev.resizable);
|
||||
platformAdapter.centerOnCurrentMonitor();
|
||||
prevWindowRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [
|
||||
viewExtensionOpened,
|
||||
ui,
|
||||
hasExplicitWindowSize,
|
||||
uiWidth,
|
||||
uiHeight,
|
||||
recomputeScale,
|
||||
]);
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && isFullscreen) {
|
||||
applyFullscreen(false);
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKeyDown, { capture: true });
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown, {
|
||||
capture: true,
|
||||
} as any);
|
||||
};
|
||||
}, [isFullscreen, applyFullscreen]);
|
||||
|
||||
const {
|
||||
resizable,
|
||||
scale,
|
||||
iframeRef,
|
||||
isFullscreen,
|
||||
toggleFullscreen,
|
||||
focusIframe,
|
||||
} = useViewExtensionWindow();
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
@@ -384,17 +188,7 @@ const ViewExtension: React.FC = () => {
|
||||
: t("viewExtension.fullscreen.enter")
|
||||
}
|
||||
className="absolute top-2 right-2 z-10 rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
|
||||
onClick={async () => {
|
||||
const next = !isFullscreen;
|
||||
await applyFullscreen(next);
|
||||
setIsFullscreen(next);
|
||||
if (next) {
|
||||
iframeRef.current?.focus();
|
||||
try {
|
||||
iframeRef.current?.contentWindow?.focus();
|
||||
} catch {}
|
||||
}
|
||||
}}
|
||||
onClick={toggleFullscreen}
|
||||
>
|
||||
{isFullscreen ? (
|
||||
<Minimize2 className="size-4" />
|
||||
@@ -408,27 +202,16 @@ const ViewExtension: React.FC = () => {
|
||||
<button
|
||||
aria-label={t("viewExtension.focus")}
|
||||
className="absolute top-2 right-12 z-10 rounded-md bg-black/40 text-white p-2 hover:bg-black/60 focus:outline-none"
|
||||
onClick={() => {
|
||||
iframeRef.current?.focus();
|
||||
try {
|
||||
iframeRef.current?.contentWindow?.focus();
|
||||
} catch {}
|
||||
}}
|
||||
onClick={focusIframe}
|
||||
>
|
||||
<Focus className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className="w-full h-full flex items-center justify-center"
|
||||
onMouseDownCapture={() => {
|
||||
iframeRef.current?.focus();
|
||||
}}
|
||||
onPointerDown={() => {
|
||||
iframeRef.current?.focus();
|
||||
}}
|
||||
onClickCapture={() => {
|
||||
iframeRef.current?.focus();
|
||||
}}
|
||||
className="w-full h-full flex items-center justify-center overflow-hidden"
|
||||
onMouseDownCapture={focusIframe}
|
||||
onPointerDown={focusIframe}
|
||||
onClickCapture={focusIframe}
|
||||
>
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
|
||||
243
src/hooks/useViewExtensionWindow.ts
Normal file
243
src/hooks/useViewExtensionWindow.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import platformAdapter from "@/utils/platformAdapter";
|
||||
import { isMac } from "@/utils/platform";
|
||||
import type { ViewExtensionUISettingsOrNull } from "@/components/Settings/Extensions";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
import { useSearchStore } from "@/stores/searchStore";
|
||||
|
||||
type WindowSnapshot = {
|
||||
width: number;
|
||||
height: number;
|
||||
resizable: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export function useViewExtensionWindow() {
|
||||
const isTauri = useAppStore((state) => state.isTauri);
|
||||
const viewExtensionOpened = useSearchStore((state) => state.viewExtensionOpened);
|
||||
|
||||
if (viewExtensionOpened == null) {
|
||||
throw new Error(
|
||||
"ViewExtension Error: viewExtensionOpened is null. This should not happen."
|
||||
);
|
||||
}
|
||||
|
||||
const ui: ViewExtensionUISettingsOrNull = useMemo(() => {
|
||||
return viewExtensionOpened[4] as ViewExtensionUISettingsOrNull;
|
||||
}, [viewExtensionOpened]);
|
||||
const resizable = ui?.resizable;
|
||||
|
||||
const uiWidth = ui && typeof ui.width === "number" ? ui.width : null;
|
||||
const uiHeight = ui && typeof ui.height === "number" ? ui.height : null;
|
||||
const hasExplicitWindowSize = uiWidth != null && uiHeight != null;
|
||||
|
||||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const prevWindowRef = useRef<WindowSnapshot | null>(null);
|
||||
const fullscreenPrevRef = useRef<WindowSnapshot | null>(null);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [fallbackViewSize, setFallbackViewSize] = useState<{
|
||||
width: number;
|
||||
height: number;
|
||||
} | null>(() => {
|
||||
if (typeof window === "undefined") return null;
|
||||
return { width: window.innerWidth, height: window.innerHeight };
|
||||
});
|
||||
|
||||
const baseWidth = useMemo(() => {
|
||||
if (uiWidth != null) return uiWidth;
|
||||
if (fallbackViewSize != null) return fallbackViewSize.width;
|
||||
return 0;
|
||||
}, [uiWidth, fallbackViewSize]);
|
||||
const baseHeight = useMemo(() => {
|
||||
if (uiHeight != null) return uiHeight;
|
||||
if (fallbackViewSize != null) return fallbackViewSize.height;
|
||||
return 0;
|
||||
}, [uiHeight, fallbackViewSize]);
|
||||
|
||||
const focusIframe = useCallback(() => {
|
||||
iframeRef.current?.focus();
|
||||
try {
|
||||
iframeRef.current?.contentWindow?.focus();
|
||||
} catch {}
|
||||
}, []);
|
||||
|
||||
const focusIframeSoon = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
focusIframe();
|
||||
}, 0);
|
||||
}, [focusIframe]);
|
||||
|
||||
const recomputeScale = useCallback(async () => {
|
||||
if (!hasExplicitWindowSize) {
|
||||
setScale(1);
|
||||
return;
|
||||
}
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const nextScale = Math.min(size.width / baseWidth, size.height / baseHeight);
|
||||
setScale(Math.max(nextScale, 0.1));
|
||||
}, [baseHeight, baseWidth, hasExplicitWindowSize]);
|
||||
|
||||
const applyFullscreen = useCallback(
|
||||
async (next: boolean, options?: { centerOnExit?: boolean }) => {
|
||||
const centerOnExit = options?.centerOnExit ?? true;
|
||||
if (next) {
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const resizable = await platformAdapter.isWindowResizable();
|
||||
const pos = await platformAdapter.getWindowPosition();
|
||||
fullscreenPrevRef.current = {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
resizable,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
};
|
||||
|
||||
if (isMac && isTauri) {
|
||||
const monitor = await platformAdapter.getMonitorFromCursor();
|
||||
if (!monitor) return;
|
||||
|
||||
const window = await platformAdapter.getCurrentWebviewWindow();
|
||||
const factor = await window.scaleFactor();
|
||||
|
||||
const { size, position } = monitor;
|
||||
const { width, height } = size.toLogical(factor);
|
||||
const { x, y } = position.toLogical(factor);
|
||||
|
||||
await platformAdapter.setWindowSize(width, height);
|
||||
await platformAdapter.setWindowPosition(x, y);
|
||||
await platformAdapter.setWindowResizable(true);
|
||||
await recomputeScale();
|
||||
} else {
|
||||
await platformAdapter.setWindowFullscreen(true);
|
||||
await recomputeScale();
|
||||
}
|
||||
} else {
|
||||
const prevPos =
|
||||
fullscreenPrevRef.current != null
|
||||
? { x: fullscreenPrevRef.current.x, y: fullscreenPrevRef.current.y }
|
||||
: null;
|
||||
|
||||
if (!isMac) {
|
||||
await platformAdapter.setWindowFullscreen(false);
|
||||
}
|
||||
|
||||
if (fullscreenPrevRef.current) {
|
||||
const prev = fullscreenPrevRef.current;
|
||||
await platformAdapter.setWindowSize(prev.width, prev.height);
|
||||
await platformAdapter.setWindowResizable(prev.resizable);
|
||||
fullscreenPrevRef.current = null;
|
||||
} else if (hasExplicitWindowSize) {
|
||||
const nextResizable =
|
||||
ui && typeof ui.resizable === "boolean" ? ui.resizable : true;
|
||||
await platformAdapter.setWindowSize(uiWidth, uiHeight);
|
||||
await platformAdapter.setWindowResizable(nextResizable);
|
||||
}
|
||||
|
||||
if (centerOnExit) {
|
||||
await platformAdapter.centerOnCurrentMonitor();
|
||||
} else if (prevPos != null) {
|
||||
await platformAdapter.setWindowPosition(prevPos.x, prevPos.y);
|
||||
}
|
||||
|
||||
await recomputeScale();
|
||||
focusIframeSoon();
|
||||
}
|
||||
},
|
||||
[
|
||||
focusIframeSoon,
|
||||
hasExplicitWindowSize,
|
||||
isTauri,
|
||||
recomputeScale,
|
||||
ui,
|
||||
uiHeight,
|
||||
uiWidth,
|
||||
]
|
||||
);
|
||||
|
||||
const toggleFullscreen = useCallback(async () => {
|
||||
const next = !isFullscreen;
|
||||
await applyFullscreen(next);
|
||||
setIsFullscreen(next);
|
||||
if (next) focusIframe();
|
||||
}, [applyFullscreen, focusIframe, isFullscreen]);
|
||||
|
||||
useEffect(() => {
|
||||
const applyWindowSettings = async () => {
|
||||
const size = await platformAdapter.getWindowSize();
|
||||
const resizable = await platformAdapter.isWindowResizable();
|
||||
const pos = await platformAdapter.getWindowPosition();
|
||||
setFallbackViewSize({ width: size.width, height: size.height });
|
||||
prevWindowRef.current = {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
resizable,
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
};
|
||||
|
||||
if (hasExplicitWindowSize) {
|
||||
const nextResizable =
|
||||
ui && typeof ui.resizable === "boolean" ? ui.resizable : true;
|
||||
await platformAdapter.setWindowSize(uiWidth, uiHeight);
|
||||
await platformAdapter.setWindowResizable(nextResizable);
|
||||
await platformAdapter.centerOnCurrentMonitor();
|
||||
await recomputeScale();
|
||||
} else {
|
||||
await recomputeScale();
|
||||
}
|
||||
focusIframeSoon();
|
||||
};
|
||||
|
||||
applyWindowSettings();
|
||||
return () => {
|
||||
if (prevWindowRef.current) {
|
||||
const prev = prevWindowRef.current;
|
||||
if (!isMac && fullscreenPrevRef.current != null) {
|
||||
platformAdapter.setWindowFullscreen(false);
|
||||
}
|
||||
platformAdapter.setWindowSize(prev.width, prev.height);
|
||||
platformAdapter.setWindowResizable(prev.resizable);
|
||||
platformAdapter.setWindowPosition(prev.x, prev.y);
|
||||
|
||||
prevWindowRef.current = null;
|
||||
fullscreenPrevRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [
|
||||
focusIframeSoon,
|
||||
hasExplicitWindowSize,
|
||||
recomputeScale,
|
||||
ui,
|
||||
uiHeight,
|
||||
uiWidth,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && isFullscreen) {
|
||||
applyFullscreen(false);
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKeyDown, { capture: true });
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown, {
|
||||
capture: true,
|
||||
} as any);
|
||||
};
|
||||
}, [applyFullscreen, isFullscreen]);
|
||||
|
||||
return {
|
||||
ui,
|
||||
resizable,
|
||||
scale,
|
||||
iframeRef,
|
||||
isFullscreen,
|
||||
toggleFullscreen,
|
||||
focusIframe,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ export interface TauriPlatformAdapter extends BasePlatformAdapter {
|
||||
setWindowMaximized: (enable: boolean) => Promise<void>;
|
||||
getWindowPosition: () => Promise<{ x: number; y: number }>;
|
||||
setWindowPosition: (x: number, y: number) => Promise<void>;
|
||||
centerWindow: () => Promise<void>;
|
||||
getMonitorFromCursor: () => Promise<Monitor | null>;
|
||||
centerOnCurrentMonitor: () => Promise<unknown>;
|
||||
}
|
||||
@@ -81,9 +80,6 @@ export const createTauriAdapter = (): TauriPlatformAdapter => {
|
||||
async setWindowPosition(x, y) {
|
||||
return windowWrapper.setLogicalPosition(x, y);
|
||||
},
|
||||
async centerWindow() {
|
||||
return windowWrapper.center();
|
||||
},
|
||||
|
||||
async getMonitorFromCursor() {
|
||||
const appWindow = getCurrentWebviewWindow();
|
||||
|
||||
@@ -17,9 +17,9 @@ export interface WebPlatformAdapter extends BasePlatformAdapter {
|
||||
getWindowSize: () => Promise<{ width: number; height: number }>;
|
||||
setWindowFullscreen: (enable: boolean) => Promise<void>;
|
||||
getMonitorFromCursor: () => Promise<any>;
|
||||
centerOnCurrentMonitor: () => Promise<void>;
|
||||
getWindowPosition: () => Promise<{ x: number; y: number }>;
|
||||
setWindowPosition: (x: number, y: number) => Promise<void>;
|
||||
centerOnCurrentMonitor: () => Promise<void>;
|
||||
}
|
||||
|
||||
// Create Web adapter functions
|
||||
@@ -71,6 +71,7 @@ export const createWebAdapter = (): WebPlatformAdapter => {
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async centerOnCurrentMonitor() {
|
||||
// Not applicable in web mode
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as commands from "@/commands";
|
||||
import { WINDOW_CENTER_BASELINE_HEIGHT } from "@/constants";
|
||||
import platformAdapter from "../platformAdapter";
|
||||
|
||||
// Window operations
|
||||
@@ -16,9 +15,6 @@ export const windowWrapper = {
|
||||
const window = await this.getCurrentWebviewWindow();
|
||||
if (window) {
|
||||
await window.setSize(new LogicalSize(width, height));
|
||||
if (height < WINDOW_CENTER_BASELINE_HEIGHT) {
|
||||
await window.center();
|
||||
}
|
||||
}
|
||||
},
|
||||
async getLogicalSize() {
|
||||
@@ -51,12 +47,6 @@ export const windowWrapper = {
|
||||
const win = getCurrentWindow();
|
||||
return win.setFullscreen(enable);
|
||||
},
|
||||
async center() {
|
||||
const window = await this.getCurrentWebviewWindow();
|
||||
if (window) {
|
||||
return window.center();
|
||||
}
|
||||
},
|
||||
|
||||
async setLogicalPosition(x: number, y: number) {
|
||||
const { LogicalPosition } = await import("@tauri-apps/api/dpi");
|
||||
|
||||
Reference in New Issue
Block a user