Updated window rendering

This commit is contained in:
Prozilla 2023-08-10 13:27:09 +02:00
parent e0c7518070
commit 60a5e5ba30
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
7 changed files with 94 additions and 40 deletions

12
package-lock.json generated
View file

@ -20,7 +20,6 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-draggable": "^4.4.5", "react-draggable": "^4.4.5",
"react-iframe": "^1.8.5",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-svg": "^16.1.18", "react-svg": "^16.1.18",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
@ -14853,17 +14852,6 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
}, },
"node_modules/react-iframe": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/react-iframe/-/react-iframe-1.8.5.tgz",
"integrity": "sha512-F4cQJGs3ydaG6fJWfuz9yLwOU0Trzl6kttXuUG+vYwosH8enOOFxZWEDQCSbNVO8ayjfYZeqLxEvdvcsSy4GvA==",
"dependencies": {
"object-assign": "^4.1.1"
},
"peerDependencies": {
"react": ">=16.x.x"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",

View file

@ -18,7 +18,6 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-draggable": "^4.4.5", "react-draggable": "^4.4.5",
"react-iframe": "^1.8.5",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-svg": "^16.1.18", "react-svg": "^16.1.18",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
@ -40,20 +39,31 @@
"jsdoc" "jsdoc"
], ],
"rules": { "rules": {
"indent": ["error", "tab", { "SwitchCase": 1 }], "indent": [
"error",
"tab",
{
"SwitchCase": 1
}
],
"semi": "error", "semi": "error",
"no-var": "error", "no-var": "error",
"prefer-const": "error", "prefer-const": "error",
"quotes": ["error", "double"], "quotes": [
"error",
"double"
],
"jsdoc/no-undefined-types": "warn", "jsdoc/no-undefined-types": "warn",
"jsdoc/require-param": "warn", "jsdoc/require-param": "warn",
"jsdoc/check-tag-names": "warn", "jsdoc/check-tag-names": "warn",
"jsdoc/check-types": ["warn", {}], "jsdoc/check-types": [
"warn",
{}
],
"jsdoc/check-values": "warn", "jsdoc/check-values": "warn",
"jsdoc/empty-tags": "warn", "jsdoc/empty-tags": "warn",
"jsdoc/check-param-names": "warn", "jsdoc/check-param-names": "warn",
"jsdoc/check-property-names": "warn", "jsdoc/check-property-names": "warn",
"jsdoc/check-access": "warn", "jsdoc/check-access": "warn",
"jsdoc/check-alignment": "warn", "jsdoc/check-alignment": "warn",
"jsdoc/multiline-blocks": "warn", "jsdoc/multiline-blocks": "warn",
@ -63,10 +73,10 @@
"settings": { "settings": {
"jsdoc": { "jsdoc": {
"preferredTypes": { "preferredTypes": {
"Object":"object", "Object": "object",
"object.<>":"Object<>", "object.<>": "Object<>",
"Object.<>":"Object<>", "Object.<>": "Object<>",
"object<>":"Object<>" "object<>": "Object<>"
} }
} }
} }

View file

@ -1,13 +1,47 @@
import Iframe from "react-iframe"; import { useCallback, useEffect, useState } from "react";
import styles from "./WebView.module.css"; import styles from "./WebView.module.css";
/** /**
* @param {Object} props * @param {Object} props
* @param {String} props.source * @param {String} props.source
* @returns
*/ */
export function WebView(props) { export function WebView(props) {
const [hovered, setHovered] = useState(false);
const { source, focus } = props;
useEffect(() => {
window.focus();
const onBlur = (event) => {
if (hovered) {
focus(event);
}
};
window.addEventListener("blur", onBlur);
return () => {
window.removeEventListener("blur", onBlur);
};
}, [hovered]);
const onMouseOver = () => {
setHovered(true);
};
const onMouseOut = () => {
window.focus();
setHovered(false);
};
return ( return (
<Iframe url={props.source} className={styles["Web-view"]} {...props}/> <div className={styles.Container} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
<iframe
src={source}
className={styles["Web-view"]}
/>
</div>
); );
} }

View file

@ -1,3 +1,11 @@
.Container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.Web-view { .Web-view {
width: 100%; width: 100%;
height: 100%; height: 100%;

View file

@ -4,10 +4,8 @@ import { faMinus, faWindowMaximize as fasWindowMaximize, faXmark } from "@fortaw
import { ReactSVG } from "react-svg"; import { ReactSVG } from "react-svg";
import { useWindowsManager } from "../../hooks/windows/WindowsManagerContext.js"; import { useWindowsManager } from "../../hooks/windows/WindowsManagerContext.js";
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import { useEffect, useRef, useState } from "react"; import { memo, useEffect, useRef, useState } from "react";
import Application from "../../features/applications/application.js"; import Application from "../../features/applications/application.js";
import Vector2 from "../../features/math/vector2.js"; import Vector2 from "../../features/math/vector2.js";
import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons"; import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons";
@ -21,7 +19,7 @@ import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons";
* @param {Function} props.onInteract * @param {Function} props.onInteract
* @param {object} props.options * @param {object} props.options
*/ */
export function Window({ id, app, size, position, focused = false, onInteract, options }) { export const Window = memo(function Window({ id, app, size, position, focused = false, onInteract, options }) {
const windowsManager = useWindowsManager(); const windowsManager = useWindowsManager();
const nodeRef = useRef(null); const nodeRef = useRef(null);
@ -72,12 +70,15 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
windowsManager.close(id); windowsManager.close(id);
}; };
const focus = (event) => { const focus = (event, force = false) => {
if (force)
return onInteract();
if (event.defaultPrevented) if (event.defaultPrevented)
return; return;
if ((event.target.closest(".Handle") == null || event.target.closest("button") == null)) if (event.target?.closest?.(".Handle") == null || event.target?.closest?.("button") == null)
onInteract(event); onInteract();
}; };
const classNames = [styles["Window-container"]]; const classNames = [styles["Window-container"]];
@ -86,8 +87,11 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
if (minimized) if (minimized)
classNames.push(styles.Minimized); classNames.push(styles.Minimized);
// console.log(`Rendering window: ${id}`);
return ( return (
<Draggable <Draggable
key={id}
axis="both" axis="both"
handle={".Handle"} handle={".Handle"}
defaultPosition={startPosition} defaultPosition={startPosition}
@ -122,17 +126,21 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
<button title="Minimize" tabIndex={0} onClick={() => setMinimized(!minimized)}> <button title="Minimize" tabIndex={0} onClick={() => setMinimized(!minimized)}>
<FontAwesomeIcon icon={faMinus}/> <FontAwesomeIcon icon={faMinus}/>
</button> </button>
<button title="Maximize" tabIndex={0} onClick={() => setMaximized(!maximized)}> <button title="Maximize" tabIndex={0} id="maximize-window" onClick={(event) => {
event.preventDefault();
setMaximized(!maximized);
focus(event, true);
}}>
<FontAwesomeIcon icon={maximized ? fasWindowMaximize : faWindowMaximize}/> <FontAwesomeIcon icon={maximized ? fasWindowMaximize : faWindowMaximize}/>
</button> </button>
<button title="Close" tabIndex={0} id="close-window" onClick={close}> <button title="Close" tabIndex={0} id="close-window" onClick={close}>
<FontAwesomeIcon icon={faXmark}/> <FontAwesomeIcon icon={faXmark}/>
</button> </button>
</div> </div>
<div className={styles["Window-content"]}> <div className={styles["Window-content"]} onClick={(event) => { console.log(event); }}>
<app.WindowContent {...options} setTitle={setTitle} close={close}/> <app.WindowContent {...options} setTitle={setTitle} close={close} focus={focus}/>
</div> </div>
</div> </div>
</Draggable> </Draggable>
); );
} });

View file

@ -1,17 +1,23 @@
import { Window } from "./Window.jsx"; import { Window } from "./Window.jsx";
import { useWindows } from "../../hooks/windows/WindowsContext.js"; import { useWindows } from "../../hooks/windows/WindowsContext.js";
import { useWindowsManager } from "../../hooks/windows/WindowsManagerContext.js"; import { useWindowsManager } from "../../hooks/windows/WindowsManagerContext.js";
import { useEffect, useState } from "react";
export function WindowsView() { export function WindowsView() {
const windows = useWindows(); const windows = useWindows();
const windowsManager = useWindowsManager(); const windowsManager = useWindowsManager();
const [sortedWindows, setSortedWindows] = useState([]);
useEffect(() => {
setSortedWindows([...windows].sort((windowA, windowB) =>
windowA.lastInteraction - windowB.lastInteraction
));
}, [windows]);
// TO DO: prevent windows from being rerendered when order is changed // TO DO: prevent windows from being rerendered when order is changed
return (<div> return (<div>
{windows.sort((windowA, windowB) => {sortedWindows.map(({ id, app, size, position, options }) =>
windowA.lastInteraction - windowB.lastInteraction
).map(({ id, app, size, position, options }) =>
<Window <Window
onInteract={() => { windowsManager.focus(id); }} onInteract={() => { windowsManager.focus(id); }}
id={id} id={id}

View file

@ -13,7 +13,7 @@ export default class ApplicationsManager {
new Application("Terminal", "terminal", Terminal), new Application("Terminal", "terminal", Terminal),
new Application("Settings", "settings", Settings), new Application("Settings", "settings", Settings),
// new Application("Browser", "browser"), // new Application("Browser", "browser"),
new Application("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }), // new Application("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }),
new Application("Text Editor", "text-editor", TextEditor), new Application("Text Editor", "text-editor", TextEditor),
// new Application("Code Editor", "code-editor"), // new Application("Code Editor", "code-editor"),
new Application("File Explorer", "file-explorer", FileExplorer), new Application("File Explorer", "file-explorer", FileExplorer),