diff --git a/docs/README.md b/docs/README.md index d71b95c..586b143 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,7 +5,9 @@ ## Table of Contents - [Features](features/README.md) +- [Configuration](configuration/README.md) - [Design](design/README.md) +- [FAQ](faq/README.md) - [Roadmap](roadmap/README.md) - [Contributing](contributing/README.md) @@ -28,7 +30,7 @@ #### Deploying to GitHub Pages -1. Open [deploy.sh](../deploy.sh) and change `DOMAIN` to your domain, `COMMIT_MESSAGE` to your commit message and `REPO_URL` to the URL of your repo. Make sure you don't add or remove any quotes when editing these values. +1. Open [deploy.sh](../deploy.sh) and change `DOMAIN` to your domain, `COMMIT_MESSAGE` to your commit message and `REPO_URL` to the URL of your repo. Make sure you don't accidentally add or remove any quotes when editing these values. 2. Run `npm run deploy` - This will create a new build by running the redeploy script, add a CNAME pointing to your domain and then push the build folder to a branch called `gh-pages`. 3. Configure your GitHub Pages to deploy the `gh-pages` branch to your domain. @@ -36,6 +38,10 @@ Once the initial setup is complete, you can simply run `npm run deploy` and your > Note: When the deployment process is complete, you won't see the changes reflected on your website until a few moments later. This is because GitHub Pages needs to run its own build step before the website updates. You can view details about this process in the `Actions` tab on GitHub. +### Configuration + +See [docs/configuration](configuration/README.md) for more information. + ### Structure - [src](../src) directory diff --git a/docs/configuration/README.md b/docs/configuration/README.md new file mode 100644 index 0000000..09babef --- /dev/null +++ b/docs/configuration/README.md @@ -0,0 +1,11 @@ +[← Back](../README.md) + +# Configuration + +Prozilla OS can be configured in numerous ways. The most important one being via the application itself, by going to the Settings app or directly changing the config files in the `~/.config` folder. + +As a developer, there are also the following ways to configure Prozilla OS: + +- `src/config` - The `src/config` directory holds all global variables used in the rest of the `src` directory, which are mostly string and number constants, but also includes some arrays and dictionaries that can be adjusted to configure Prozilla oS. +- `styles` - Everything related to styles, can be configured in `styles` directory. Most configurations will happen inside `styles/global`, where you can define the fonts, css variables/properties and other details. +- `public/config` - This directory has XML files that serve as the default data for config files used by the app in the virtual drive. These can be edited by the user once they're loaded during initialisation. diff --git a/docs/faq/README.md b/docs/faq/README.md new file mode 100644 index 0000000..9b27bc4 --- /dev/null +++ b/docs/faq/README.md @@ -0,0 +1,11 @@ +[← Back](../README.md) + +# FAQ + +### Q: What's the purpose of Prozilla OS? + +A: Obviously Prozilla OS doesn't solve any issues or anything like that. It's just a fun project I started because I wanted to refine my web development and design skills, as well as my React.js proficiency. At the same time, I also wanted to create an interesting and innovative web application and share it with the community via open source. + +### Q: What tools did you use to make Prozilla OS? + +A: I used Visual Studio Code for coding, Figma for designing, Notion for project management, GitHub for source control. I didn't use any libraries for the main parts of the OS. The libraries I used for specific purposes like certain applications or utilities, can be found in the `package.json` file. I got the icons from Font Awesome and the fonts from Google Fonts. \ No newline at end of file diff --git a/docs/features/README.md b/docs/features/README.md index 995d978..228a887 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -17,4 +17,5 @@ To see the status and to-do's of each feature, check the [task board](https://pr - [Storage](storage/README.md) - [Taskbar](taskbar/README.md) - [Virtual Drive](virtual-drive//README.md) -- [Windows](windows/README.md) \ No newline at end of file +- [Windows](windows/README.md) +- [Z-index](z-index/README.md) \ No newline at end of file diff --git a/docs/features/z-index/README.md b/docs/features/z-index/README.md new file mode 100644 index 0000000..7e697e9 --- /dev/null +++ b/docs/features/z-index/README.md @@ -0,0 +1,5 @@ +[← Back](../README.md) + +# Z-index + +A collection of helper classes to generate z-indexes that prevent overlapping and make sure everything is displayed in the correct order. One example use case is the windows that all need to be displayed in order and should always be displayed under the taskbar, this is made possible by these helper classes. It works by creating groups that can contain any amount of indexes, each group's offset is defined by the combined length of all preceding groups. \ No newline at end of file diff --git a/docs/roadmap/README.md b/docs/roadmap/README.md index 9e34887..e7eca4c 100644 --- a/docs/roadmap/README.md +++ b/docs/roadmap/README.md @@ -6,18 +6,14 @@ This is a list of planned/in progress features. For more detailed information, check the [task board](https://prozilla.notion.site/8325fabca1fb4f9885b6d6dfd5aa64c8?v=1a59f7ce50914f5ea711fe6460e52868&pvs=4) on Notion. -## Visual Studio Code clone +### Visual Studio Code clone A fully functional VSC clone called Code Editor. -## Modals - -Modular windows, including alerts, confirmation dialogs and a file selector. - -## Calculator App +### Calculator App Simple calculator that can do basic equations. -## App centre +### App centre Allows user to download additional apps \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 96ea759..80841fe 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import { Desktop } from "./components/desktop/Desktop.jsx"; import { SettingsManagerProvider } from "./hooks/settings/settingsManagerContext.js"; import { ModalsView } from "./components/modals/ModalsView.jsx"; import { useEffect } from "react"; +import { ZIndexManagerProvider } from "./hooks/z-index/zIndexManagerContext.js"; function App() { useEffect(() => { @@ -23,16 +24,18 @@ function App() { return ( - - -
- - - - -
-
-
+ + + +
+ + + + +
+
+
+
); } diff --git a/src/components/apps/text-editor/TextEditor.jsx b/src/components/apps/text-editor/TextEditor.jsx index b9e72a4..8271dc9 100644 --- a/src/components/apps/text-editor/TextEditor.jsx +++ b/src/components/apps/text-editor/TextEditor.jsx @@ -120,8 +120,6 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modal }; } - console.log(currentFile); - return (
{ const ref = useRef(null); @@ -49,6 +51,8 @@ export const Taskbar = memo(() => { }); const [pins, setPins] = useState([]); + const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 0 }); + const modalsZIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 1 }); const apps = useMemo(() => AppsManager.APPS.sort((appA, appB) => { const indexA = pins.indexOf(appA.id); @@ -119,9 +123,9 @@ export const Taskbar = memo(() => { }; return (<> - +
{ diff --git a/src/components/taskbar/Taskbar.module.css b/src/components/taskbar/Taskbar.module.css index 58f5031..0fb49f3 100644 --- a/src/components/taskbar/Taskbar.module.css +++ b/src/components/taskbar/Taskbar.module.css @@ -14,7 +14,6 @@ height: var(--taskbar-height); background-color: var(--taskbar-color); backdrop-filter: var(--taskbar-filter); - z-index: 10; } .Taskbar button { diff --git a/src/components/windows/WindowView.jsx b/src/components/windows/WindowView.jsx index 05f358b..abdf169 100644 --- a/src/components/windows/WindowView.jsx +++ b/src/components/windows/WindowView.jsx @@ -18,6 +18,8 @@ import { Actions } from "../actions/Actions.jsx"; import { useScreenDimensions } from "../../hooks/_utils/screen.js"; import { NAME } from "../../config/branding.config.js"; import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils.js"; +import { ZIndexManager } from "../../features/z-index/zIndexManager.js"; +import { useZIndex } from "../../hooks/z-index/zIndex.js"; /** * @typedef {object} windowProps @@ -43,7 +45,7 @@ import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser * @param {boolean} props.minimized * @param {Function} props.toggleMinimized */ -export const WindowView = memo(({ id, app, size, position, onInteract, options, active, minimized, toggleMinimized }) => { +export const WindowView = memo(({ id, app, size, position, onInteract, options, active, minimized, toggleMinimized, index }) => { const windowsManager = useWindowsManager(); const nodeRef = useRef(null); const [modalsManager, modals] = useModals(); @@ -54,6 +56,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options, const [screenWidth, screenHeight] = useScreenDimensions(); const [title, setTitle] = useState(app.name); const [iconUrl, setIconUrl] = useState(AppsManager.getAppIconUrl(app.id)); + const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.WINDOWS, index }); const { onContextMenu, ShortcutsListener } = useContextMenu({ modalsManager, Actions: (props) => @@ -123,7 +126,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options, if (minimized) classNames.push(styles.Minimized); - return (<> + return (
- ); +
); }); \ No newline at end of file diff --git a/src/components/windows/WindowsView.jsx b/src/components/windows/WindowsView.jsx index f8cc95c..8cfe2fb 100644 --- a/src/components/windows/WindowsView.jsx +++ b/src/components/windows/WindowsView.jsx @@ -48,14 +48,17 @@ export const WindowsView = memo(() => { // TO DO: prevent windows from being rerendered when order is changed return (
- {sortedWindows.map(({ id, app, size, position, options, minimized }, index) => - { + const { id, app, size, position, options, minimized } = window; + const index = sortedWindows.indexOf(window); + return { windowsManager.focus(id); }} active={index === sortedWindows.length - 1} id={id} app={app} size={size} + index={index} position={position} options={options} minimized={minimized} @@ -64,7 +67,7 @@ export const WindowsView = memo(() => { event.stopPropagation(); windowsManager.setMinimized(id, !minimized); }} - /> - )} + />; + })}
); }); \ No newline at end of file diff --git a/src/features/z-index/zIndexGroup.js b/src/features/z-index/zIndexGroup.js new file mode 100644 index 0000000..19cddb1 --- /dev/null +++ b/src/features/z-index/zIndexGroup.js @@ -0,0 +1,67 @@ +import { ZIndexManager } from "./zIndexManager.js"; + +export class ZIndexGroup { + /** @type {number} */ + length = 0; + + /** @type {number} */ + offset = 0; + + /** @type {number} */ + groupIndex = 0; + + /** @type {ZIndexManager} */ + zIndexManager = null; + + /** + * @param {ZIndexManager} zIndexManager + * @param {number} initialLength + */ + constructor(zIndexManager, initialLength) { + this.setManager(zIndexManager); + this.setLength(initialLength ?? 0); + } + + /** + * @param {ZIndexManager} zIndexManager + * @returns {ZIndexGroup} + */ + setManager(zIndexManager) { + this.zIndexManager = zIndexManager; + return this; + } + + /** + * @param {number} offset + * @returns {ZIndexGroup} + */ + setOffset(offset) { + this.offset = offset; + return this; + } + + /** + * @param {number} length + * @returns {ZIndexGroup} + */ + setLength(length) { + if (this.length === length) + return; + + this.length = length; + this.zIndexManager.update(); + return this; + } + + /** + * @param {number} index + * @returns {number} + */ + getIndex(index) { + if (this.length < index + 1) { + this.setLength(index + 1); + } + + return this.offset + index; + } +} \ No newline at end of file diff --git a/src/features/z-index/zIndexManager.js b/src/features/z-index/zIndexManager.js new file mode 100644 index 0000000..2c7cd82 --- /dev/null +++ b/src/features/z-index/zIndexManager.js @@ -0,0 +1,45 @@ +import { EventEmitter } from "../_utils/event.utils.js"; +import { ZIndexGroup } from "./zIndexGroup.js"; + +export class ZIndexManager extends EventEmitter { + static GROUPS = { + WINDOWS: 0, + TASKBAR: 1, + MODALS: 2, + }; + + static EVENT_NAMES = { + INDEX_CHANGE: "indexchange" + }; + + /** @type {ZIndexGroup[]} */ + groups = []; + + constructor() { + super(); + for (let i = 0; i < Object.keys(ZIndexManager.GROUPS).length; i++) { + const group = new ZIndexGroup(this); + this.groups.push(group); + } + } + + update() { + let offset = 0; + let changed = false; + for (let i = 0; i < this.groups.length; i++) { + const group = this.groups[i]; + if (group.offset !== offset) { + changed = true; + group.setOffset(offset); + } + offset += group.length; + } + if (changed) { + this.emit(ZIndexManager.EVENT_NAMES.INDEX_CHANGE); + } + } + + getIndex(groupIndex, index) { + return this.groups[groupIndex].getIndex(index); + } +} \ No newline at end of file diff --git a/src/hooks/z-index/zIndex.js b/src/hooks/z-index/zIndex.js new file mode 100644 index 0000000..6e3d94a --- /dev/null +++ b/src/hooks/z-index/zIndex.js @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; +import { useZIndexManager } from "./zIndexManagerContext.js"; +import { ZIndexManager } from "../../features/z-index/zIndexManager.js"; + +export function useZIndex({ groupIndex, index }) { + const initialIndex = (groupIndex * 10) + index; + const [zIndex, setZIndex] = useState(initialIndex); + const zIndexManager = useZIndexManager(); + + useEffect(() => { + const updateIndex = () => { + const newZIndex = zIndexManager.getIndex(groupIndex, index); + + if (zIndex !== newZIndex) + setZIndex(newZIndex); + }; + + updateIndex(); + zIndexManager.on(ZIndexManager.EVENT_NAMES.INDEX_CHANGE, updateIndex); + + return () => { + zIndexManager.off(ZIndexManager.EVENT_NAMES.INDEX_CHANGE, updateIndex); + }; + }, [groupIndex, index, zIndex, zIndexManager]); + + return zIndex; +} \ No newline at end of file diff --git a/src/hooks/z-index/zIndexManagerContext.js b/src/hooks/z-index/zIndexManagerContext.js new file mode 100644 index 0000000..723b9d2 --- /dev/null +++ b/src/hooks/z-index/zIndexManagerContext.js @@ -0,0 +1,24 @@ +import { createContext, useContext } from "react"; +import { ZIndexManager } from "../../features/z-index/zIndexManager.js"; + +const ZIndexManagerContext = createContext(); + +/** + * @param {object} props + * @param {import("react").ElementType} props.children + * @returns {import("react").Provider} + */ +export function ZIndexManagerProvider({ children }) { + const zIndexManager = new ZIndexManager(); + + return + {children} + ; +} + +/** + * @returns {ZIndexManager} + */ +export function useZIndexManager() { + return useContext(ZIndexManagerContext); +} \ No newline at end of file