From 68748309cb71ff70252c240d46e5210977294b26 Mon Sep 17 00:00:00 2001 From: Radoslav Vitanov Date: Thu, 11 Aug 2016 23:44:38 +0300 Subject: [PATCH] Custom window action bar to address #3126 #3036 --- .../linux/titlebutton-close-hover.png | Bin 0 -> 606 bytes .../linux/titlebutton-close.png | Bin 0 -> 472 bytes .../linux/titlebutton-maximize-hover.png | Bin 0 -> 589 bytes .../linux/titlebutton-maximize.png | Bin 0 -> 464 bytes .../linux/titlebutton-minimize-hover.png | Bin 0 -> 485 bytes .../linux/titlebutton-minimize.png | Bin 0 -> 462 bytes .../windows/10/close-btn-white.svg | 63 +++++++++ img/window-actions/windows/10/close-btn.svg | 62 +++++++++ .../windows/10/max-btn-fullscreen.svg | 79 +++++++++++ img/window-actions/windows/10/max-btn.svg | 74 ++++++++++ img/window-actions/windows/10/min-btn.svg | 74 ++++++++++ js/components/main.js | 8 +- js/components/windowActionBar.js | 71 ++++++++++ js/stores/appStore.js | 3 +- less/navigationBar.less | 131 ++++++++++++++++++ less/variables.less | 2 +- test/components/windowTest.js | 52 ++++++- test/lib/selectors.js | 3 + 18 files changed, 614 insertions(+), 8 deletions(-) create mode 100755 img/window-actions/linux/titlebutton-close-hover.png create mode 100755 img/window-actions/linux/titlebutton-close.png create mode 100755 img/window-actions/linux/titlebutton-maximize-hover.png create mode 100755 img/window-actions/linux/titlebutton-maximize.png create mode 100755 img/window-actions/linux/titlebutton-minimize-hover.png create mode 100755 img/window-actions/linux/titlebutton-minimize.png create mode 100644 img/window-actions/windows/10/close-btn-white.svg create mode 100644 img/window-actions/windows/10/close-btn.svg create mode 100644 img/window-actions/windows/10/max-btn-fullscreen.svg create mode 100644 img/window-actions/windows/10/max-btn.svg create mode 100644 img/window-actions/windows/10/min-btn.svg create mode 100644 js/components/windowActionBar.js diff --git a/img/window-actions/linux/titlebutton-close-hover.png b/img/window-actions/linux/titlebutton-close-hover.png new file mode 100755 index 0000000000000000000000000000000000000000..73405fc18816276d32184e67e1431502a7f2910f GIT binary patch literal 606 zcmV-k0-^nhP)kjesU*2*9B7*&f5M4EmZZt*JVapx)0jQl#`JLS zs@GWpFB%7$Sl({4d;$N8xoKnaZKUm8^*X73y(txCb|6Tltm(LLy#Vqb14fpOJVU|d za+r-L?&H{~k_;0ZbE1$10M`$2b~C{J#9V^zCnnCW2XMRq0KjbtxrkwoXuiORUF+d2=yVLP{M}f>OSP(+M6!IFSnM`?=R;9=zju!v`P-_aFmH6MNrzx$_ z81Y&m)>ogNY2=4GfEP(+I5p9`)1mGtAOuU=Rq^T9FbH(=!=87dFj}hj=~jnYQ&1Io zREj**nu7DMI!2ZaU~yB=cH60by(tMYJ19z}tmV3JykIHB`g0hL^DwLaGJx7DmE{i` z|6u^*`}pntW%qQ&a%yW+ES_*%LXMEXEoT{1$C{Xj`6P)NlO?{0f{HW;-|Uh#-coq^g{S03exBVoVg9 zBrZ$!-#?uSAq1iv*9b2N`LoqN9M2cX4!cVo0PF}0=mkrl;{zb0sU~Sor(%4uWRqm;tP}ALHMu`#fw{mN9 z>8&lh@$mZc3>|<0i%wMPkuPuw++ESI$!m(Sy0aX*=$(`}m0VZ$Oy^{T?>`9@JuGjYNAx{GORyaFN+P~L50 zL)b*WXU`v;woXTj=uD90Oj?=WlQI0&#`G?M36enN;~qq*n03d_DVgI~Bc5q=-NnKW zKr$3|j}O3Z7ZSBa1OT8Y3ORPmBsXZL9KU(334U8Y6WBPw){gLER+?cn5{RB9pTRVR z*Pr*-z(hp)VbJX!Z2KBNMb$AP$;}n0WAM?=1prvsa{pM zJl~KNHRF#w^nTodNVUINDL{CuuI_!iJF^G2zBIspZ~T7^Fh-}-K6Z|7FA~dL z>4qp2&qb-2U2C~Pf98yw6X)pm0!Vok0MzfQ9Ff`69Lt$BLu<=L1kIWEQ1kr9ukMHT bAg=ub9qq@0D=Wz700000NkvXXu0mjfxtIrj literal 0 HcmV?d00001 diff --git a/img/window-actions/linux/titlebutton-maximize.png b/img/window-actions/linux/titlebutton-maximize.png new file mode 100755 index 0000000000000000000000000000000000000000..9fb27510706cf376531a3b1c43aa6bf85234c3e4 GIT binary patch literal 464 zcmV;>0WbcEP)00!@ZkX!LeU8LC)Tj>7{(?ZLUtU&cn`gU zJ41R1+jtBYHEOHCqksiLp068iP19KXRma8;ZTH6H%fGc8$*9tn)}Vha+aAY>5NkOADC>rKzhJK*-oU@VrUP z1%0*Ut`5E2p_mROEq@`8Bt_u~A;TY2nsQklXY)Cn!nGaf zZ%eJ#wkF(clr81*#g{^{$e*;n*sR}ow*G!YIPg75Q4&c2Ajxu?P`*pjg4p=|(iMKJv;Cud<#9Yufbv;Nb+J>=ZyZwNuEqCQ?jHYWl|4Oc9!!nI#z2mHZBKIgY z@&$P$DRP>S)SvTg!`ND;(Y(92o`9pQ0>JbkTO^e43Zkf`D6Ieh3&XkZ`D3=*59%Ff bedzrJAHb4B3YWa^00000NkvXXu0mjf6d%r) literal 0 HcmV?d00001 diff --git a/img/window-actions/linux/titlebutton-minimize.png b/img/window-actions/linux/titlebutton-minimize.png new file mode 100755 index 0000000000000000000000000000000000000000..6ced316d8202e43a1a87366761f6bf8231aa0ea2 GIT binary patch literal 462 zcmV;<0WtoGP)eFP2u?6cZ$_K%a>2N@T^F@E-66 zO1dMBPXsFi#sDq!3dNq*ja8xvmVY&i`6e^TFmRNaCDj<<2E1_9CWO7s0U)Jxoo3717LQxwy&V#wF3rDz(5h{y9F%QDzHP7-Wfv(@$NZ#W$*hIUh*NOA>ECG-Pu z-rtN?*Rz?KCDkgj{#w;_?xgz4X1&_`&gu>s;0B7Ol|ldjimLKRxIXfN*#3RfsUWf- zh@yey@}gc^6_}6)LY|-NfC0#nex!?YAx+Z-?&8?avaCF!6yhkJV9q4Nt>YwRq3^mo zx<8Dt*$J1y#x+Af_!otq58l=_A9}(1=jEQ2wp>*$MN@f1_Ro60>+Za*Yu-P4F91is z3IH&Dl5`|oUl2uuC8PlWK+|*{NAZO252J@(ur7K(0m_JreGPJO#Q*>R07*qoM6N<$ Ef)tv=^#A|> literal 0 HcmV?d00001 diff --git a/img/window-actions/windows/10/close-btn-white.svg b/img/window-actions/windows/10/close-btn-white.svg new file mode 100644 index 00000000000..72e603cbd55 --- /dev/null +++ b/img/window-actions/windows/10/close-btn-white.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/img/window-actions/windows/10/close-btn.svg b/img/window-actions/windows/10/close-btn.svg new file mode 100644 index 00000000000..8dc9e16250f --- /dev/null +++ b/img/window-actions/windows/10/close-btn.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/img/window-actions/windows/10/max-btn-fullscreen.svg b/img/window-actions/windows/10/max-btn-fullscreen.svg new file mode 100644 index 00000000000..c534778f540 --- /dev/null +++ b/img/window-actions/windows/10/max-btn-fullscreen.svg @@ -0,0 +1,79 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/img/window-actions/windows/10/max-btn.svg b/img/window-actions/windows/10/max-btn.svg new file mode 100644 index 00000000000..c6beccbfddb --- /dev/null +++ b/img/window-actions/windows/10/max-btn.svg @@ -0,0 +1,74 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/img/window-actions/windows/10/min-btn.svg b/img/window-actions/windows/10/min-btn.svg new file mode 100644 index 00000000000..22f67f36884 --- /dev/null +++ b/img/window-actions/windows/10/min-btn.svg @@ -0,0 +1,74 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/js/components/main.js b/js/components/main.js index 6eea1f4f078..bfd6f4178d9 100644 --- a/js/components/main.js +++ b/js/components/main.js @@ -39,6 +39,7 @@ const ContextMenu = require('./contextMenu') const PopupWindow = require('./popupWindow') const NoScriptInfo = require('./noScriptInfo') const LongPressButton = require('./longPressButton') +const WindowActionBar = require('./windowActionBar') // Constants const config = require('../constants/config') @@ -584,11 +585,7 @@ class Main extends ImmutableComponent { if (!e.target.className.includes('navigatorWrapper')) { return } - if (currentWindow.isMaximized()) { - currentWindow.maximize() - } else { - currentWindow.unmaximize() - } + return (!currentWindow.isMaximized()) ? currentWindow.maximize() : currentWindow.unmaximize() } onMouseDown (e) { @@ -894,6 +891,7 @@ class Main extends ImmutableComponent { braveShieldsDown: !braverySettings.shieldsUp })} onClick={this.onBraveMenu} /> + {process.platform === 'darwin' ? null : } diff --git a/js/components/windowActionBar.js b/js/components/windowActionBar.js new file mode 100644 index 00000000000..3a763a06986 --- /dev/null +++ b/js/components/windowActionBar.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const os = require('os') +const React = require('react') +const ImmutableComponent = require('./immutableComponent') +const currentWindow = require('../../app/renderer/currentWindow') + +class WindowActionBar extends ImmutableComponent { + constructor () { + super() + this.onDoubleClick = this.onDoubleClick.bind(this) + this.onMinimizeClick = this.onMinimizeClick.bind(this) + this.onMaximizeClick = this.onMaximizeClick.bind(this) + this.onCloseClick = this.onCloseClick.bind(this) + this.osClass = this.getPlatformCssClass() + } + + get buttonClass () { + return (this.props.windowMaximized ? 'fullscreen' : '') + } + + getPlatformCssClass () { + switch (os.platform()) { + case 'linux': + return 'linux' + case 'win32': + if (/10.0./.test(os.release())) { + return 'win-10' + } else if (/6.1./.test(os.release())) { + return 'win-7' + } else { + return 'win' + } + default: + return 'hidden' + } + } + + onMinimizeClick (e) { + currentWindow.minimize() + } + + onMaximizeClick (e) { + return (!currentWindow.isMaximized()) ? currentWindow.maximize() : currentWindow.unmaximize() + } + + onCloseClick (e) { + currentWindow.close() + } + + onDoubleClick (e) { + if (!e.target.className.includes('navigatorWrapper')) { + return + } + this.onMaximizeClick(e) + } + + render () { + return
+
+ + + +
+
+ } +} + +module.exports = WindowActionBar diff --git a/js/stores/appStore.js b/js/stores/appStore.js index 84852d32a3b..2f91a14cde4 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -136,7 +136,8 @@ const createWindow = (browserOpts, defaults, frameOpts, windowState) => { titleBarStyle: 'hidden-inset', autoHideMenuBar: autoHideMenuBarSetting, title: appConfig.name, - webPreferences: defaults.webPreferences + webPreferences: defaults.webPreferences, + frame: (process.platform === 'darwin') } if (process.platform === 'linux') { diff --git a/less/navigationBar.less b/less/navigationBar.less index a892788a257..48e1932c635 100644 --- a/less/navigationBar.less +++ b/less/navigationBar.less @@ -29,6 +29,137 @@ - (@navbarBraveButtonWidth + 2 * @navbarButtonSpacing); } +// Window Header +.window-header { + -webkit-user-select: none; + -webkit-app-region: drag; + height: 18px; + padding: 3px 0; + margin: 0px; + display: flex; + line-height: 30px; + + .hidden { + display: none; + } + + .title-bar-btns { + -webkit-app-region: no-drag; + } + + .win-7, .win-10 { + margin: -13px 3px 0 0; + + button.win-action-btn { + outline: 0; + cursor: pointer; + width: 45px; + height: 23px; + color: #b1acac; + background-size: 12px 12px!important; + background-position: center!important; + border: 1px solid #e1e1e1; + display: inline-block; + padding: 12px; + + &.fullscreen { + height:21px; + padding:6px; + } + + &.close-btn { + background: url('../img/window-actions/windows/10/close-btn.svg') no-repeat; + &:hover { + background: url('../img/window-actions/windows/10/close-btn-white.svg') no-repeat; + background-color: #e5182c; + } + &:active { + background: url('../img/window-actions/windows/10/close-btn-white.svg') no-repeat; + background-color: #ef717c; + } + } + + &.min-btn { + border-right: 0px; + border-bottom-left-radius: 5px; + background: url('../img/window-actions/windows/10/min-btn.svg') no-repeat; + &:hover { + background-color: #e5e5e5; + } + &:active { + background-color: #cacacb; + } + } + + &.max-btn { + background: url('../img/window-actions/windows/10/max-btn.svg') no-repeat; + border-right: 0px; + &:hover { + background-color: #e5e5e5; + } + &:active { + background-color: #cacacb; + } + &.fullscreen { + background: url('../img/window-actions/windows/10/max-btn-fullscreen.svg') no-repeat; + &:hover { + background-color: #e5e5e5; + } + &:active { + background-color: #cacacb; + } + } + } + } + + } + + .linux { + button { + outline: 0; + cursor: pointer; + width: 14px; + height: 14px; + color: transparent; + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + border-width: 0; + border-radius: 50%; + display: inline-block; + padding: 0px; + margin-right: 15px; + } + + min-btn:backdrop, .max-btn:backdrop, .close-btn:backdrop { + opacity: 1; + } + + .close-btn { + background: url('../img/window-actions/linux/titlebutton-close.png') 0 0 / contain no-repeat; + } + + .min-btn { + background: url('../img/window-actions/linux/titlebutton-minimize.png') 0 0 / contain no-repeat; + } + + .max-btn { + background: url('../img/window-actions/linux/titlebutton-maximize.png') 0 0 / contain no-repeat; + } + + .close-btn:hover { + background: url('../img/window-actions/linux/titlebutton-close-hover.png') 0 0 / contain no-repeat; + } + + .min-btn:hover { + background: url('../img/window-actions/linux/titlebutton-minimize-hover.png') 0 0 / contain no-repeat; + } + + .max-btn:hover { + background: url('../img/window-actions/linux/titlebutton-maximize-hover.png') 0 0 / contain no-repeat; + } + } +} // Here I grouped the rules to keep the layout of the top bar consistent. // The tricky part is to keep the title in `#navigator` centered when the diff --git a/less/variables.less b/less/variables.less index 21c188f6e1f..5fa1b548227 100644 --- a/less/variables.less +++ b/less/variables.less @@ -69,7 +69,7 @@ @bookmarksFileIconSize: 13px; @bookmarksFolderIconSize: 15px; -@navbarButtonSpacing: 4px; +@navbarButtonSpacing: 14px; @navbarButtonWidth: 35px; @navbarBraveButtonWidth: 23px; @navbarBraveButtonMarginLeft: 80px; diff --git a/test/components/windowTest.js b/test/components/windowTest.js index fb255f5fec2..596322e3355 100644 --- a/test/components/windowTest.js +++ b/test/components/windowTest.js @@ -1,7 +1,7 @@ /* global describe, it, before */ const Brave = require('../lib/brave') -const {activeWebview} = require('../lib/selectors') +const {activeWebview, minimizeButton, maximizeButton, closeButton} = require('../lib/selectors') describe('application window', function () { describe('application launch', function () { @@ -146,6 +146,56 @@ describe('application window', function () { }) }) + if (process.platform !== 'darwin') { + describe('window top action buttons', function () { + Brave.beforeAll(this) + + before(function * () { + yield this.app.client + .waitUntilWindowLoaded() + .waitForUrl(Brave.newTabUrl) + .windowByIndex(0) + .resizeWindow(600, 700) + .waitUntilWindowLoaded() + }) + + it('should be maximized when maximize button is clicked', function * () { + yield this.app.client + .click(maximizeButton) + .windowByIndex(0) + .getWindowWidth().should.eventually.be.getPrimaryDisplayWidth() + .getWindowHeight().should.eventually.be.getPrimaryDisplayHeight() + }) + + it('should be minimized when minimize button is clicked', function * () { + yield this.app.client + .click(minimizeButton) + .waitUntil(function () { + return this.windowByIndex(0).isWindowMinimized() + }) + }) + + it('should close the new window when close button is clicked', function * () { + yield this.app.client + .windowByIndex(0) + .newWindowAction() + .waitUntil(function () { + return this.getWindowCount().then((count) => { + return count === 2 + }) + }) + .windowByIndex(1) + .waitUntilWindowLoaded() + .click(closeButton) + .waitUntil(function () { + return this.getWindowCount().then((count) => { + return count === 1 + }) + }) + }) + }) + } + describe('windw.open with click', function () { describe('with features', function () { Brave.beforeAll(this) diff --git a/test/lib/selectors.js b/test/lib/selectors.js index 4ff78271d1f..7a748caa0ad 100644 --- a/test/lib/selectors.js +++ b/test/lib/selectors.js @@ -1,4 +1,7 @@ module.exports = { + minimizeButton: '.min-btn', + maximizeButton: '.max-btn', + closeButton: '.close-btn', urlInput: '#urlInput', activeWebview: '.frameWrapper.isActive webview', activeTab: '.tab.active',