hyper/lib/components/terms.js
Martin Ek a7595c1a45 Split Panes (#693)
* npm: add .npmrc with save-exact=true

* split panes: create initial implementation

This allows users to split their Hyperterm terms into
multiple nested splits, both vertical and horizontal.

Fixes #56

* split panes: suport closing tabs and individual panes

* split panes: ensure new splits are placed at the correct index

New split panes should be placed after the currently active
pane, not at the end like they were previously.

* split panes: add explicit dependency to uuid

* split panes: implement split pane cycling

This adds menu buttons for moving back and forward between
open split panes in the currect terminal tab.
Doesn't add a hotkey yet, needs some bikeshedding.

* split panes: move activeSessionUid to its own object

It made little sense to have so many objects with `activeSessionUid`
set to `null` when it only mattered on the top level.
Now it's an object mapping term-group `uid` to `sessionUid` instead.

* split panes: make sure closing the last split pane exits the app

* split panes: fix a crash after closing specific panes

Sometimes the terminal would crash when a specific
split pane was closed, because the `activeSessions`
mapping wasn't updated correctly.

* split panes: fix a bug that caused initial session sizing to be wrong

* fix all our focus / blur issues in one fell swoop :O (famous last words)

* get rid of react warning

* hterm: make sure not to lose focus when VT listens on clicks

* term: restore onactive callback

* add missing `return` to override (just in case)

* split pane: new split pane implementation

* goodbye react-split-pane

* added term group resizing action and reducer

* terms: supply border color so that we can use it for splits

* term-group: add resizing hook

* term-groups: add resizing constant

* remove split pane css side-effect

* split panes: pass existing hterm instances to Term

* split panes: add keybindings for split pane cycling

* split panes: remove unused action

* split panes: remove unused styling

* split-pane: remove `console.log`

* split-pane: remove `console.log`

* split panes: rebalance sizes on insert/removal

* split panes: pass existing hterm instances to Term

* split panes: add keybindings for split pane cycling

* split panes: remove unused action

* split panes: remove unused styling

* split panes: rebalance sizes on insert/removal

* split panes: set a minimum size for resizing

* split-pane: fix vertical splits

* css :|

* package: bump electron

* split panes: attach onFocus listener to webviews

* 1.4.1 and 1.4.2 are broken. they have the following regression:
- open google.com on the main window
- open a new tab
- come back to previous tab. webview is gone :|

* split panes: handle PTY exits

* split panes: add linux friendly keybindings
2016-10-03 19:00:50 -07:00

149 lines
3.5 KiB
JavaScript

import React from 'react';
import Component from '../component';
import {decorate, getTermGroupProps} from '../utils/plugins';
import TermGroup_ from './term-group';
const TermGroup = decorate(TermGroup_, 'TermGroup');
export default class Terms extends Component {
constructor(props, context) {
super(props, context);
this.terms = {};
this.bound = new WeakMap();
this.onRef = this.onRef.bind(this);
props.ref_(this);
}
componentWillReceiveProps(next) {
const {write} = next;
if (write && this.props.write !== write) {
this.getTermByUid(write.uid).write(write.data);
}
}
shouldComponentUpdate(nextProps) {
for (const i in nextProps) {
if (i === 'write') {
continue;
}
if (this.props[i] !== nextProps[i]) {
return true;
}
}
for (const i in this.props) {
if (i === 'write') {
continue;
}
if (this.props[i] !== nextProps[i]) {
return true;
}
}
return false;
}
onRef(uid, term) {
if (term) {
this.terms[uid] = term;
} else if (!this.props.sessions[uid]) {
delete this.terms[uid];
}
}
getTermByUid(uid) {
return this.terms[uid];
}
getActiveTerm() {
return this.getTermByUid(this.props.activeSession);
}
getLastTermIndex() {
return this.props.sessions.length - 1;
}
onTerminal(uid, term) {
this.terms[uid] = term;
}
componentWillUnmount() {
this.props.ref_(null);
}
template(css) {
return (<div
className={css('terms')}
>
{ this.props.customChildrenBefore }
{
this.props.termGroups.map(termGroup => {
const {uid} = termGroup;
const isActive = uid === this.props.activeRootGroup;
const props = getTermGroupProps(uid, this.props, {
termGroup,
terms: this.terms,
sessions: this.props.sessions,
customCSS: this.props.customCSS,
fontSize: this.props.fontSize,
borderColor: this.props.borderColor,
cursorColor: this.props.cursorColor,
cursorShape: this.props.cursorShape,
fontFamily: this.props.fontFamily,
fontSmoothing: this.props.fontSmoothing,
foregroundColor: this.props.foregroundColor,
backgroundColor: this.props.backgroundColor,
padding: this.props.padding,
colors: this.props.colors,
bell: this.props.bell,
bellSoundURL: this.props.bellSoundURL,
copyOnSelect: this.props.copyOnSelect,
modifierKeys: this.props.modifierKeys,
onActive: this.props.onActive,
onResize: this.props.onResize,
onTitle: this.props.onTitle,
onData: this.props.onData,
onURLAbort: this.props.onURLAbort
});
return (
<div
key={`d${uid}`}
className={css('termGroup', isActive && 'termGroupActive')}
>
<TermGroup
key={uid}
ref_={this.onRef}
{...props}
/>
</div>
);
})
}
{ this.props.customChildren }
</div>);
}
styles() {
return {
terms: {
position: 'absolute',
marginTop: '34px',
top: 0,
right: 0,
left: 0,
bottom: 0,
color: '#fff'
},
termGroup: {
display: 'none',
width: '100%',
height: '100%'
},
termGroupActive: {
display: 'block'
}
};
}
}