added auto-scaling and scaling via key combination

This commit is contained in:
coolcoala
2025-07-26 06:50:18 +03:00
parent fbd1c55f44
commit 06ad23d904
4 changed files with 101 additions and 1 deletions

View File

@@ -18,6 +18,7 @@
"autostart:allow-disable",
"autostart:allow-is-enabled",
"core:window:allow-set-theme",
"notification:default"
"notification:default",
"core:webview:allow-set-webview-zoom"
]
}

View File

@@ -0,0 +1,93 @@
import { useEffect, useState, useCallback } from 'react';
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
// Константы для управления масштабом
const ZOOM_STEP = 0.1;
const ZOOM_WHEEL_STEP = 0.05;
const MIN_ZOOM = 0.5; // 50%
const MAX_ZOOM = 2.0; // 200%
export const useZoomControls = () => {
const [zoomLevel, setZoomLevel] = useState(1.0);
const appWindow = WebviewWindow.getCurrent();
useEffect(() => {
const setInitialZoom = async () => {
// 1. Получаем и физический размер, и коэффициент масштабирования
const size = await appWindow.innerSize();
const scaleFactor = await appWindow.scaleFactor();
// 2. Вычисляем логическую ширину
const logicalWidth = size.width / scaleFactor;
let initialZoom = 1.0;
console.log(`Physical width: ${size.width}, Scale Factor: ${scaleFactor}, Logical width: ${logicalWidth}`);
// 3. Используем логическую ширину для принятия решения
if (logicalWidth < 1300) {
initialZoom = 1.0;
} else if (logicalWidth > 2000) {
initialZoom = 2.0;
}
await appWindow.setZoom(initialZoom);
setZoomLevel(initialZoom);
};
setInitialZoom();
}, []);
const handleZoom = useCallback((delta: number, isReset = false) => {
setZoomLevel(currentZoom => {
const newZoom = isReset ? 1.0 : currentZoom + delta;
const clampedZoom = Math.max(MIN_ZOOM, Math.min(newZoom, MAX_ZOOM));
const roundedZoom = Math.round(clampedZoom * 100) / 100;
appWindow.setZoom(roundedZoom);
const newStrokeWidth = 2 / roundedZoom;
document.documentElement.style.setProperty('--icon-stroke-width', newStrokeWidth.toString());
return roundedZoom;
});
}, [appWindow]);
useEffect(() => {
const handleWheel = (event: WheelEvent) => {
if (event.ctrlKey || event.metaKey) {
event.preventDefault();
const delta = event.deltaY > 0 ? -ZOOM_WHEEL_STEP : ZOOM_WHEEL_STEP;
handleZoom(delta);
}
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.ctrlKey || event.metaKey) {
switch (event.code) {
case 'Equal':
case 'NumpadAdd':
event.preventDefault();
handleZoom(ZOOM_STEP);
break;
case 'Minus':
case 'NumpadSubtract':
event.preventDefault();
handleZoom(-ZOOM_STEP);
break;
case 'Digit0':
case 'Numpad0':
event.preventDefault();
handleZoom(0, true);
break;
}
}
};
window.addEventListener('wheel', handleWheel, { passive: false });
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('wheel', handleWheel);
window.removeEventListener('keydown', handleKeyDown);
};
}, [handleZoom]);
};

View File

@@ -125,3 +125,7 @@
/* h-full уже применен выше к body */
}
}
svg {
stroke-width: var(--icon-stroke-width, 2);
}

View File

@@ -24,6 +24,7 @@ import { showNotice } from "@/services/noticeService";
import { NoticeManager } from "@/components/base/NoticeManager";
import { SidebarProvider, useSidebar } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/layout/sidebar";
import {useZoomControls} from "@/hooks/useZoomControls";
const appWindow = getCurrentWebviewWindow();
export let portableFlag = false;
@@ -143,6 +144,7 @@ const handleNoticeMessage = (
const Layout = () => {
const mode = useThemeMode();
useZoomControls();
const isDark = mode === "light" ? false : true;
const { t } = useTranslation();
useCustomTheme();