feat: electron tray & menu & touchbar supported

This commit is contained in:
kunkka 2020-10-29 18:46:54 +08:00
parent 249ad01cf1
commit 9db57acb71
11 changed files with 314 additions and 314 deletions

View File

@ -1,2 +1,4 @@
VUE_APP_NETEASE_API_URL=http://localhost:3000
VUE_APP_ENABLE_SENTRY=false
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

BIN
public/img/icons/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -25,6 +25,8 @@
import Navbar from "./components/Navbar.vue";
import Player from "./components/Player.vue";
import GlobalEvents from "vue-global-events";
const electron = window.require('electron')
const ipcRenderer = electron.ipcRenderer
export default {
name: "App",
@ -33,6 +35,45 @@ export default {
Player,
GlobalEvents,
},
created(){
// listens to the main process 'changeRouteTo' event and changes the route from
// inside this Vue instance, according to what path the main process requires.
// responds to Menu click() events at the main process and changes the route accordingly.
ipcRenderer.on('changeRouteTo', (event, path) => {
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();

View File

@ -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();

View File

@ -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)
});

View File

@ -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'
},
},
{
label: "全屏",
accelerator: (() => {
if (process.platform === "darwin") {
return "Ctrl+Command+F";
} else {
return "F11";
}
})(),
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
{ 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: "检查",
accelerator: "F12",
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.toggleDevTools();
}
{
label: 'Next',
accelerator: 'CmdOrCtrl+Right',
click: () => {
win.webContents.send("next")
},
},
},
],
},
];
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;
{
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: '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()
}
},
}
] : [
{ role: 'close' }
])
]
},
{
label: 'Help',
submenu: [
{
label: 'Github',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://github.com/qier222/YesPlayMusic')
}
},
{
label: 'Electron',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
},
]
}
});
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();
},
},
],
});
}
app.on('ready', () => {
const menu = Menu.buildFromTemplate(template)
]
// 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)
})
}
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
})

View File

@ -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}`)
})
})

View File

@ -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
// 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)
})
// app.whenReady().then(() => {
// window = new BrowserWindow({
// frame: false,
// titleBarStyle: 'hiddenInset',
// backgroundColor: '#000'
// })
// window.loadURL('about:blank')
// window.setTouchBar(touchBar)
// })
module.exports = touchBar

24
src/electron/tray.js Normal file
View File

@ -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

View File

@ -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,
});