mirror of
https://github.com/DustinBrett/daedalOS.git
synced 2025-12-06 12:20:20 +01:00
This commit is contained in:
parent
2d16d6edb4
commit
c00d4285da
|
|
@ -6,6 +6,7 @@
|
|||
"alpha-value-notation": "percentage",
|
||||
"color-function-notation": "legacy",
|
||||
"hue-degree-notation": "number",
|
||||
"no-empty-source": null,
|
||||
"order/properties-alphabetical-order": true,
|
||||
"value-keyword-case": [
|
||||
"lower",
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
const StyledQuake3 = styled.div`
|
||||
display: flex;
|
||||
place-content: center;
|
||||
|
||||
canvas {
|
||||
background-color: #000;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledQuake3;
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import StyledQuake3 from "components/apps/Quake3/StyledQuake3";
|
||||
import useQuake3 from "components/apps/Quake3/useQuake3";
|
||||
import AppContainer from "components/system/Apps/AppContainer";
|
||||
import { type ComponentProcessProps } from "components/system/Apps/RenderComponent";
|
||||
|
||||
const Quake3: FC<ComponentProcessProps> = ({ id }) => (
|
||||
<AppContainer StyledComponent={StyledQuake3} id={id} useHook={useQuake3} />
|
||||
<AppContainer id={id} useHook={useQuake3} />
|
||||
);
|
||||
|
||||
export default Quake3;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useTheme } from "styled-components";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { type ContainerHookProps } from "components/system/Apps/AppContainer";
|
||||
import useEmscriptenMount from "components/system/Files/FileManager/useEmscriptenMount";
|
||||
import { type EmscriptenFS } from "contexts/fileSystem/useAsyncFs";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { useSession } from "contexts/session";
|
||||
import { TRANSITIONS_IN_MILLISECONDS } from "utils/constants";
|
||||
import { PREVENT_SCROLL, TRANSITIONS_IN_MILLISECONDS } from "utils/constants";
|
||||
import { loadFiles, pxToNum } from "utils/functions";
|
||||
import useIsolatedContentWindow from "hooks/useIsolatedContentWindow";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
@ -22,7 +23,7 @@ declare global {
|
|||
exit: () => void;
|
||||
exitHandler: (error: Error | null) => void;
|
||||
setCanvasSize: (width: number, height: number) => void;
|
||||
viewport: HTMLDivElement | null;
|
||||
viewport: HTMLElement | null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -52,70 +53,104 @@ const useQuake3 = ({
|
|||
const wasMaximized = useRef(false);
|
||||
const mountEmFs = useEmscriptenMount();
|
||||
const { size } = windowState || {};
|
||||
const focusCanvas = useCallback((focusedWindow: Window) => {
|
||||
if (focusedWindow?.ioq3?.canvas) {
|
||||
focusedWindow.ioq3.canvas.focus(PREVENT_SCROLL);
|
||||
} else {
|
||||
requestAnimationFrame(() => focusCanvas(focusedWindow));
|
||||
}
|
||||
}, []);
|
||||
const getContentWindow = useIsolatedContentWindow(
|
||||
id,
|
||||
containerRef,
|
||||
focusCanvas,
|
||||
`
|
||||
body { display: flex; place-content: center; place-items: center; }
|
||||
canvas { background-color: #000; height: 100%; width: 100%; }
|
||||
canvas:focus-visible { outline: none; }
|
||||
`
|
||||
);
|
||||
const [contentWindow, setContentWindow] = useState<Window>();
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
loadFiles(libs).then(() => {
|
||||
if (!window.ioq3) return;
|
||||
const newContentWindow = getContentWindow?.();
|
||||
|
||||
window.ioq3.viewport = containerRef.current;
|
||||
window.ioq3.elementPointerLock = true;
|
||||
window.ioq3.callMain([]);
|
||||
if (!newContentWindow) return;
|
||||
|
||||
setLoading(false);
|
||||
mountEmFs(window.FS as EmscriptenFS, "Quake3");
|
||||
});
|
||||
loadFiles(libs, undefined, undefined, undefined, newContentWindow).then(
|
||||
() => {
|
||||
if (!newContentWindow.ioq3) return;
|
||||
|
||||
newContentWindow.ioq3.viewport = newContentWindow.document.body;
|
||||
newContentWindow.ioq3.elementPointerLock = true;
|
||||
newContentWindow.ioq3.callMain([]);
|
||||
|
||||
setLoading(false);
|
||||
mountEmFs(window.FS as EmscriptenFS, "Quake3");
|
||||
setContentWindow(newContentWindow);
|
||||
}
|
||||
);
|
||||
}
|
||||
}, [containerRef, libs, loading, mountEmFs, setLoading]);
|
||||
}, [getContentWindow, libs, loading, mountEmFs, setLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!window.ioq3) return;
|
||||
if (!contentWindow?.ioq3) return;
|
||||
|
||||
const updateSize = (): void => {
|
||||
if (!contentWindow.ioq3?.canvas) return;
|
||||
|
||||
wasMaximized.current = maximized;
|
||||
|
||||
const { height, width } =
|
||||
(!maximized && size) || componentWindow?.getBoundingClientRect() || {};
|
||||
|
||||
if (!height || !width) return;
|
||||
|
||||
const aspectRatio = defaultSize
|
||||
? pxToNum(defaultSize.width) / pxToNum(defaultSize.height)
|
||||
: 4 / 3;
|
||||
const numWidth = pxToNum(width);
|
||||
const hasGreaterWidth = numWidth > pxToNum(height) - titleBar.height;
|
||||
const newWidth =
|
||||
maximized && hasGreaterWidth ? numWidth / aspectRatio : numWidth;
|
||||
const newHeight = newWidth / aspectRatio;
|
||||
|
||||
if (newHeight > 0 && newWidth > 0) {
|
||||
contentWindow.ioq3.setCanvasSize(newWidth, newHeight);
|
||||
contentWindow.ioq3.canvas.setAttribute(
|
||||
"style",
|
||||
`object-fit: ${hasGreaterWidth ? "contain" : "scale-down"}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
wasMaximized.current = maximized;
|
||||
|
||||
const { height, width } =
|
||||
(!maximized && size) ||
|
||||
componentWindow?.getBoundingClientRect() ||
|
||||
{};
|
||||
|
||||
if (!height || !width) return;
|
||||
|
||||
const aspectRatio = defaultSize
|
||||
? pxToNum(defaultSize.width) / pxToNum(defaultSize.height)
|
||||
: 4 / 3;
|
||||
const numWidth = pxToNum(width);
|
||||
const hasGreaterWidth = numWidth > pxToNum(height) - titleBar.height;
|
||||
const newWidth =
|
||||
maximized && hasGreaterWidth ? numWidth / aspectRatio : numWidth;
|
||||
const newHeight = newWidth / aspectRatio;
|
||||
|
||||
if (newHeight > 0 && newWidth > 0 && window.ioq3?.canvas) {
|
||||
window.ioq3.setCanvasSize(newWidth, newHeight);
|
||||
window.ioq3.canvas.setAttribute(
|
||||
"style",
|
||||
`object-fit: ${hasGreaterWidth ? "contain" : "scale-down"}`
|
||||
);
|
||||
}
|
||||
},
|
||||
updateSize,
|
||||
maximized || wasMaximized.current
|
||||
? TRANSITIONS_IN_MILLISECONDS.WINDOW + 10
|
||||
: 0
|
||||
);
|
||||
}, [componentWindow, defaultSize, maximized, size, titleBar.height]);
|
||||
}, [
|
||||
componentWindow,
|
||||
contentWindow,
|
||||
defaultSize,
|
||||
maximized,
|
||||
size,
|
||||
titleBar.height,
|
||||
]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
try {
|
||||
window.ioq3?.exit();
|
||||
contentWindow?.ioq3?.exit();
|
||||
} catch {
|
||||
// Ignore error on exit
|
||||
}
|
||||
|
||||
window.AL?.contexts.forEach(({ ctx }) => ctx.close());
|
||||
contentWindow?.AL?.contexts.forEach(({ ctx }) => ctx.close());
|
||||
},
|
||||
[]
|
||||
[contentWindow]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
const StyledTic80 = styled.div`
|
||||
iframe {
|
||||
background-color: #1a1c2c;
|
||||
border: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledTic80;
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import useTic80 from "components/apps/Tic80/useTic80";
|
||||
import StyledTic80 from "components/apps/Tic80/StyledTic80";
|
||||
import AppContainer from "components/system/Apps/AppContainer";
|
||||
import { type ComponentProcessProps } from "components/system/Apps/RenderComponent";
|
||||
|
||||
const Tic80: FC<ComponentProcessProps> = ({ id }) => (
|
||||
<AppContainer StyledComponent={StyledTic80} id={id} useHook={useTic80} />
|
||||
<AppContainer id={id} useHook={useTic80} />
|
||||
);
|
||||
|
||||
export default Tic80;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,13 @@ const useTic80 = ({
|
|||
const { readFile } = useFileSystem();
|
||||
const loadedUrl = useRef<string>(undefined);
|
||||
const { appendFileToTitle } = useTitle(id);
|
||||
const getContentWindow = useIsolatedContentWindow(id, containerRef, true);
|
||||
const getContentWindow = useIsolatedContentWindow(
|
||||
id,
|
||||
containerRef,
|
||||
undefined,
|
||||
"canvas { image-rendering: pixelated; }",
|
||||
true
|
||||
);
|
||||
const loadComputer = useCallback(
|
||||
async (fileUrl?: string) => {
|
||||
const loadApp = async (blobUrl?: string): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { memo, useMemo, useRef, useState } from "react";
|
||||
import { type IStyledComponent } from "styled-components";
|
||||
import styled, { type IStyledComponent } from "styled-components";
|
||||
import { type FastOmit } from "styled-components/dist/types";
|
||||
import StyledLoading from "components/system/Files/FileManager/StyledLoading";
|
||||
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
|
||||
|
|
@ -16,7 +16,7 @@ export type ContainerHookProps = {
|
|||
type ContainerHook = (props: ContainerHookProps) => void;
|
||||
|
||||
type AppContainerProps = {
|
||||
StyledComponent: IStyledComponent<
|
||||
StyledComponent?: IStyledComponent<
|
||||
"web",
|
||||
FastOmit<
|
||||
React.DetailedHTMLProps<
|
||||
|
|
@ -30,6 +30,8 @@ type AppContainerProps = {
|
|||
useHook: ContainerHook;
|
||||
};
|
||||
|
||||
const StyledAppContainer = styled.div``;
|
||||
|
||||
const AppContainer: FC<AppContainerProps> = ({
|
||||
id,
|
||||
useHook,
|
||||
|
|
@ -48,19 +50,16 @@ const AppContainer: FC<AppContainerProps> = ({
|
|||
}),
|
||||
[loading]
|
||||
);
|
||||
const StyledWrapper = StyledComponent || StyledAppContainer;
|
||||
|
||||
useHook({ containerRef, id, loading, setLoading, url });
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <StyledLoading />}
|
||||
<StyledComponent
|
||||
ref={containerRef}
|
||||
style={style}
|
||||
{...useFileDrop({ id })}
|
||||
>
|
||||
<StyledWrapper ref={containerRef} style={style} {...useFileDrop({ id })}>
|
||||
{children}
|
||||
</StyledComponent>
|
||||
</StyledWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -79,14 +79,15 @@ const useFileDrop = ({
|
|||
[id, mkdirRecursive, updateFolder, url, writeFile]
|
||||
);
|
||||
const { openTransferDialog } = useTransferDialog();
|
||||
|
||||
return {
|
||||
onDragLeave,
|
||||
onDragOver: (event) => {
|
||||
const onDragOverThenHaltEvent = useCallback(
|
||||
(event: DragEvent | React.DragEvent<HTMLElement>): void => {
|
||||
onDragOver?.(event);
|
||||
haltEvent(event);
|
||||
},
|
||||
onDrop: (event) => {
|
||||
[onDragOver]
|
||||
);
|
||||
const onDrop = useCallback(
|
||||
(event: DragEvent | React.DragEvent<HTMLElement>): void => {
|
||||
if (MOUNTABLE_EXTENSIONS.has(getExtension(directory))) return;
|
||||
|
||||
if (updatePositions && event.target instanceof HTMLElement) {
|
||||
|
|
@ -189,6 +190,25 @@ const useFileDrop = ({
|
|||
hasUpdateId
|
||||
);
|
||||
},
|
||||
[
|
||||
callback,
|
||||
directory,
|
||||
exists,
|
||||
iconPositions,
|
||||
id,
|
||||
openTransferDialog,
|
||||
processesRef,
|
||||
setIconPositions,
|
||||
sortOrders,
|
||||
updatePositions,
|
||||
updateProcessUrl,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
onDragLeave,
|
||||
onDragOver: onDragOverThenHaltEvent,
|
||||
onDrop,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import { useState, useEffect, useCallback, useLayoutEffect } from "react";
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
|
||||
import { PREVENT_SCROLL } from "utils/constants";
|
||||
import { useProcesses } from "contexts/process";
|
||||
|
|
@ -19,18 +25,28 @@ const createCanvas = (hostDocument: Document): HTMLCanvasElement => {
|
|||
|
||||
const createIframe = (
|
||||
id: string,
|
||||
container: HTMLDivElement
|
||||
container: HTMLDivElement,
|
||||
styles?: string
|
||||
): HTMLIFrameElement => {
|
||||
const iframe = document.createElement("iframe");
|
||||
|
||||
iframe.title = id;
|
||||
|
||||
iframe.style.backgroundColor = "transparent";
|
||||
iframe.style.border = "0";
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "100%";
|
||||
|
||||
container.append(iframe);
|
||||
|
||||
const contentDocument = iframe.contentDocument as Document;
|
||||
|
||||
contentDocument.open();
|
||||
contentDocument.write("<!DOCTYPE html><head /><body />");
|
||||
contentDocument.write(`
|
||||
<!DOCTYPE html>
|
||||
${styles ? `<head><style>${styles}</style></head>` : "<head />"}
|
||||
<body />
|
||||
`);
|
||||
contentDocument.close();
|
||||
|
||||
const contentWindow = iframe.contentWindow as Window;
|
||||
|
|
@ -51,6 +67,8 @@ type IsolatedContentWindow = (() => Window | undefined) | undefined;
|
|||
const useIsolatedContentWindow = (
|
||||
id: string,
|
||||
containerRef: React.RefObject<HTMLDivElement | null>,
|
||||
focusFunction?: (window: Window) => void,
|
||||
styles?: string,
|
||||
withCanvas = false
|
||||
): IsolatedContentWindow => {
|
||||
const [container, setContainer] = useState<HTMLDivElement>();
|
||||
|
|
@ -63,7 +81,7 @@ const useIsolatedContentWindow = (
|
|||
|
||||
container.querySelector("iframe")?.remove();
|
||||
|
||||
const iframe = createIframe(id, container);
|
||||
const iframe = createIframe(id, container, styles);
|
||||
const newContentWindow = iframe.contentWindow as Window;
|
||||
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
|
@ -86,22 +104,21 @@ const useIsolatedContentWindow = (
|
|||
setContentWindow(newContentWindow);
|
||||
|
||||
return newContentWindow;
|
||||
}, [container, id, onDragOver, onDrop, setForegroundId, withCanvas]);
|
||||
}, [container, id, onDragOver, onDrop, setForegroundId, styles, withCanvas]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (contentWindow && foregroundId === id) {
|
||||
requestAnimationFrame(() => {
|
||||
if (withCanvas) {
|
||||
if (focusFunction) focusFunction(contentWindow);
|
||||
else if (withCanvas) {
|
||||
contentWindow.document
|
||||
.querySelector<HTMLCanvasElement>("canvas")
|
||||
?.focus(PREVENT_SCROLL);
|
||||
} else {
|
||||
contentWindow.focus();
|
||||
}
|
||||
} else contentWindow.focus();
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks-addons/no-unused-deps
|
||||
}, [contentWindow, foregroundId, id, maximized, withCanvas]);
|
||||
}, [contentWindow, focusFunction, foregroundId, id, maximized, withCanvas]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!container) {
|
||||
|
|
@ -119,7 +136,10 @@ const useIsolatedContentWindow = (
|
|||
}
|
||||
}, [container, containerRef]);
|
||||
|
||||
return container ? createContentWindow : undefined;
|
||||
return useMemo(
|
||||
() => (container ? createContentWindow : undefined),
|
||||
[container, createContentWindow]
|
||||
);
|
||||
};
|
||||
|
||||
export default useIsolatedContentWindow;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user