Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 6x 1903x 6x 16x 14x 6x 6x 36x 36x 33x 33x 1x 32x 32x 32x 32x 51x 50x 4x 4x 46x 46x 46x 45x 1x 46x 46x 1x 1x 32x 1x 1x 32x 32x 27x 27x | // SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 Max Boksem. See NOTICE for additional terms under GPLv3 section 7.
import { getCurrentWindow } from "@tauri-apps/api/window";
import type { ChangeTracker } from "@/lib/change-tracker";
import { isMobilePlatform } from "@/lib/platform";
import type { LayoutMode } from "@/lib/types";
/**
* Whether the player shows a full screen button. The phone layout already
* plays full screen. Every other layout has the sidebar next to the video,
* including an iPad's, so it needs the button. A device without a touch
* screen, like an Android TV box, keeps it for its remote in any layout.
*/
export const hasFullscreenButton = (touch: boolean, layoutMode: LayoutMode): boolean =>
!touch || layoutMode !== "mobile";
/**
* Puts the window in or out of full screen. On iOS and Android the window
* already fills its screen or its Split View pane, and tao's iOS version
* would resize it to the whole screen, so there full screen only hides the
* sidebar.
*/
export const setWindowFullscreen = async (on: boolean): Promise<void> => {
if (isMobilePlatform()) return;
await getCurrentWindow().setFullscreen(on);
};
/**
* How long after one of the app's own full screen changes to read the window
* again, in milliseconds. It's longer than the macOS animation, so the change
* has landed or been turned down by then.
*/
export const RECHECK_MS = 1000;
/** Follows the window's full screen state. See `followWindowFullscreen`. */
export interface FullscreenFollower {
/**
* Reads the window again. Call it a while after one of the app's own
* changes, since macOS can turn a change down without resizing the window.
*/
recheck: () => void;
/** Stops following, and passes nothing more on. */
stop: () => void;
}
/**
* Reads whether the window is full screen once it can follow its resizes,
* then after each resize, and passes it to `onChange` until `stop` is
* called. Going in or out of full screen resizes the window, so this also
* catches the ways the app doesn't start itself, such as the green button
* on macOS.
*
* A read sent before one of the app's own changes in `changes` is dropped,
* since it may come back with the state from before the change. Dragging the
* window's edge resizes it many times a second, so only one read runs at a
* time, with one more after it if the window resized meanwhile. Does nothing
* on iOS and Android, where the window never goes full screen, or outside
* Tauri, where there's no window to follow.
*/
export const followWindowFullscreen = (
onChange: (on: boolean) => void,
changes: ChangeTracker
): FullscreenFollower => {
const idle = { recheck: () => {}, stop: () => {} };
if (isMobilePlatform()) return idle;
let win: ReturnType<typeof getCurrentWindow>;
try {
win = getCurrentWindow();
} catch {
return idle;
}
let stopped = false;
let reading = false;
let resizedMeanwhile = false;
const read = () => {
if (stopped) return;
if (reading) {
resizedMeanwhile = true;
return;
}
reading = true;
const mark = changes.mark();
win.isFullscreen()
.then(
(on) => {
if (!stopped && !changes.isStale(mark)) onChange(on);
},
(e) => console.error("[fullscreen] isFullscreen failed:", e)
)
.finally(() => {
reading = false;
if (resizedMeanwhile) {
resizedMeanwhile = false;
read();
}
});
};
const unlisten = win.onResized(read).catch((e) => {
console.error("[fullscreen] onResized failed:", e);
return () => {};
});
// Reading only once the listener is in place means a change made while
// it was being set up isn't missed.
unlisten.then(() => read());
return {
recheck: read,
stop: () => {
stopped = true;
unlisten.then((fn) => fn());
},
};
};
|