mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-11 19:04:35 +00:00
allow rearranging panes within a tab
This commit is contained in:
@@ -152,6 +152,14 @@ export interface SplitDropZoneInfo {
|
|||||||
(tabDropped)='onTabDropped($event, dropZone)'
|
(tabDropped)='onTabDropped($event, dropZone)'
|
||||||
>
|
>
|
||||||
</split-tab-drop-zone>
|
</split-tab-drop-zone>
|
||||||
|
<split-tab-pane-label
|
||||||
|
*ngFor='let tab of getAllTabs()'
|
||||||
|
cdkDropList
|
||||||
|
cdkAutoDropGroup='app-tabs'
|
||||||
|
[tab]='tab'
|
||||||
|
[parent]='this'
|
||||||
|
>
|
||||||
|
</split-tab-pane-label>
|
||||||
`,
|
`,
|
||||||
styles: [require('./splitTab.component.scss')],
|
styles: [require('./splitTab.component.scss')],
|
||||||
})
|
})
|
||||||
@@ -374,6 +382,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (thing instanceof BaseTabComponent) {
|
if (thing instanceof BaseTabComponent) {
|
||||||
|
if (thing.parent instanceof SplitTabComponent) {
|
||||||
|
thing.parent.removeTab(thing)
|
||||||
|
}
|
||||||
|
thing.removeFromContainer()
|
||||||
thing.parent = this
|
thing.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -34,7 +34,7 @@ export class SplitTabDropZoneComponent extends SelfPositioningComponent {
|
|||||||
) {
|
) {
|
||||||
super(element)
|
super(element)
|
||||||
this.subscribeUntilDestroyed(app.tabDragActive$, tab => {
|
this.subscribeUntilDestroyed(app.tabDragActive$, tab => {
|
||||||
this.isActive = !!tab && tab !== this.parent
|
this.isActive = !!tab && tab !== this.parent && tab !== this.dropZone.relativeToTab
|
||||||
this.layout()
|
this.layout()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
34
tabby-core/src/components/splitTabPaneLabel.component.scss
Normal file
34
tabby-core/src/components/splitTabPaneLabel.component.scss
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
:host {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(255, 255, 255, .25);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0;
|
||||||
|
transition: .125s opacity cubic-bezier(0.86, 0, 0.07, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
background: rgba(0, 0, 0, .7);
|
||||||
|
padding: 20px 30px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host.active {
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
pointer-events: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
80
tabby-core/src/components/splitTabPaneLabel.component.ts
Normal file
80
tabby-core/src/components/splitTabPaneLabel.component.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { Component, Input, HostBinding, ElementRef } from '@angular/core'
|
||||||
|
import { HotkeysService } from '../services/hotkeys.service'
|
||||||
|
import { AppService } from '../services/app.service'
|
||||||
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
|
import { SelfPositioningComponent } from './selfPositioning.component'
|
||||||
|
|
||||||
|
/** @hidden */
|
||||||
|
@Component({
|
||||||
|
selector: 'split-tab-pane-label',
|
||||||
|
template: `
|
||||||
|
<div
|
||||||
|
cdkDrag
|
||||||
|
[cdkDragData]='tab'
|
||||||
|
(cdkDragStarted)='onTabDragStart(tab)'
|
||||||
|
(cdkDragEnded)='onTabDragEnd()'
|
||||||
|
>
|
||||||
|
<i class="fa fa-window-maximize mr-3"></i>
|
||||||
|
<label>{{tab.title}}</label>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styles: [require('./splitTabPaneLabel.component.scss')],
|
||||||
|
})
|
||||||
|
export class SplitTabPaneLabelComponent extends SelfPositioningComponent {
|
||||||
|
@Input() tab: BaseTabComponent
|
||||||
|
@Input() parent: BaseTabComponent
|
||||||
|
@HostBinding('class.active') isActive = false
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||||
|
constructor (
|
||||||
|
element: ElementRef,
|
||||||
|
hotkeys: HotkeysService,
|
||||||
|
private app: AppService,
|
||||||
|
) {
|
||||||
|
super(element)
|
||||||
|
this.subscribeUntilDestroyed(hotkeys.hotkey$, hk => {
|
||||||
|
if (hk === 'rearrange-panes' && this.parent.hasFocus) {
|
||||||
|
this.isActive = true
|
||||||
|
this.layout()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.subscribeUntilDestroyed(hotkeys.hotkeyOff$, hk => {
|
||||||
|
if (hk === 'rearrange-panes') {
|
||||||
|
this.isActive = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges () {
|
||||||
|
this.layout()
|
||||||
|
}
|
||||||
|
|
||||||
|
onTabDragStart (tab: BaseTabComponent): void {
|
||||||
|
this.app.emitTabDragStarted(tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
onTabDragEnd (): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.app.emitTabDragEnded()
|
||||||
|
this.app.emitTabsChanged()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
layout () {
|
||||||
|
const tabElement: HTMLElement|undefined = this.tab.viewContainerEmbeddedRef?.rootNodes[0]
|
||||||
|
|
||||||
|
if (!tabElement) {
|
||||||
|
// being destroyed
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setDimensions(
|
||||||
|
tabElement.offsetLeft,
|
||||||
|
tabElement.offsetTop,
|
||||||
|
tabElement.clientWidth,
|
||||||
|
tabElement.clientHeight,
|
||||||
|
'px'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -18,6 +18,8 @@ hotkeys:
|
|||||||
- 'Ctrl-Shift-PageUp'
|
- 'Ctrl-Shift-PageUp'
|
||||||
move-tab-right:
|
move-tab-right:
|
||||||
- 'Ctrl-Shift-PageDown'
|
- 'Ctrl-Shift-PageDown'
|
||||||
|
rearrange-panes:
|
||||||
|
- 'Ctrl-Shift'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
tab-2:
|
tab-2:
|
||||||
|
@@ -16,6 +16,8 @@ hotkeys:
|
|||||||
- '⌘-Shift-Left'
|
- '⌘-Shift-Left'
|
||||||
move-tab-right:
|
move-tab-right:
|
||||||
- '⌘-Shift-Right'
|
- '⌘-Shift-Right'
|
||||||
|
rearrange-panes:
|
||||||
|
- '⌘-Shift'
|
||||||
tab-1:
|
tab-1:
|
||||||
- '⌘-1'
|
- '⌘-1'
|
||||||
tab-2:
|
tab-2:
|
||||||
|
@@ -19,6 +19,8 @@ hotkeys:
|
|||||||
- 'Ctrl-Shift-PageUp'
|
- 'Ctrl-Shift-PageUp'
|
||||||
move-tab-right:
|
move-tab-right:
|
||||||
- 'Ctrl-Shift-PageDown'
|
- 'Ctrl-Shift-PageDown'
|
||||||
|
rearrange-panes:
|
||||||
|
- 'Ctrl-Shift'
|
||||||
tab-1:
|
tab-1:
|
||||||
- 'Alt-1'
|
- 'Alt-1'
|
||||||
tab-2:
|
tab-2:
|
||||||
|
@@ -46,6 +46,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
|||||||
id: 'move-tab-right',
|
id: 'move-tab-right',
|
||||||
name: 'Move tab to the right',
|
name: 'Move tab to the right',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'rearrange-panes',
|
||||||
|
name: 'Show pane labels (for rearranging)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'tab-1',
|
id: 'tab-1',
|
||||||
name: 'Tab 1',
|
name: 'Tab 1',
|
||||||
|
@@ -23,6 +23,7 @@ import { SelectorModalComponent } from './components/selectorModal.component'
|
|||||||
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
||||||
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
|
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
|
||||||
import { SplitTabDropZoneComponent } from './components/splitTabDropZone.component'
|
import { SplitTabDropZoneComponent } from './components/splitTabDropZone.component'
|
||||||
|
import { SplitTabPaneLabelComponent } from './components/splitTabPaneLabel.component'
|
||||||
import { UnlockVaultModalComponent } from './components/unlockVaultModal.component'
|
import { UnlockVaultModalComponent } from './components/unlockVaultModal.component'
|
||||||
import { WelcomeTabComponent } from './components/welcomeTab.component'
|
import { WelcomeTabComponent } from './components/welcomeTab.component'
|
||||||
import { TransfersMenuComponent } from './components/transfersMenu.component'
|
import { TransfersMenuComponent } from './components/transfersMenu.component'
|
||||||
@@ -100,6 +101,7 @@ const PROVIDERS = [
|
|||||||
SplitTabComponent,
|
SplitTabComponent,
|
||||||
SplitTabSpannerComponent,
|
SplitTabSpannerComponent,
|
||||||
SplitTabDropZoneComponent,
|
SplitTabDropZoneComponent,
|
||||||
|
SplitTabPaneLabelComponent,
|
||||||
UnlockVaultModalComponent,
|
UnlockVaultModalComponent,
|
||||||
WelcomeTabComponent,
|
WelcomeTabComponent,
|
||||||
TransfersMenuComponent,
|
TransfersMenuComponent,
|
||||||
|
@@ -92,9 +92,6 @@ export class XTermFrontend extends Frontend {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
ret = false
|
ret = false
|
||||||
}
|
}
|
||||||
this.hotkeysService.processKeystrokes()
|
|
||||||
this.hotkeysService.emitKeyEvent(event)
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user