diff --git a/terminus-core/src/components/splitTab.component.ts b/terminus-core/src/components/splitTab.component.ts index 54f43e35..eef42015 100644 --- a/terminus-core/src/components/splitTab.component.ts +++ b/terminus-core/src/components/splitTab.component.ts @@ -157,6 +157,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit /** @hidden */ _spanners: SplitSpannerInfo[] = [] + /** @hidden */ + _allFocusMode = false + + /** @hidden */ private focusedTab: BaseTabComponent private maximizedTab: BaseTabComponent|null = null private hotkeysSubscription: Subscription @@ -480,6 +484,12 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit } } + layout () { + this.root.normalize() + this._spanners = [] + this.layoutInternal(this.root, 0, 0, 100, 100) + } + private attachTabView (tab: BaseTabComponent) { const ref = this.viewContainer.insert(tab.hostView) as EmbeddedViewRef // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion this.viewRefs.set(tab, ref) @@ -505,12 +515,6 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit } } - private layout () { - this.root.normalize() - this._spanners = [] - this.layoutInternal(this.root, 0, 0, 100, 100) - } - private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) { const size = root.orientation === 'v' ? h : w const sizes = root.ratios.map(x => x * size) @@ -535,7 +539,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit element.classList.toggle('child', true) element.classList.toggle('maximized', child === this.maximizedTab) element.classList.toggle('minimized', this.maximizedTab && child !== this.maximizedTab) - element.classList.toggle('focused', child === this.focusedTab) + element.classList.toggle('focused', this._allFocusMode || child === this.focusedTab) element.style.left = `${childX}%` element.style.top = `${childY}%` element.style.width = `${childW}%` diff --git a/terminus-terminal/src/api/baseTerminalTab.component.ts b/terminus-terminal/src/api/baseTerminalTab.component.ts index bb69e5bb..d949146a 100644 --- a/terminus-terminal/src/api/baseTerminalTab.component.ts +++ b/terminus-terminal/src/api/baseTerminalTab.component.ts @@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr' import colors from 'ansi-colors' import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core' import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations' -import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger, TabContextMenuItemProvider } from 'terminus-core' +import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent } from 'terminus-core' import { BaseSession, SessionsService } from '../services/sessions.service' import { TerminalFrontendService } from '../services/terminalFrontend.service' @@ -92,6 +92,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit private hotkeysSubscription: Subscription private bellPlayer: HTMLAudioElement private termContainerSubscriptions: Subscription[] = [] + private allFocusModeSubscription: Subscription|null = null get input$ (): Observable { return this.frontend.input$ } get output$ (): Observable { return this.output } @@ -172,6 +173,9 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit this.element.nativeElement.querySelector('.search-input').focus() }) break + case 'pane-focus-all': + this.focusAllPanes() + break } }) this.bellPlayer = document.createElement('audio') @@ -255,6 +259,10 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit }) this.frontend.focus() + + this.blurred$.subscribe(() => { + this.cancelFocusAllPanes() + }) } async buildContextMenu (): Promise { @@ -366,6 +374,35 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit this.frontend.setZoom(this.zoom) } + focusAllPanes (): void { + if (this.allFocusModeSubscription) { + return + } + if (this.parent instanceof SplitTabComponent) { + this.parent._allFocusMode = true + this.parent.layout() + this.allFocusModeSubscription = this.frontend.input$.subscribe(data => { + for (const tab of (this.parent as SplitTabComponent).getAllTabs()) { + if (tab !== this && tab instanceof BaseTerminalTabComponent) { + tab.sendInput(data) + } + } + }) + } + } + + cancelFocusAllPanes (): void { + if (!this.allFocusModeSubscription) { + return + } + if (this.parent instanceof SplitTabComponent) { + this.allFocusModeSubscription?.unsubscribe?.() + this.allFocusModeSubscription = null + this.parent._allFocusMode = false + this.parent.layout() + } + } + /** @hidden */ ngOnDestroy (): void { this.frontend.detach(this.content.nativeElement) diff --git a/terminus-terminal/src/config.ts b/terminus-terminal/src/config.ts index edddcb96..9511981d 100644 --- a/terminus-terminal/src/config.ts +++ b/terminus-terminal/src/config.ts @@ -109,6 +109,9 @@ export class TerminalConfigProvider extends ConfigProvider { search: [ '⌘-F', ], + 'pane-focus-all': [ + '⌘-Shift-I', + ], }, }, [Platform.Windows]: { @@ -152,6 +155,9 @@ export class TerminalConfigProvider extends ConfigProvider { search: [ 'Ctrl-Shift-F', ], + 'pane-focus-all': [ + 'Ctrl-Shift-I', + ], }, }, [Platform.Linux]: { @@ -192,6 +198,9 @@ export class TerminalConfigProvider extends ConfigProvider { search: [ 'Ctrl-Shift-F', ], + 'pane-focus-all': [ + 'Ctrl-Shift-I', + ], }, }, } diff --git a/terminus-terminal/src/hotkeys.ts b/terminus-terminal/src/hotkeys.ts index c049427e..205b8abb 100644 --- a/terminus-terminal/src/hotkeys.ts +++ b/terminus-terminal/src/hotkeys.ts @@ -66,6 +66,10 @@ export class TerminalHotkeyProvider extends HotkeyProvider { id: 'search', name: 'Search', }, + { + id: 'pane-focus-all', + name: 'Focus all panes at once', + }, ] constructor ( diff --git a/terminus-terminal/src/tabContextMenu.ts b/terminus-terminal/src/tabContextMenu.ts index e16ef20a..33c95702 100644 --- a/terminus-terminal/src/tabContextMenu.ts +++ b/terminus-terminal/src/tabContextMenu.ts @@ -1,6 +1,6 @@ import { Injectable, NgZone, Optional, Inject } from '@angular/core' import { ToastrService } from 'ngx-toastr' -import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent } from 'terminus-core' +import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent } from 'terminus-core' import { TerminalTabComponent } from './components/terminalTab.component' import { UACService } from './services/uac.service' import { TerminalService } from './services/terminal.service' @@ -113,6 +113,15 @@ export class NewTabContextMenu extends TabContextMenuItemProvider { }) } + if (tab instanceof BaseTerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) { + items.push({ + label: 'Focus all panes', + click: () => this.zone.run(() => { + tab.focusAllPanes() + }), + }) + } + return items } }