mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Simplify terminal split model, only allow one dimension
This commit is contained in:
parent
1633d0959a
commit
1a59decdf1
|
@ -182,13 +182,6 @@ export interface ITerminalService {
|
|||
setWorkspaceShellAllowed(isAllowed: boolean): void;
|
||||
}
|
||||
|
||||
export const enum Direction {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Up = 2,
|
||||
Down = 3
|
||||
}
|
||||
|
||||
export interface ITerminalTab {
|
||||
activeInstance: ITerminalInstance;
|
||||
terminalInstances: ITerminalInstance[];
|
||||
|
@ -196,7 +189,8 @@ export interface ITerminalTab {
|
|||
onDisposed: Event<ITerminalTab>;
|
||||
onInstancesChanged: Event<void>;
|
||||
|
||||
focusDirection(direction: Direction): void;
|
||||
focusPreviousPane(): void;
|
||||
focusNextPane(): void;
|
||||
setActiveInstanceByIndex(index: number): void;
|
||||
attachToElement(element: HTMLElement): void;
|
||||
setVisible(visible: boolean): void;
|
||||
|
|
|
@ -18,7 +18,7 @@ import { getTerminalDefaultShellUnixLike, getTerminalDefaultShellWindows } from
|
|||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KillTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitVerticalTerminalAction, FocusTerminalLeftAction, FocusTerminalRightAction, FocusTerminalUpAction, FocusTerminalDownAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
|
||||
import { KillTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitVerticalTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
|
@ -274,8 +274,8 @@ configurationRegistry.registerConfiguration({
|
|||
TogglePanelAction.ID,
|
||||
'workbench.action.quickOpenView',
|
||||
SplitVerticalTerminalAction.ID,
|
||||
FocusTerminalLeftAction.ID,
|
||||
FocusTerminalRightAction.ID
|
||||
FocusPreviousPaneTerminalAction.ID,
|
||||
FocusNextPaneTerminalAction.ID
|
||||
].sort()
|
||||
},
|
||||
'terminal.integrated.env.osx': {
|
||||
|
@ -416,22 +416,14 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitVerticalTer
|
|||
primary: null,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_D }
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split Vertically', category);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalLeftAction, FocusTerminalLeftAction.ID, FocusTerminalLeftAction.LABEL, {
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, {
|
||||
primary: KeyMod.Alt | KeyCode.LeftArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.LeftArrow }
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Terminal To Left', category);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalRightAction, FocusTerminalRightAction.ID, FocusTerminalRightAction.LABEL, {
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, {
|
||||
primary: KeyMod.Alt | KeyCode.RightArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.RightArrow }
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Terminal To Right', category);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalDownAction, FocusTerminalDownAction.ID, FocusTerminalDownAction.LABEL, {
|
||||
primary: KeyMod.Alt | KeyCode.DownArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow }
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Terminal Below', category);
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalUpAction, FocusTerminalUpAction.ID, FocusTerminalUpAction.LABEL, {
|
||||
primary: KeyMod.Alt | KeyCode.UpArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow }
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Terminal Above', category);
|
||||
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category);
|
||||
|
||||
terminalCommands.setup();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as os from 'os';
|
|||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { EndOfLinePreference } from 'vs/editor/common/model';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
|
@ -298,7 +298,7 @@ export class CreateNewInActiveWorkspaceTerminalAction extends Action {
|
|||
|
||||
export class SplitVerticalTerminalAction extends Action {
|
||||
public static readonly ID = 'workbench.action.terminal.splitVertical';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.splitVertical', "Split the terminal vertically");
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.splitVertical', "Split Terminal Vertically");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
|
@ -317,10 +317,12 @@ export class SplitVerticalTerminalAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class BaseFocusDirectionTerminalAction extends Action {
|
||||
export class FocusPreviousPaneTerminalAction extends Action {
|
||||
public static readonly ID = 'workbench.action.terminal.focusPreviousPane';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusPreviousPane', "Focus Previous Pane");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
private _direction: Direction,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService
|
||||
) {
|
||||
super(id, label);
|
||||
|
@ -331,44 +333,29 @@ export abstract class BaseFocusDirectionTerminalAction extends Action {
|
|||
if (!tab) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
tab.focusDirection(this._direction);
|
||||
tab.focusPreviousPane();
|
||||
return this._terminalService.showPanel(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusTerminalLeftAction extends BaseFocusDirectionTerminalAction {
|
||||
public static readonly ID = 'workbench.action.terminal.focusTerminalLeft';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusTerminalLeft', "Focus terminal to the left");
|
||||
export class FocusNextPaneTerminalAction extends Action {
|
||||
public static readonly ID = 'workbench.action.terminal.focusNextPane';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusNextPane', "Focus Next Pane");
|
||||
|
||||
constructor(id: string, label: string, @ITerminalService terminalService: ITerminalService) {
|
||||
super(id, label, Direction.Left, terminalService);
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusTerminalRightAction extends BaseFocusDirectionTerminalAction {
|
||||
public static readonly ID = 'workbench.action.terminal.focusTerminalRight';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusTerminalRight', "Focus terminal to the right");
|
||||
|
||||
constructor(id: string, label: string, @ITerminalService terminalService: ITerminalService) {
|
||||
super(id, label, Direction.Right, terminalService);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusTerminalUpAction extends BaseFocusDirectionTerminalAction {
|
||||
public static readonly ID = 'workbench.action.terminal.focusTerminalUp';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusTerminalUp', "Focus terminal above");
|
||||
|
||||
constructor(id: string, label: string, @ITerminalService terminalService: ITerminalService) {
|
||||
super(id, label, Direction.Up, terminalService);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusTerminalDownAction extends BaseFocusDirectionTerminalAction {
|
||||
public static readonly ID = 'workbench.action.terminal.focusTerminalDown';
|
||||
public static readonly LABEL = nls.localize('workbench.action.terminal.focusTerminalDown', "Focus terminal below");
|
||||
|
||||
constructor(id: string, label: string, @ITerminalService terminalService: ITerminalService) {
|
||||
super(id, label, Direction.Down, terminalService);
|
||||
public run(event?: any): TPromise<any> {
|
||||
const tab = this._terminalService.getActiveTab();
|
||||
if (!tab) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
tab.focusNextPane();
|
||||
return this._terminalService.showPanel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -237,8 +237,5 @@ export class TerminalService extends AbstractTerminalService implements ITermina
|
|||
this._configHelper.panelContainer = panelContainer;
|
||||
this._terminalContainer = terminalContainer;
|
||||
this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer));
|
||||
this._terminalInstances.forEach(terminalInstance => {
|
||||
terminalInstance.attachToElement(this._terminalContainer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITerminalInstance, IShellLaunchConfig, ITerminalTab, Direction } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { ITerminalInstance, IShellLaunchConfig, ITerminalTab } from 'vs/workbench/parts/terminal/common/terminal';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
|
||||
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -12,71 +12,33 @@ import Event, { Emitter, anyEvent } from 'vs/base/common/event';
|
|||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { SplitView, Orientation, IView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
class SplitPane implements IView {
|
||||
public minimumSize: number = 100;
|
||||
class SplitPaneContainer implements IView {
|
||||
public minimumSize: number = 40;
|
||||
public maximumSize: number = Number.MAX_VALUE;
|
||||
|
||||
public instance: ITerminalInstance;
|
||||
public orientation: Orientation | undefined;
|
||||
|
||||
protected _size: number;
|
||||
|
||||
private _splitView: SplitView | undefined;
|
||||
// TODO: Swap height and width when rotation is implemented
|
||||
private _height: number;
|
||||
private _width: number;
|
||||
private _splitView: SplitView;
|
||||
private _children: SplitPane[] = [];
|
||||
private _container: HTMLElement;
|
||||
private _isContainerSet: boolean = false;
|
||||
private _onDidChange: Event<number | undefined> = Event.None;
|
||||
|
||||
public get children(): SplitPane[] { return this._children; }
|
||||
private _onDidChange: Event<number | undefined> = Event.None;
|
||||
public get onDidChange(): Event<number | undefined> { return this._onDidChange; }
|
||||
|
||||
constructor(
|
||||
private _parent?: SplitPane,
|
||||
public orthogonalSize?: number,
|
||||
private _needsReattach?: boolean
|
||||
private _container: HTMLElement,
|
||||
public orientation: Orientation
|
||||
) {
|
||||
}
|
||||
|
||||
protected branch(container: HTMLElement, orientation: Orientation, instance: ITerminalInstance): void {
|
||||
this.orientation = orientation;
|
||||
container.removeChild((<any>this.instance)._wrapperElement);
|
||||
|
||||
this._splitView = new SplitView(container, { orientation });
|
||||
this._width = this._container.offsetWidth;
|
||||
this._height = this._container.offsetHeight;
|
||||
this._splitView = new SplitView(this._container, { orientation: this.orientation });
|
||||
this._splitView.onDidSashReset(() => this._resetSize());
|
||||
this.layout(this._size);
|
||||
this.orthogonalLayout(this.orthogonalSize);
|
||||
|
||||
this.addChild(this.orthogonalSize / 2, this._size, this.instance, 0, this._isContainerSet);
|
||||
this.addChild(this.orthogonalSize / 2, this._size, instance);
|
||||
|
||||
// Instance is now owned by the first child
|
||||
this.instance = null;
|
||||
this.render(this._container);
|
||||
this._splitView.layout(this._width);
|
||||
}
|
||||
|
||||
public split(instance: ITerminalInstance): void {
|
||||
if (this._parent && this._parent.orientation === this.orientation) {
|
||||
// TODO: Splitting sizes can be a bit weird when not splitting the right-most pane
|
||||
// If we kept proportions when adding the view to the splitview it would be alright
|
||||
const index = this._parent._children.indexOf(this);
|
||||
this._parent.addChild(this._size / 2, this.orthogonalSize, instance, index + 1);
|
||||
} else {
|
||||
this.branch(this._container, this.orientation, instance);
|
||||
}
|
||||
}
|
||||
|
||||
private addChild(size: number, orthogonalSize: number, instance: ITerminalInstance, index?: number, needsReattach?: boolean): void {
|
||||
const child = new SplitPane(this, orthogonalSize, needsReattach);
|
||||
child.orientation = this.orientation;
|
||||
child.instance = instance;
|
||||
this._splitView.addView(child, size, index);
|
||||
|
||||
if (typeof index === 'number') {
|
||||
this._children.splice(index, 0, child);
|
||||
} else {
|
||||
this._children.push(child);
|
||||
}
|
||||
|
||||
this._onDidChange = anyEvent(...this._children.map(c => c.onDidChange));
|
||||
public split(instance: ITerminalInstance, index: number = this._children.length): void {
|
||||
this._addChild(this._width / (this._children.length + 1), instance, index);
|
||||
}
|
||||
|
||||
private _resetSize(): void {
|
||||
|
@ -90,31 +52,93 @@ class SplitPane implements IView {
|
|||
}
|
||||
}
|
||||
|
||||
public remove(): void {
|
||||
if (!this._parent) {
|
||||
private _addChild(size: number, instance: ITerminalInstance, index: number): void {
|
||||
const child = new SplitPane(this._height);
|
||||
child.orientation = this.orientation;
|
||||
child.instance = instance;
|
||||
this._splitView.addView(child, size, index);
|
||||
|
||||
if (typeof index === 'number') {
|
||||
this._children.splice(index, 0, child);
|
||||
} else {
|
||||
this._children.push(child);
|
||||
}
|
||||
|
||||
this._resetSize();
|
||||
|
||||
this._onDidChange = anyEvent(...this._children.map(c => c.onDidChange));
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this._container = container;
|
||||
}
|
||||
|
||||
public remove(instance: ITerminalInstance): void {
|
||||
let index = null;
|
||||
for (let i = 0; i < this._children.length; i++) {
|
||||
if (this._children[i].instance === instance) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (index !== null) {
|
||||
this._children.splice(index, 1);
|
||||
this._splitView.removeView(index);
|
||||
this._resetSize();
|
||||
}
|
||||
}
|
||||
|
||||
public layoutBox(width: number, height: number): void {
|
||||
if (this.orientation === Orientation.HORIZONTAL) {
|
||||
this.layout(height);
|
||||
this.orthogonalLayout(width);
|
||||
} else {
|
||||
this.layout(width);
|
||||
this.orthogonalLayout(height);
|
||||
}
|
||||
}
|
||||
|
||||
public layout(size: number): void {
|
||||
// Only layout when both sizes are known
|
||||
this._height = size;
|
||||
if (!this._height) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._parent.removeChild(this);
|
||||
this._children.forEach(c => c.orthogonalLayout(this._height));
|
||||
}
|
||||
|
||||
public removeChild(child: SplitPane): void {
|
||||
const index = this._children.indexOf(child);
|
||||
this._children.splice(index, 1);
|
||||
this._splitView.removeView(index);
|
||||
public orthogonalLayout(size: number): void {
|
||||
this._width = size;
|
||||
|
||||
if (this._splitView) {
|
||||
this._splitView.layout(this._width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SplitPane implements IView {
|
||||
public minimumSize: number = 40;
|
||||
public maximumSize: number = Number.MAX_VALUE;
|
||||
|
||||
public instance: ITerminalInstance;
|
||||
public orientation: Orientation | undefined;
|
||||
protected _size: number;
|
||||
private _isContainerSet: boolean = false;
|
||||
|
||||
private _onDidChange: Event<number | undefined> = Event.None;
|
||||
public get onDidChange(): Event<number | undefined> { return this._onDidChange; }
|
||||
|
||||
constructor(
|
||||
public orthogonalSize: number
|
||||
) {
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
this._container = container;
|
||||
if (!this._isContainerSet && this.instance) {
|
||||
if (this._needsReattach) {
|
||||
(<any>this.instance).reattachToElement(container);
|
||||
} else {
|
||||
this.instance.attachToElement(container);
|
||||
}
|
||||
this.instance.attachToElement(container);
|
||||
this._isContainerSet = true;
|
||||
}
|
||||
}
|
||||
|
@ -126,75 +150,21 @@ class SplitPane implements IView {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.instance) {
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
this.instance.layout({ width: this.orthogonalSize, height: this._size });
|
||||
} else {
|
||||
this.instance.layout({ width: this._size, height: this.orthogonalSize });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of this.children) {
|
||||
child.orthogonalLayout(this._size);
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
this.instance.layout({ width: this.orthogonalSize, height: this._size });
|
||||
} else {
|
||||
this.instance.layout({ width: this._size, height: this.orthogonalSize });
|
||||
}
|
||||
}
|
||||
|
||||
public orthogonalLayout(size: number): void {
|
||||
this.orthogonalSize = size;
|
||||
|
||||
if (this._splitView) {
|
||||
this._splitView.layout(this.orthogonalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RootSplitPane extends SplitPane {
|
||||
private static _lastKnownWidth: number;
|
||||
private static _lastKnownHeight: number;
|
||||
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
|
||||
protected branch(container: HTMLElement, orientation: Orientation, instance: ITerminalInstance): void {
|
||||
if (orientation === Orientation.VERTICAL) {
|
||||
this._size = this._width || RootSplitPane._lastKnownWidth;
|
||||
this.orthogonalSize = this._height || RootSplitPane._lastKnownHeight;
|
||||
} else {
|
||||
this._size = this._height || RootSplitPane._lastKnownHeight;
|
||||
this.orthogonalSize = this._width || RootSplitPane._lastKnownWidth;
|
||||
}
|
||||
|
||||
super.branch(container, orientation, instance);
|
||||
}
|
||||
|
||||
public layoutBox(width: number, height: number): void {
|
||||
RootSplitPane._lastKnownWidth = width;
|
||||
RootSplitPane._lastKnownHeight = height;
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
this.layout(width);
|
||||
this.orthogonalLayout(height);
|
||||
} else if (this.orientation === Orientation.HORIZONTAL) {
|
||||
this.layout(height);
|
||||
this.orthogonalLayout(width);
|
||||
} else {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this.instance.layout({ width, height });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const directionOrientation: { [direction: number]: Orientation } = {
|
||||
[Direction.Left]: Orientation.HORIZONTAL,
|
||||
[Direction.Right]: Orientation.HORIZONTAL,
|
||||
[Direction.Up]: Orientation.VERTICAL,
|
||||
[Direction.Down]: Orientation.VERTICAL
|
||||
};
|
||||
|
||||
export class TerminalTab extends Disposable implements ITerminalTab {
|
||||
private _terminalInstances: ITerminalInstance[] = [];
|
||||
private _rootSplitPane: RootSplitPane;
|
||||
private _splitPaneContainer: SplitPaneContainer | undefined;
|
||||
private _tabElement: HTMLElement;
|
||||
|
||||
private _activeInstanceIndex: number;
|
||||
|
@ -226,9 +196,6 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
this._initInstanceListeners(instance);
|
||||
this._activeInstanceIndex = 0;
|
||||
|
||||
this._rootSplitPane = new RootSplitPane();
|
||||
this._rootSplitPane.instance = instance;
|
||||
|
||||
if (this._container) {
|
||||
this.attachToElement(this._container);
|
||||
}
|
||||
|
@ -272,10 +239,9 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
this.activeInstance.focus(true);
|
||||
}
|
||||
|
||||
// Find the instance's SplitPane and unsplit it
|
||||
const pane = this._findSplitPane(instance);
|
||||
if (pane) {
|
||||
pane.remove();
|
||||
// Remove the instance from the split pane if it has been created
|
||||
if (this._splitPaneContainer) {
|
||||
this._splitPaneContainer.remove(instance);
|
||||
}
|
||||
|
||||
// Fire events and dispose tab if it was the last instance
|
||||
|
@ -286,44 +252,6 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
}
|
||||
}
|
||||
|
||||
private _findSplitPane(instance: ITerminalInstance): SplitPane {
|
||||
const openList: SplitPane[] = [this._rootSplitPane];
|
||||
while (openList.length > 0) {
|
||||
const current = openList.shift();
|
||||
if (current.instance === instance) {
|
||||
return current;
|
||||
}
|
||||
openList.push.apply(openList, current.children);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Should this live inside SplitPane?
|
||||
private _findSplitPanePath(instance: ITerminalInstance, path: SplitPane[] = [this._rootSplitPane]): SplitPane[] {
|
||||
// Gets all split panes from the root to the pane containing the instance.
|
||||
const pane = path[path.length - 1];
|
||||
|
||||
// Base case: path found
|
||||
if (pane.instance === instance) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// Rescurse child panes
|
||||
for (let i = 0; i < pane.children.length; i++) {
|
||||
const child = pane.children[i];
|
||||
|
||||
const subPath = path.slice();
|
||||
subPath.push(child);
|
||||
const result = this._findSplitPanePath(instance, subPath);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// No children contain instance
|
||||
return null;
|
||||
}
|
||||
|
||||
private _setActiveInstance(instance: ITerminalInstance): void {
|
||||
this.setActiveInstanceByIndex(this._getIndexFromId(instance.id));
|
||||
}
|
||||
|
@ -343,7 +271,7 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
|
||||
public setActiveInstanceByIndex(index: number): void {
|
||||
// Check for invalid value
|
||||
if (index >= this._terminalInstances.length) {
|
||||
if (index < 0 || index >= this._terminalInstances.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -360,7 +288,10 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
this._tabElement = document.createElement('div');
|
||||
this._tabElement.classList.add('terminal-tab');
|
||||
this._container.appendChild(this._tabElement);
|
||||
this._rootSplitPane.render(this._tabElement);
|
||||
if (!this._splitPaneContainer) {
|
||||
this._splitPaneContainer = new SplitPaneContainer(this._tabElement, Orientation.HORIZONTAL);
|
||||
this.terminalInstances.forEach(instance => this._splitPaneContainer.split(instance));
|
||||
}
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
|
@ -390,23 +321,14 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
shellLaunchConfig);
|
||||
// TODO: Should this be pulled from the splitpanes instead? Currently there are 2 sources of truth.
|
||||
// _terminalInstances is also the order they were created, not the order in which they appear
|
||||
this._terminalInstances.push(instance);
|
||||
this._terminalInstances.splice(this._activeInstanceIndex + 1, 0, instance);
|
||||
this._initInstanceListeners(instance);
|
||||
|
||||
if (this._rootSplitPane.instance) {
|
||||
this._rootSplitPane.orientation = Orientation.HORIZONTAL;
|
||||
this._rootSplitPane.split(instance);
|
||||
} else {
|
||||
// The original branch has already occured, find the inner SplitPane and split it
|
||||
const activePane = this._findSplitPane(this.activeInstance);
|
||||
activePane.orientation = Orientation.HORIZONTAL;
|
||||
activePane.split(instance);
|
||||
}
|
||||
if (this._tabElement) {
|
||||
this._rootSplitPane.render(this._tabElement);
|
||||
}
|
||||
this._setActiveInstance(instance);
|
||||
|
||||
if (this._splitPaneContainer) {
|
||||
this._splitPaneContainer.split(instance, this._activeInstanceIndex);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -415,67 +337,16 @@ export class TerminalTab extends Disposable implements ITerminalTab {
|
|||
}
|
||||
|
||||
public layout(width: number, height: number): void {
|
||||
this._rootSplitPane.layoutBox(width, height);
|
||||
if (this._splitPaneContainer) {
|
||||
this._splitPaneContainer.layoutBox(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public focusDirection(direction: Direction): void {
|
||||
const activeInstance = this.activeInstance;
|
||||
if (!activeInstance) {
|
||||
return null;
|
||||
}
|
||||
public focusPreviousPane(): void {
|
||||
this.setActiveInstanceByIndex(this._activeInstanceIndex - 1);
|
||||
}
|
||||
|
||||
const desiredOrientation = directionOrientation[direction];
|
||||
const isUpOrLeft = direction === Direction.Left || direction === Direction.Up;
|
||||
|
||||
// Find the closest horizontal SplitPane ancestor with a child to the left
|
||||
let closestHorizontalPane: SplitPane = null;
|
||||
const panePath = this._findSplitPanePath(activeInstance);
|
||||
let index = panePath.length - 1;
|
||||
let ancestorIndex: number;
|
||||
while (--index >= 0) {
|
||||
const pane = panePath[index];
|
||||
// Continue up the path if not the desired orientation
|
||||
if (pane.orientation !== desiredOrientation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find index of the panePath pane and break out of loop if it's not the left-most child
|
||||
ancestorIndex = pane.children.indexOf(panePath[index + 1]);
|
||||
// Make sure that the pane is not on the boundary
|
||||
if (isUpOrLeft) {
|
||||
if (ancestorIndex > 0) {
|
||||
closestHorizontalPane = pane;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ancestorIndex < pane.children.length - 1) {
|
||||
closestHorizontalPane = pane;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There are no panes to the left
|
||||
if (!closestHorizontalPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
let current: SplitPane;
|
||||
if (isUpOrLeft) {
|
||||
// Find the bottom/right-most instance
|
||||
current = closestHorizontalPane.children[ancestorIndex - 1];
|
||||
while (current.children && current.children.length > 0) {
|
||||
current = current.children[current.children.length - 1];
|
||||
}
|
||||
} else {
|
||||
// Find the top/left-most instance
|
||||
current = closestHorizontalPane.children[ancestorIndex + 1];
|
||||
while (current.children && current.children.length > 0) {
|
||||
current = current.children[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Focus the instance to the left
|
||||
current.instance.focus();
|
||||
public focusNextPane(): void {
|
||||
this.setActiveInstanceByIndex(this._activeInstanceIndex + 1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue