From 9db57acb71604d17f1940a50e1e9d0c8076d7350 Mon Sep 17 00:00:00 2001 From: kunkka Date: Thu, 29 Oct 2020 18:46:54 +0800 Subject: [PATCH] feat: electron tray & menu & touchbar supported --- .env.example | 6 +- public/img/icons/menu.png | Bin 0 -> 311 bytes public/img/icons/menu@88.png | Bin 0 -> 1084 bytes src/App.vue | 41 +++++ src/background.js | 47 ++--- src/electron/command.js | 121 ------------- src/electron/menu.js | 336 ++++++++++++++++++++--------------- src/electron/services.js | 7 +- src/electron/touchbar.js | 32 ++-- src/electron/tray.js | 24 +++ src/utils/request.js | 10 +- 11 files changed, 312 insertions(+), 312 deletions(-) create mode 100644 public/img/icons/menu.png create mode 100644 public/img/icons/menu@88.png delete mode 100644 src/electron/command.js create mode 100644 src/electron/tray.js diff --git a/.env.example b/.env.example index eec8514..36eb51b 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,4 @@ -VUE_APP_NETEASE_API_URL=http://localhost:3000 -VUE_APP_ENABLE_SENTRY=false \ No newline at end of file +VUE_APP_NETEASE_API_URL=http://127.0.0.1:3000 +VUE_APP_ELECTRON_API_URL=http://127.0.0.1:10754 +VUE_APP_ENABLE_SENTRY=false +DEV_SERVER_PORT=20201 \ No newline at end of file diff --git a/public/img/icons/menu.png b/public/img/icons/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..bd91a03da961e78f9e048e2b0fad63bd9e198eb1 GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=O$GvO>yQ24)M&xjw?O&@ z%PTgk#zH0uM)OOYFW7nt_ANKf%-Jgcsad_U*x}^lx~Cn-`ZmSS*y&=ay!530mXofU zOXmhoRJ2`wd3LA8p=qBp@6GFcIw#m}s{f1RZkZQb!vkAd%yTNbce$-IY534NM`_dX z$4yL|dxh#cT&tv9G7Nv487yTfunp5N-So$sBd}4aJXidexyl^}wbRy0tgZq=*QRvT zYLuUHw~qHIeqk~9Tpr)PZ>qmT17$X?I(B=X+iB*0!@K9sr10zn`isHS)z4*}Q$iB} DR(W*O literal 0 HcmV?d00001 diff --git a/public/img/icons/menu@88.png b/public/img/icons/menu@88.png new file mode 100644 index 0000000000000000000000000000000000000000..cc14a82d1a3de3faff2aaea28d180e0885c8780a GIT binary patch literal 1084 zcmV-C1jGA@P)ZVtY z+*8a+6OLu#leUB5(nv z;i>dQ7Z0wW{DC}Jq@5^(E8l(c0Fi1UwTJ>r8&Qd%b1gzqfrCIQ@R${|4aGji+#J6k z4z?sGJ%)_21m>ZdefwvMjVUN`m$X2*h=#m;Tg2JFeo3P9kPT<_DI&+W!2j*flgKBi zt;4BA7!tuh8rUBDB`S%hT=Clo*7c~)pL8Qgk^ zvDXH%I4AlHW??Bh3ADXpP|(r_(3(M8}ZC=IvL6I}$}Kxw#?p6KGi+$rEKWm4WJ zO+rbrH~5w^<^#P>1r3A?mtCjC8O)e0<{H5qd2&PIK5==2Q%}zP=MGSB6`LE}OnnYV zoV&`rvWE`u%opYYU+b#Nmbaa15oUV+3VCEQU*pPLvolT}+{Wx%!{x@J?`H)Hx0(*j zUE7IX)ig6TpG|*d{+JYOgRgQ@qV zgMk?Rwzjsv+D15nm(7b=D0{UlilQirq9}@@Ncjziaia>bFNlf&0000 { + console.log(event) + this.$router.push(path) + }) + ipcRenderer.on('play', () => { + this.$refs.player.play() + }) + ipcRenderer.on('next', () => { + this.$refs.player.next() + }) + ipcRenderer.on('previous', () => { + this.$refs.player.previous() + }) + ipcRenderer.on('increaseVolume', () => { + if (this.$refs.player.volume + 0.1 >= 1) { + return this.$refs.player.volume = 1 + } + this.$refs.player.volume += 0.1 + }) + ipcRenderer.on('decreaseVolume', () => { + if (this.$refs.player.volume - 0.1 <= 0) { + return this.$refs.player.volume = 0 + } + this.$refs.player.volume -= 0.1 + }) + ipcRenderer.on('like', () => { + this.$refs.player.likeCurrentSong() + }) + ipcRenderer.on('repeat', () => { + this.$refs.player.repeat() + }) + ipcRenderer.on('shuffle', () => { + this.$refs.player.shuffle() + }) + }, methods: { play(e) { e.preventDefault(); diff --git a/src/background.js b/src/background.js index fc26be9..01f1f53 100644 --- a/src/background.js +++ b/src/background.js @@ -8,14 +8,11 @@ import { BrowserWindow, ipcMain, dialog, - Tray, globalShortcut, } from "electron"; import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer"; -// maybe use for modify app menu -// import contextMenu from 'electron-context-menu' const isDevelopment = process.env.NODE_ENV !== "production"; // Keep a global reference of the window object, if you don't, the window will @@ -29,26 +26,41 @@ protocol.registerSchemesAsPrivileged([ const iconString = path.join(__static, "img/icons/apple-touch-icon.png") let bounceId = app.dock.bounce() -// app.dock.setBadge('Yes Play Music') app.dock.setIcon(iconString) + function createWindow() { - require('./electron/services') - // TODO Set the tray icon, need a white icon - // const trayIcon = path.join(__static, "img/icons/32x32.png") - // const tray = new Tray(trayIcon) -// Create the browser window. + const touchbar = require('./electron/touchbar.js') + const tray = require('./electron/tray.js') + const createMenu = require('./electron/menu.js') + tray.on('click', function () { + if (win.isVisible()) { + win.hide() + } else { + win.show() + } + }) win = new BrowserWindow({ width: 1440, height: 768, icon: iconString, + titleBarStyle: 'default', webPreferences: { webSecurity: false, - // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info nodeIntegration: true, }, preload: path.join(__dirname, "./electron/preload.js"), }); + try { + createMenu(win) + win.setTouchBar(touchbar) + win.setAutoHideCursor(true) + app.dock.cancelBounce(bounceId) + // autoUpdater.checkForUpdatesAndNotify() + } catch (error) { + console.log(error) + } + if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode win.loadURL(process.env.WEBPACK_DEV_SERVER_URL); @@ -57,9 +69,6 @@ function createWindow() { createProtocol("app"); // Load the index.html when not in development win.loadURL("app://./index.html"); - app.dock.cancelBounce(bounceId) - - // autoUpdater.checkForUpdatesAndNotify() } win.on("closed", () => { @@ -67,8 +76,6 @@ function createWindow() { }); } - - // Quit when all windows are closed. app.on("window-all-closed", () => { // On macOS it is common for applications and their menu bar @@ -90,6 +97,8 @@ app.on("activate", () => { // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on("ready", async () => { + // 启动 api 服务器 + require('./electron/services.js') if (isDevelopment && !process.env.IS_TEST) { // Install Vue Devtools try { @@ -149,14 +158,6 @@ if (isDevelopment) { function initialize() { const shouldQuit = !app.requestSingleInstanceLock(); if (shouldQuit) return app.quit(); - // loadComponent() } -/** - * 注册主线程文件里的所有js - */ -// function loadComponent () { -// require('./electron/menu.js') -// } - initialize(); diff --git a/src/electron/command.js b/src/electron/command.js deleted file mode 100644 index 6364763..0000000 --- a/src/electron/command.js +++ /dev/null @@ -1,121 +0,0 @@ -"use strict"; - -const { app, ipcMain, Menu, MenuItem, BrowserWindow, globalShortcut } = require('electron') - -let loginWindow, senders, win - -function openWindow(url) { - win = new BrowserWindow({ - height: 500, - width: 350, - useContentSize: true, - transparent: false, - frame: false, - darkTheme: true, - backgroundColor: "#FFF", - }); - - win.loadURL(url); - - win.on("closed", () => { - loginWindow = null; - }); - - return win; -} - -const menu = new Menu(); - -const settingsMenu = { - playMenu: function () { - const settings = { - paly: { - label: "播放", - click: function () { - senders.send("play-start"); - }, - }, - addPlayList: { - label: "添加到播放列表", - click: function () { - senders.send("add-play-list"); - }, - }, - collect: { - label: "收藏", - submenu: [ - { - label: "创建新歌单", - click: function () { - senders.send("i-like-star"); - }, - }, - ], - }, - share: { - label: "分享", - }, - copyLink: { - label: "复制链接", - }, - }; - menu.append(new MenuItem(settings.paly)); - menu.append(new MenuItem(settings.addPlayList)); - menu.append(new MenuItem({ type: "separator" })); - menu.append(new MenuItem(settings.collect)); - menu.append(new MenuItem(settings.share)); - menu.append(new MenuItem(settings.copyLink)); - }, -}; - -function command(mainWindow, winURL) { - // 显示播放菜单 - settingsMenu.playMenu(); - // 接收显示菜单指令 - ipcMain.on("show-content-menu", (event) => { - senders = event.sender; - const win = BrowserWindow.fromWebContents(senders); - menu.popup(win); - }); - // 设置app名称 - app.setName("网易云音乐App"); - // 关闭window窗口 - ipcMain.on("window-close", (event) => { - app.quit(); - }); - // 最大化window窗口 - ipcMain.on("window-max", (event) => { - if (mainWindow.isMaximized()) { - mainWindow.unmaximize(); - } else { - mainWindow.maximize(); - } - }); - // 最小化window窗口 - ipcMain.on("window-min", (event) => { - if (!mainWindow.isMinimized()) { - mainWindow.minimize(); - } - }); - // 新建登录窗口 - ipcMain.on("open-login-window", (event, url) => { - if (loginWindow) { - loginWindow.focus(); - } else { - loginWindow = openWindow(url); - } - }); - // 关闭登录窗口 - ipcMain.on("close-login-window", (event) => { - loginWindow.close(); - }); - // 触发调试 Shift+i - globalShortcut.register("Shift+i", () => { - require("electron-debug")({ showDevTools: true }); - }); -} - -app.on("ready", async () => { - openWindow(); - command(win) -}); diff --git a/src/electron/menu.js b/src/electron/menu.js index 7e2713f..a50d2f5 100644 --- a/src/electron/menu.js +++ b/src/electron/menu.js @@ -1,161 +1,203 @@ -const { Menu, app } = require("electron"); +const { app, Menu } = require('electron') +// import { autoUpdater } from "electron-updater" +// const version = app.getVersion(); -const version = app.getVersion(); +const isMac = process.platform === 'darwin' -let win; -let updateSource = "menu"; // 更新事件触发来源 menu:通过菜单触发 vue:通过vue页面触发 -let template = [ - { - label: "编辑", - submenu: [ - { - label: "剪切", - accelerator: (() => { - if (process.platform === "darwin") { - return "CmdOrCtrl+X"; - } else { - return "Ctrl+X"; - } - })(), - role: "cut", - }, - { - label: "复制", - accelerator: (() => { - if (process.platform === "darwin") { - return "CmdOrCtrl+C"; - } else { - return "Ctrl+C"; - } - })(), - role: "copy", - }, - { - label: "粘贴", - accelerator: (() => { - if (process.platform === "darwin") { - return "CmdOrCtrl+V"; - } else { - return "Ctrl+V"; - } - })(), - role: "paste", - }, - ], - }, - { - label: "工具", - submenu: [ - { - label: "刷新", - accelerator: (() => { - if (process.platform === "darwin") { - return "CmdOrCtrl+R"; - } else { - return "F5"; - } - })(), - click: (item, focusedWindow) => { - if (focusedWindow) { - focusedWindow.reload(); +function createMenu(win) { + let menu = null + const template = [ + ...(isMac ? [{ + label: app.name, + submenu: [ + { role: 'about' }, + { type: 'separator' }, + { role: 'services' }, + { type: 'separator' }, + { type: 'separator' }, + { + label: 'Preferences...', + accelerator: (() => isMac ? 'CmdOrCtrl+,' : 'Ctrl+,')(), + click: () => { + win.webContents.send("changeRouteTo", "/settings") + }, + role: 'preferences' + }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideothers' }, + { role: 'unhide' }, + { type: 'separator' }, + { role: 'quit' } + ] + }] : []), + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'separator' }, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + ...(isMac ? [ + { role: 'delete' }, + { role: 'selectAll' }, + { type: 'separator' }, + { + label: 'Speech', + submenu: [ + { role: 'startspeaking' }, + { role: 'stopspeaking' } + ] } + ] : [ + { role: 'delete' }, + { type: 'separator' }, + { role: 'selectAll' } + ]) + ] + }, + { + label: 'Controls', + submenu: [ + { + label: 'Play', + accelerator: 'Space', + click: () => { + win.webContents.send("play") + }, + }, + { + label: 'Next', + accelerator: 'CmdOrCtrl+Right', + click: () => { + win.webContents.send("next") + }, + }, + { + label: 'Previous', + accelerator: 'CmdOrCtrl+Left', + click: () => { + win.webContents.send("previous") + }, + }, + { + label: 'Increase Volume', + accelerator: 'CmdOrCtrl+Up', + click: () => { + win.webContents.send("increaseVolume") + }, + }, + { + label: 'Decrease Volume', + accelerator: 'CmdOrCtrl+Down', + click: () => { + win.webContents.send("decreaseVolume") + }, + }, + { + label: 'Like', + accelerator: 'CmdOrCtrl+L', + click: () => { + win.webContents.send("like") + }, + }, + { + label: 'Repeat', + accelerator: 'Alt+R', + click: () => { + win.webContents.send("repeat") + }, }, - }, - { - label: "全屏", - accelerator: (() => { - if (process.platform === "darwin") { - return "Ctrl+Command+F"; - } else { - return "F11"; + { + label: 'Shuffle', + accelerator: 'Alt+S', + click: () => { + win.webContents.send("shuffle") + }, + } + ] + }, + { + label: 'Window', + submenu: [ + { role: 'minimize' }, + { role: 'zoom' }, + { role: 'reload' }, + { role: 'forcereload' }, + { role: 'toggledevtools' }, + { type: 'separator' }, + { role: 'togglefullscreen' }, + ...(isMac ? [ + { type: 'separator' }, + { role: 'front' }, + { type: 'separator' }, + { + role: 'window', + id: 'window', + label: 'Yes Play Music', + type: 'checkbox', + checked: true, + click: () => { + const current = menu.getMenuItemById('window') + if (current.checked === false) { + win.hide() + } else { + win.show() + } + }, } - })(), - click: (item, focusedWindow) => { - if (focusedWindow) { - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); + ] : [ + { role: 'close' } + ]) + ] + }, + { + label: 'Help', + submenu: [ + { + label: 'Github', + click: async () => { + const { shell } = require('electron') + await shell.openExternal('https://github.com/qier222/YesPlayMusic') } }, - }, - { - label: "检查", - accelerator: "F12", - click: (item, focusedWindow) => { - if (focusedWindow) { - focusedWindow.toggleDevTools(); + { + label: 'Electron', + click: async () => { + const { shell } = require('electron') + await shell.openExternal('https://electronjs.org') } }, - }, - ], - }, -]; - -function findReopenMenuItem() { - const menu = Menu.getApplicationMenu(); - if (!menu) return; - - let reopenMenuItem; - menu.items.forEach((item) => { - if (item.submenu) { - item.submenu.items.forEach((item) => { - if (item.key === "reopenMenuItem") { - reopenMenuItem = item; - } - }); + ] } - }); - return reopenMenuItem; -} - -// mac 添加退出 -if (process.platform === "darwin") { - const name = app.getName(); - template.unshift({ - label: name + " v" + version, - submenu: [ - { - label: "退出", - accelerator: "Command+Q", - click: () => { - app.quit(); - }, - }, - ], - }); -} -// win 添加更新菜单 -if (process.platform === "win32") { - template.push({ - label: "帮助", - submenu: [ - { - label: `当前版本 v${version}`, - enabled: false, - }, - { - label: "检查更新", - accelerator: "Ctrl+U", - click: (item, focusedWindow) => { - // 执行自动更新检查 - win = focusedWindow; - updateSource = "menu"; - autoUpdater.checkForUpdates(); - }, - }, - ], - }); + ] + // for window + // if (process.platform === "win32") { + // template.push({ + // label: "Help", + // submenu: [ + // { + // label: `Current version v${version}`, + // enabled: false, + // }, + // { + // label: "Check for update", + // accelerator: "Ctrl+U", + // click: (item, focusedWindow) => { + // win = focusedWindow; + // updateSource = "menu"; + // autoUpdater.checkForUpdates(); + // }, + // }, + // ], + // }); + // } + menu = Menu.buildFromTemplate(template) + Menu.setApplicationMenu(menu) } -app.on('ready', () => { - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) -}) +module.exports = createMenu -app.on('browser-window-created', () => { - let reopenMenuItem = findReopenMenuItem() - if (reopenMenuItem) reopenMenuItem.enabled = false -}) -app.on('window-all-closed', () => { - let reopenMenuItem = findReopenMenuItem() - if (reopenMenuItem) reopenMenuItem.enabled = true -}) diff --git a/src/electron/services.js b/src/electron/services.js index 585eae5..f449b07 100644 --- a/src/electron/services.js +++ b/src/electron/services.js @@ -1,5 +1,4 @@ const express = require("express"); -const path = require("path"); const bodyParser = require('body-parser') const cache = require('../../napi/util/apicache').middleware const fileUpload = require('express-fileupload') @@ -50,9 +49,9 @@ Object.keys(routes).forEach(route => { app.use(route, routes[route]) }) -const port = process.env.PORT || 3000 -const host = process.env.HOST || '' +const port = process.env.PORT || 10754 +const host = process.env.HOST || '127.0.0.1' app.server = app.listen(port, host, () => { console.log(`server running @ http://${host ? host : 'localhost'}:${port}`) -}) \ No newline at end of file +}) diff --git a/src/electron/touchbar.js b/src/electron/touchbar.js index 448f823..6633adb 100644 --- a/src/electron/touchbar.js +++ b/src/electron/touchbar.js @@ -1,4 +1,8 @@ -const { app, BrowserWindow, TouchBar } = require('electron') +const { + app, + BrowserWindow, + TouchBar +} = require('electron') const { TouchBarLabel, TouchBarButton, TouchBarSpacer } = TouchBar @@ -88,16 +92,16 @@ const touchBar = new TouchBar({ ] }) -let window - -app.whenReady().then(() => { - window = new BrowserWindow({ - frame: false, - titleBarStyle: 'hiddenInset', - width: 200, - height: 200, - backgroundColor: '#000' - }) - window.loadURL('about:blank') - window.setTouchBar(touchBar) -}) +// let window + +// app.whenReady().then(() => { +// window = new BrowserWindow({ +// frame: false, +// titleBarStyle: 'hiddenInset', +// backgroundColor: '#000' +// }) +// window.loadURL('about:blank') +// window.setTouchBar(touchBar) +// }) + +module.exports = touchBar \ No newline at end of file diff --git a/src/electron/tray.js b/src/electron/tray.js new file mode 100644 index 0000000..13100c5 --- /dev/null +++ b/src/electron/tray.js @@ -0,0 +1,24 @@ +const path = require('path') +const { Menu, Tray } = require('electron') + +let tray = null + +const macIcon = path.join(__static, "img/icons/menu.png") +const winIcon = path.join(__static, "img/icons/icon.ico") + +tray = new Tray(macIcon) + +// Temporary no need for menu. +// const contextMenu = Menu.buildFromTemplate([ +// { label: 'Item1', type: 'radio' }, +// { label: 'Item2', type: 'radio' } +// ]) + +// Make a change to the context menu +// contextMenu.items[1].checked = false + +// tray.setToolTip('Yes Play Music') + +// Call this again for Linux because we modified the context menu +// tray.setContextMenu(contextMenu) +module.exports = tray \ No newline at end of file diff --git a/src/utils/request.js b/src/utils/request.js index 34b74dc..e35bb11 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,7 +1,15 @@ import axios from "axios"; +let baseURL = '' +// Web 和 Electron 跑在不同端口避免同时启动时冲突 +if (process.env.IS_ELECTRON) { + baseURL = process.env.VUE_APP_ELECTRON_API_URL +} else { + baseURL = process.env.VUE_APP_NETEASE_API_URL +} + const service = axios.create({ - baseURL: process.env.VUE_APP_NETEASE_API_URL, + baseURL, withCredentials: true, timeout: 15000, });