Files
youtube-music/src/tray.ts
2025-12-29 01:39:53 +09:00

156 lines
3.5 KiB
TypeScript

import { Menu, nativeImage, screen, Tray } from 'electron';
import is from 'electron-is';
import TrayIcon from '@assets/tray.png?asset&asarUnpack';
import PausedTrayIcon from '@assets/tray-paused.png?asset&asarUnpack';
import TrayIconWhite from '@assets/tray-white.png?asset&asarUnpack';
import PausedTrayIconWhite from '@assets/tray-paused-white.png?asset&asarUnpack';
import * as config from './config';
import { restart } from './providers/app-controls';
import { registerCallback, SongInfoEvent } from './providers/song-info';
import { getSongControls } from './providers/song-controls';
import { APPLICATION_NAME, t } from '@/i18n';
import type { MenuTemplate } from './menu';
// Prevent tray being garbage collected
let tray: Electron.Tray | undefined;
type TrayEvent = (
event: Electron.KeyboardEvent,
bounds: Electron.Rectangle,
) => void;
export const setTrayOnClick = (fn: TrayEvent) => {
if (!tray) {
return;
}
tray.removeAllListeners('click');
tray.on('click', fn);
};
// Won't do anything on macOS since its disabled
export const setTrayOnDoubleClick = (fn: TrayEvent) => {
if (!tray) {
return;
}
tray.removeAllListeners('double-click');
tray.on('double-click', fn);
};
export const setUpTray = (app: Electron.App, win: Electron.BrowserWindow) => {
if (!config.get('options.tray')) {
tray = undefined;
return;
}
const { playPause, next, previous } = getSongControls(win);
const pixelRatio = is.windows()
? screen.getPrimaryDisplay().scaleFactor || 1
: 1;
const defaultTrayIcon = nativeImage
.createFromPath(is.macOS() ? TrayIconWhite : TrayIcon)
.resize({
width: 16 * pixelRatio,
height: 16 * pixelRatio,
});
const pausedTrayIcon = nativeImage
.createFromPath(is.macOS() ? PausedTrayIconWhite : PausedTrayIcon)
.resize({
width: 16 * pixelRatio,
height: 16 * pixelRatio,
});
tray = new Tray(defaultTrayIcon);
tray.setToolTip(
t('main.tray.tooltip.default', {
applicationName: APPLICATION_NAME,
}),
);
// MacOS only
tray.setIgnoreDoubleClickEvents(true);
tray.on('click', () => {
if (config.get('options.trayClickPlayPause')) {
playPause();
} else if (win.isVisible()) {
win.hide();
app.dock?.hide();
} else {
win.show();
app.dock?.show();
}
});
const template: MenuTemplate = [
{
label: t('main.tray.play-pause'),
click() {
playPause();
},
},
{
label: t('main.tray.next'),
click() {
next();
},
},
{
label: t('main.tray.previous'),
click() {
previous();
},
},
{
label: t('main.tray.show'),
click() {
win.show();
app.dock?.show();
},
},
{ type: 'separator' },
{
label: t('main.tray.restart'),
click: restart,
},
{ type: 'separator' },
{
label: t('main.tray.quit'),
role: 'quit',
},
];
const trayMenu = Menu.buildFromTemplate(template);
tray.setContextMenu(trayMenu);
registerCallback((songInfo, event) => {
if (event === SongInfoEvent.TimeChanged) return;
if (tray) {
if (typeof songInfo.isPaused === 'undefined') {
tray.setImage(defaultTrayIcon);
return;
}
tray.setToolTip(
t('main.tray.tooltip.with-song-info', {
artist: songInfo.artist,
title: songInfo.title,
applicationName: APPLICATION_NAME,
}),
);
tray.setImage(songInfo.isPaused ? pausedTrayIcon : defaultTrayIcon);
}
});
};