mirror of
https://github.com/Eugeny/tabby.git
synced 2025-01-30 14:20:18 +08:00
reworked drop zones to allow more pane drop positions
This commit is contained in:
parent
0df5fb4a34
commit
85be974e64
@ -127,8 +127,14 @@ export interface SplitSpannerInfo {
|
|||||||
* Represents a tab drop zone
|
* Represents a tab drop zone
|
||||||
*/
|
*/
|
||||||
export interface SplitDropZoneInfo {
|
export interface SplitDropZoneInfo {
|
||||||
relativeToTab: BaseTabComponent
|
container?: SplitContainer
|
||||||
side: SplitDirection
|
position?: number
|
||||||
|
relativeTo?: BaseTabComponent|SplitContainer
|
||||||
|
side?: SplitDirection
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
w: number
|
||||||
|
h: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +376,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
/**
|
/**
|
||||||
* Inserts a new `tab` to the `side` of the `relative` tab
|
* Inserts a new `tab` to the `side` of the `relative` tab
|
||||||
*/
|
*/
|
||||||
async add (thing: BaseTabComponent|SplitContainer, relative: BaseTabComponent|null, side: SplitDirection): Promise<void> {
|
async add (thing: BaseTabComponent|SplitContainer, relative: BaseTabComponent|SplitContainer|null, side: SplitDirection): Promise<void> {
|
||||||
if (thing instanceof SplitTabComponent) {
|
if (thing instanceof SplitTabComponent) {
|
||||||
const tab = thing
|
const tab = thing
|
||||||
thing = tab.root
|
thing = tab.root
|
||||||
@ -389,31 +395,40 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
thing.parent = this
|
thing.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = (relative ? this.getParentOf(relative) : null) ?? this.root
|
let target = relative ? this.getParentOf(relative) : null
|
||||||
let insertIndex = relative ? target.children.indexOf(relative) : -1
|
if (!target) {
|
||||||
|
// Rewrap the root container just in case the orientation isn't compatibile
|
||||||
|
target = new SplitContainer()
|
||||||
|
target.orientation = ['l', 'r'].includes(side) ? 'h' : 'v'
|
||||||
|
target.children = [this.root]
|
||||||
|
target.ratios = [1]
|
||||||
|
this.root = target
|
||||||
|
}
|
||||||
|
|
||||||
|
let insertIndex = relative
|
||||||
|
? target.children.indexOf(relative) + ('tl'.includes(side) ? 0 : 1)
|
||||||
|
: 'tl'.includes(side) ? 0 : -1
|
||||||
|
|
||||||
if (
|
if (
|
||||||
target.orientation === 'v' && ['l', 'r'].includes(side) ||
|
target.orientation === 'v' && ['l', 'r'].includes(side) ||
|
||||||
target.orientation === 'h' && ['t', 'b'].includes(side)
|
target.orientation === 'h' && ['t', 'b'].includes(side)
|
||||||
) {
|
) {
|
||||||
|
// Inserting into a container but the orientation isn't compatible
|
||||||
const newContainer = new SplitContainer()
|
const newContainer = new SplitContainer()
|
||||||
newContainer.orientation = target.orientation === 'v' ? 'h' : 'v'
|
newContainer.orientation = ['l', 'r'].includes(side) ? 'h' : 'v'
|
||||||
newContainer.children = relative ? [relative] : []
|
newContainer.children = relative ? [relative] : []
|
||||||
newContainer.ratios = [1]
|
newContainer.ratios = [1]
|
||||||
target.children[insertIndex] = newContainer
|
target.children.splice(relative ? target.children.indexOf(relative) : -1, 1, newContainer)
|
||||||
target = newContainer
|
target = newContainer
|
||||||
insertIndex = 0
|
insertIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insertIndex === -1) {
|
|
||||||
insertIndex = 0
|
|
||||||
} else {
|
|
||||||
insertIndex += side === 'l' || side === 't' ? 0 : 1
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < target.children.length; i++) {
|
for (let i = 0; i < target.children.length; i++) {
|
||||||
target.ratios[i] *= target.children.length / (target.children.length + 1)
|
target.ratios[i] *= target.children.length / (target.children.length + 1)
|
||||||
}
|
}
|
||||||
|
if (insertIndex === -1) {
|
||||||
|
insertIndex = target.ratios.length
|
||||||
|
}
|
||||||
target.ratios.splice(insertIndex, 0, 1 / (target.children.length + 1))
|
target.ratios.splice(insertIndex, 0, 1 / (target.children.length + 1))
|
||||||
target.children.splice(insertIndex, 0, thing)
|
target.children.splice(insertIndex, 0, thing)
|
||||||
|
|
||||||
@ -570,7 +585,11 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.add(tab, zone.relativeToTab, zone.side)
|
if (zone.container) {
|
||||||
|
this.add(tab, zone.container.children[zone.position!], zone.container.orientation === 'h' ? 'r' : 'b')
|
||||||
|
} else {
|
||||||
|
this.add(tab, null, zone.side!)
|
||||||
|
}
|
||||||
this.tabAdopted.next(tab)
|
this.tabAdopted.next(tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,6 +641,38 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) {
|
private layoutInternal (root: SplitContainer, x: number, y: number, w: number, h: number) {
|
||||||
const size = root.orientation === 'v' ? h : w
|
const size = root.orientation === 'v' ? h : w
|
||||||
const sizes = root.ratios.map(ratio => ratio * size)
|
const sizes = root.ratios.map(ratio => ratio * size)
|
||||||
|
const thickness = 10
|
||||||
|
|
||||||
|
if (root === this.root) {
|
||||||
|
this._dropZones.push({
|
||||||
|
x: x - thickness / 2,
|
||||||
|
y: y + thickness,
|
||||||
|
w: thickness,
|
||||||
|
h: h - thickness * 2,
|
||||||
|
side: 'l',
|
||||||
|
})
|
||||||
|
this._dropZones.push({
|
||||||
|
x,
|
||||||
|
y: y - thickness / 2,
|
||||||
|
w,
|
||||||
|
h: thickness,
|
||||||
|
side: 't',
|
||||||
|
})
|
||||||
|
this._dropZones.push({
|
||||||
|
x: x + w - thickness / 2,
|
||||||
|
y: y + thickness,
|
||||||
|
w: thickness,
|
||||||
|
h: h - thickness * 2,
|
||||||
|
side: 'r',
|
||||||
|
})
|
||||||
|
this._dropZones.push({
|
||||||
|
x,
|
||||||
|
y: y + h - thickness / 2,
|
||||||
|
w,
|
||||||
|
h: thickness,
|
||||||
|
side: 'b',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
root.x = x
|
root.x = x
|
||||||
root.y = y
|
root.y = y
|
||||||
@ -655,17 +706,60 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
element.style.width = '90%'
|
element.style.width = '90%'
|
||||||
element.style.height = '90%'
|
element.style.height = '90%'
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const side of ['t', 'r', 'b', 'l']) {
|
|
||||||
this._dropZones.push({
|
|
||||||
relativeToTab: child,
|
|
||||||
side: side as SplitDirection,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += sizes[i]
|
offset += sizes[i]
|
||||||
|
|
||||||
|
if (i !== root.ratios.length - 1) {
|
||||||
|
// Spanner area
|
||||||
|
this._dropZones.push({
|
||||||
|
relativeTo: root.children[i],
|
||||||
|
side: root.orientation === 'v' ? 'b': 'r',
|
||||||
|
x: root.orientation === 'v' ? childX + thickness : childX + offset - thickness / 2,
|
||||||
|
y: root.orientation === 'v' ? childY + offset - thickness / 2 : childY + thickness,
|
||||||
|
w: root.orientation === 'v' ? childW - thickness * 2 : thickness,
|
||||||
|
h: root.orientation === 'v' ? thickness : childH - thickness * 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sides
|
||||||
|
if (root.orientation === 'v') {
|
||||||
|
this._dropZones.push({
|
||||||
|
x: childX,
|
||||||
|
y: childY + thickness,
|
||||||
|
w: thickness,
|
||||||
|
h: childH - thickness * 2,
|
||||||
|
relativeTo: child,
|
||||||
|
side: 'l',
|
||||||
|
})
|
||||||
|
this._dropZones.push({
|
||||||
|
x: childX + w - thickness,
|
||||||
|
y: childY + thickness,
|
||||||
|
w: thickness,
|
||||||
|
h: childH - thickness * 2,
|
||||||
|
relativeTo: child,
|
||||||
|
side: 'r',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this._dropZones.push({
|
||||||
|
x: childX + thickness,
|
||||||
|
y: childY,
|
||||||
|
w: childW - thickness * 2,
|
||||||
|
h: thickness,
|
||||||
|
relativeTo: child,
|
||||||
|
side: 't',
|
||||||
|
})
|
||||||
|
this._dropZones.push({
|
||||||
|
x: childX + thickness,
|
||||||
|
y: childY + childH - thickness,
|
||||||
|
w: childW - thickness * 2,
|
||||||
|
h: thickness,
|
||||||
|
relativeTo: child,
|
||||||
|
side: 'b',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (i !== 0) {
|
if (i !== 0) {
|
||||||
this._spanners.push({
|
this._spanners.push({
|
||||||
container: root,
|
container: root,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
background: rgba(255, 255, 255, .125);
|
background: rgba(255, 255, 255, .125);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -21,6 +22,7 @@
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
|
opacity: 1;
|
||||||
background: rgba(255, 255, 255, .5);
|
background: rgba(255, 255, 255, .5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 && tab !== this.dropZone.relativeToTab
|
this.isActive = !!tab && tab !== this.parent && tab !== this.dropZone.container?.children[this.dropZone.position!]
|
||||||
this.layout()
|
this.layout()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -44,26 +44,11 @@ export class SplitTabDropZoneComponent extends SelfPositioningComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
layout () {
|
layout () {
|
||||||
const tabElement: HTMLElement|undefined = this.dropZone.relativeToTab.viewContainerEmbeddedRef?.rootNodes[0]
|
|
||||||
|
|
||||||
if (!tabElement) {
|
|
||||||
// being destroyed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = {
|
|
||||||
t: [0, 0, tabElement.clientWidth, tabElement.clientHeight / 5],
|
|
||||||
l: [0, tabElement.clientHeight / 5, tabElement.clientWidth / 3, tabElement.clientHeight * 3 / 5],
|
|
||||||
r: [tabElement.clientWidth * 2 / 3, tabElement.clientHeight / 5, tabElement.clientWidth / 3, tabElement.clientHeight * 3 / 5],
|
|
||||||
b: [0, tabElement.clientHeight * 4 / 5, tabElement.clientWidth, tabElement.clientHeight / 5],
|
|
||||||
}[this.dropZone.side]
|
|
||||||
|
|
||||||
this.setDimensions(
|
this.setDimensions(
|
||||||
args[0] + tabElement.offsetLeft,
|
this.dropZone.x,
|
||||||
args[1] + tabElement.offsetTop,
|
this.dropZone.y,
|
||||||
args[2],
|
this.dropZone.w,
|
||||||
args[3],
|
this.dropZone.h,
|
||||||
'px'
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user