mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-12 20:18:41 -09:00
Windows titlebar improvements (#1307)
* Make double-clicking on the titlebar work * We don't actually need this * Draw a Restore icon when the window is Maximized * Update React State when BrowserWindow changes behind our back * Cleanup * Don't show the border if the window is maximized * Fight with the linter
This commit is contained in:
parent
95e98006c0
commit
0ff1cb9584
8 changed files with 71 additions and 37 deletions
|
|
@ -139,7 +139,7 @@ app.on('ready', () => installDevExtensions(isDev).then(() => {
|
|||
backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
|
||||
// we want to go frameless on windows and linux
|
||||
frame: process.platform === 'darwin',
|
||||
transparent: true,
|
||||
transparent: process.platform === 'darwin',
|
||||
icon: resolve(__dirname, 'static/icon.png'),
|
||||
// we only want to show when the prompt is ready for user input
|
||||
// HYPERTERM_DEBUG for backwards compatibility with hyperterm
|
||||
|
|
@ -346,6 +346,13 @@ app.on('ready', () => installDevExtensions(isDev).then(() => {
|
|||
pluginsUnsubscribe();
|
||||
});
|
||||
|
||||
// Same deal as above, grabbing the window titlebar when the window
|
||||
// is maximized on Windows results in unmaximize, without hitting any
|
||||
// app buttons
|
||||
for (const ev of ['maximize', 'unmaximize', 'minimize', 'restore']) {
|
||||
win.on(ev, () => rpc.emit('windowGeometry change'));
|
||||
}
|
||||
|
||||
win.on('closed', () => {
|
||||
if (process.platform !== 'darwin' && windowSet.size === 0) {
|
||||
app.quit();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,19 @@
|
|||
<use stroke="currentColor" stroke-width="2" mask="url(#maximize-window-b)" xlink:href="#maximize-window-a"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="restore-window" viewBox="0 0 10.2 10.2">
|
||||
<title>restore window</title>
|
||||
<defs>
|
||||
<mask id="restore-window-b" width="10.2" height="10.2" x="0" y="0">
|
||||
<use xlink:href="#restore-window-a"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="currentColor"
|
||||
d='M2.1,0v2H0v8.1h8.2v-2h2V0H2.1z M7.2,9.2H1.1V3h6.1V9.2z M9.2,7.1h-1V2H3.1V1h6.1V7.1z' />
|
||||
<use stroke="currentColor" xlink:href="#restore-window-a"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="close-window" viewBox="0 0 10 10">
|
||||
<title>close window</title>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.3 KiB |
|
|
@ -24,6 +24,7 @@ import {
|
|||
UI_MOVE_NEXT_PANE,
|
||||
UI_MOVE_PREV_PANE,
|
||||
UI_SHOW_PREFERENCES,
|
||||
UI_WINDOW_GEOMETRY_CHANGED,
|
||||
UI_WINDOW_MOVE,
|
||||
UI_OPEN_FILE
|
||||
} from '../constants/ui';
|
||||
|
|
@ -88,6 +89,12 @@ export function setFontSmoothing() {
|
|||
};
|
||||
}
|
||||
|
||||
export function windowGeometryUpdated() {
|
||||
return {
|
||||
type: UI_WINDOW_GEOMETRY_CHANGED
|
||||
};
|
||||
}
|
||||
|
||||
// Find all sessions that are below the given
|
||||
// termGroup uid in the hierarchy:
|
||||
const findChildSessions = (termGroups, uid) => {
|
||||
|
|
@ -246,6 +253,17 @@ export function windowMove() {
|
|||
};
|
||||
}
|
||||
|
||||
export function windowGeometryChange() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: UI_WINDOW_MOVE,
|
||||
effect() {
|
||||
dispatch(setFontSmoothing());
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function openFile(path) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export default class Header extends Component {
|
|||
constructor() {
|
||||
super();
|
||||
this.onChangeIntent = this.onChangeIntent.bind(this);
|
||||
this.handleHeaderClick = this.handleHeaderClick.bind(this);
|
||||
this.handleHeaderMouseDown = this.handleHeaderMouseDown.bind(this);
|
||||
this.handleHamburgerMenuClick = this.handleHamburgerMenuClick.bind(this);
|
||||
this.handleMaximizeClick = this.handleMaximizeClick.bind(this);
|
||||
|
|
@ -43,35 +42,6 @@ export default class Header extends Component {
|
|||
this.headerMouseDownWindowY = window.screenY;
|
||||
}
|
||||
|
||||
handleHeaderClick(event) {
|
||||
this.clicks = this.clicks || 0;
|
||||
|
||||
// Reset clicks if mouse moved between clicks
|
||||
if (this.headerClickPointerX !== event.clientX ||
|
||||
this.headerClickPointerY !== event.clientY) {
|
||||
this.clicks = 0;
|
||||
clearTimeout(this.clickTimer);
|
||||
|
||||
this.headerClickPointerX = event.clientX;
|
||||
this.headerClickPointerY = event.clientY;
|
||||
}
|
||||
if (++this.clicks === 2) {
|
||||
if (this.props.maximized) {
|
||||
this.props.unmaximize();
|
||||
} else {
|
||||
this.props.maximize();
|
||||
}
|
||||
this.clicks = 0;
|
||||
clearTimeout(this.clickTimer);
|
||||
} else {
|
||||
// http://www.quirksmode.org/dom/events/click.html
|
||||
// https://en.wikipedia.org/wiki/Double-click
|
||||
this.clickTimer = setTimeout(() => {
|
||||
this.clicks = 0;
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
handleHamburgerMenuClick(event) {
|
||||
let {right: x, bottom: y} = event.currentTarget.getBoundingClientRect();
|
||||
x -= 15; // to compensate padding
|
||||
|
|
@ -135,9 +105,12 @@ export default class Header extends Component {
|
|||
}
|
||||
const {hambMenu, winCtrls} = this.getWindowHeaderConfig();
|
||||
const left = winCtrls === 'left';
|
||||
const maxButtonHref = this.props.maximized ?
|
||||
'./dist/assets/icons.svg#restore-window' :
|
||||
'./dist/assets/icons.svg#maximize-window';
|
||||
|
||||
return (<header
|
||||
className={css('header', isMac && 'headerRounded')}
|
||||
onClick={this.handleHeaderClick}
|
||||
onMouseDown={this.handleHeaderMouseDown}
|
||||
>
|
||||
{
|
||||
|
|
@ -169,7 +142,7 @@ export default class Header extends Component {
|
|||
className={css('shape', left && 'maximizeWindowLeft')}
|
||||
onClick={this.handleMaximizeClick}
|
||||
>
|
||||
<use xlinkHref="./dist/assets/icons.svg#maximize-window"/>
|
||||
<use xlinkHref={maxButtonHref}/>
|
||||
</svg>
|
||||
<svg
|
||||
className={css('shape', 'closeWindow', left && 'closeWindowLeft')}
|
||||
|
|
@ -210,6 +183,7 @@ export default class Header extends Component {
|
|||
left: '1px',
|
||||
right: '1px',
|
||||
WebkitAppRegion: 'drag',
|
||||
WebkitUserSelect: 'none',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export const UI_SHOW_PREFERENCES = 'UI_SHOW_PREFERENCES';
|
|||
export const UI_WINDOW_MOVE = 'UI_WINDOW_MOVE';
|
||||
export const UI_WINDOW_MAXIMIZE = 'UI_WINDOW_MAXIMIZE';
|
||||
export const UI_WINDOW_UNMAXIMIZE = 'UI_WINDOW_UNMAXIMIZE';
|
||||
export const UI_WINDOW_GEOMETRY_CHANGED = 'UI_WINDOW_GEOMETRY_CHANGED';
|
||||
export const UI_OPEN_FILE = 'UI_OPEN_FILE';
|
||||
export const UI_OPEN_HAMBURGER_MENU = 'UI_OPEN_HAMBURGER_MENU';
|
||||
export const UI_WINDOW_MINIMIZE = 'UI_WINDOW_MINIMIZE';
|
||||
|
|
|
|||
|
|
@ -97,10 +97,13 @@ class Hyper extends Component {
|
|||
}
|
||||
|
||||
template(css) {
|
||||
const {isMac, customCSS, borderColor} = this.props;
|
||||
const {isMac, customCSS, borderColor, maximized} = this.props;
|
||||
const borderWidth = isMac ? '' :
|
||||
`${maximized ? '0' : '1'}px`;
|
||||
|
||||
return (<div>
|
||||
<div
|
||||
style={{borderColor}}
|
||||
style={{borderColor, borderWidth}}
|
||||
className={css('main', isMac && 'mainRounded')}
|
||||
>
|
||||
<HeaderContainer/>
|
||||
|
|
@ -139,7 +142,8 @@ const HyperContainer = connect(
|
|||
customCSS: state.ui.css,
|
||||
borderColor: state.ui.borderColor,
|
||||
activeSession: state.sessions.activeUid,
|
||||
backgroundColor: state.ui.backgroundColor
|
||||
backgroundColor: state.ui.backgroundColor,
|
||||
maximized: state.ui.maximized
|
||||
};
|
||||
},
|
||||
dispatch => {
|
||||
|
|
|
|||
|
|
@ -120,6 +120,10 @@ rpc.on('move', () => {
|
|||
store_.dispatch(uiActions.windowMove());
|
||||
});
|
||||
|
||||
rpc.on('windowGeometry change', () => {
|
||||
store_.dispatch(uiActions.windowGeometryUpdated());
|
||||
});
|
||||
|
||||
rpc.on('add notification', ({text, url, dismissable}) => {
|
||||
store_.dispatch(addNotificationMessage(text, url, dismissable));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import {remote} from 'electron';
|
||||
import Immutable from 'seamless-immutable';
|
||||
import {decorateUIReducer} from '../utils/plugins';
|
||||
import {CONFIG_LOAD, CONFIG_RELOAD} from '../constants/config';
|
||||
|
|
@ -6,7 +7,8 @@ import {
|
|||
UI_FONT_SIZE_RESET,
|
||||
UI_FONT_SMOOTHING_SET,
|
||||
UI_WINDOW_MAXIMIZE,
|
||||
UI_WINDOW_UNMAXIMIZE
|
||||
UI_WINDOW_UNMAXIMIZE,
|
||||
UI_WINDOW_GEOMETRY_CHANGED
|
||||
} from '../constants/ui';
|
||||
import {NOTIFICATION_MESSAGE, NOTIFICATION_DISMISS} from '../constants/notifications';
|
||||
import {
|
||||
|
|
@ -86,8 +88,11 @@ const initial = Immutable({
|
|||
showWindowControls: ''
|
||||
});
|
||||
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
|
||||
const reducer = (state = initial, action) => {
|
||||
let state_ = state;
|
||||
let isMax;
|
||||
|
||||
switch (action.type) { // eslint-disable-line default-case
|
||||
case CONFIG_LOAD:
|
||||
|
|
@ -284,6 +289,14 @@ const reducer = (state = initial, action) => {
|
|||
state_ = state.set('maximized', false);
|
||||
break;
|
||||
|
||||
case UI_WINDOW_GEOMETRY_CHANGED:
|
||||
isMax = currentWindow.isMaximized();
|
||||
if (state.maximized !== isMax) {
|
||||
state_ = state.set('maximized', isMax);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NOTIFICATION_DISMISS:
|
||||
state_ = state.merge({
|
||||
notifications: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue