为 Windows 平台添加沉浸式标题栏 (#277)

master
Mr. Will 4 years ago committed by GitHub
parent c0c4597cd8
commit 51fc57efe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -57,6 +57,7 @@
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"svg-sprite-loader": "^5.0.0", "svg-sprite-loader": "^5.0.0",
"tunnel": "^0.0.6", "tunnel": "^0.0.6",
"vscode-codicons": "^0.0.14",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-analytics": "^5.22.1", "vue-analytics": "^5.22.1",
"vue-electron": "^1.0.6", "vue-electron": "^1.0.6",

@ -88,15 +88,20 @@ class Background {
createWindow() { createWindow() {
console.log("creating app window"); console.log("creating app window");
// Only for Windows, a special title bar for it
const withoutFrame = process.platform == "win32";
this.window = new BrowserWindow({ this.window = new BrowserWindow({
width: this.store.get("window.width") | 1440, width: this.store.get("window.width") | 1440,
height: this.store.get("window.height") | 840, height: this.store.get("window.height") | 840,
minWidth: 1080, minWidth: 360,
minHeight: 720, minHeight: 240,
titleBarStyle: "hiddenInset", titleBarStyle: "hiddenInset",
frame: !withoutFrame,
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
nodeIntegration: true, nodeIntegration: true,
enableRemoteModule: true,
}, },
}); });

@ -1,5 +1,26 @@
<template> <template>
<nav :class="{ 'search-box-open': isSearchBoxOpen }"> <nav :class="{ 'search-box-open': isSearchBoxOpen }">
<div class="win32-titlebar">
<div class="title">YesPlayMusic</div>
<div class="controls">
<div
class="button minimize codicon codicon-chrome-minimize"
@click="windowMinimize"
></div>
<div
class="button max-restore codicon"
@click="windowMaxRestore"
:class="{
'codicon-chrome-restore': windowIsMaximized,
'codicon-chrome-maximize': !windowIsMaximized,
}"
></div>
<div
class="button close codicon codicon-chrome-close"
@click="windowClose"
></div>
</div>
</div>
<div class="navigation-buttons"> <div class="navigation-buttons">
<button-icon @click.native="go('back')" <button-icon @click.native="go('back')"
><svg-icon icon-class="arrow-left" ><svg-icon icon-class="arrow-left"
@ -55,6 +76,15 @@
<script> <script>
import ButtonIcon from "@/components/ButtonIcon.vue"; import ButtonIcon from "@/components/ButtonIcon.vue";
import { mapState } from "vuex"; import { mapState } from "vuex";
const platformIsWin32 = window.require
? window.require("os").platform() == "win32"
? true
: false
: false;
const win = platformIsWin32
? window.require("electron").remote.getCurrentWindow()
: null;
export default { export default {
name: "Navbar", name: "Navbar",
@ -67,6 +97,7 @@ export default {
langs: ["zh-CN", "en"], langs: ["zh-CN", "en"],
keywords: "", keywords: "",
isSearchBoxOpen: false, isSearchBoxOpen: false,
windowIsMaximized: win ? win.isMaximized() : true,
}; };
}, },
computed: { computed: {
@ -93,6 +124,21 @@ export default {
toggleSearchBox() { toggleSearchBox() {
this.isSearchBoxOpen = !this.isSearchBoxOpen; this.isSearchBoxOpen = !this.isSearchBoxOpen;
}, },
windowMinimize() {
win.minimize();
},
windowMaxRestore() {
if (win.isMaximized()) {
win.restore();
this.windowIsMaximized = false;
} else {
win.maximize();
this.windowIsMaximized = true;
}
},
windowClose() {
win.close();
},
}, },
}; };
</script> </script>
@ -133,6 +179,70 @@ nav {
} }
} }
.win32-titlebar {
display: none;
}
[data-electron-platform-win32="yes"] {
nav {
padding-top: 20px;
-webkit-app-region: no-drag;
}
.win32-titlebar {
color: var(--color-text);
position: fixed;
left: 0;
top: 0;
right: 0;
-webkit-app-region: drag;
display: flex;
align-items: center;
--hover: #e6e6e6;
--active: #cccccc;
.title {
padding: 8px;
font-size: 12px;
font-family: "Segoe UI", "Microsoft YaHei UI", "Microsoft YaHei",
sans-serif;
}
.controls {
height: 32px;
margin-left: auto;
justify-content: flex-end;
display: flex;
.button {
height: 100%;
width: 46px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
-webkit-app-region: no-drag;
&:hover {
background: var(--hover);
}
&:active {
background: var(--active);
}
&.close {
&:hover {
background: rgba(232, 17, 35, 0.9);
}
&:active {
background: #f1707a;
color: #000;
}
}
}
}
}
&[data-theme="dark"] .win32-titlebar {
--hover: #191919;
--active: #333333;
}
}
.navigation-buttons { .navigation-buttons {
flex: 1; flex: 1;
display: flex; display: flex;
@ -265,6 +375,7 @@ nav {
} }
.search-button { .search-button {
display: none; display: none;
-webkit-app-region: no-drag;
} }
@media (max-width: 600px) { @media (max-width: 600px) {
.search-button { .search-button {
@ -316,6 +427,13 @@ nav {
} }
} }
[data-electron-platform-win32="yes"] {
.search-box {
// Add more 20px to top for title bar
top: calc(56px + 20px);
}
}
nav.search-box-open .container { nav.search-box-open .container {
opacity: 1; opacity: 1;
height: 32px; height: 32px;

@ -61,4 +61,12 @@ export function initIpcMain(win, store) {
ipcMain.on("settings", (event, options) => { ipcMain.on("settings", (event, options) => {
store.set("settings", options); store.set("settings", options);
}); });
ipcMain.on("max-restore", () => {
if (win.isMaximized()) {
win.restore();
} else {
win.maximize();
}
});
} }

@ -2,6 +2,10 @@ export function ipcRenderer(vueInstance) {
const self = vueInstance; const self = vueInstance;
// 添加专有的类名 // 添加专有的类名
document.body.setAttribute("data-electron", "yes"); document.body.setAttribute("data-electron", "yes");
document.body.setAttribute(
"data-electron-platform-win32",
window.require("os").platform() == "win32" ? "yes" : "no"
);
// ipc message channel // ipc message channel
const electron = window.require("electron"); const electron = window.require("electron");
const ipcRenderer = electron.ipcRenderer; const ipcRenderer = electron.ipcRenderer;

@ -13,6 +13,10 @@ import * as Sentry from "@sentry/browser";
import { Vue as VueIntegration } from "@sentry/integrations"; import { Vue as VueIntegration } from "@sentry/integrations";
import { Integrations } from "@sentry/tracing"; import { Integrations } from "@sentry/tracing";
// import icons for win32 title bar
// icons by https://github.com/microsoft/vscode-codicons
import "vscode-codicons/dist/codicon.css";
Vue.use(VueAnalytics, { Vue.use(VueAnalytics, {
id: "UA-180189423-1", id: "UA-180189423-1",
router, router,

@ -11176,6 +11176,11 @@ vm-browserify@^1.0.1:
resolved "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz?cache=0&sync_timestamp=1589682787766&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvm-browserify%2Fdownload%2Fvm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" resolved "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz?cache=0&sync_timestamp=1589682787766&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvm-browserify%2Fdownload%2Fvm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha1-eGQcSIuObKkadfUR56OzKobl3aA= integrity sha1-eGQcSIuObKkadfUR56OzKobl3aA=
vscode-codicons@^0.0.14:
version "0.0.14"
resolved "https://registry.yarnpkg.com/vscode-codicons/-/vscode-codicons-0.0.14.tgz#e0d05418e2e195564ff6f6a2199d70415911c18f"
integrity sha512-6CEH5KT9ct5WMw7n5dlX7rB8ya4CUI2FSq1Wk36XaW+c5RglFtAanUV0T+gvZVVFhl/WxfjTvFHq06Hz9c1SLA==
vue-analytics@^5.22.1: vue-analytics@^5.22.1:
version "5.22.1" version "5.22.1"
resolved "https://registry.yarnpkg.com/vue-analytics/-/vue-analytics-5.22.1.tgz#9d6b32da56daee1b9dfb23a267b50349a03f710f" resolved "https://registry.yarnpkg.com/vue-analytics/-/vue-analytics-5.22.1.tgz#9d6b32da56daee1b9dfb23a267b50349a03f710f"

Loading…
Cancel
Save