diff --git a/client/web/assets/12px-solid/fullscreen-enter.svg b/client/web/assets/12px-solid/fullscreen-enter.svg new file mode 100644 index 00000000..87111c99 --- /dev/null +++ b/client/web/assets/12px-solid/fullscreen-enter.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/web/assets/12px-solid/fullscreen-exit.svg b/client/web/assets/12px-solid/fullscreen-exit.svg new file mode 100644 index 00000000..c9d36c93 --- /dev/null +++ b/client/web/assets/12px-solid/fullscreen-exit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/web/assets/12px-solid/info.svg b/client/web/assets/12px-solid/info.svg new file mode 100644 index 00000000..6d28348e --- /dev/null +++ b/client/web/assets/12px-solid/info.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/web/src/App.vue b/client/web/src/App.vue index 4cdf5f65..c5695c72 100644 --- a/client/web/src/App.vue +++ b/client/web/src/App.vue @@ -196,10 +196,12 @@ img { diff --git a/client/web/src/components/window/title-bar/WindowButtonsMac.vue b/client/web/src/components/window/title-bar/WindowButtonsMac.vue index a41b719c..c9dcac66 100644 --- a/client/web/src/components/window/title-bar/WindowButtonsMac.vue +++ b/client/web/src/components/window/title-bar/WindowButtonsMac.vue @@ -20,15 +20,15 @@ border-radius: 50%; &.close { - background: #ff5f57; + background: #ff5a52; } &.minimize { - background: #ffbd2f; + background: #e6c029; } &.zoom { - background: #29c93f; + background: #54c22b; } } } diff --git a/client/web/src/components/window/title-bar/WindowButtonsWeb.vue b/client/web/src/components/window/title-bar/WindowButtonsWeb.vue new file mode 100644 index 00000000..e102cc41 --- /dev/null +++ b/client/web/src/components/window/title-bar/WindowButtonsWeb.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/client/web/src/main.ts b/client/web/src/main.ts index c1cb0f2b..4b83aaef 100644 --- a/client/web/src/main.ts +++ b/client/web/src/main.ts @@ -1,9 +1,11 @@ import { createApp } from "vue"; +import { fullscreenModeChanged } from "@/utilities/fullscreen"; import { handleKeyUp, handleKeyDown } from "@/utilities/input"; import App from "./App.vue"; // Bind global browser events document.addEventListener("contextmenu", (e) => e.preventDefault()); +document.addEventListener("fullscreenchange", () => fullscreenModeChanged()); window.addEventListener("keyup", (e: KeyboardEvent) => handleKeyUp(e)); window.addEventListener("keydown", (e: KeyboardEvent) => handleKeyDown(e)); diff --git a/client/web/src/utilities/fullscreen.ts b/client/web/src/utilities/fullscreen.ts new file mode 100644 index 00000000..744e0a7b --- /dev/null +++ b/client/web/src/utilities/fullscreen.ts @@ -0,0 +1,37 @@ +import { reactive, readonly } from "vue"; + +const state = reactive({ + windowFullscreen: false, + keyboardLocked: false, +}); + +export function fullscreenModeChanged() { + state.windowFullscreen = Boolean(document.fullscreenElement); + if (!state.windowFullscreen) state.keyboardLocked = false; +} + +export function keyboardLockApiSupported(): boolean { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return "keyboard" in navigator && "lock" in (navigator as any).keyboard; +} + +export async function enterFullscreen() { + await document.documentElement.requestFullscreen(); + + if (keyboardLockApiSupported()) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await (navigator as any).keyboard.lock(["ControlLeft", "ControlRight"]); + state.keyboardLocked = true; + } +} + +export async function exitFullscreen() { + await document.exitFullscreen(); +} + +export async function toggleFullscreen() { + if (state.windowFullscreen) await exitFullscreen(); + else await enterFullscreen(); +} + +export default readonly(state); diff --git a/client/web/src/utilities/input.ts b/client/web/src/utilities/input.ts index b6954985..eacb6189 100644 --- a/client/web/src/utilities/input.ts +++ b/client/web/src/utilities/input.ts @@ -1,3 +1,5 @@ +import { toggleFullscreen } from "@/utilities/fullscreen"; + const wasm = import("@/../wasm/pkg"); export function shouldRedirectKeyboardEventToBackend(e: KeyboardEvent): boolean { @@ -6,7 +8,11 @@ export function shouldRedirectKeyboardEventToBackend(e: KeyboardEvent): boolean if (target.nodeName === "INPUT" || target.nodeName === "TEXTAREA" || target.isContentEditable) return false; // Don't redirect a fullscreen request - if (e.key.toLowerCase() === "f11") return false; + if (e.key.toLowerCase() === "f11" && e.type === "keydown" && !e.repeat) { + e.preventDefault(); + toggleFullscreen(); + return false; + } // Don't redirect a reload request if (e.key.toLowerCase() === "f5") return false;