feat(components/Navbar): 自訂和微調各系統的 titlebar (#1343)

* feat: linux custom titlebar
* add settings init
* Update zh-TW.js
* fix: color

Co-authored-by: memorydream <34763046+memorydream@users.noreply.github.com>
master
pan93412 3 years ago committed by GitHub
parent 3e1dc62fa0
commit 3d5d40c476
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

@ -180,7 +180,10 @@ class Background {
minWidth: 1080, minWidth: 1080,
minHeight: 720, minHeight: 720,
titleBarStyle: 'hiddenInset', titleBarStyle: 'hiddenInset',
frame: !isWindows, frame: !(
isWindows ||
(isLinux && this.store.get('settings.linuxEnableCustomTitlebar'))
),
title: 'YesPlayMusic', title: 'YesPlayMusic',
show: false, show: false,
webPreferences: { webPreferences: {

@ -0,0 +1,132 @@
<template>
<div class="linux-titlebar">
<div class="logo">
<img src="img/logos/yesplaymusic-white24x24.png" />
</div>
<div class="title">{{ title }}</div>
<div class="controls">
<div
class="button minimize codicon codicon-chrome-minimize"
@click="windowMinimize"
></div>
<div
class="button max-restore codicon"
:class="{
'codicon-chrome-restore': !isShowMaximized,
'codicon-chrome-maximize': isShowMaximized,
}"
@click="windowMaxRestore"
></div>
<div
class="button close codicon codicon-chrome-close"
@click="windowClose"
></div>
</div>
</div>
</template>
<script>
// icons by https://github.com/microsoft/vscode-codicons
import 'vscode-codicons/dist/codicon.css';
import { mapState } from 'vuex';
const electron =
process.env.IS_ELECTRON === true ? window.require('electron') : null;
const ipcRenderer =
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
export default {
name: 'LinuxTitlebar',
data() {
return {
isShowMaximized: true,
};
},
computed: {
...mapState(['title']),
},
created() {
if (process.env.IS_ELECTRON === true) {
ipcRenderer.on('isMaximized', (_, value) => {
// valuefalse
// valuetrue
this.isShowMaximized = value;
});
}
},
methods: {
windowMinimize() {
ipcRenderer.send('minimize');
},
windowMaxRestore() {
ipcRenderer.send('maximizeOrUnmaximize');
},
windowClose() {
ipcRenderer.send('close');
},
},
};
</script>
<style lang="scss" scoped>
.linux-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;
.logo {
padding: 0 8px;
}
.title {
padding: 8px;
font-size: 12px;
font-family: 'Segoe UI', 'Microsoft YaHei UI', 'Microsoft YaHei', sans-serif;
justify-self: center;
margin: 0 auto;
}
.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: #c42c1b;
color: rgba(255, 255, 255, 0.8);
}
&:active {
background: #f1707a;
color: #000;
}
}
}
}
}
[data-theme='dark'] .linux-titlebar {
--hover: #191919;
--active: #333333;
}
</style>

@ -1,27 +1,8 @@
<template> <template>
<div> <div>
<nav> <nav :class="{ 'has-custom-titlebar': hasCustomTitlebar }">
<div class="win32-titlebar"> <Win32Titlebar v-if="enableWin32Titlebar" />
<div class="title">YesPlayMusic</div> <LinuxTitlebar v-if="enableLinuxTitlebar" />
<div class="controls">
<div
class="button minimize codicon codicon-chrome-minimize"
@click="windowMinimize"
></div>
<div
class="button max-restore codicon"
:class="{
'codicon-chrome-restore': !isWindowMaximized,
'codicon-chrome-maximize': isWindowMaximized,
}"
@click="windowMaxRestore"
></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"
@ -96,17 +77,16 @@ import { isLooseLoggedIn, doLogout } from '@/utils/auth';
// icons by https://github.com/microsoft/vscode-codicons // icons by https://github.com/microsoft/vscode-codicons
import 'vscode-codicons/dist/codicon.css'; import 'vscode-codicons/dist/codicon.css';
import Win32Titlebar from '@/components/Win32Titlebar.vue';
import LinuxTitlebar from '@/components/LinuxTitlebar.vue';
import ContextMenu from '@/components/ContextMenu.vue'; import ContextMenu from '@/components/ContextMenu.vue';
import ButtonIcon from '@/components/ButtonIcon.vue'; import ButtonIcon from '@/components/ButtonIcon.vue';
const electron =
process.env.IS_ELECTRON === true ? window.require('electron') : null;
const ipcRenderer =
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
export default { export default {
name: 'Navbar', name: 'Navbar',
components: { components: {
Win32Titlebar,
LinuxTitlebar,
ButtonIcon, ButtonIcon,
ContextMenu, ContextMenu,
}, },
@ -115,7 +95,8 @@ export default {
inputFocus: false, inputFocus: false,
langs: ['zh-CN', 'zh-TW', 'en', 'tr'], langs: ['zh-CN', 'zh-TW', 'en', 'tr'],
keywords: '', keywords: '',
isWindowMaximized: false, enableWin32Titlebar: false,
enableLinuxTitlebar: false,
}; };
}, },
computed: { computed: {
@ -128,12 +109,18 @@ export default {
? `${this.data?.user?.avatarUrl}?param=512y512` ? `${this.data?.user?.avatarUrl}?param=512y512`
: 'http://s4.music.126.net/style/web2/img/default/default_avatar.jpg?param=60y60'; : 'http://s4.music.126.net/style/web2/img/default/default_avatar.jpg?param=60y60';
}, },
hasCustomTitlebar() {
return this.enableWin32Titlebar || this.enableLinuxTitlebar;
},
}, },
created() { created() {
if (process.env.IS_ELECTRON === true) { if (process.platform === 'win32') {
ipcRenderer.on('isMaximized', (event, value) => { this.enableWin32Titlebar = true;
this.isWindowMaximized = value; } else if (
}); process.platform === 'linux' &&
this.settings.linuxEnableCustomTitlebar
) {
this.enableLinuxTitlebar = true;
} }
}, },
methods: { methods: {
@ -175,15 +162,6 @@ export default {
this.$router.push({ name: 'login' }); this.$router.push({ name: 'login' });
} }
}, },
windowMinimize() {
ipcRenderer.send('minimize');
},
windowMaxRestore() {
ipcRenderer.send('maximizeOrUnmaximize');
},
windowClose() {
ipcRenderer.send('close');
},
}, },
}; };
</script> </script>
@ -221,68 +199,9 @@ nav {
} }
} }
.win32-titlebar { nav.has-custom-titlebar {
display: none;
}
[data-electron-os='win32'] {
nav {
padding-top: 20px; padding-top: 20px;
-webkit-app-region: no-drag; -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 {

@ -0,0 +1,123 @@
<template>
<div class="win32-titlebar">
<div class="title">{{ title }}</div>
<div class="controls">
<div
class="button minimize codicon codicon-chrome-minimize"
@click="windowMinimize"
></div>
<div
class="button max-restore codicon"
:class="{
'codicon-chrome-restore': !isShowMaximized,
'codicon-chrome-maximize': isShowMaximized,
}"
@click="windowMaxRestore"
></div>
<div
class="button close codicon codicon-chrome-close"
@click="windowClose"
></div>
</div>
</div>
</template>
<script>
// icons by https://github.com/microsoft/vscode-codicons
import 'vscode-codicons/dist/codicon.css';
import { mapState } from 'vuex';
const electron =
process.env.IS_ELECTRON === true ? window.require('electron') : null;
const ipcRenderer =
process.env.IS_ELECTRON === true ? electron.ipcRenderer : null;
export default {
name: 'Win32Titlebar',
data() {
return {
isShowMaximized: true,
};
},
computed: {
...mapState(['title']),
},
created() {
if (process.env.IS_ELECTRON === true) {
ipcRenderer.on('isMaximized', (_, value) => {
// valuefalse
// valuetrue
this.isShowMaximized = value;
});
}
},
methods: {
windowMinimize() {
ipcRenderer.send('minimize');
},
windowMaxRestore() {
ipcRenderer.send('maximizeOrUnmaximize');
},
windowClose() {
ipcRenderer.send('close');
},
},
};
</script>
<style lang="scss" scoped>
.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 12px;
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: #c42c1b;
color: rgba(255, 255, 255, 0.8);
}
&:active {
background: #f1707a;
color: #000;
}
}
}
}
}
[data-theme='dark'] .win32-titlebar {
--hover: #191919;
--active: #333333;
}
</style>

@ -158,6 +158,7 @@ export default {
showLibraryDefault: 'Show Library after App Launched', showLibraryDefault: 'Show Library after App Launched',
subTitleDefault: 'Show Alias for Subtitle by default', subTitleDefault: 'Show Alias for Subtitle by default',
enableReversedMode: 'Enable Reversed Mode (Experimental)', enableReversedMode: 'Enable Reversed Mode (Experimental)',
enableCustomTitlebar: 'Enable custom title bar (Need restart)',
lyricsBackground: { lyricsBackground: {
text: 'Show Lyrics Background', text: 'Show Lyrics Background',
off: 'Off', off: 'Off',

@ -150,7 +150,9 @@ export default {
showPlaylistsByAppleMusic: "Apple Music'in Çalma Listelerini Göster", showPlaylistsByAppleMusic: "Apple Music'in Çalma Listelerini Göster",
enableDiscordRichPresence: 'Discord gösterimini aktifleştir', enableDiscordRichPresence: 'Discord gösterimini aktifleştir',
showLibraryDefault: 'Kitaplık Varsayılanını göster', showLibraryDefault: 'Kitaplık Varsayılanını göster',
subTitleDefault: 'Sub title alia default', subTitleDefault: 'Show Alias for Subtitle by default',
enableReversedMode: 'Enable Reversed Mode (Experimental)',
enableCustomTitlebar: 'Enable custom title bar (Need restart)',
lyricsBackground: { lyricsBackground: {
text: 'Şarkı Sözleri Arka Planını Göster', text: 'Şarkı Sözleri Arka Planını Göster',
off: 'kapalı', off: 'kapalı',

@ -159,6 +159,7 @@ export default {
showLibraryDefault: '启动后显示音乐库', showLibraryDefault: '启动后显示音乐库',
subTitleDefault: '副标题使用别名', subTitleDefault: '副标题使用别名',
enableReversedMode: '启用倒序播放功能 (实验性功能)', enableReversedMode: '启用倒序播放功能 (实验性功能)',
enableCustomTitlebar: '启用自定义标题栏 (重启后生效)',
lyricsBackground: { lyricsBackground: {
text: '显示歌词背景', text: '显示歌词背景',
off: '关闭', off: '关闭',

@ -156,6 +156,7 @@ export default {
showLibraryDefault: '啟動後顯示音樂庫', showLibraryDefault: '啟動後顯示音樂庫',
subTitleDefault: '副標題使用別名', subTitleDefault: '副標題使用別名',
enableReversedMode: '啟用倒序播放功能 (實驗性功能)', enableReversedMode: '啟用倒序播放功能 (實驗性功能)',
enableCustomTitlebar: '啟用自訂標題列(重新啟動後生效)',
lyricsBackground: { lyricsBackground: {
text: '顯示歌詞背景', text: '顯示歌詞背景',
off: '關閉', off: '關閉',

@ -28,6 +28,7 @@ let localStorage = {
enableGlobalShortcut: true, enableGlobalShortcut: true,
showLibraryDefault: false, showLibraryDefault: false,
subTitleDefault: false, subTitleDefault: false,
linuxEnableCustomTitlebar: false,
enabledPlaylistCategories, enabledPlaylistCategories,
proxyConfig: { proxyConfig: {
protocol: 'noProxy', protocol: 'noProxy',

@ -72,4 +72,7 @@ export default {
enableScrolling(state, status = null) { enableScrolling(state, status = null) {
state.enableScrolling = status ? status : !state.enableScrolling; state.enableScrolling = status ? status : !state.enableScrolling;
}, },
updateTitle(state, title) {
state.title = title;
},
}; };

@ -13,6 +13,7 @@ updateApp();
export default { export default {
showLyrics: false, showLyrics: false,
enableScrolling: true, enableScrolling: true,
title: 'YesPlayMusic',
liked: { liked: {
songs: [], songs: [],
songsWithDetails: [], // 只有前12首 songsWithDetails: [], // 只有前12首

@ -34,6 +34,7 @@ function setTitle(track) {
if (isCreateTray) { if (isCreateTray) {
ipcRenderer.send('updateTrayTooltip', document.title); ipcRenderer.send('updateTrayTooltip', document.title);
} }
store.commit('updateTitle', document.title);
} }
function setTrayLikeState(isLiked) { function setTrayLikeState(isLiked) {

@ -331,6 +331,23 @@
</div> </div>
</div> </div>
<div v-if="isElectron && isLinux" class="item">
<div class="left">
<div class="title"> {{ $t('settings.enableCustomTitlebar') }} </div>
</div>
<div class="right">
<div class="toggle">
<input
id="enable-custom-titlebar"
v-model="enableCustomTitlebar"
type="checkbox"
name="enable-custom-titlebar"
/>
<label for="enable-custom-titlebar"></label>
</div>
</div>
</div>
<div v-if="isElectron" class="item"> <div v-if="isElectron" class="item">
<div class="left"> <div class="left">
<div class="title"> {{ $t('settings.showLibraryDefault') }}</div> <div class="title"> {{ $t('settings.showLibraryDefault') }}</div>
@ -592,6 +609,9 @@ export default {
isMac() { isMac() {
return /macintosh|mac os x/i.test(navigator.userAgent); return /macintosh|mac os x/i.test(navigator.userAgent);
}, },
isLinux() {
return process.platform === 'linux';
},
version() { version() {
return pkg.version; return pkg.version;
}, },
@ -928,6 +948,17 @@ export default {
}); });
}, },
}, },
enableCustomTitlebar: {
get() {
return this.settings.linuxEnableCustomTitlebar;
},
set(value) {
this.$store.commit('updateSettings', {
key: 'linuxEnableCustomTitlebar',
value,
});
},
},
isLastfmConnected() { isLastfmConnected() {
return this.lastfm.key !== undefined; return this.lastfm.key !== undefined;
}, },

Loading…
Cancel
Save