feat: windows缩略图工具栏 (#1551)
* 从 v1 引入图标 * feat: windows缩略图工具栏 * 更新windows任务栏控制图标 图标基于microsoft fluent icon修改 移除like和unlike * update * 启动时显示taskbar buttons
BIN
src/main/assets/icons/taskbar/next.png
Normal file
|
After Width: | Height: | Size: 936 B |
BIN
src/main/assets/icons/taskbar/pause.png
Normal file
|
After Width: | Height: | Size: 612 B |
BIN
src/main/assets/icons/taskbar/play.png
Normal file
|
After Width: | Height: | Size: 844 B |
BIN
src/main/assets/icons/taskbar/previous.png
Normal file
|
After Width: | Height: | Size: 890 B |
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 953 B After Width: | Height: | Size: 953 B |
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
|
|
@ -14,6 +14,7 @@ import log from './log'
|
|||
import { initIpcMain } from './ipcMain'
|
||||
import { createTray, YPMTray } from './tray'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { createTaskbar, Thumbar } from './windowsTaskbar'
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isMac = process.platform === 'darwin'
|
||||
|
|
@ -32,6 +33,7 @@ interface TypedElectronStore {
|
|||
class Main {
|
||||
win: BrowserWindow | null = null
|
||||
tray: YPMTray | null = null
|
||||
thumbar: Thumbar | null = null
|
||||
store = new Store<TypedElectronStore>({
|
||||
defaults: {
|
||||
window: {
|
||||
|
|
@ -62,7 +64,8 @@ class Main {
|
|||
this.handleAppEvents()
|
||||
this.handleWindowEvents()
|
||||
this.createTray()
|
||||
initIpcMain(this.win, this.tray)
|
||||
this.createThumbar()
|
||||
initIpcMain(this.win, this.tray, this.thumbar)
|
||||
this.initDevTools()
|
||||
})
|
||||
}
|
||||
|
|
@ -93,6 +96,10 @@ class Main {
|
|||
}
|
||||
}
|
||||
|
||||
createThumbar() {
|
||||
if (isWindows) this.thumbar = createTaskbar(this.win!)
|
||||
}
|
||||
|
||||
createWindow() {
|
||||
const options: BrowserWindowConstructorOptions = {
|
||||
title: 'YesPlayMusic',
|
||||
|
|
@ -150,7 +157,7 @@ class Main {
|
|||
handleAppEvents() {
|
||||
app.on('window-all-closed', () => {
|
||||
this.win = null
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
if (!isMac) app.quit()
|
||||
})
|
||||
|
||||
app.on('second-instance', () => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import log from './log'
|
|||
import fs from 'fs'
|
||||
import { APIs } from '../shared/CacheAPIs'
|
||||
import { YPMTray } from './tray'
|
||||
import { Thumbar } from './windowsTaskbar'
|
||||
|
||||
const on = <T extends keyof IpcChannelsParams>(
|
||||
channel: T,
|
||||
|
|
@ -14,9 +15,14 @@ const on = <T extends keyof IpcChannelsParams>(
|
|||
ipcMain.on(channel, listener)
|
||||
}
|
||||
|
||||
export function initIpcMain(win: BrowserWindow | null, tray: YPMTray | null) {
|
||||
export function initIpcMain(
|
||||
win: BrowserWindow | null,
|
||||
tray: YPMTray | null,
|
||||
thumbar: Thumbar | null
|
||||
) {
|
||||
initWindowIpcMain(win)
|
||||
initTrayIpcMain(tray)
|
||||
initTaskbarIpcMain(thumbar)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +51,7 @@ function initWindowIpcMain(win: BrowserWindow | null) {
|
|||
function initTrayIpcMain(tray: YPMTray | null) {
|
||||
on(IpcChannels.SetTrayTooltip, (e, { text }) => tray?.setTooltip(text))
|
||||
|
||||
on(IpcChannels.SetTrayLikeState, (e, { isLiked }) =>
|
||||
on(IpcChannels.Like, (e, { isLiked }) =>
|
||||
tray?.setLikeState(isLiked)
|
||||
)
|
||||
|
||||
|
|
@ -55,6 +61,15 @@ function initTrayIpcMain(tray: YPMTray | null) {
|
|||
on(IpcChannels.Repeat, (e, { mode }) => tray?.setRepeatMode(mode))
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理需要thumbar对象的事件
|
||||
* @param {Thumbar} thumbar
|
||||
*/
|
||||
function initTaskbarIpcMain(thumbar: Thumbar | null) {
|
||||
on(IpcChannels.Play, () => thumbar?.setPlayState(true))
|
||||
on(IpcChannels.Pause, () => thumbar?.setPlayState(false))
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除API缓存
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import { RepeatMode } from '@/shared/playerDataTypes'
|
|||
|
||||
const iconDirRoot =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? path.join(process.cwd(), './src/main/assets/icons')
|
||||
: path.join(__dirname, './assets/icons')
|
||||
? path.join(process.cwd(), './src/main/assets/icons/tray')
|
||||
: path.join(__dirname, './assets/icons/tray')
|
||||
|
||||
enum MenuItemIDs {
|
||||
Play = 'play',
|
||||
|
|
|
|||
86
src/main/windowsTaskbar.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { BrowserWindow, nativeImage, ThumbarButton } from 'electron'
|
||||
import path from 'path'
|
||||
|
||||
enum ItemKeys {
|
||||
Play = 'play',
|
||||
Pause = 'pause',
|
||||
Previous = 'previous',
|
||||
Next = 'next',
|
||||
}
|
||||
|
||||
type ThumbarButtonMap = Map<ItemKeys, ThumbarButton>
|
||||
|
||||
const iconDirRoot =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? path.join(process.cwd(), './src/main/assets/icons/taskbar')
|
||||
: path.join(__dirname, './assets/icons/taskbar')
|
||||
|
||||
function createNativeImage(filename: string) {
|
||||
return nativeImage.createFromPath(path.join(iconDirRoot, filename))
|
||||
}
|
||||
|
||||
function createThumbarButtons(win: BrowserWindow): ThumbarButtonMap {
|
||||
return new Map<ItemKeys, ThumbarButton>()
|
||||
.set(ItemKeys.Play, {
|
||||
click: () => win.webContents.send(IpcChannels.Play),
|
||||
icon: createNativeImage('play.png'),
|
||||
tooltip: '播放',
|
||||
})
|
||||
.set(ItemKeys.Pause, {
|
||||
click: () => win.webContents.send(IpcChannels.Pause),
|
||||
icon: createNativeImage('pause.png'),
|
||||
tooltip: '暂停',
|
||||
})
|
||||
.set(ItemKeys.Previous, {
|
||||
click: () => win.webContents.send(IpcChannels.Previous),
|
||||
icon: createNativeImage('previous.png'),
|
||||
tooltip: '上一首',
|
||||
})
|
||||
.set(ItemKeys.Next, {
|
||||
click: () => win.webContents.send(IpcChannels.Next),
|
||||
icon: createNativeImage('next.png'),
|
||||
tooltip: '下一首',
|
||||
})
|
||||
}
|
||||
|
||||
export interface Thumbar {
|
||||
setPlayState(isPlaying: boolean): void
|
||||
}
|
||||
|
||||
class ThumbarImpl implements Thumbar {
|
||||
private _win: BrowserWindow
|
||||
private _buttons: ThumbarButtonMap
|
||||
|
||||
private _playOrPause: ThumbarButton
|
||||
private _previous: ThumbarButton
|
||||
private _next: ThumbarButton
|
||||
|
||||
constructor(win: BrowserWindow) {
|
||||
this._win = win
|
||||
this._buttons = createThumbarButtons(win)
|
||||
|
||||
this._playOrPause = this._buttons.get(ItemKeys.Play)!
|
||||
this._previous = this._buttons.get(ItemKeys.Previous)!
|
||||
this._next = this._buttons.get(ItemKeys.Next)!
|
||||
}
|
||||
|
||||
private _updateThumbarButtons(clear: boolean) {
|
||||
this._win.setThumbarButtons(
|
||||
clear
|
||||
? []
|
||||
: [this._previous, this._playOrPause, this._next]
|
||||
)
|
||||
}
|
||||
|
||||
setPlayState(isPlaying: boolean) {
|
||||
this._playOrPause = this._buttons.get(
|
||||
isPlaying ? ItemKeys.Pause : ItemKeys.Play
|
||||
)!
|
||||
this._updateThumbarButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
export function createTaskbar(win: BrowserWindow): Thumbar {
|
||||
return new ThumbarImpl(win)
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ const IpcRendererReact = () => {
|
|||
}, [track])
|
||||
|
||||
useEffect(() => {
|
||||
window.ipcRenderer?.send(IpcChannels.SetTrayLikeState, {
|
||||
window.ipcRenderer?.send(IpcChannels.Like, {
|
||||
isLiked: userLikedSongs?.ids?.includes(track?.id ?? 0) ?? false,
|
||||
})
|
||||
}, [userLikedSongs, track])
|
||||
|
|
@ -46,6 +46,13 @@ const IpcRendererReact = () => {
|
|||
setIsPlaying(playing)
|
||||
}, [state])
|
||||
|
||||
useEffectOnce(() => {
|
||||
// 用于显示 windows taskbar buttons
|
||||
if (playerSnapshot.track?.id) {
|
||||
window.ipcRenderer?.send(IpcChannels.Pause)
|
||||
}
|
||||
})
|
||||
|
||||
return <></>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export const enum IpcChannels {
|
|||
DevDbExportJson = 'dev-db-export-json',
|
||||
CacheCoverColor = 'cache-cover-color',
|
||||
SetTrayTooltip = 'set-tray-tooltip',
|
||||
SetTrayLikeState = 'set-tray-like-state',
|
||||
// 准备三个播放相关channel, 为 mpris 预留接口
|
||||
Play = 'play',
|
||||
Pause = 'pause',
|
||||
|
|
@ -41,9 +40,6 @@ export interface IpcChannelsParams {
|
|||
[IpcChannels.SetTrayTooltip]: {
|
||||
text: string
|
||||
}
|
||||
[IpcChannels.SetTrayLikeState]: {
|
||||
isLiked: boolean
|
||||
}
|
||||
[IpcChannels.Play]: void
|
||||
[IpcChannels.Pause]: void
|
||||
[IpcChannels.PlayOrPause]: void
|
||||
|
|
@ -68,7 +64,6 @@ export interface IpcChannelsReturns {
|
|||
[IpcChannels.DevDbExportJson]: void
|
||||
[IpcChannels.CacheCoverColor]: void
|
||||
[IpcChannels.SetTrayTooltip]: void
|
||||
[IpcChannels.SetTrayLikeState]: void
|
||||
[IpcChannels.Play]: void
|
||||
[IpcChannels.Pause]: void
|
||||
[IpcChannels.PlayOrPause]: void
|
||||
|
|
|
|||