From 1444bbefa250a961e01603d9d64cc2c1b52c71c0 Mon Sep 17 00:00:00 2001 From: memorydream <34763046+memorydream@users.noreply.github.com> Date: Sat, 9 Apr 2022 00:19:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90windows=E4=B8=8Elinux?= =?UTF-8?q?=E4=B8=8B=E7=9A=84Titlebar=20(#1482)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 完成windows与linux下的Titlebar * update * fix: win titlebar * 优化部分api命名 * 视觉效果更好的标题间距 * fix: Linux title没有居中 * update * fix: Main style * 向renderer公开IpcRenderer.on * update --- src/main/index.ts | 14 ++++ src/main/ipcMain.ts | 20 +++++- src/main/rendererPreload.ts | 11 ++- src/renderer/App.tsx | 2 +- src/renderer/auto-imports.d.ts | 1 + src/renderer/components/Main.tsx | 8 ++- src/renderer/components/Sidebar.tsx | 4 +- src/renderer/components/TitleBar.tsx | 100 +++++++++++++++++++++++---- src/renderer/components/Topbar.tsx | 4 +- src/renderer/global.d.ts | 1 + vite.config.ts | 1 + 11 files changed, 147 insertions(+), 19 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 9129f07..a932f7f 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -11,6 +11,7 @@ import Store from 'electron-store' import { release } from 'os' import path, { join } from 'path' import logger from './logger' +import { initIpcMain } from './ipcMain' const isWindows = process.platform === 'win32' const isMac = process.platform === 'darwin' @@ -64,6 +65,7 @@ async function createWindow() { minHeight: 720, vibrancy: 'fullscreen-ui', titleBarStyle: 'hiddenInset', + frame: !(isWindows || isLinux), // TODO: 适用于linux下独立的启用开关 } if (store.get('window')) { options.x = store.get('window.x') @@ -99,6 +101,8 @@ async function createWindow() { app.whenReady().then(async () => { logger.info('[index] App ready') createWindow() + handleWindowEvents() + initIpcMain(win) // Install devtool extension if (isDev) { @@ -138,3 +142,13 @@ app.on('activate', () => { createWindow() } }) + +const handleWindowEvents = () => { + win?.on('maximize', () => { + win?.webContents.send('is-maximized', true) + }) + + win?.on('unmaximize', () => { + win?.webContents.send('is-maximized', false) + }) +} diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 3299928..e7e409f 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -1,8 +1,11 @@ -import { ipcMain } from 'electron' +import { BrowserWindow, ipcMain, app } from 'electron' import { db, Tables } from './db' export enum Events { ClearAPICache = 'clear-api-cache', + Minimize = 'minimize', + MaximizeOrUnmaximize = 'maximize-or-unmaximize', + Close = 'close', } ipcMain.on(Events.ClearAPICache, () => { @@ -15,3 +18,18 @@ ipcMain.on(Events.ClearAPICache, () => { db.truncate(Tables.AUDIO) db.vacuum() }) + +export function initIpcMain(win: BrowserWindow | null) { + ipcMain.on(Events.Minimize, () => { + win?.minimize() + }) + + ipcMain.on(Events.MaximizeOrUnmaximize, () => { + if (!win) return + win.isMaximized() ? win.unmaximize() : win.maximize() + }) + + ipcMain.on(Events.Close, () => { + app.exit() + }) +} diff --git a/src/main/rendererPreload.ts b/src/main/rendererPreload.ts index c2dd10f..1a4337d 100644 --- a/src/main/rendererPreload.ts +++ b/src/main/rendererPreload.ts @@ -1,8 +1,17 @@ const { contextBridge, ipcRenderer } = require('electron') -contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer) +contextBridge.exposeInMainWorld('ipcRenderer', { + sendSync: ipcRenderer.sendSync, + send: ipcRenderer.send, + on: (channel, listener) => { + ipcRenderer.on(channel, listener) + return () => ipcRenderer.removeListener(channel, listener) + }, +}) contextBridge.exposeInMainWorld('env', { isElectron: true, + isEnableTitlebar: + process.platform === 'win32' || process.platform === 'linux', isLinux: process.platform === 'linux', isMac: process.platform === 'darwin', isWin: process.platform === 'win32', diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 74d85f2..41b9104 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -11,7 +11,7 @@ import Lyric from '@/components/Lyric' const App = () => { return ( - {window.env?.isWin && } + {window.env?.isEnableTitlebar && }
diff --git a/src/renderer/auto-imports.d.ts b/src/renderer/auto-imports.d.ts index e924c1d..d158698 100644 --- a/src/renderer/auto-imports.d.ts +++ b/src/renderer/auto-imports.d.ts @@ -7,6 +7,7 @@ declare global { const useContext: typeof import('react')['useContext'] const useDebugValue: typeof import('react')['useDebugValue'] const useEffect: typeof import('react')['useEffect'] + const useEffectOnce: typeof import('react-use')['useEffectOnce'] const useImperativeHandle: typeof import('react')['useImperativeHandle'] const useInfiniteQuery: typeof import('react-query')['useInfiniteQuery'] const useMemo: typeof import('react')['useMemo'] diff --git a/src/renderer/components/Main.tsx b/src/renderer/components/Main.tsx index 4ffa6ec..4995761 100644 --- a/src/renderer/components/Main.tsx +++ b/src/renderer/components/Main.tsx @@ -8,7 +8,13 @@ const Main = () => { className='relative flex h-screen max-h-screen flex-grow flex-col overflow-y-auto bg-white dark:bg-[#1d1d1d]' > -
+
diff --git a/src/renderer/components/Sidebar.tsx b/src/renderer/components/Sidebar.tsx index ad4d9b8..82049d3 100644 --- a/src/renderer/components/Sidebar.tsx +++ b/src/renderer/components/Sidebar.tsx @@ -35,7 +35,9 @@ const primaryTabs: PrimaryTab[] = [ const PrimaryTabs = () => { return (
-
+
{primaryTabs.map(tab => ( scrollToTop()} diff --git a/src/renderer/components/TitleBar.tsx b/src/renderer/components/TitleBar.tsx index 2ae80db..82887bf 100644 --- a/src/renderer/components/TitleBar.tsx +++ b/src/renderer/components/TitleBar.tsx @@ -1,20 +1,94 @@ +import { player } from '@/store' import SvgIcon from './SvgIcon' +const Controls = () => { + const [isMaximized, setIsMaximized] = useState(false) + + useEffectOnce(() => { + return window.ipcRenderer?.on('is-maximized', (e, value) => { + setIsMaximized(value) + }) + }) + + const minimize = () => { + window.ipcRenderer?.send('minimize') + } + + const maxRestore = () => { + window.ipcRenderer?.send('maximize-or-unmaximize') + } + + const close = () => { + window.ipcRenderer?.send('close') + } + + return ( +
+ + + +
+ ) +} + +const Title = ({ className }: { className?: string }) => { + const playerSnapshot = useSnapshot(player) + const track = useMemo(() => playerSnapshot.track, [playerSnapshot.track]) + + return ( +
+ {track?.name && ( + <> + {track.name} + - + + )} + YesPlayMusic +
+ ) +} + +const Win = () => { + return ( +
+ + <Controls /> + </div> + ) +} + +const Linux = () => { + return ( + <div className='flex h-8 w-screen items-center justify-between bg-gray-50'> + <div></div> + <Title className='text-center' /> + <Controls /> + </div> + ) +} + const TitleBar = () => { return ( - <div className='app-region-drag flex h-8 w-screen items-center justify-between bg-gray-50'> - <div className='ml-2 text-sm text-gray-500'>YesPlayMusic</div> - <div className='flex h-full'> - <button className='app-region-no-drag flex w-[2.875rem] items-center justify-center hover:bg-[#e9e9e9]'> - <SvgIcon className='h-3 w-3' name='windows-minimize' /> - </button> - <button className='app-region-no-drag flex w-[2.875rem] items-center justify-center hover:bg-[#e9e9e9]'> - <SvgIcon className='h-3 w-3' name='windows-maximize' /> - </button> - <button className='app-region-no-drag flex w-[2.875rem] items-center justify-center hover:bg-[#c42b1c] hover:text-white'> - <SvgIcon className='h-3 w-3' name='windows-close' /> - </button> - </div> + <div className='app-region-drag fixed z-30'> + {window.env?.isWin ? <Win /> : <Linux />} </div> ) } diff --git a/src/renderer/components/Topbar.tsx b/src/renderer/components/Topbar.tsx index 0d792f3..890e578 100644 --- a/src/renderer/components/Topbar.tsx +++ b/src/renderer/components/Topbar.tsx @@ -119,7 +119,9 @@ const Topbar = () => { return ( <div className={classNames( - 'app-region-drag sticky top-0 z-30 flex h-16 min-h-[4rem] w-full cursor-default items-center justify-between px-8 transition duration-300', + 'sticky z-30 flex h-16 min-h-[4rem] w-full cursor-default items-center justify-between px-8 transition duration-300', + window.env?.isMac && 'app-region-drag', + window.env?.isEnableTitlebar ? 'top-8' : 'top-0', !scroll.arrivedState.top && 'bg-white bg-opacity-[.86] backdrop-blur-xl backdrop-saturate-[1.8] dark:bg-[#222] dark:bg-opacity-[.86]' )} diff --git a/src/renderer/global.d.ts b/src/renderer/global.d.ts index b62c23e..81f5f57 100644 --- a/src/renderer/global.d.ts +++ b/src/renderer/global.d.ts @@ -6,6 +6,7 @@ declare global { ipcRenderer?: import('electron').IpcRenderer env?: { isElectron: boolean + isEnableTitlebar: boolean isLinux: boolean isMac: boolean isWin: boolean diff --git a/vite.config.ts b/vite.config.ts index ca8843a..14b72a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -36,6 +36,7 @@ export default defineConfig({ { 'react-query': ['useQuery', 'useMutation', 'useInfiniteQuery'] }, { 'react-router-dom': ['useNavigate', 'useParams'] }, { 'react-hot-toast': ['toast'] }, + { 'react-use': ['useEffectOnce']}, { valtio: ['useSnapshot'] }, ], }),