diff --git a/src/__tests__/App.test.tsx b/src/__tests__/App.test.tsx index e4d5137..74fd7d0 100644 --- a/src/__tests__/App.test.tsx +++ b/src/__tests__/App.test.tsx @@ -77,6 +77,7 @@ const miscState: General = { modalPages: [], isProcessingPlaylist: [], dynamicBackground: false, + titleBar: 'windows', }; const mockInitialState = { diff --git a/src/components/layout/Titlebar.tsx b/src/components/layout/Titlebar.tsx index d479a78..127d1fb 100644 --- a/src/components/layout/Titlebar.tsx +++ b/src/components/layout/Titlebar.tsx @@ -1,5 +1,12 @@ import React, { useEffect, useState } from 'react'; -import { TitleHeader, DragRegion, WindowControl, WindowControlButton } from './styled'; +import { + TitleHeader, + DragRegion, + WindowControl, + WindowControlButton, + MacControl, + MacControlButton, +} from './styled'; import { useAppSelector } from '../../redux/hooks'; import { getCurrentEntryList } from '../../shared/utils'; import logo from '../../../assets/icon.png'; @@ -7,7 +14,11 @@ import logo from '../../../assets/icon.png'; const Titlebar = ({ font }: any) => { const playQueue = useAppSelector((state) => state.playQueue); const player = useAppSelector((state) => state.player); + const misc = useAppSelector((state) => state.misc); const [title, setTitle] = useState(document.title); + const [hoverMin, setHoverMin] = useState(false); + const [hoverMax, setHoverMax] = useState(false); + const [hoverClose, setHoverClose] = useState(false); useEffect(() => { const currentEntryList = getCurrentEntryList(playQueue); @@ -27,46 +38,116 @@ const Titlebar = ({ font }: any) => { return ( -
- - - {title} - -
- - - - - - - - - - - - - - + {misc.titleBar === 'mac' && ( + <> +
+ {title} +
+ + + setHoverMin(true)} + onMouseLeave={() => setHoverMin(false)} + > + + + setHoverMax(true)} + onMouseLeave={() => setHoverMax(false)} + > + + + setHoverMax(true)} + onMouseLeave={() => setHoverMax(false)} + > + + + setHoverClose(true)} + onMouseLeave={() => setHoverClose(false)} + > + + + + + )} + + {misc.titleBar === 'windows' && ( + <> +
+ + + {title} + +
+ + + + + + + + + + + + + + + + )}
); diff --git a/src/components/layout/styled.tsx b/src/components/layout/styled.tsx index c4ff85b..ea532ba 100644 --- a/src/components/layout/styled.tsx +++ b/src/components/layout/styled.tsx @@ -71,6 +71,31 @@ export const WindowControl = styled.div` -webkit-app-region: no-drag; `; +export const MacControl = styled.div` + display: grid; + grid-template-columns: repeat(3, 30px); + position: absolute; + top: 0; + left: 0; + height: 100%; + + -webkit-app-region: no-drag; +`; + +export const MacControlButton = styled.div<{ + minButton?: boolean; + maxButton?: boolean; + restoreButton?: boolean; +}>` + user-select: none; + grid-row: 1 / span 1; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + grid-column: ${(props) => (props.minButton ? 2 : props.maxButton || props.restoreButton ? 3 : 1)}; +`; + export const WindowControlButton = styled.div<{ minButton?: boolean; maxButton?: boolean; diff --git a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx index 0148168..f9547d8 100644 --- a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx +++ b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx @@ -12,7 +12,7 @@ import { import ListViewConfig from './ListViewConfig'; import { Fonts } from '../Fonts'; import { useAppDispatch } from '../../../redux/hooks'; -import { setTheme, setFont, setDynamicBackground } from '../../../redux/miscSlice'; +import { setTheme, setFont, setDynamicBackground, setMiscSetting } from '../../../redux/miscSlice'; import { songColumnPicker, songColumnList, @@ -83,6 +83,7 @@ const LookAndFeelConfig = () => { { settings.setSync('font', e); @@ -91,6 +92,30 @@ const LookAndFeelConfig = () => { />
+
+ Titlebar style (requires app restart) + +
+ { + settings.setSync('titleBarStyle', e); + dispatch(setMiscSetting({ setting: 'titleBar', value: e })); + }} + /> +
+

Select the columns you want displayed on pages with a list-view.

{ settings.setSync('cachePath', path.join(path.dirname(settings.file()))); } + if (force || !settings.hasSync('titleBarStyle')) { + settings.setSync('titleBarStyle', 'windows'); + } + if (force || !settings.hasSync('scrobble')) { settings.setSync('scrobble', false); } diff --git a/src/img/icons/close-mac-hover.png b/src/img/icons/close-mac-hover.png new file mode 100644 index 0000000..7f371fd Binary files /dev/null and b/src/img/icons/close-mac-hover.png differ diff --git a/src/img/icons/close-mac.png b/src/img/icons/close-mac.png new file mode 100644 index 0000000..9c6ca5c Binary files /dev/null and b/src/img/icons/close-mac.png differ diff --git a/src/img/icons/max-mac-hover.png b/src/img/icons/max-mac-hover.png new file mode 100644 index 0000000..d890aa2 Binary files /dev/null and b/src/img/icons/max-mac-hover.png differ diff --git a/src/img/icons/max-mac.png b/src/img/icons/max-mac.png new file mode 100644 index 0000000..3a86691 Binary files /dev/null and b/src/img/icons/max-mac.png differ diff --git a/src/img/icons/min-mac-hover.png b/src/img/icons/min-mac-hover.png new file mode 100644 index 0000000..0790f58 Binary files /dev/null and b/src/img/icons/min-mac-hover.png differ diff --git a/src/img/icons/min-mac.png b/src/img/icons/min-mac.png new file mode 100644 index 0000000..54e512c Binary files /dev/null and b/src/img/icons/min-mac.png differ diff --git a/src/redux/miscSlice.ts b/src/redux/miscSlice.ts index 9875abe..55d284e 100644 --- a/src/redux/miscSlice.ts +++ b/src/redux/miscSlice.ts @@ -42,6 +42,7 @@ export interface General { isProcessingPlaylist: string[]; contextMenu: ContextMenu; dynamicBackground: boolean; + titleBar: 'windows' | 'mac' | string; } const initialState: General = { @@ -58,6 +59,7 @@ const initialState: General = { show: false, }, dynamicBackground: Boolean(parsedSettings.dynamicBackground), + titleBar: String(parsedSettings.titleBarStyle), }; const miscSlice = createSlice({ @@ -72,6 +74,16 @@ const miscSlice = createSlice({ state.expandSidebar = action.payload; }, + setMiscSetting: (state, action: PayloadAction<{ setting: string; value: any }>) => { + switch (action.payload.setting) { + case 'titleBar': + state.titleBar = action.payload.value; + break; + default: + break; + } + }, + setContextMenu: (state, action: PayloadAction) => { state.contextMenu.show = action.payload.show; state.contextMenu.xPos = action.payload.xPos; @@ -156,5 +168,6 @@ export const { setContextMenu, setExpandSidebar, setDynamicBackground, + setMiscSetting, } = miscSlice.actions; export default miscSlice.reducer; diff --git a/src/styles/App.global.css b/src/styles/App.global.css index 642191c..95a5319 100644 --- a/src/styles/App.global.css +++ b/src/styles/App.global.css @@ -106,6 +106,25 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + user-select: none; +} + +#window-title-wrapper-mac { + white-space: nowrap; + height: 100%; + padding: 2px; + text-align: center; +} + +#window-title-mac { + max-width: 60%; + width: 100%; + display: inline-block; + vertical-align: middle; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + user-select: none; } #restore-button {