Fix: attempt for clipping bug - not fixed yet

This commit is contained in:
DaZuo0122
2026-02-14 17:31:55 +08:00
parent 1fa7080210
commit f2954ad22b
4 changed files with 176 additions and 10 deletions

View File

@@ -25,9 +25,12 @@ type UiSpritePackOption = {
};
const WINDOW_PADDING = 16;
const WINDOW_WORKAREA_MARGIN = 80;
const MIN_WINDOW_SIZE = 64;
const SIZE_EPSILON = 0.5;
const SCALE_EPSILON = 0.0001;
const SCALE_MIN = 0.5;
const SCALE_MAX = 3.0;
async function invokeSetSpritePack(packIdOrPath: string): Promise<UiSnapshot> {
return invoke<UiSnapshot>("set_sprite_pack", { packIdOrPath });
@@ -56,6 +59,24 @@ function fittedWindowSize(
return { width, height };
}
function maxVisibleScale(
pack: UiSpritePack,
workArea: { width: number; height: number }
): number {
const availableWidth = Math.max(
workArea.width - WINDOW_PADDING - WINDOW_WORKAREA_MARGIN,
MIN_WINDOW_SIZE
);
const availableHeight = Math.max(
workArea.height - WINDOW_PADDING - WINDOW_WORKAREA_MARGIN,
MIN_WINDOW_SIZE
);
const maxByWidth = availableWidth / Math.max(pack.frame_width, 1);
const maxByHeight = availableHeight / Math.max(pack.frame_height, 1);
const cap = Math.min(maxByWidth, maxByHeight);
return Math.max(SCALE_MIN, Math.min(cap, SCALE_MAX));
}
async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise<void> {
const window = getCurrentWindow();
const [outerPosition, innerSize] = await Promise.all([window.outerPosition(), window.innerSize()]);
@@ -81,8 +102,16 @@ async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise<voi
}
if (monitor !== null) {
targetWidth = Math.min(targetWidth, monitor.workArea.size.width);
targetHeight = Math.min(targetHeight, monitor.workArea.size.height);
const widthCap = Math.max(
monitor.workArea.size.width - WINDOW_WORKAREA_MARGIN,
MIN_WINDOW_SIZE
);
const heightCap = Math.max(
monitor.workArea.size.height - WINDOW_WORKAREA_MARGIN,
MIN_WINDOW_SIZE
);
targetWidth = Math.min(targetWidth, widthCap);
targetHeight = Math.min(targetHeight, heightCap);
targetX = centerX - targetWidth / 2;
targetY = centerY - targetHeight / 2;
}
@@ -311,6 +340,7 @@ function MainOverlayWindow(): JSX.Element {
function SettingsWindow(): JSX.Element {
const [settings, setSettings] = React.useState<UiSettingsSnapshot | null>(null);
const [packs, setPacks] = React.useState<UiSpritePackOption[]>([]);
const [activePack, setActivePack] = React.useState<UiSpritePack | null>(null);
const [error, setError] = React.useState<string | null>(null);
const [pending, setPending] = React.useState(false);
@@ -319,19 +349,32 @@ function SettingsWindow(): JSX.Element {
let mounted = true;
Promise.all([
invoke<UiSettingsSnapshot>("settings_snapshot"),
invoke<UiSpritePackOption[]>("list_sprite_packs")
invoke<UiSpritePackOption[]>("list_sprite_packs"),
invoke<UiSpritePack>("load_active_sprite_pack")
])
.then(async ([snapshot, options]) => {
.then(async ([snapshot, options, pack]) => {
if (!mounted) {
return;
}
setSettings(snapshot);
setPacks(options);
setActivePack(pack);
unlisten = await listen<UiSnapshot>("runtime:snapshot", (event) => {
if (!mounted) {
return;
}
const payload = event.payload;
if (payload.active_sprite_pack !== activePack?.id) {
void invoke<UiSpritePack>("load_active_sprite_pack")
.then((nextPack) => {
if (mounted) {
setActivePack(nextPack);
}
})
.catch(() => {
// Keep existing pack metadata if reload fails.
});
}
setSettings((prev) => {
if (prev === null) {
return prev;
@@ -356,7 +399,7 @@ function SettingsWindow(): JSX.Element {
unlisten();
}
};
}, []);
}, [activePack?.id]);
const withPending = React.useCallback(async <T,>(fn: () => Promise<T>): Promise<T | null> => {
setPending(true);
@@ -378,6 +421,10 @@ function SettingsWindow(): JSX.Element {
if (next === null) {
return;
}
const refreshedPack = await withPending(() => invoke<UiSpritePack>("load_active_sprite_pack"));
if (refreshedPack !== null) {
setActivePack(refreshedPack);
}
setSettings((prev) =>
prev === null
? prev
@@ -396,13 +443,30 @@ function SettingsWindow(): JSX.Element {
if (!Number.isFinite(value)) {
return;
}
const next = await withPending(() => invokeSetScale(value));
let effectiveScale = value;
if (activePack !== null) {
try {
const monitor = await currentMonitor();
if (monitor !== null) {
effectiveScale = Math.min(
value,
maxVisibleScale(activePack, {
width: monitor.workArea.size.width,
height: monitor.workArea.size.height
})
);
}
} catch {
// Keep requested value if monitor query fails.
}
}
const next = await withPending(() => invokeSetScale(effectiveScale));
if (next === null) {
return;
}
setSettings((prev) => (prev === null ? prev : { ...prev, scale: next.scale }));
},
[withPending]
[activePack, withPending]
);
const onVisibleChange = React.useCallback(
@@ -457,8 +521,8 @@ function SettingsWindow(): JSX.Element {
<span>Scale: {settings.scale.toFixed(2)}x</span>
<input
type="range"
min={0.5}
max={3.0}
min={SCALE_MIN}
max={SCALE_MAX}
step={0.05}
value={settings.scale}
disabled={pending}