diff --git a/tabby-core/src/components/appRoot.component.ts b/tabby-core/src/components/appRoot.component.ts index e380fed1..6fae8426 100644 --- a/tabby-core/src/components/appRoot.component.ts +++ b/tabby-core/src/components/appRoot.component.ts @@ -112,6 +112,12 @@ export class AppRootComponent { if (hotkey === 'duplicate-tab') { this.app.duplicateTab(this.app.activeTab) } + if (hotkey === 'explode-tab' && this.app.activeTab instanceof SplitTabComponent) { + this.app.explodeTab(this.app.activeTab) + } + if (hotkey === 'combine-tabs' && this.app.activeTab instanceof SplitTabComponent) { + this.app.combineTabsInto(this.app.activeTab) + } } if (hotkey === 'reopen-tab') { this.app.reopenLastTab() diff --git a/tabby-core/src/components/splitTab.component.ts b/tabby-core/src/components/splitTab.component.ts index 9c1d1e09..a6dd5fbc 100644 --- a/tabby-core/src/components/splitTab.component.ts +++ b/tabby-core/src/components/splitTab.component.ts @@ -82,6 +82,18 @@ export class SplitContainer { this.ratios = this.ratios.map(x => x / s) } + /** + * Makes all tabs have the same size + */ + equalize (): void { + for (const child of this.children) { + if (child instanceof SplitContainer) { + child.equalize() + } + } + this.ratios.fill(1 / this.ratios.length) + } + /** * Gets the left/top side offset for the given element index (between 0 and 1) */ @@ -449,6 +461,8 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit this.attachTabView(tab) this.onAfterTabAdded(tab) } + + this.root.normalize() } removeTab (tab: BaseTabComponent): void { @@ -644,6 +658,11 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit } } + equalize (): void { + this.root.normalize() + this.root.equalize() + } + private updateTitle (): void { this.setTitle([...new Set(this.getAllTabs().map(x => x.title))].join(' | ')) } diff --git a/tabby-core/src/components/splitTabPaneLabel.component.scss b/tabby-core/src/components/splitTabPaneLabel.component.scss index ef8dcd6d..61b0e98b 100644 --- a/tabby-core/src/components/splitTabPaneLabel.component.scss +++ b/tabby-core/src/components/splitTabPaneLabel.component.scss @@ -13,7 +13,8 @@ div { background: rgba(0, 0, 0, .7); padding: 20px 30px; - font-size: 18px; + margin: 20px; + font-size: 16px; color: #fff; display: flex; align-items: center; diff --git a/tabby-core/src/configDefaults.linux.yaml b/tabby-core/src/configDefaults.linux.yaml index a669a085..c0c150fb 100644 --- a/tabby-core/src/configDefaults.linux.yaml +++ b/tabby-core/src/configDefaults.linux.yaml @@ -21,6 +21,10 @@ hotkeys: rearrange-panes: - 'Ctrl-Shift' duplicate-tab: [] + explode-tab: + - 'Ctrl-Shift-.' + combine-tabs: + - 'Ctrl-Shift-,' tab-1: - 'Alt-1' tab-2: diff --git a/tabby-core/src/configDefaults.macos.yaml b/tabby-core/src/configDefaults.macos.yaml index 942da497..5db9b8f3 100644 --- a/tabby-core/src/configDefaults.macos.yaml +++ b/tabby-core/src/configDefaults.macos.yaml @@ -39,6 +39,10 @@ hotkeys: tab-10: - '⌘-0' duplicate-tab: [] + explode-tab: + - '⌘-Shift-.' + combine-tabs: + - '⌘-Shift-,' tab-11: [] tab-12: [] tab-13: [] diff --git a/tabby-core/src/configDefaults.windows.yaml b/tabby-core/src/configDefaults.windows.yaml index 544a7ca6..7c5f6d17 100644 --- a/tabby-core/src/configDefaults.windows.yaml +++ b/tabby-core/src/configDefaults.windows.yaml @@ -22,6 +22,10 @@ hotkeys: rearrange-panes: - 'Ctrl-Shift' duplicate-tab: [] + explode-tab: + - 'Ctrl-Shift-.' + combine-tab: + - 'Ctrl-Shift-,' tab-1: - 'Alt-1' tab-2: diff --git a/tabby-core/src/hotkeys.ts b/tabby-core/src/hotkeys.ts index 46880464..5f008b55 100644 --- a/tabby-core/src/hotkeys.ts +++ b/tabby-core/src/hotkeys.ts @@ -56,6 +56,14 @@ export class AppHotkeyProvider extends HotkeyProvider { id: 'duplicate-tab', name: this.translate.instant('Duplicate tab'), }, + { + id: 'explode-tab', + name: this.translate.instant('Turn current tab\'s panes into separate tabs'), + }, + { + id: 'combine-tabs', + name: this.translate.instant('Combine all tabs into the current tab'), + }, { id: 'tab-1', name: this.translate.instant('Tab {number}', { number: 1 }), diff --git a/tabby-core/src/services/app.service.ts b/tabby-core/src/services/app.service.ts index 77c9105d..a49630e9 100644 --- a/tabby-core/src/services/app.service.ts +++ b/tabby-core/src/services/app.service.ts @@ -399,4 +399,40 @@ export class AppService { showSelector (name: string, options: SelectorOption[]): Promise { return this.selector.show(name, options) } + + explodeTab (tab: SplitTabComponent): SplitTabComponent[] { + const result: SplitTabComponent[] = [] + for (const child of tab.getAllTabs().slice(1)) { + tab.removeTab(child) + result.push(this.wrapAndAddTab(child)) + } + return result + } + + combineTabsInto (into: SplitTabComponent): void { + this.explodeTab(into) + + let allChildren: BaseTabComponent[] = [] + for (const tab of this.tabs) { + if (into === tab) { + continue + } + let children = [tab] + if (tab instanceof SplitTabComponent) { + children = tab.getAllTabs() + } + allChildren = allChildren.concat(children) + } + + let x = 1 + let previous: BaseTabComponent|null = null + const stride = Math.ceil(Math.sqrt(allChildren.length + 1)) + for (const child of allChildren) { + into.add(child, x ? previous : null, x ? 'r' : 'b') + previous = child + x = (x + 1) % stride + } + + into.equalize() + } }