Updated taskbar
This commit is contained in:
parent
5263cfd2b6
commit
861851872a
25 changed files with 231 additions and 108 deletions
|
|
@ -1,5 +1,3 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<!DOCTYPE xml>
|
|
||||||
<options>
|
<options>
|
||||||
<wallpaper>/media/wallpapers/mesh-gradient-wallpaper-red-purple.png</wallpaper>
|
<wallpaper>/media/wallpapers/mesh-gradient-wallpaper-red-purple.png</wallpaper>
|
||||||
</options>
|
</options>
|
||||||
3
public/config/taskbar.xml
Normal file
3
public/config/taskbar.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<options>
|
||||||
|
<pins>terminal,settings,media-viewer,text-editor,file-explorer,wordle,balls,minesweeper</pins>
|
||||||
|
</options>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import styles from "./App.module.css";
|
import styles from "./App.module.css";
|
||||||
import { Taskbar } from "./components/task-bar/TaskBar.jsx";
|
import { Taskbar } from "./components/taskbar/Taskbar.jsx";
|
||||||
import { WindowsManagerProvider } from "./hooks/windows/windowsManagerContext.js";
|
import { WindowsManagerProvider } from "./hooks/windows/windowsManagerContext.js";
|
||||||
import { WindowsView } from "./components/windows/WindowsView.jsx";
|
import { WindowsView } from "./components/windows/WindowsView.jsx";
|
||||||
import { VirtualRootProvider } from "./hooks/virtual-drive/virtualRootContext.js";
|
import { VirtualRootProvider } from "./hooks/virtual-drive/virtualRootContext.js";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from "react";
|
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from "react";
|
||||||
import { useShortcuts } from "../../hooks/utils/keyboard.js";
|
import { useShortcuts } from "../../hooks/utils/keyboard.js";
|
||||||
import style from "./Actions.module.css";
|
import styles from "./Actions.module.css";
|
||||||
import { useScreenDimensions } from "../../hooks/utils/screen.js";
|
import { useScreenDimensions } from "../../hooks/utils/screen.js";
|
||||||
import { TASK_BAR_HEIGHT } from "../../constants/taskBar.js";
|
import { TASKBAR_HEIGHT } from "../../constants/taskBar.js";
|
||||||
|
|
||||||
export const STYLES = {
|
export const STYLES = {
|
||||||
CONTEXT_MENU: style["Context-menu"],
|
CONTEXT_MENU: styles["Context-menu"],
|
||||||
SHORTCUTS_LISTENER: style["Shortcuts-listener"],
|
SHORTCUTS_LISTENER: styles["Shortcuts-listener"],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,7 +16,7 @@ export const STYLES = {
|
||||||
* @param {Function} props.onAnyTrigger
|
* @param {Function} props.onAnyTrigger
|
||||||
* @param {import("react").ElementType} props.children
|
* @param {import("react").ElementType} props.children
|
||||||
* @param {*} props.triggerParams
|
* @param {*} props.triggerParams
|
||||||
* @param {boolean} props.avoidTaskBar
|
* @param {boolean} props.avoidTaskbar
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,7 +31,7 @@ export const STYLES = {
|
||||||
* }}
|
* }}
|
||||||
* />
|
* />
|
||||||
*/
|
*/
|
||||||
export function Actions({ children, className, onAnyTrigger, triggerParams, avoidTaskBar = true }) {
|
export function Actions({ children, className, onAnyTrigger, triggerParams, avoidTaskbar = true }) {
|
||||||
const isListener = (className === STYLES.SHORTCUTS_LISTENER);
|
const isListener = (className === STYLES.SHORTCUTS_LISTENER);
|
||||||
|
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
|
@ -88,8 +88,8 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
||||||
const maxX = screenWidth;
|
const maxX = screenWidth;
|
||||||
let maxY = screenHeight;
|
let maxY = screenHeight;
|
||||||
|
|
||||||
if (avoidTaskBar)
|
if (avoidTaskbar)
|
||||||
maxY -= TASK_BAR_HEIGHT;
|
maxY -= TASKBAR_HEIGHT;
|
||||||
|
|
||||||
const isOverflowingRight = (rect.x + rect.width > maxX);
|
const isOverflowingRight = (rect.x + rect.width > maxX);
|
||||||
const isOverflowingBottom = (rect.y + rect.height > maxY);
|
const isOverflowingBottom = (rect.y + rect.height > maxY);
|
||||||
|
|
@ -100,20 +100,20 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
||||||
setAlignTop(true);
|
setAlignTop(true);
|
||||||
|
|
||||||
setInitiated(true);
|
setInitiated(true);
|
||||||
}, [alignLeft, avoidTaskBar, screenHeight, screenWidth]);
|
}, [alignLeft, avoidTaskbar, screenHeight, screenWidth]);
|
||||||
|
|
||||||
if (isListener)
|
if (isListener)
|
||||||
return iterateOverChildren(children);
|
return iterateOverChildren(children);
|
||||||
|
|
||||||
const classNames = [style.Container];
|
const classNames = [styles.Container];
|
||||||
if (className != null)
|
if (className != null)
|
||||||
classNames.push(className);
|
classNames.push(className);
|
||||||
if (alignLeft)
|
if (alignLeft)
|
||||||
classNames.push(style["Align-left"]);
|
classNames.push(styles["Align-left"]);
|
||||||
if (alignTop)
|
if (alignTop)
|
||||||
classNames.push(style["Align-top"]);
|
classNames.push(styles["Align-top"]);
|
||||||
if (!initiated)
|
if (!initiated)
|
||||||
classNames.push(style.Uninitiated);
|
classNames.push(styles.Uninitiated);
|
||||||
|
|
||||||
return (<div ref={ref} className={classNames.join(" ")}>
|
return (<div ref={ref} className={classNames.join(" ")}>
|
||||||
{iterateOverChildren(children)}
|
{iterateOverChildren(children)}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import { memo, useEffect, useState } from "react";
|
|
||||||
import Application from "../../features/applications/application.js";
|
|
||||||
import { useWindows } from "../../hooks/windows/windowsContext.js";
|
|
||||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
|
|
||||||
import styles from "./TaskBar.module.css";
|
|
||||||
import { ReactSVG } from "react-svg";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} props
|
|
||||||
* @param {Application} props.app
|
|
||||||
*/
|
|
||||||
export const AppButton = memo(({ app }) => {
|
|
||||||
const [active, setActive] = useState(false);
|
|
||||||
const windows = useWindows();
|
|
||||||
const windowsManager = useWindowsManager();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setActive(windowsManager.isAppActive(app.id));
|
|
||||||
}, [app.id, windows, windowsManager]);
|
|
||||||
|
|
||||||
const classNames = [styles["App-icon"]];
|
|
||||||
if (active)
|
|
||||||
classNames.push(styles.Active);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={app.id}
|
|
||||||
tabIndex={0}
|
|
||||||
className={classNames.join(" ")}
|
|
||||||
onClick={() => { windowsManager.open(app.id); }}
|
|
||||||
title={app.name}
|
|
||||||
>
|
|
||||||
<ReactSVG src={`${process.env.PUBLIC_URL}/media/applications/icons/${app.id}.svg`}/>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { memo, useRef, useState } from "react";
|
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styles from "./TaskBar.module.css";
|
import styles from "./Taskbar.module.css";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
|
import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||||
import AppsManager from "../../features/applications/applications.js";
|
import AppsManager from "../../features/applications/applications.js";
|
||||||
|
|
@ -12,7 +12,7 @@ import { Volume } from "./indicators/Volume.jsx";
|
||||||
import { SearchMenu } from "./menus/SearchMenu.jsx";
|
import { SearchMenu } from "./menus/SearchMenu.jsx";
|
||||||
import { Calendar } from "./indicators/Calendar.jsx";
|
import { Calendar } from "./indicators/Calendar.jsx";
|
||||||
import { useScrollWithShadow } from "../../hooks/utils/scrollWithShadows.js";
|
import { useScrollWithShadow } from "../../hooks/utils/scrollWithShadows.js";
|
||||||
import { AppButton } from "./AppButton.jsx";
|
import { AppButton } from "./app-icon/AppIcon.jsx";
|
||||||
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
|
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
|
||||||
import { Actions } from "../actions/Actions.jsx";
|
import { Actions } from "../actions/Actions.jsx";
|
||||||
import { useModals } from "../../hooks/modals/modals.js";
|
import { useModals } from "../../hooks/modals/modals.js";
|
||||||
|
|
@ -20,13 +20,17 @@ import { ClickAction } from "../actions/actions/ClickAction.jsx";
|
||||||
import { APPS, APP_NAMES } from "../../constants/applications.js";
|
import { APPS, APP_NAMES } from "../../constants/applications.js";
|
||||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
|
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
|
||||||
import { ModalsView } from "../modals/ModalsView.jsx";
|
import { ModalsView } from "../modals/ModalsView.jsx";
|
||||||
import { TASK_BAR_HEIGHT } from "../../constants/taskBar.js";
|
import { TASKBAR_HEIGHT } from "../../constants/taskBar.js";
|
||||||
|
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext.js";
|
||||||
|
import { SettingsManager } from "../../features/settings/settingsManager.js";
|
||||||
|
import { useWindows } from "../../hooks/windows/windowsContext.js";
|
||||||
|
|
||||||
export const Taskbar = memo(() => {
|
export const Taskbar = memo(() => {
|
||||||
|
const ref = useRef(null);
|
||||||
|
const settingsManager = useSettingsManager();
|
||||||
const [showHome, setShowHome] = useState(false);
|
const [showHome, setShowHome] = useState(false);
|
||||||
const [showSearch, setShowSearch] = useState(false);
|
const [showSearch, setShowSearch] = useState(false);
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const ref = useRef(null);
|
|
||||||
const { boxShadow, onUpdate } = useScrollWithShadow({ ref, shadow: {
|
const { boxShadow, onUpdate } = useScrollWithShadow({ ref, shadow: {
|
||||||
offset: 20,
|
offset: 20,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
|
|
@ -36,13 +40,49 @@ export const Taskbar = memo(() => {
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [modalsManager, modals] = useModals();
|
const [modalsManager, modals] = useModals();
|
||||||
const windowsManager = useWindowsManager();
|
const windowsManager = useWindowsManager();
|
||||||
|
const windows = useWindows();
|
||||||
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
|
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||||
<Actions avoidTaskBar={false} {...props}>
|
<Actions avoidTaskbar={false} {...props}>
|
||||||
<ClickAction label={`Open ${APP_NAMES.SETTINGS}`} icon={faCog} onTrigger={() => {
|
<ClickAction label={`Open ${APP_NAMES.SETTINGS}`} icon={faCog} onTrigger={() => {
|
||||||
windowsManager.open(APPS.SETTINGS);
|
windowsManager.open(APPS.SETTINGS);
|
||||||
}}/>
|
}}/>
|
||||||
</Actions>
|
</Actions>
|
||||||
});
|
});
|
||||||
|
const [pins, setPins] = useState([]);
|
||||||
|
|
||||||
|
const apps = useMemo(() => AppsManager.APPLICATIONS.sort((appA, appB) => {
|
||||||
|
const indexA = pins.indexOf(appA.id);
|
||||||
|
const indexB = pins.indexOf(appB.id);
|
||||||
|
if (indexA < 0 && indexB > 0) {
|
||||||
|
return 1;
|
||||||
|
} else if (indexA > 0 && indexB < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (indexA < 0 && indexB < 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return indexA - indexB;
|
||||||
|
}
|
||||||
|
}).map((app) => {
|
||||||
|
const isActive = windows.map((window) => window.app.id).includes(app.id);
|
||||||
|
const shouldBeShown = (pins.includes(app.id) || isActive);
|
||||||
|
return (<AppButton
|
||||||
|
modalsManager={modalsManager}
|
||||||
|
pins={pins}
|
||||||
|
app={app}
|
||||||
|
key={app.id}
|
||||||
|
active={isActive}
|
||||||
|
visible={shouldBeShown}
|
||||||
|
/>);
|
||||||
|
}), [modalsManager, pins, windows]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.taskbar);
|
||||||
|
settings.get("pins", (pins) => {
|
||||||
|
setPins(pins.split(","));
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}, [settingsManager]);
|
||||||
|
|
||||||
const updateShowHome = (value) => {
|
const updateShowHome = (value) => {
|
||||||
setShowHome(value);
|
setShowHome(value);
|
||||||
|
|
@ -82,8 +122,8 @@ export const Taskbar = memo(() => {
|
||||||
return (<>
|
return (<>
|
||||||
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
||||||
<div
|
<div
|
||||||
style={{ "--task-bar-height": `${TASK_BAR_HEIGHT}px` }}
|
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px` }}
|
||||||
className={styles["Task-bar"]}
|
className={styles["Taskbar"]}
|
||||||
data-allow-context-menu={true}
|
data-allow-context-menu={true}
|
||||||
onContextMenu={(event) => {
|
onContextMenu={(event) => {
|
||||||
if (event.target.getAttribute("data-allow-context-menu"))
|
if (event.target.getAttribute("data-allow-context-menu"))
|
||||||
|
|
@ -132,9 +172,7 @@ export const Taskbar = memo(() => {
|
||||||
onResize={onUpdate}
|
onResize={onUpdate}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{AppsManager.APPLICATIONS.map((app) =>
|
{apps}
|
||||||
<AppButton app={app} key={app.id}/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["Util-icons"]}>
|
<div className={styles["Util-icons"]}>
|
||||||
|
|
@ -142,7 +180,7 @@ export const Taskbar = memo(() => {
|
||||||
<Network/>
|
<Network/>
|
||||||
<Volume/>
|
<Volume/>
|
||||||
<Calendar/>
|
<Calendar/>
|
||||||
<button title="View Desktop" id="desktop-button"/>
|
<button title="Show Desktop" id="desktop-button"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
:root {
|
:root {
|
||||||
--task-bar-height: 3rem;
|
--taskbar-height: 3rem;
|
||||||
--task-bar-color: rgba(0, 0, 0, 75%);
|
--taskbar-color: rgba(0, 0, 0, 75%);
|
||||||
--task-bar-button-hover-color: rgba(255, 255, 255, 5%);
|
--taskbar-button-hover-color: rgba(255, 255, 255, 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Task-bar {
|
.Taskbar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: flex;
|
display: flex;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: var(--task-bar-height);
|
height: var(--taskbar-height);
|
||||||
background-color: var(--task-bar-color);
|
background-color: var(--taskbar-color);
|
||||||
backdrop-filter: blur(15px);
|
backdrop-filter: blur(15px);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Task-bar button {
|
.Taskbar button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--foreground-color-a);
|
color: var(--foreground-color-a);
|
||||||
|
|
@ -28,9 +28,9 @@
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Task-bar button:hover,
|
.Taskbar button:hover,
|
||||||
.Task-bar button:focus-visible {
|
.Taskbar button:focus-visible {
|
||||||
background-color: var(--task-bar-button-hover-color);
|
background-color: var(--taskbar-button-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Home-container,
|
.Home-container,
|
||||||
|
|
@ -94,35 +94,6 @@
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-icon {
|
|
||||||
position: relative;
|
|
||||||
width: var(--task-bar-height);
|
|
||||||
height: var(--task-bar-height);
|
|
||||||
aspect-ratio: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-icon::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
bottom: 0;
|
|
||||||
width: 90%;
|
|
||||||
height: 0.15rem;
|
|
||||||
background-color: var(--grey-a);
|
|
||||||
transition: height 200ms ease-in-out, width 200ms ease-in-out;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
-webkit-transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-icon:hover::after,
|
|
||||||
.App-icon:focus-visible::after {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-icon:not(.Active)::after {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Util-icons {
|
.Util-icons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
66
src/components/taskbar/app-icon/AppIcon.jsx
Normal file
66
src/components/taskbar/app-icon/AppIcon.jsx
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { memo } from "react";
|
||||||
|
import Application from "../../../features/applications/application.js";
|
||||||
|
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
|
||||||
|
import styles from "./AppIcon.module.css";
|
||||||
|
import { ReactSVG } from "react-svg";
|
||||||
|
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext.js";
|
||||||
|
import { useContextMenu } from "../../../hooks/modals/contextMenu.js";
|
||||||
|
import ModalsManager from "../../../features/modals/modals.js";
|
||||||
|
import { Actions } from "../../actions/Actions.jsx";
|
||||||
|
import { ClickAction } from "../../actions/actions/ClickAction.jsx";
|
||||||
|
import { faThumbTack, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { SettingsManager } from "../../../features/settings/settingsManager.js";
|
||||||
|
import { removeFromArray } from "../../../features/utils/array.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} props
|
||||||
|
* @param {Application} props.app
|
||||||
|
* @param {ModalsManager} props.modalsManager
|
||||||
|
* @param {string[]} props.pins
|
||||||
|
*/
|
||||||
|
export const AppButton = memo(({ app, modalsManager, pins, active, visible }) => {
|
||||||
|
const isPinned = pins.includes(app.id);
|
||||||
|
|
||||||
|
const windowsManager = useWindowsManager();
|
||||||
|
const settingsManager = useSettingsManager();
|
||||||
|
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||||
|
<Actions avoidTaskbar={false} {...props}>
|
||||||
|
<ClickAction label={isPinned ? "Unpin from taskbar" : "Pin to taskbar"} icon={faThumbTack} onTrigger={() => {
|
||||||
|
const newPins = [...pins];
|
||||||
|
if (isPinned) {
|
||||||
|
removeFromArray(app.id, pins);
|
||||||
|
} else {
|
||||||
|
newPins.push(app.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.taskbar);
|
||||||
|
settings.set("pins", newPins.join(","));
|
||||||
|
}}/>
|
||||||
|
{active && <ClickAction label="Close window" icon={faTimes} onTrigger={() => {
|
||||||
|
windowsManager.close(windowsManager.getAppWindowId(app.id));
|
||||||
|
}}/>}
|
||||||
|
</Actions>
|
||||||
|
});
|
||||||
|
|
||||||
|
const classNames = [styles["App-icon"]];
|
||||||
|
if (active)
|
||||||
|
classNames.push(styles.Active);
|
||||||
|
if (!visible)
|
||||||
|
classNames.push(styles.Hidden);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={app.id}
|
||||||
|
tabIndex={0}
|
||||||
|
className={classNames.join(" ")}
|
||||||
|
onClick={() => { windowsManager.open(app.id); }}
|
||||||
|
onContextMenu={(event) => {
|
||||||
|
if (visible)
|
||||||
|
onContextMenu(event);
|
||||||
|
}}
|
||||||
|
title={app.name}
|
||||||
|
>
|
||||||
|
<ReactSVG src={`${process.env.PUBLIC_URL}/media/applications/icons/${app.id}.svg`}/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
});
|
||||||
58
src/components/taskbar/app-icon/AppIcon.module.css
Normal file
58
src/components/taskbar/app-icon/AppIcon.module.css
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
.App-icon {
|
||||||
|
opacity: 1;
|
||||||
|
position: relative;
|
||||||
|
width: var(--taskbar-height);
|
||||||
|
height: var(--taskbar-height);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
animation: appear 100ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-icon.Hidden {
|
||||||
|
animation: disappear 400ms ease-in-out forwards;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes appear {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes disappear {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
opacity: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
margin-right: calc(var(--taskbar-height) * -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-icon::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 0;
|
||||||
|
width: 90%;
|
||||||
|
height: 0.15rem;
|
||||||
|
background-color: var(--grey-a);
|
||||||
|
transition: height 200ms ease-in-out, width 200ms ease-in-out;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
-webkit-transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-icon:hover::after,
|
||||||
|
.App-icon:focus-visible::after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.App-icon:not(.Active)::after {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
.Window-container.Maximized {
|
.Window-container.Maximized {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - var(--task-bar-height));
|
height: calc(100% - var(--taskbar-height));
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
export const TASK_BAR_HEIGHT = 3 * 16;
|
export const TASKBAR_HEIGHT = 3 * 16;
|
||||||
|
|
@ -6,7 +6,8 @@ export class SettingsManager {
|
||||||
* @type {Object<string, string>}
|
* @type {Object<string, string>}
|
||||||
*/
|
*/
|
||||||
static VIRTUAL_PATHS = {
|
static VIRTUAL_PATHS = {
|
||||||
"desktop": "~/.config/desktop.xml"
|
desktop: "~/.config/desktop.xml",
|
||||||
|
taskbar: "~/.config/taskbar.xml"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ export class VirtualRoot extends VirtualFolder {
|
||||||
.createFolder(".config", (folder) => {
|
.createFolder(".config", (folder) => {
|
||||||
folder.createFile("desktop", "xml", (file) => {
|
folder.createFile("desktop", "xml", (file) => {
|
||||||
file.setSource("/config/desktop.xml");
|
file.setSource("/config/desktop.xml");
|
||||||
|
}).createFile("taskbar", "xml", (file) => {
|
||||||
|
file.setSource("/config/taskbar.xml");
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.createFolder("Images", (folder) => {
|
.createFolder("Images", (folder) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { TASK_BAR_HEIGHT } from "../../constants/taskBar.js";
|
import { TASKBAR_HEIGHT } from "../../constants/taskBar.js";
|
||||||
import { SCREEN_MARGIN } from "../../constants/windows.js";
|
import { SCREEN_MARGIN } from "../../constants/windows.js";
|
||||||
import AppsManager from "../applications/applications.js";
|
import AppsManager from "../applications/applications.js";
|
||||||
import { randomRange } from "../math/random.js";
|
import { randomRange } from "../math/random.js";
|
||||||
|
|
@ -26,7 +26,7 @@ export default class WindowsManager {
|
||||||
|
|
||||||
const size = options?.size ?? app.windowOptions?.size ?? new Vector2(700, 400);
|
const size = options?.size ?? app.windowOptions?.size ?? new Vector2(700, 400);
|
||||||
const position = new Vector2(randomRange(SCREEN_MARGIN, window.innerWidth - size.x - SCREEN_MARGIN),
|
const position = new Vector2(randomRange(SCREEN_MARGIN, window.innerWidth - size.x - SCREEN_MARGIN),
|
||||||
randomRange(SCREEN_MARGIN, window.innerHeight - size.y - SCREEN_MARGIN - TASK_BAR_HEIGHT));
|
randomRange(SCREEN_MARGIN, window.innerHeight - size.y - SCREEN_MARGIN - TASKBAR_HEIGHT));
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
while (this.windowIds.includes(id.toString())) {
|
while (this.windowIds.includes(id.toString())) {
|
||||||
|
|
@ -46,6 +46,8 @@ export default class WindowsManager {
|
||||||
lastInteraction: new Date().valueOf()
|
lastInteraction: new Date().valueOf()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
app.isActive = true;
|
||||||
|
|
||||||
this.updateWindows(this.windows);
|
this.updateWindows(this.windows);
|
||||||
return this.windows[id];
|
return this.windows[id];
|
||||||
// console.log(this);
|
// console.log(this);
|
||||||
|
|
@ -72,6 +74,9 @@ export default class WindowsManager {
|
||||||
console.warn(`Failed to close window ${windowId}: window not found`);
|
console.warn(`Failed to close window ${windowId}: window not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { app } = this.windows[windowId];
|
||||||
|
app.isActive = this.isAppActive(app.id);
|
||||||
|
|
||||||
console.info(`Closing window ${windowId}`);
|
console.info(`Closing window ${windowId}`);
|
||||||
delete this.windows[windowId];
|
delete this.windows[windowId];
|
||||||
|
|
@ -114,6 +119,23 @@ export default class WindowsManager {
|
||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} appId
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getAppWindowId(appId) {
|
||||||
|
let windowId = null;
|
||||||
|
|
||||||
|
Object.values(this.windows).forEach((window) => {
|
||||||
|
if (window.app.id === appId) {
|
||||||
|
windowId = window.id;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return windowId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Function} updateWindows
|
* @param {Function} updateWindows
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue