mirror of https://github.com/keeweb/keeweb.git
fix #689: custom title bar on Windows
This commit is contained in:
parent
7e2823903b
commit
0f4434a361
|
@ -312,6 +312,18 @@ const Launcher = {
|
||||||
},
|
},
|
||||||
setGlobalShortcuts(appSettings) {
|
setGlobalShortcuts(appSettings) {
|
||||||
this.remoteApp().setGlobalShortcuts(appSettings);
|
this.remoteApp().setGlobalShortcuts(appSettings);
|
||||||
|
},
|
||||||
|
minimizeMainWindow() {
|
||||||
|
this.getMainWindow().minimize();
|
||||||
|
},
|
||||||
|
maximizeMainWindow() {
|
||||||
|
this.getMainWindow().maximize();
|
||||||
|
},
|
||||||
|
restoreMainWindow() {
|
||||||
|
this.getMainWindow().restore();
|
||||||
|
},
|
||||||
|
mainWindowMaximized() {
|
||||||
|
return this.getMainWindow().isMaximized();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -319,6 +331,8 @@ Events.on('launcher-exit-request', () => {
|
||||||
setTimeout(() => Launcher.exit(), 0);
|
setTimeout(() => Launcher.exit(), 0);
|
||||||
});
|
});
|
||||||
Events.on('launcher-minimize', () => setTimeout(() => Events.emit('app-minimized'), 0));
|
Events.on('launcher-minimize', () => setTimeout(() => Events.emit('app-minimized'), 0));
|
||||||
|
Events.on('launcher-maximize', () => setTimeout(() => Events.emit('app-maximized'), 0));
|
||||||
|
Events.on('launcher-unmaximize', () => setTimeout(() => Events.emit('app-unmaximized'), 0));
|
||||||
Events.on('launcher-started-minimized', () => setTimeout(() => Launcher.minimizeApp(), 0));
|
Events.on('launcher-started-minimized', () => setTimeout(() => Launcher.minimizeApp(), 0));
|
||||||
Events.on('start-profile', (data) => StartProfiler.reportAppProfile(data));
|
Events.on('start-profile', (data) => StartProfiler.reportAppProfile(data));
|
||||||
Events.on('log', (e) => new Logger(e.category || 'remote-app')[e.method || 'info'](e.message));
|
Events.on('log', (e) => new Logger(e.category || 'remote-app')[e.method || 'info'](e.message));
|
||||||
|
|
|
@ -18,7 +18,10 @@ const Features = {
|
||||||
isLocal: location.origin.indexOf('localhost') >= 0,
|
isLocal: location.origin.indexOf('localhost') >= 0,
|
||||||
|
|
||||||
supportsTitleBarStyles() {
|
supportsTitleBarStyles() {
|
||||||
return this.isMac;
|
return isDesktop && (this.isMac || this.isWindows);
|
||||||
|
},
|
||||||
|
renderCustomTitleBar() {
|
||||||
|
return isDesktop && this.isWindows;
|
||||||
},
|
},
|
||||||
hasUnicodeFlags() {
|
hasUnicodeFlags() {
|
||||||
return this.isMac;
|
return this.isMac;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { OpenView } from 'views/open-view';
|
||||||
import { SettingsView } from 'views/settings/settings-view';
|
import { SettingsView } from 'views/settings/settings-view';
|
||||||
import { TagView } from 'views/tag-view';
|
import { TagView } from 'views/tag-view';
|
||||||
import { ImportCsvView } from 'views/import-csv-view';
|
import { ImportCsvView } from 'views/import-csv-view';
|
||||||
|
import { TitlebarButtonsView } from 'views/titlebar-buttons-view';
|
||||||
import template from 'templates/app.hbs';
|
import template from 'templates/app.hbs';
|
||||||
|
|
||||||
class AppView extends View {
|
class AppView extends View {
|
||||||
|
@ -45,6 +46,9 @@ class AppView extends View {
|
||||||
|
|
||||||
constructor(model) {
|
constructor(model) {
|
||||||
super(model);
|
super(model);
|
||||||
|
|
||||||
|
this.titlebarStyle = this.model.settings.titlebarStyle;
|
||||||
|
|
||||||
this.views.menu = new MenuView(this.model.menu, { ownParent: true });
|
this.views.menu = new MenuView(this.model.menu, { ownParent: true });
|
||||||
this.views.menuDrag = new DragView('x', { parent: '.app__menu-drag' });
|
this.views.menuDrag = new DragView('x', { parent: '.app__menu-drag' });
|
||||||
this.views.footer = new FooterView(this.model, { ownParent: true });
|
this.views.footer = new FooterView(this.model, { ownParent: true });
|
||||||
|
@ -54,12 +58,13 @@ class AppView extends View {
|
||||||
this.views.list.dragView = this.views.listDrag;
|
this.views.list.dragView = this.views.listDrag;
|
||||||
this.views.details = new DetailsView(undefined, { ownParent: true });
|
this.views.details = new DetailsView(undefined, { ownParent: true });
|
||||||
this.views.details.appModel = this.model;
|
this.views.details.appModel = this.model;
|
||||||
|
if (this.titlebarStyle !== 'default' && Features.renderCustomTitleBar()) {
|
||||||
|
this.views.titlebarButtons = new TitlebarButtonsView(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
this.views.menu.listenDrag(this.views.menuDrag);
|
this.views.menu.listenDrag(this.views.menuDrag);
|
||||||
this.views.list.listenDrag(this.views.listDrag);
|
this.views.list.listenDrag(this.views.listDrag);
|
||||||
|
|
||||||
this.titlebarStyle = this.model.settings.titlebarStyle;
|
|
||||||
|
|
||||||
this.listenTo(this.model.settings, 'change:theme', this.setTheme);
|
this.listenTo(this.model.settings, 'change:theme', this.setTheme);
|
||||||
this.listenTo(this.model.settings, 'change:locale', this.setLocale);
|
this.listenTo(this.model.settings, 'change:locale', this.setLocale);
|
||||||
this.listenTo(this.model.settings, 'change:fontSize', this.setFontSize);
|
this.listenTo(this.model.settings, 'change:fontSize', this.setFontSize);
|
||||||
|
@ -120,6 +125,9 @@ class AppView extends View {
|
||||||
}
|
}
|
||||||
if (this.titlebarStyle !== 'default') {
|
if (this.titlebarStyle !== 'default') {
|
||||||
document.body.classList.add('titlebar-' + this.titlebarStyle);
|
document.body.classList.add('titlebar-' + this.titlebarStyle);
|
||||||
|
if (Features.renderCustomTitleBar()) {
|
||||||
|
document.body.classList.add('titlebar-custom');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Features.isMobile) {
|
if (Features.isMobile) {
|
||||||
document.body.classList.add('mobile');
|
document.body.classList.add('mobile');
|
||||||
|
@ -129,7 +137,8 @@ class AppView extends View {
|
||||||
render() {
|
render() {
|
||||||
super.render({
|
super.render({
|
||||||
beta: this.model.isBeta,
|
beta: this.model.isBeta,
|
||||||
titlebarStyle: this.titlebarStyle
|
titlebarStyle: this.titlebarStyle,
|
||||||
|
customTitlebar: Features.renderCustomTitleBar()
|
||||||
});
|
});
|
||||||
this.panelEl = this.$el.find('.app__panel:first');
|
this.panelEl = this.$el.find('.app__panel:first');
|
||||||
this.views.listWrap.render();
|
this.views.listWrap.render();
|
||||||
|
@ -139,6 +148,7 @@ class AppView extends View {
|
||||||
this.views.list.render();
|
this.views.list.render();
|
||||||
this.views.listDrag.render();
|
this.views.listDrag.render();
|
||||||
this.views.details.render();
|
this.views.details.render();
|
||||||
|
this.views.titlebarButtons?.render();
|
||||||
this.showLastOpenFile();
|
this.showLastOpenFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ class SettingsGeneralView extends View {
|
||||||
directAutotype: AppSettingsModel.directAutotype,
|
directAutotype: AppSettingsModel.directAutotype,
|
||||||
fieldLabelDblClickAutoType: AppSettingsModel.fieldLabelDblClickAutoType,
|
fieldLabelDblClickAutoType: AppSettingsModel.fieldLabelDblClickAutoType,
|
||||||
useLegacyAutoType: AppSettingsModel.useLegacyAutoType,
|
useLegacyAutoType: AppSettingsModel.useLegacyAutoType,
|
||||||
supportsTitleBarStyles: Launcher && Features.supportsTitleBarStyles(),
|
supportsTitleBarStyles: Features.supportsTitleBarStyles(),
|
||||||
titlebarStyle: AppSettingsModel.titlebarStyle,
|
titlebarStyle: AppSettingsModel.titlebarStyle,
|
||||||
storageProviders,
|
storageProviders,
|
||||||
showReloadApp: Features.isStandalone,
|
showReloadApp: Features.isStandalone,
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { View } from 'framework/views/view';
|
||||||
|
import { Events } from 'framework/events';
|
||||||
|
import { Launcher } from 'comp/launcher';
|
||||||
|
import template from 'templates/titlebar-buttons.hbs';
|
||||||
|
|
||||||
|
class TitlebarButtonsView extends View {
|
||||||
|
parent = '.app__titlebar';
|
||||||
|
|
||||||
|
template = template;
|
||||||
|
|
||||||
|
events = {
|
||||||
|
'click .titlebar-buttons-minimize': 'clickMinimize',
|
||||||
|
'click .titlebar-buttons-maximize': 'clickMaximize',
|
||||||
|
'click .titlebar-buttons-restore': 'clickRestore',
|
||||||
|
'click .titlebar-buttons-close': 'clickClose'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.maximized = Launcher.mainWindowMaximized();
|
||||||
|
|
||||||
|
this.listenTo(Events, 'app-maximized', this.appMaximized);
|
||||||
|
this.listenTo(Events, 'app-unmaximized', this.appUnmaximized);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
super.render({
|
||||||
|
maximized: this.maximized
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clickMinimize() {
|
||||||
|
Launcher.minimizeMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
clickMaximize() {
|
||||||
|
Launcher.maximizeMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
clickRestore() {
|
||||||
|
Launcher.restoreMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
clickClose() {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
appMaximized() {
|
||||||
|
this.maximized = true;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
appUnmaximized() {
|
||||||
|
this.maximized = false;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TitlebarButtonsView };
|
|
@ -28,6 +28,18 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__titlebar {
|
||||||
|
.titlebar-custom & {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__menu {
|
&__menu {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -123,6 +135,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.titlebar-custom & {
|
||||||
|
margin-top: $custom-titlebar-height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__panel {
|
&__panel {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.titlebar-buttons {
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
> .fa {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 4px 16px;
|
||||||
|
height: $custom-titlebar-height;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&:hover {
|
||||||
|
background: var(--titlebar-button-background-color);
|
||||||
|
}
|
||||||
|
&.fa-titlebar-close {
|
||||||
|
&:hover {
|
||||||
|
background: $titlebar-close-button-background-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -196,3 +196,7 @@ $fa-var-at: next-fa-glyph();
|
||||||
$fa-var-usb-token: next-fa-glyph();
|
$fa-var-usb-token: next-fa-glyph();
|
||||||
$fa-var-bell: next-fa-glyph();
|
$fa-var-bell: next-fa-glyph();
|
||||||
$fa-var-fingerprint: next-fa-glyph();
|
$fa-var-fingerprint: next-fa-glyph();
|
||||||
|
$fa-var-titlebar-close: next-fa-glyph();
|
||||||
|
$fa-var-titlebar-maximize: next-fa-glyph();
|
||||||
|
$fa-var-titlebar-minimize: next-fa-glyph();
|
||||||
|
$fa-var-titlebar-restore: next-fa-glyph();
|
||||||
|
|
|
@ -69,7 +69,8 @@
|
||||||
selectable-on-secondary-item-color:
|
selectable-on-secondary-item-color:
|
||||||
mix(map-get($t, medium-color), map-get($t, background-color), 14%),
|
mix(map-get($t, medium-color), map-get($t, background-color), 14%),
|
||||||
clickable-on-secondary-color:
|
clickable-on-secondary-color:
|
||||||
mix(map-get($t, medium-color), map-get($t, background-color), 75%)
|
mix(map-get($t, medium-color), map-get($t, background-color), 75%),
|
||||||
|
titlebar-button-background-color: rgba(map-get($t, text-color), 0.085)
|
||||||
),
|
),
|
||||||
$t
|
$t
|
||||||
);
|
);
|
||||||
|
|
|
@ -84,3 +84,7 @@ $z-index-modal: 100000;
|
||||||
// Screen sizes
|
// Screen sizes
|
||||||
$tablet-width: 736px;
|
$tablet-width: 736px;
|
||||||
$mobile-width: 620px;
|
$mobile-width: 620px;
|
||||||
|
|
||||||
|
// Title bar and window buttons
|
||||||
|
$custom-titlebar-height: 32px;
|
||||||
|
$titlebar-close-button-background-color: #d71525;
|
||||||
|
|
|
@ -38,3 +38,4 @@ $fa-font-path: '~font-awesome/fonts';
|
||||||
@import 'areas/open';
|
@import 'areas/open';
|
||||||
@import 'areas/settings';
|
@import 'areas/settings';
|
||||||
@import 'areas/import-csv';
|
@import 'areas/import-csv';
|
||||||
|
@import 'areas/titlebar-buttons';
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<div class="app">
|
<div class="app">
|
||||||
{{#if beta}}<div class="app__beta"><i class="fa fa-exclamation-triangle"></i> {{res 'appBeta'}}</div>{{/if}}
|
{{#if beta}}<div class="app__beta"><i class="fa fa-exclamation-triangle"></i> {{res 'appBeta'}}</div>{{/if}}
|
||||||
{{#ifeq titlebarStyle 'hidden'}}<div class="app__titlebar-drag"></div>{{/ifeq}}
|
{{#if customTitlebar}}
|
||||||
|
<div class="app__titlebar"></div>
|
||||||
|
{{else}}
|
||||||
|
{{#ifeq titlebarStyle 'hidden'}}<div class="app__titlebar-drag"></div>{{/ifeq}}
|
||||||
|
{{/if}}
|
||||||
<div class="app__body">
|
<div class="app__body">
|
||||||
<div class="app__menu"></div>
|
<div class="app__menu"></div>
|
||||||
<div class="app__menu-drag"></div>
|
<div class="app__menu-drag"></div>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="titlebar-buttons">
|
||||||
|
<i class="fa fa-titlebar-minimize titlebar-buttons-minimize"></i>
|
||||||
|
{{#if maximized}}
|
||||||
|
<i class="fa fa-titlebar-restore titlebar-buttons-restore"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="fa fa-titlebar-maximize titlebar-buttons-maximize"></i>
|
||||||
|
{{/if}}
|
||||||
|
<i class="fa fa-titlebar-close titlebar-buttons-close"></i>
|
||||||
|
</div>
|
|
@ -263,6 +263,9 @@ function createMainWindow() {
|
||||||
theme = selectDarkOrLightTheme(theme);
|
theme = selectDarkOrLightTheme(theme);
|
||||||
}
|
}
|
||||||
const bgColor = themeBgColors[theme] || defaultBgColor;
|
const bgColor = themeBgColors[theme] || defaultBgColor;
|
||||||
|
const frameless =
|
||||||
|
process.platform === 'win32' &&
|
||||||
|
['hidden', 'hidden-inset'].includes(appSettings.titlebarStyle);
|
||||||
const windowOptions = {
|
const windowOptions = {
|
||||||
show: false,
|
show: false,
|
||||||
width: 1000,
|
width: 1000,
|
||||||
|
@ -270,6 +273,7 @@ function createMainWindow() {
|
||||||
minWidth: 700,
|
minWidth: 700,
|
||||||
minHeight: 400,
|
minHeight: 400,
|
||||||
titleBarStyle: appSettings.titlebarStyle,
|
titleBarStyle: appSettings.titlebarStyle,
|
||||||
|
frame: !frameless,
|
||||||
backgroundColor: bgColor,
|
backgroundColor: bgColor,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
|
@ -324,9 +328,11 @@ function createMainWindow() {
|
||||||
});
|
});
|
||||||
mainWindow.on('maximize', () => {
|
mainWindow.on('maximize', () => {
|
||||||
mainWindowMaximized = true;
|
mainWindowMaximized = true;
|
||||||
|
emitRemoteEvent('launcher-maximize');
|
||||||
});
|
});
|
||||||
mainWindow.on('unmaximize', () => {
|
mainWindow.on('unmaximize', () => {
|
||||||
mainWindowMaximized = false;
|
mainWindowMaximized = false;
|
||||||
|
emitRemoteEvent('launcher-unmaximize');
|
||||||
});
|
});
|
||||||
mainWindow.on('leave-full-screen', () => {
|
mainWindow.on('leave-full-screen', () => {
|
||||||
emitRemoteEvent('leave-full-screen');
|
emitRemoteEvent('leave-full-screen');
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="512" height="512"><path d="M445 500l-285 -285l55 -55l285 285l285 -285l55 55l-285 285l285 285l-55 55l-285 -285l-285 285l-55 -55z"/></svg>
|
After Width: | Height: | Size: 208 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="512" height="512"><path d="M188 813v-625h625v625h-625zM750 250h-500v500h500v-500z"/></svg>
|
After Width: | Height: | Size: 162 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="512" height="512"><path d="M875 500v-62h-687v62h687z"/></svg>
|
After Width: | Height: | Size: 133 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="512" height="512"><path d="M188 688v-563h562v563h-562zM688 188h-438v437h438v-437zM313 688h62v62h438v-437h-63v-63h125v563h-562v-125z"/></svg>
|
After Width: | Height: | Size: 212 B |
|
@ -2,11 +2,12 @@ Release notes
|
||||||
-------------
|
-------------
|
||||||
##### v1.17.0 (TBD)
|
##### v1.17.0 (TBD)
|
||||||
`+` opening files with Touch ID on macOS
|
`+` opening files with Touch ID on macOS
|
||||||
`+` password quality warnings
|
`+` password quality warnings
|
||||||
`+` "Have I Been Pwned" service integration (opt-in)
|
`+` "Have I Been Pwned" service integration (opt-in)
|
||||||
`+` automatically switching between dark and light theme
|
`+` automatically switching between dark and light theme
|
||||||
|
`+` custom title bar on Windows
|
||||||
`*` new updater capable to upgrade major versions
|
`*` new updater capable to upgrade major versions
|
||||||
`+` clear searchbox button
|
`+` clear searchbox button
|
||||||
`+` more options for auto-lock timeout
|
`+` more options for auto-lock timeout
|
||||||
`+` favicon download improvements
|
`+` favicon download improvements
|
||||||
`+` auto-type field selection dropdown improvements
|
`+` auto-type field selection dropdown improvements
|
||||||
|
|
Loading…
Reference in New Issue