feat: 实现托盘菜单 (#1538)

* 从 v1 添加托盘相关图标

* feat: ipcRenderer事件

* feat: 托盘菜单实现

* 修复合并后的错误

* fix: 托盘图标的like

* 将 tray 相关的 ipc 放入ipcMain.ts

* update

* update

* feat: 设置托盘Tooltip

* fix

* fix: tray play/pause fade

* fix: 暂时将tray like与tooltip的设置移入Player组件中

useUserLikedTracksIDs 会在重新聚焦而不是切换track时触发,导致托盘无法实时更新数据

基于以上一点,在Player组件中有了一个用于设置tray数据的useEffect,故将tray tooltip的设置也放入其中,使tray的数据尽可能简单的和player数据保持一致

* 将部分ipcRenderer调用挪到单独的IpcRendererReact组件

* 移除SetTrayPlayState,复用已有channel

* update
This commit is contained in:
memorydream 2022-04-20 20:25:20 +08:00 committed by GitHub
parent b1fd51233a
commit ffdf66b57e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 392 additions and 19 deletions

173
src/main/tray.ts Normal file
View file

@ -0,0 +1,173 @@
import path from 'path'
import {
app,
BrowserWindow,
Menu,
MenuItemConstructorOptions,
nativeImage,
Tray,
} from 'electron'
import { IpcChannels } from '@/shared/IpcChannels'
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')
enum MenuItemIDs {
Play = 'play',
Pause = 'pause',
Like = 'like',
Unlike = 'unlike',
}
export interface YPMTray {
setTooltip(text: string): void
setLikeState(isLiked: boolean): void
setPlayState(isPlaying: boolean): void
setRepeatMode(mode: RepeatMode): void
}
function createNativeImage(filename: string) {
return nativeImage.createFromPath(path.join(iconDirRoot, filename))
}
function createMenuTemplate(win: BrowserWindow): MenuItemConstructorOptions[] {
let template: MenuItemConstructorOptions[] =
process.platform === 'linux'
? [
{
label: '显示主面板',
click: () => win.show(),
},
{
type: 'separator',
},
]
: []
return template.concat([
{
label: '播放',
click: () => win.webContents.send(IpcChannels.Play),
icon: createNativeImage('play.png'),
id: MenuItemIDs.Play,
},
{
label: '暂停',
click: () => win.webContents.send(IpcChannels.Pause),
icon: createNativeImage('pause.png'),
id: MenuItemIDs.Pause,
visible: false,
},
{
label: '上一首',
click: () => win.webContents.send(IpcChannels.Previous),
icon: createNativeImage('left.png'),
},
{
label: '下一首',
click: () => win.webContents.send(IpcChannels.Next),
icon: createNativeImage('right.png'),
},
{
label: '循环模式',
icon: createNativeImage('repeat.png'),
submenu: [
{
label: '关闭循环',
click: () => win.webContents.send(IpcChannels.Repeat, RepeatMode.Off),
id: RepeatMode.Off,
checked: true,
type: 'radio',
},
{
label: '列表循环',
click: () => win.webContents.send(IpcChannels.Repeat, RepeatMode.On),
id: RepeatMode.On,
type: 'radio',
},
{
label: '单曲循环',
click: () => win.webContents.send(IpcChannels.Repeat, RepeatMode.One),
id: RepeatMode.One,
type: 'radio',
},
],
},
{
label: '加入喜欢',
click: () => win.webContents.send(IpcChannels.Like),
icon: createNativeImage('like.png'),
id: MenuItemIDs.Like,
},
{
label: '取消喜欢',
click: () => win.webContents.send(IpcChannels.Like),
icon: createNativeImage('unlike.png'),
id: MenuItemIDs.Unlike,
visible: false,
},
{
label: '退出',
click: () => app.exit(),
icon: createNativeImage('exit.png'),
},
])
}
class YPMTrayImpl implements YPMTray {
private _win: BrowserWindow
private _tray: Tray
private _template: MenuItemConstructorOptions[]
private _contextMenu: Menu
constructor(win: BrowserWindow) {
this._win = win
const icon = createNativeImage('menu@88.png').resize({
height: 20,
width: 20,
})
this._tray = new Tray(icon)
this._template = createMenuTemplate(this._win)
this._contextMenu = Menu.buildFromTemplate(this._template)
this._updateContextMenu()
this.setTooltip('YesPlayMusic')
this._tray.on('click', () => win.show())
}
private _updateContextMenu() {
this._tray.setContextMenu(this._contextMenu)
}
setTooltip(text: string) {
this._tray.setToolTip(text)
}
setLikeState(isLiked: boolean) {
this._contextMenu.getMenuItemById(MenuItemIDs.Like)!.visible = !isLiked
this._contextMenu.getMenuItemById(MenuItemIDs.Unlike)!.visible = isLiked
this._updateContextMenu()
}
setPlayState(isPlaying: boolean) {
this._contextMenu.getMenuItemById(MenuItemIDs.Play)!.visible = !isPlaying
this._contextMenu.getMenuItemById(MenuItemIDs.Pause)!.visible = isPlaying
this._updateContextMenu()
}
setRepeatMode(mode: RepeatMode) {
const item = this._contextMenu.getMenuItemById(mode)
if (item) {
item.checked = true
this._updateContextMenu()
}
}
}
export function createTray(win: BrowserWindow): YPMTray {
return new YPMTrayImpl(win)
}