From b2ab7a1b447559ac3b071dc47bb75050937f18ee Mon Sep 17 00:00:00 2001 From: devklick Date: Sat, 21 Oct 2023 21:40:21 +0100 Subject: [PATCH 1/3] Enable accent and icon colors --- src/components/AppSideBar/AppSideBar.tsx | 9 ++++++++- src/components/AppSideBar/styles.ts | 6 ++++-- src/programs/Calculator/styles.ts | 1 + .../FileBrowser/DirectoryOrFile/DirectoryOrFile.tsx | 7 ++++++- src/programs/FileBrowser/DirectoryOrFile/styles.ts | 11 ++++++++--- src/programs/TextEditor/TextEditor.tsx | 4 +++- src/programs/TextEditor/styles.ts | 6 +++++- src/stores/systemSettingsStore.ts | 4 ++-- 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/components/AppSideBar/AppSideBar.tsx b/src/components/AppSideBar/AppSideBar.tsx index de1719d..c68a886 100644 --- a/src/components/AppSideBar/AppSideBar.tsx +++ b/src/components/AppSideBar/AppSideBar.tsx @@ -1,3 +1,4 @@ +import useSystemSettings from "../../stores/systemSettingsStore"; import { StyledItem, StyledItemContainer, StyledSideBar } from "./styles"; interface SideBarItem { @@ -11,11 +12,17 @@ interface AppSideBarProps { } function AppSideBar({ items }: AppSideBarProps) { + const settings = useSystemSettings(); return ( {items.map((item) => ( - + {item.title} ))} diff --git a/src/components/AppSideBar/styles.ts b/src/components/AppSideBar/styles.ts index 5e33423..9acd637 100644 --- a/src/components/AppSideBar/styles.ts +++ b/src/components/AppSideBar/styles.ts @@ -11,13 +11,15 @@ export const StyledItemContainer = styled.div` box-sizing: border-box; `; -export const StyledItem = styled.div` +export const StyledItem = styled.div<{ active: boolean; activeColor: string }>` border-radius: 10px; padding: 6px 10px; box-sizing: border-box; box-shadow: 2px 2px 4px rgb(0, 0, 0, 0); + background-color: ${(props) => + props.active ? props.activeColor : undefined}; &:hover { - backdrop-filter: brightness(150%); + background-color: ${(props) => props.activeColor}; transition: ease-in 0.2s; } &:active { diff --git a/src/programs/Calculator/styles.ts b/src/programs/Calculator/styles.ts index 889dc89..154a8c0 100644 --- a/src/programs/Calculator/styles.ts +++ b/src/programs/Calculator/styles.ts @@ -20,6 +20,7 @@ export const StyledInputOutput = styled.div<{ direction: "input" | "output"; }>` box-shadow: 0px 0px 4px rgb(0, 0, 0, 0.5) inset; + font-size: ${(props) => (props.direction === "input" ? 22 : 18)}px; width: 100%; height: 100%; display: flex; diff --git a/src/programs/FileBrowser/DirectoryOrFile/DirectoryOrFile.tsx b/src/programs/FileBrowser/DirectoryOrFile/DirectoryOrFile.tsx index e4d1120..0b54783 100644 --- a/src/programs/FileBrowser/DirectoryOrFile/DirectoryOrFile.tsx +++ b/src/programs/FileBrowser/DirectoryOrFile/DirectoryOrFile.tsx @@ -15,6 +15,7 @@ import InputField from "../../../components/InputField"; import Row from "../../../components/Row"; import Button from "../../../components/Button"; import { StyledFolderIcon, StyledItem, StyledItemName } from "./styles"; +import useSystemSettings from "../../../stores/systemSettingsStore"; interface DirectoryOrFileProps { fsObject: FSObject; @@ -38,6 +39,7 @@ function DirectoryOrFile({ const [contextAction, setContextAction] = useState( null ); + const settings = useSystemSettings(); function handleRightClick(event: React.MouseEvent) { clickPosition.current = { x: event.clientX, y: event.clientY }; @@ -49,6 +51,7 @@ function DirectoryOrFile({ return ( openFSObject(fsObject)} onClick={() => setSelected(fsObject.path)} key={fsObject.path} @@ -81,7 +84,9 @@ function DirectoryOrFile({ fsObject={fsObject} /> )} - {isFSDirectory(fsObject) ? : null} + {isFSDirectory(fsObject) ? ( + + ) : null} {fsObject.name} ); diff --git a/src/programs/FileBrowser/DirectoryOrFile/styles.ts b/src/programs/FileBrowser/DirectoryOrFile/styles.ts index 8436527..4a437de 100644 --- a/src/programs/FileBrowser/DirectoryOrFile/styles.ts +++ b/src/programs/FileBrowser/DirectoryOrFile/styles.ts @@ -1,7 +1,10 @@ import styled from "@emotion/styled"; import { ReactComponent as FolderIcon } from "../../../assets/icons/folder-icon.svg"; -export const StyledItem = styled.div<{ selected: boolean }>` +export const StyledItem = styled.div<{ + selected: boolean; + selectedColor: string; +}>` width: 100%; padding: 10px; overflow-wrap: break-word; @@ -15,14 +18,16 @@ export const StyledItem = styled.div<{ selected: boolean }>` flex-direction: column; gap: 10px; border-radius: 10px; + background-color: ${(props) => + props.selected ? props.selectedColor : undefined}; `; export const StyledItemName = styled.span``; -export const StyledFolderIcon = styled(FolderIcon)` +export const StyledFolderIcon = styled(FolderIcon)<{ fillColor: string }>` height: 80%; width: 80%; path { - fill: white; + fill: ${(props) => props.fillColor}; } `; diff --git a/src/programs/TextEditor/TextEditor.tsx b/src/programs/TextEditor/TextEditor.tsx index b40e653..9fee765 100644 --- a/src/programs/TextEditor/TextEditor.tsx +++ b/src/programs/TextEditor/TextEditor.tsx @@ -1,12 +1,14 @@ +import useSystemSettings from "../../stores/systemSettingsStore"; import { StyledTextArea, StyledTextEditor } from "./styles"; interface TextEditorProps {} // eslint-disable-next-line no-empty-pattern function TextEditor({}: TextEditorProps) { + const settings = useSystemSettings(); return ( - + ); } diff --git a/src/programs/TextEditor/styles.ts b/src/programs/TextEditor/styles.ts index aac57f3..56208c4 100644 --- a/src/programs/TextEditor/styles.ts +++ b/src/programs/TextEditor/styles.ts @@ -5,7 +5,7 @@ export const StyledTextEditor = styled.div` width: 100%; `; -export const StyledTextArea = styled.textarea` +export const StyledTextArea = styled.textarea<{ selectedColor: string }>` width: 100%; height: 100%; border: none; @@ -22,4 +22,8 @@ export const StyledTextArea = styled.textarea` &:focus-visible { outline: none; } + + ::selection { + background-color: ${(props) => props.selectedColor}; + } `; diff --git a/src/stores/systemSettingsStore.ts b/src/stores/systemSettingsStore.ts index 87a797e..87a6bac 100644 --- a/src/stores/systemSettingsStore.ts +++ b/src/stores/systemSettingsStore.ts @@ -18,9 +18,9 @@ export const useSystemSettings = create()( persist( (set) => ({ mainColor: "#2e3440", - accentColor: "#ffffff", + accentColor: "#454e60", fontColor: "#ffffff", - iconColor: "#ffffff", + iconColor: "#9298b9", background: "https://regolith-linux.org/images/releases/nord-dark.png", setAccentColor(accentColor) { set({ accentColor }); From ec7c74a5a83e0a663624361868e57945b366f540 Mon Sep 17 00:00:00 2001 From: devklick Date: Sat, 21 Oct 2023 22:33:49 +0100 Subject: [PATCH 2/3] Launcher context menus --- src/components/BorderedApp/BorderedApp.tsx | 1 + .../BottomBar/Launcher/Launcher.tsx | 56 ++++++++++++++----- src/components/ContextMenu/ContextMenu.tsx | 2 + src/components/MenuItems/MenuItems.tsx | 2 + src/components/MenuItems/styles.ts | 5 +- src/hooks/useWindowMinMax.ts | 3 +- src/stores/windowManagerStore.ts | 1 + 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/components/BorderedApp/BorderedApp.tsx b/src/components/BorderedApp/BorderedApp.tsx index cc67801..31309b5 100644 --- a/src/components/BorderedApp/BorderedApp.tsx +++ b/src/components/BorderedApp/BorderedApp.tsx @@ -71,6 +71,7 @@ function BorderedApp({ function onClickClose() { winMan.closeWindow(type, id); } + console.log("Bordered app, hidden", String(hidden)); return ( ) { const winMan = useWindowManagerStore(); const ref = useRef(null); + const [contextOpen, setContextOpen] = useState(false); function addWindow() { const id = windowId ?? uuid(); @@ -50,6 +52,7 @@ function Launcher({ // we want to focus them. This means revealing them if they // are minimized and bring them to the top of the window stack. if (winMan.windowsOfTypeExist(windowType)) { + console.log("Attempting to focus windows"); winMan.focusWindowsOfType(windowType); return; } @@ -58,10 +61,7 @@ function Launcher({ addWindow(); } function onRightClick() { - // TODO: Display a context menu with various options that are - // specified by the program-specific launcher and passed via props. - // This will include things like "open new window", "close windows", etc. - console.log("right clicked"); + setContextOpen(true); } useConditionalClick({ @@ -76,14 +76,44 @@ function Launcher({ clickHandler: onRightClick, }); + function getContextMenu() { + const items: Array = [ + { + title: "New Window", + action: () => { + addWindow(); + setContextOpen(false); + }, + }, + ]; + return ( + setContextOpen(false)} + items={items} + position={getContextPosition(items.length)} + /> + ); + } + function getContextPosition(numberOfItems: number): Position { + const rect = ref.current?.getBoundingClientRect(); + if (!rect) return { x: 0, y: 0 }; + console.log(rect); + return { + x: rect.x, + y: rect.y - 20 - numberOfItems * 30, + }; + } return ( - - - + <> + {contextOpen && getContextMenu()} + + + + ); } diff --git a/src/components/ContextMenu/ContextMenu.tsx b/src/components/ContextMenu/ContextMenu.tsx index 26fb3de..73a6551 100644 --- a/src/components/ContextMenu/ContextMenu.tsx +++ b/src/components/ContextMenu/ContextMenu.tsx @@ -3,6 +3,7 @@ import MenuItems, { MenuItemProps } from "../MenuItems"; import useDetectMouseDownOutside from "../../hooks/useDetectMouseDownOutside"; import { StyledContextMenu } from "./styles"; +import useBindKeyToAction from "../../hooks/useBindKeyToAction"; interface ContextMenuProps { items: Array; @@ -14,6 +15,7 @@ function ContextMenu({ items, position, close }: ContextMenuProps) { const elementRef = useRef(null); useDetectMouseDownOutside({ elementRef, onMouseDown: close }); + useBindKeyToAction({ keys: ["Escape"], action: close }); return ( diff --git a/src/components/MenuItems/MenuItems.tsx b/src/components/MenuItems/MenuItems.tsx index b039653..da19422 100644 --- a/src/components/MenuItems/MenuItems.tsx +++ b/src/components/MenuItems/MenuItems.tsx @@ -60,6 +60,7 @@ function MenuItem({ const hoverOpenDelayRef = useRef(); const hoverCloseDelayRef = useRef(); const [open, setOpen] = useState(false); + const settings = useSystemSettings(); useEffect(() => { return () => { @@ -130,6 +131,7 @@ function MenuItem({ onClick={handleOnClick} onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave} + hoverColor={settings.accentColor} > {title} {items && open && ( diff --git a/src/components/MenuItems/styles.ts b/src/components/MenuItems/styles.ts index d2f0b89..c525ce1 100644 --- a/src/components/MenuItems/styles.ts +++ b/src/components/MenuItems/styles.ts @@ -22,7 +22,10 @@ export const StyledItemsContent = styled.div` box-sizing: border-box; `; -export const StyledMenuItem = styled.div` +export const StyledMenuItem = styled.div<{ hoverColor: string }>` padding: 5px 12px; height: 20px; + :hover { + background-color: ${(props) => props.hoverColor}; + } `; diff --git a/src/hooks/useWindowMinMax.ts b/src/hooks/useWindowMinMax.ts index d4edac8..f352aa2 100644 --- a/src/hooks/useWindowMinMax.ts +++ b/src/hooks/useWindowMinMax.ts @@ -58,12 +58,11 @@ function useWindowMinMax({ const window = windowRef.current; if (!window?.style) return; if (e.target !== window) return; + winMan.hideWindow(windowType, windowId); - window.style.display = "none"; window.style.transition = oldTransition.current; window.style.transform = oldTransform.current; window.style.opacity = oldOpacity.current; - winMan.hideWindow(windowType, windowId); } useEffect(() => { diff --git a/src/stores/windowManagerStore.ts b/src/stores/windowManagerStore.ts index e30f68a..c27fdb0 100644 --- a/src/stores/windowManagerStore.ts +++ b/src/stores/windowManagerStore.ts @@ -85,6 +85,7 @@ const useWindowManagerStore = create()((set, get) => ({ const windowsOfType = windowsMap.get(windowType); // If no windows of this type exist, nothing to do + console.log(`${windowsOfType?.size} Windows of type ${windowType}`); if (!windowsOfType?.size) return; let highestZIndex = get().highestZIndex; From 7a20cce6be97468952d814f71047a7871266beaa Mon Sep 17 00:00:00 2001 From: devklick Date: Sat, 21 Oct 2023 22:57:04 +0100 Subject: [PATCH 3/3] Init app windows in centre of screen --- src/components/BorderedApp/BorderedApp.tsx | 6 +++++- src/components/BorderedApp/styles.ts | 5 ++++- src/components/BottomBar/Launcher/Launcher.tsx | 14 ++++++++++++++ src/hooks/usePositionableElement.ts | 10 ++++++---- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/components/BorderedApp/BorderedApp.tsx b/src/components/BorderedApp/BorderedApp.tsx index 31309b5..e9c92ac 100644 --- a/src/components/BorderedApp/BorderedApp.tsx +++ b/src/components/BorderedApp/BorderedApp.tsx @@ -2,7 +2,7 @@ import React, { useRef } from "react"; import useWindowManagerStore, { BaseProps, } from "../../stores/windowManagerStore"; -import { Dimensions } from "../../hooks/useDragToResize"; +import { Dimensions, Position } from "../../hooks/useDragToResize"; import BorderedAppMenu from "./BorderedAppMenu/BorderedAppMenu"; import usePositionableElement from "../../hooks/usePositionableElement"; import useSystemSettings from "../../stores/systemSettingsStore"; @@ -27,6 +27,7 @@ interface BorderedAppProps extends BaseProps { type: string; id: string; initialDimensions: Dimensions; + initialPosition: Position; maxDimensions?: Dimensions; minDimensions?: Dimensions; menus?: Array; @@ -38,6 +39,7 @@ function BorderedApp({ id, children, initialDimensions, + initialPosition, minDimensions = { height: 350, width: 350 }, menus, zIndex, @@ -64,6 +66,7 @@ function BorderedApp({ } = usePositionableElement({ elementRef: appRef, minDimensions, + initialPosition, windowType: type, windowId: id, }); @@ -78,6 +81,7 @@ function BorderedApp({ ref={appRef} onMouseDown={() => winMan.focusWindow(type, id)} initialDimensions={initialDimensions} + initialPosition={initialPosition} zIndex={zIndex} backgroundColor={settings.mainColor} display={hidden === true ? "none" : "grid"} diff --git a/src/components/BorderedApp/styles.ts b/src/components/BorderedApp/styles.ts index dee40a9..77313ee 100644 --- a/src/components/BorderedApp/styles.ts +++ b/src/components/BorderedApp/styles.ts @@ -1,8 +1,9 @@ import styled from "@emotion/styled"; -import { Dimensions } from "../../hooks/useDragToResize"; +import { Dimensions, Position } from "../../hooks/useDragToResize"; interface StyledBorderedAppProps { initialDimensions: Dimensions; + initialPosition: Position; zIndex: number | undefined; backgroundColor: string; display: "none" | "grid"; @@ -25,6 +26,8 @@ export const StyledBorderedApp = styled.div` z-index: ${(props) => props.zIndex}; background-color: ${(props) => props.backgroundColor}; display: ${(props) => props.display}; + left: ${(props) => props.initialPosition.x}px; + top: ${(props) => props.initialPosition.y}px; `; export const StyledCorner = styled.div<{ location: "ne" | "se" | "sw" | "nw"; diff --git a/src/components/BottomBar/Launcher/Launcher.tsx b/src/components/BottomBar/Launcher/Launcher.tsx index e7f1327..99c1607 100644 --- a/src/components/BottomBar/Launcher/Launcher.tsx +++ b/src/components/BottomBar/Launcher/Launcher.tsx @@ -34,6 +34,16 @@ function Launcher({ function addWindow() { const id = windowId ?? uuid(); + const boundingRect = winMan.contentRef.current?.getBoundingClientRect(); + function getInitialPosition(axis: "x" | "y"): number { + if (!boundingRect) return 0; + const dimension = axis === "x" ? "width" : "height"; + return ( + (boundingRect[axis] ?? 0) + + (boundingRect[dimension] ?? 0) / 2 - + initialDimensions[dimension] / 2 + ); + } winMan.addWindow(windowType, id, { component: BorderedApp, props: { @@ -41,6 +51,10 @@ function Launcher({ title: WindowTitle, type: windowType, initialDimensions, + initialPosition: { + x: getInitialPosition("x"), + y: getInitialPosition("y"), + }, menus, }, key: id, diff --git a/src/hooks/usePositionableElement.ts b/src/hooks/usePositionableElement.ts index e1f2a98..e29d6aa 100644 --- a/src/hooks/usePositionableElement.ts +++ b/src/hooks/usePositionableElement.ts @@ -1,5 +1,5 @@ import { useEffect, useRef } from "react"; -import useDragToResize, { Dimensions, Rect } from "./useDragToResize"; +import useDragToResize, { Dimensions, Position, Rect } from "./useDragToResize"; import useDragToMove from "./useDragToMove"; import useWindowMinMax from "./useWindowMinMax"; @@ -8,6 +8,7 @@ interface UsePositionableElementProps { minDimensions: Dimensions; windowType: string; windowId: string; + initialPosition: Position; } /** @@ -21,6 +22,7 @@ function usePositionableElement({ minDimensions, windowType, windowId, + initialPosition, }: UsePositionableElementProps) { // Hold a single ref for the elements rect, // so we dont have to call getBoundingClientRect every @@ -40,11 +42,11 @@ function usePositionableElement({ elementRect.current = { height: rect.height, width: rect.width, - left: rect.left, - top: rect.top, + left: initialPosition.x, + top: initialPosition.y, }; } - }, [elementRef]); + }, [elementRef, initialPosition.x, initialPosition.y]); // The resize hook allows the app to be resized // by dragging the corners or edges of the element.