@witchcraft/layout 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/FrameDragHandle.vue +3 -3
- package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +3 -3
- package/dist/runtime/components/LayoutDragEdgeHandle.d.vue.ts +2 -2
- package/dist/runtime/components/LayoutDragEdgeHandle.vue +2 -2
- package/dist/runtime/components/LayoutDragEdgeHandle.vue.d.ts +2 -2
- package/dist/runtime/components/LayoutDragEdges.vue +5 -5
- package/dist/runtime/components/LayoutEdgesActiveFrame.vue +2 -2
- package/dist/runtime/components/LayoutFrame.vue +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.d.vue.ts +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.vue +2 -2
- package/dist/runtime/components/LayoutIntersectionHandle.vue.d.ts +2 -2
- package/dist/runtime/components/LayoutIntersections.vue +4 -4
- package/dist/runtime/components/LayoutWindow.d.vue.ts +54 -45
- package/dist/runtime/components/LayoutWindow.vue +29 -29
- package/dist/runtime/components/LayoutWindow.vue.d.ts +54 -45
- package/dist/runtime/composables/useFrames.d.ts +142 -129
- package/dist/runtime/composables/useFrames.js +122 -94
- package/dist/runtime/demo/App.vue +16 -16
- package/dist/runtime/demo/DemoControls.d.vue.ts +2 -2
- package/dist/runtime/demo/DemoControls.vue +4 -4
- package/dist/runtime/demo/DemoControls.vue.d.ts +2 -2
- package/dist/runtime/demo/tailwind.css +1 -1
- package/dist/runtime/helpers/createZoneSideClipPath.d.ts +2 -2
- package/dist/runtime/helpers/findEdgesTouchingWindow.d.ts +7 -0
- package/dist/runtime/helpers/findEdgesTouchingWindow.js +20 -0
- package/dist/runtime/helpers/{findFrameDraggableEdges.d.ts → findFrameMoveableEdges.d.ts} +2 -2
- package/dist/runtime/helpers/{findFrameDraggableEdges.js → findFrameMoveableEdges.js} +3 -3
- package/dist/runtime/helpers/{findDraggableEdge.d.ts → findMoveableEdge.d.ts} +1 -1
- package/dist/runtime/helpers/{findDraggableEdge.js → findMoveableEdge.js} +1 -1
- package/dist/runtime/helpers/index.d.ts +10 -3
- package/dist/runtime/helpers/index.js +10 -3
- package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +1 -1
- package/dist/runtime/layout/createSplitDecoFromDrag.js +2 -2
- package/dist/runtime/layout/debugFrame.js +5 -1
- package/dist/runtime/layout/findSafeSplitEdge.d.ts +1 -1
- package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
- package/dist/runtime/layout/getCloseFrameInfo.js +4 -4
- package/dist/runtime/layout/getFrameCollapseInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameCollapseInfo.js +9 -29
- package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameDockInfo.js +61 -44
- package/dist/runtime/layout/getFrameExpandInfo.d.ts +11 -0
- package/dist/runtime/layout/getFrameExpandInfo.js +49 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +2 -2
- package/dist/runtime/layout/getFrameRearrangeInfo.js +8 -8
- package/dist/runtime/layout/getFrameShrinkInfo.d.ts +14 -0
- package/dist/runtime/layout/getFrameShrinkInfo.js +48 -0
- package/dist/runtime/layout/getFrameSplitInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameSplitInfo.js +2 -2
- package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +1 -1
- package/dist/runtime/layout/getFrameUncollapseInfo.js +6 -20
- package/dist/runtime/layout/{getFrameDragZones.d.ts → getFrameZones.d.ts} +3 -3
- package/dist/runtime/layout/{getFrameDragZones.js → getFrameZones.js} +1 -1
- package/dist/runtime/layout/getUpdateWindowSizeInfo.js +10 -12
- package/dist/runtime/layout/{getWindowDragZones.d.ts → getWindowZones.d.ts} +1 -1
- package/dist/runtime/layout/{getWindowDragZones.js → getWindowZones.js} +1 -1
- package/dist/runtime/layout/getZones.d.ts +8 -0
- package/dist/runtime/layout/getZones.js +32 -0
- package/dist/runtime/layout/index.d.ts +6 -3
- package/dist/runtime/layout/index.js +6 -3
- package/dist/runtime/{drag/DragActionHandler.d.ts → move/ActionHandler.d.ts} +18 -18
- package/dist/runtime/{drag/DragActionHandler.js → move/ActionHandler.js} +19 -19
- package/dist/runtime/{drag → move}/CloseAction.d.ts +8 -8
- package/dist/runtime/{drag → move}/CloseAction.js +29 -26
- package/dist/runtime/{drag → move}/FrameDragAction.d.ts +8 -8
- package/dist/runtime/{drag → move}/FrameDragAction.js +23 -21
- package/dist/runtime/{drag/DragDirectionStore.d.ts → move/MoveDirectionStore.d.ts} +4 -4
- package/dist/runtime/{drag/DragDirectionStore.js → move/MoveDirectionStore.js} +9 -9
- package/dist/runtime/{drag → move}/SplitAction.d.ts +9 -9
- package/dist/runtime/{drag → move}/SplitAction.js +46 -43
- package/dist/runtime/{drag → move}/createDefaultHandlers.d.ts +2 -2
- package/dist/runtime/types/index.d.ts +242 -158
- package/dist/runtime/types/index.js +4 -3
- package/dist/runtime/types/vue.d.ts +25 -18
- package/dist/runtime/types/vue.js +1 -1
- package/package.json +3 -2
- package/src/runtime/components/FrameDragHandle.vue +3 -3
- package/src/runtime/components/LayoutDragEdgeGrabbed.vue +3 -3
- package/src/runtime/components/LayoutDragEdgeHandle.vue +4 -4
- package/src/runtime/components/LayoutDragEdges.vue +5 -5
- package/src/runtime/components/LayoutEdgesActiveFrame.vue +2 -2
- package/src/runtime/components/LayoutFrame.vue +2 -2
- package/src/runtime/components/LayoutIntersectionHandle.vue +4 -4
- package/src/runtime/components/LayoutIntersections.vue +4 -4
- package/src/runtime/components/LayoutWindow.vue +35 -35
- package/src/runtime/composables/useFrames.ts +141 -121
- package/src/runtime/demo/App.vue +18 -18
- package/src/runtime/demo/DemoControls.vue +5 -5
- package/src/runtime/demo/tailwind.css +4 -0
- package/src/runtime/helpers/createZoneSideClipPath.ts +2 -2
- package/src/runtime/helpers/findEdgesTouchingWindow.ts +30 -0
- package/src/runtime/helpers/{findFrameDraggableEdges.ts → findFrameMoveableEdges.ts} +4 -4
- package/src/runtime/helpers/{findDraggableEdge.ts → findMoveableEdge.ts} +1 -1
- package/src/runtime/helpers/index.ts +10 -3
- package/src/runtime/layout/createSplitDecoFromDrag.ts +2 -2
- package/src/runtime/layout/debugFrame.ts +6 -1
- package/src/runtime/layout/findSafeSplitEdge.ts +4 -4
- package/src/runtime/layout/getCloseFrameInfo.ts +4 -4
- package/src/runtime/layout/getFrameCollapseInfo.ts +12 -41
- package/src/runtime/layout/getFrameDockInfo.ts +77 -57
- package/src/runtime/layout/getFrameExpandInfo.ts +84 -0
- package/src/runtime/layout/getFrameRearrangeInfo.ts +10 -10
- package/src/runtime/layout/getFrameShrinkInfo.ts +87 -0
- package/src/runtime/layout/getFrameSplitInfo.ts +5 -5
- package/src/runtime/layout/getFrameUncollapseInfo.ts +8 -30
- package/src/runtime/layout/{getFrameDragZones.ts → getFrameZones.ts} +4 -4
- package/src/runtime/layout/getUpdateWindowSizeInfo.ts +16 -22
- package/src/runtime/layout/{getWindowDragZones.ts → getWindowZones.ts} +1 -1
- package/src/runtime/layout/getZones.ts +48 -0
- package/src/runtime/layout/index.ts +6 -3
- package/src/runtime/{drag/DragActionHandler.ts → move/ActionHandler.ts} +44 -42
- package/src/runtime/{drag → move}/CloseAction.ts +39 -36
- package/src/runtime/{drag → move}/FrameDragAction.ts +36 -35
- package/src/runtime/{drag/DragDirectionStore.ts → move/MoveDirectionStore.ts} +10 -10
- package/src/runtime/{drag → move}/SplitAction.ts +56 -53
- package/src/runtime/{drag/defaultDragActions.ts → move/createDefaultHandlers.ts} +2 -2
- package/src/runtime/types/index.ts +104 -55
- package/src/runtime/types/vue.ts +18 -16
- package/dist/runtime/drag/defaultDragActions.d.ts +0 -9
- package/dist/runtime/drag/defaultDragActions.js +0 -10
- package/dist/runtime/layout/getDragZones.d.ts +0 -8
- package/dist/runtime/layout/getDragZones.js +0 -32
- package/src/runtime/drag/createDefaultHandlers.ts +0 -20
- package/src/runtime/layout/getDragZones.ts +0 -48
- /package/dist/runtime/{drag → move}/createDefaultHandlers.js +0 -0
|
@@ -12,6 +12,7 @@ export { assertValidWinAndFrameIds } from "./assertValidWinAndFrameIds.js"
|
|
|
12
12
|
export { assertWindowHasActiveFrame } from "./assertWindowHasActiveFrame.js"
|
|
13
13
|
export { cloneFrame } from "./cloneFrame.js"
|
|
14
14
|
export { cloneFrames } from "./cloneFrames.js"
|
|
15
|
+
export { consoleDebugWindow } from "./consoleDebugWindow.js"
|
|
15
16
|
export { containsEdge } from "./containsEdge.js"
|
|
16
17
|
export { convertLayoutWindowToWorkspace } from "./convertLayoutWindowToWorkspace.js"
|
|
17
18
|
export { copySize } from "./copySize.js"
|
|
@@ -22,19 +23,23 @@ export { dirToSide } from "./dirToSide.js"
|
|
|
22
23
|
export { doEdgesOverlap } from "./doEdgesOverlap.js"
|
|
23
24
|
export { doesEdgeContinueEdge } from "./doesEdgeContinueEdge.js"
|
|
24
25
|
export { edgeToPoints } from "./edgeToPoints.js"
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
26
|
+
export { findMoveableEdge } from "./findMoveableEdge.js"
|
|
27
|
+
export { findFrameMoveableEdges } from "./findFrameMoveableEdges.js"
|
|
28
|
+
export { framesRedistributeFix } from "./framesRedistributeFix.js"
|
|
27
29
|
export { frameToEdges } from "./frameToEdges.js"
|
|
28
30
|
export { frameToPoints } from "./frameToPoints.js"
|
|
31
|
+
export { getDockBoundaries } from "./getDockBoundaries.js"
|
|
29
32
|
export { getEdgeLength } from "./getEdgeLength.js"
|
|
30
33
|
export { getEdgeOrientation } from "./getEdgeOrientation.js"
|
|
31
34
|
export { getEdgeSharedDirection } from "./getEdgeSharedDirection.js"
|
|
32
35
|
export { getEdgeSide } from "./getEdgeSide.js"
|
|
33
36
|
export { getFrameById } from "./getFrameById.js"
|
|
34
37
|
export { getFrameConstant } from "./getFrameConstant.js"
|
|
38
|
+
export { findEdgesTouchingWindow } from "./findEdgesTouchingWindow.js"
|
|
35
39
|
export { getIntersections } from "./getIntersections.js"
|
|
36
40
|
export { getIntersectionsCss } from "./getIntersectionsCss.js"
|
|
37
41
|
export { getMoveEdgeInfo } from "./getMoveEdgeInfo.js"
|
|
42
|
+
export { getPinnedEdgesForCollapsedFrames } from "./getPinnedEdgesForCollapsedFrames.js"
|
|
38
43
|
export { getResizeLimit } from "./getResizeLimit.js"
|
|
39
44
|
export { getShapeRectCss } from "./getShapeRectCss.js"
|
|
40
45
|
export { getSideTouching } from "./getSideTouching.js"
|
|
@@ -55,9 +60,10 @@ export { isWindowEdge } from "./isWindowEdge.js"
|
|
|
55
60
|
export { isWindowEdgePoint } from "./isWindowEdgePoint.js"
|
|
56
61
|
export { moveEdge } from "./moveEdge.js"
|
|
57
62
|
export { numberToScaledPercent } from "./numberToScaledPercent.js"
|
|
58
|
-
export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js"
|
|
59
63
|
export { oppositeSide } from "./oppositeSide.js"
|
|
64
|
+
export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js"
|
|
60
65
|
export { resizeByEdge } from "./resizeByEdge.js"
|
|
66
|
+
export { rotateLayout } from "./rotateFrames.js"
|
|
61
67
|
export { scaledPointToPx } from "./scaledPointToPx.js"
|
|
62
68
|
export { sideToDirection } from "./sideToDirection.js"
|
|
63
69
|
export { sideToOrientation } from "./sideToOrientation.js"
|
|
@@ -67,3 +73,4 @@ export { toId } from "./toId.js"
|
|
|
67
73
|
export { toWindowCoord } from "./toWindowCoord.js"
|
|
68
74
|
export { unionEdges } from "./unionEdges.js"
|
|
69
75
|
export { updateWindowWithEvent } from "./updateWindowSizeWithEvent.js"
|
|
76
|
+
export { validateLayoutShape } from "./validateLayoutShape.js"
|
|
@@ -8,7 +8,7 @@ export function createSplitDecoFromDrag(
|
|
|
8
8
|
frames: Record<string, LayoutFrame>,
|
|
9
9
|
frame: LayoutFrame,
|
|
10
10
|
dragDirection: Direction,
|
|
11
|
-
|
|
11
|
+
movePoint: Point,
|
|
12
12
|
snapAmount: Point = settings.snapPointScaled,
|
|
13
13
|
minSize: Size = settings.minSizeScaled,
|
|
14
14
|
classes: {
|
|
@@ -22,7 +22,7 @@ export function createSplitDecoFromDrag(
|
|
|
22
22
|
const deco: RawSplitDeco & Partial<SplitDeco> = {
|
|
23
23
|
type: "split",
|
|
24
24
|
id: frame.id,
|
|
25
|
-
position:
|
|
25
|
+
position: movePoint[orientation === "horizontal" ? "x" : "y"],
|
|
26
26
|
direction: dragDirection
|
|
27
27
|
}
|
|
28
28
|
deco.shapes = createSplitDecoShapes(frames, deco, snapAmount, minSize, classes)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { keys } from "@alanscodelog/utils/keys"
|
|
2
|
+
|
|
1
3
|
import type { LayoutFrame } from "../types/index.js"
|
|
2
4
|
|
|
5
|
+
const builtinProperties = ["id", "x", "y", "width", "height", "docked", "collapsed"]
|
|
3
6
|
export function debugFrame(frame: LayoutFrame): string {
|
|
4
7
|
const f = frame
|
|
5
|
-
|
|
8
|
+
const otherProperties = keys(f).filter(k => !builtinProperties.includes(k)).map(k => `${k}: ${f[k]}`)
|
|
9
|
+
const otherPropertiesString = otherProperties.length > 0 ? `, ${otherProperties.join(", ")}` : ""
|
|
10
|
+
return `id: ${f.id.slice(0, 4)}, x: ${f.x}, y: ${f.y}, w: ${f.width}, h: ${f.height}\ndocked: ${f.docked}, collapsed: ${f.collapsed}${otherPropertiesString}`
|
|
6
11
|
}
|
|
@@ -8,15 +8,15 @@ import type { Direction, Edge, LayoutFrame, Point, Size } from "../types/index.j
|
|
|
8
8
|
export function findSafeSplitEdgeAndPosition(
|
|
9
9
|
frame: LayoutFrame,
|
|
10
10
|
dragDirection: Direction,
|
|
11
|
-
|
|
11
|
+
movePointOrPosition: Point | number,
|
|
12
12
|
snapAmount: Point = settings.snapPointScaled,
|
|
13
13
|
minSize: Size = settings.minSizeScaled
|
|
14
14
|
): { edge: Edge, position: number } {
|
|
15
15
|
const orientation = dirToOrientation(dragDirection)
|
|
16
16
|
const position
|
|
17
|
-
= typeof
|
|
18
|
-
?
|
|
19
|
-
:
|
|
17
|
+
= typeof movePointOrPosition === "number"
|
|
18
|
+
? movePointOrPosition
|
|
19
|
+
: movePointOrPosition[orientation === "horizontal" ? "x" : "y"]
|
|
20
20
|
|
|
21
21
|
const coordKey = orientation === "vertical" ? "y" : "x"
|
|
22
22
|
const sizeKey = orientation === "vertical" ? "height" : "width"
|
|
@@ -7,7 +7,7 @@ import { findVisualEdge } from "./findVisualEdge.js"
|
|
|
7
7
|
|
|
8
8
|
import { cloneFrame } from "../helpers/cloneFrame.js"
|
|
9
9
|
import { dirToSide } from "../helpers/dirToSide.js"
|
|
10
|
-
import {
|
|
10
|
+
import { findFrameMoveableEdges } from "../helpers/findFrameMoveableEdges.js"
|
|
11
11
|
import { getEdgeOrientation } from "../helpers/getEdgeOrientation.js"
|
|
12
12
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
13
13
|
import { settings } from "../settings.js"
|
|
@@ -119,9 +119,9 @@ export function getCloseFrameInfo<T extends "edge" | "dir">(
|
|
|
119
119
|
: closeDirOrSide as EdgeSide
|
|
120
120
|
const sideOpposite = oppositeSide(side)
|
|
121
121
|
|
|
122
|
-
const
|
|
123
|
-
if (!
|
|
124
|
-
const { edge: frameEdge } =
|
|
122
|
+
const moveableEdges = findFrameMoveableEdges(frame, visualEdges, !force, [side])
|
|
123
|
+
if (!moveableEdges) return new KnownError(LAYOUT_ERROR.CANT_CLOSE_NO_DRAG_EDGE, `Could not find draggable edge for frame ${frame.id}`, { frame })
|
|
124
|
+
const { edge: frameEdge } = moveableEdges[0]
|
|
125
125
|
|
|
126
126
|
const visualEdge = findVisualEdge(visualEdges, frameEdge)
|
|
127
127
|
if (visualEdge === undefined) unreachable()
|
|
@@ -2,13 +2,10 @@ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn"
|
|
|
2
2
|
import { walk } from "@alanscodelog/utils/walk"
|
|
3
3
|
|
|
4
4
|
import { applyFrameChanges } from "./applyFrameChanges.js"
|
|
5
|
-
import {
|
|
5
|
+
import { getFrameShrinkInfo } from "./getFrameShrinkInfo.js"
|
|
6
6
|
|
|
7
|
-
import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js"
|
|
8
|
-
import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js"
|
|
9
|
-
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
10
7
|
import { settings } from "../settings.js"
|
|
11
|
-
import type {
|
|
8
|
+
import type { LayoutChange, LayoutWindow, Size } from "../types/index.js"
|
|
12
9
|
import { LAYOUT_ERROR } from "../types/index.js"
|
|
13
10
|
import { KnownError } from "../utils/KnownError.js"
|
|
14
11
|
|
|
@@ -32,7 +29,8 @@ export function getFrameCollapseInfo(
|
|
|
32
29
|
} = {}
|
|
33
30
|
): LayoutChange
|
|
34
31
|
| KnownError<typeof LAYOUT_ERROR.CANT_COLLAPSE_NOT_DOCKED>
|
|
35
|
-
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>
|
|
32
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>
|
|
33
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME> {
|
|
36
34
|
collapseSizeScaled = collapseSizeScaled ?? settings.getCollapseSizeScaled(win)
|
|
37
35
|
win = walk(win, undefined, { save: true }) as typeof win
|
|
38
36
|
const frame = win.frames[frameId]
|
|
@@ -51,55 +49,28 @@ export function getFrameCollapseInfo(
|
|
|
51
49
|
|
|
52
50
|
const isVertical = frame.docked === "left" || frame.docked === "right"
|
|
53
51
|
const sizeKey = isVertical ? "width" : "height" as const
|
|
54
|
-
const posKey = isVertical ? "x" : "y"
|
|
55
|
-
const oppositePosKey = isVertical ? "y" : "x"
|
|
56
|
-
const oppositeSizeKey = isVertical ? "height" : "width"
|
|
57
|
-
|
|
58
52
|
|
|
59
53
|
const currentSize = frame[sizeKey]
|
|
60
|
-
const
|
|
61
|
-
const shrinkAmount = currentSize - collapseAmount
|
|
62
|
-
|
|
63
|
-
const dockedSide = frame.docked as EdgeSide
|
|
64
|
-
|
|
65
|
-
const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "shrink")
|
|
66
|
-
|
|
67
|
-
// note fully collapsed frames without an area are already excluded by getFramesRedistributeInfo
|
|
68
|
-
const otherFrameIds = Object.keys(win.frames).filter(id => id !== frameId)
|
|
69
|
-
|
|
70
|
-
const redistributeSide = oppositeSide(dockedSide)
|
|
54
|
+
const shrinkAmount = currentSize - collapseSizeScaled[sizeKey]
|
|
71
55
|
|
|
72
|
-
const
|
|
56
|
+
const shrinkResult = getFrameShrinkInfo(win, frameId, shrinkAmount, frame.docked, { allowOutOfBounds: true })
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (changes instanceof KnownError) {
|
|
77
|
-
// we should never get out of bounds and because this is a collapse there should always be space
|
|
78
|
-
if (changes.code === LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS || changes.code === LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE) {
|
|
79
|
-
changes.message = `This error should never happen, please file a bug report: ${changes.message}`
|
|
80
|
-
throw changes
|
|
81
|
-
}
|
|
82
|
-
return changes
|
|
58
|
+
if (shrinkResult instanceof KnownError) {
|
|
59
|
+
return shrinkResult
|
|
83
60
|
}
|
|
84
|
-
applyFrameChanges(win, changes)
|
|
85
|
-
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
86
|
-
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
pushIfNotIn(toExtract,
|
|
62
|
+
applyFrameChanges(win, shrinkResult)
|
|
63
|
+
pushIfNotIn(toExtract, shrinkResult.modified.map(_ => _.id))
|
|
90
64
|
|
|
91
|
-
if (frame.docked === "right" || frame.docked === "bottom") {
|
|
92
|
-
frame[posKey] = frame[posKey] + (frame[sizeKey] - collapseSizeScaled[sizeKey])
|
|
93
|
-
}
|
|
94
|
-
frame[sizeKey] = collapseSizeScaled[sizeKey]
|
|
95
65
|
frame.collapsed = currentSize
|
|
96
66
|
// when we collapse to 0 it's a special case where the frame will always fit the entire window edge
|
|
97
67
|
// for proper uncollapsing later
|
|
98
68
|
if (collapseSizeScaled[sizeKey] === 0) {
|
|
69
|
+
const oppositePosKey = isVertical ? "y" : "x"
|
|
70
|
+
const oppositeSizeKey = isVertical ? "height" : "width"
|
|
99
71
|
frame[oppositePosKey] = 0
|
|
100
72
|
frame[oppositeSizeKey] = settings.maxInt
|
|
101
73
|
}
|
|
102
74
|
|
|
103
75
|
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
104
76
|
}
|
|
105
|
-
|
|
@@ -3,10 +3,12 @@ import { walk } from "@alanscodelog/utils/walk"
|
|
|
3
3
|
|
|
4
4
|
import { applyFrameChanges } from "./applyFrameChanges.js"
|
|
5
5
|
import { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js"
|
|
6
|
+
import { getFrameShrinkInfo } from "./getFrameShrinkInfo.js"
|
|
6
7
|
import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js"
|
|
7
8
|
import { getFrameUndockInfo } from "./getFrameUndockInfo.js"
|
|
8
9
|
|
|
9
10
|
import { cloneFrame } from "../helpers/cloneFrame.js"
|
|
11
|
+
import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js"
|
|
10
12
|
import { getDockBoundaries } from "../helpers/getDockBoundaries.js"
|
|
11
13
|
import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js"
|
|
12
14
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
@@ -33,7 +35,8 @@ export function getFrameDockInfo(
|
|
|
33
35
|
| KnownError<typeof LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES>
|
|
34
36
|
| KnownError<typeof LAYOUT_ERROR.FRAME_ALREADY_DOCKED_ON_SIDE>
|
|
35
37
|
| KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME>
|
|
36
|
-
| KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES>
|
|
38
|
+
| KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES>
|
|
39
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME> {
|
|
37
40
|
// its easier to just clone the window and extract changes later
|
|
38
41
|
// setting the var ensures we don't accidentally mutate the original
|
|
39
42
|
win = walk(win, undefined, { save: true }) as typeof win
|
|
@@ -57,8 +60,9 @@ export function getFrameDockInfo(
|
|
|
57
60
|
|
|
58
61
|
const isHorizontal = side === "left" || side === "right"
|
|
59
62
|
const perpendicular = isHorizontal ? "width" : "height"
|
|
63
|
+
const posKey = isHorizontal ? "x" : "y"
|
|
64
|
+
const sizeKey = isHorizontal ? "width" : "height"
|
|
60
65
|
|
|
61
|
-
const oldFrame = cloneFrame(frame)
|
|
62
66
|
const otherFrameIds = Object.keys(win.frames).filter(_ => _ !== frameId)
|
|
63
67
|
const nonDockedFrameIds = otherFrameIds.filter(id => !win.frames[id].docked)
|
|
64
68
|
if (nonDockedFrameIds.length === 0) {
|
|
@@ -66,68 +70,84 @@ export function getFrameDockInfo(
|
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
// if its the only frame allow it to be as big as it likes
|
|
69
|
-
const
|
|
70
|
-
const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular],
|
|
73
|
+
const maxPerpendicular = maxPerpendicularLength ?? settings.maxPerpendicularLengthScaled.width
|
|
74
|
+
const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular], maxPerpendicular) : frame[perpendicular]
|
|
71
75
|
|
|
72
76
|
frame.docked = side
|
|
73
77
|
frame.collapsed = undefined
|
|
74
78
|
|
|
75
79
|
const toExtract = [frame.id]
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (changes instanceof Error) return changes
|
|
80
|
-
applyFrameChanges(win, changes)
|
|
81
|
-
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// redistribute other non-docked frames to make room for the new dock.
|
|
85
|
-
const sideToPushTowards = oppositeSide(side)
|
|
86
|
-
|
|
87
|
-
const posKey = isHorizontal ? "x" : "y"
|
|
88
|
-
const sizeKey = isHorizontal ? "width" : "height"
|
|
89
|
-
|
|
90
|
-
const pinnedEdgeCoordinates: number[] = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey)
|
|
91
|
-
|
|
92
|
-
const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates })
|
|
93
|
-
|
|
94
|
-
if (redistributeChanges instanceof KnownError) {
|
|
95
|
-
return redistributeChanges
|
|
81
|
+
if (frame[perpendicular] === perpendicularLength) {
|
|
82
|
+
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
96
83
|
}
|
|
97
|
-
applyFrameChanges(win, redistributeChanges)
|
|
98
|
-
pushIfNotIn(toExtract, redistributeChanges.modified.map(_ => _.id))
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const { minX, maxX, minY, maxY } = getDockBoundaries(win)
|
|
102
|
-
switch (side) {
|
|
103
|
-
case "left":
|
|
104
|
-
frame.x = 0
|
|
105
|
-
frame.y = minY
|
|
106
|
-
frame.width = perpendicularLength
|
|
107
|
-
frame.height = maxY - minY
|
|
108
|
-
break
|
|
109
|
-
case "right":
|
|
110
|
-
frame.x = settings.maxInt - perpendicularLength
|
|
111
|
-
frame.y = minY
|
|
112
|
-
frame.width = perpendicularLength
|
|
113
|
-
frame.height = maxY - minY
|
|
114
|
-
break
|
|
115
|
-
case "top":
|
|
116
|
-
frame.x = minX
|
|
117
|
-
frame.y = 0
|
|
118
|
-
frame.width = maxX - minX
|
|
119
|
-
frame.height = perpendicularLength
|
|
120
|
-
break
|
|
121
|
-
case "bottom":
|
|
122
|
-
frame.x = minX
|
|
123
|
-
frame.y = settings.maxInt - perpendicularLength
|
|
124
|
-
frame.width = maxX - minX
|
|
125
|
-
frame.height = perpendicularLength
|
|
126
|
-
break
|
|
127
|
-
}
|
|
128
|
-
|
|
129
84
|
|
|
130
|
-
|
|
85
|
+
// check if the frame touches the full edge it will be docked to (not just it's dock boundary)
|
|
86
|
+
// if so, we can use getFrameShrinkInfo instead of fill-empty-space + redistribute
|
|
87
|
+
// this feels better as otherwise, the other frames feel like they are unnecessarily moved to the other side
|
|
88
|
+
// this way where the frame *was* feels like it's taken into account
|
|
89
|
+
const touchesEntireEdge = findEdgesTouchingWindow(frame)[side] === "full"
|
|
90
|
+
|
|
91
|
+
if (touchesEntireEdge) {
|
|
92
|
+
const shrinkAmount = frame[sizeKey] - perpendicularLength
|
|
93
|
+
if (shrinkAmount > 0) {
|
|
94
|
+
const shrinkResult = getFrameShrinkInfo(win, frameId, shrinkAmount, side)
|
|
95
|
+
if (shrinkResult instanceof KnownError) {
|
|
96
|
+
return shrinkResult
|
|
97
|
+
}
|
|
98
|
+
applyFrameChanges(win, shrinkResult)
|
|
99
|
+
pushIfNotIn(toExtract, shrinkResult.modified.map(_ => _.id))
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const oldFrame = cloneFrame(frame)
|
|
103
|
+
|
|
104
|
+
// fills just the hole left by the frame when it was moved
|
|
105
|
+
const changes = getFillEmptySpaceInfo(win, oldFrame, [], [frameId])
|
|
106
|
+
if (changes instanceof Error) return changes
|
|
107
|
+
applyFrameChanges(win, changes)
|
|
108
|
+
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
109
|
+
|
|
110
|
+
// redistribute other non-docked frames to make room for the new dock.
|
|
111
|
+
const sideToPushTowards = oppositeSide(side)
|
|
112
|
+
|
|
113
|
+
const pinnedEdgeCoordinates: number[] = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey)
|
|
114
|
+
|
|
115
|
+
const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates })
|
|
116
|
+
|
|
117
|
+
if (redistributeChanges instanceof KnownError) {
|
|
118
|
+
return redistributeChanges
|
|
119
|
+
}
|
|
120
|
+
applyFrameChanges(win, redistributeChanges)
|
|
121
|
+
pushIfNotIn(toExtract, redistributeChanges.modified.map(_ => _.id))
|
|
122
|
+
|
|
123
|
+
const { minX, maxX, minY, maxY } = getDockBoundaries(win)
|
|
124
|
+
switch (side) {
|
|
125
|
+
case "left":
|
|
126
|
+
frame.x = 0
|
|
127
|
+
frame.y = minY
|
|
128
|
+
frame.width = perpendicularLength
|
|
129
|
+
frame.height = maxY - minY
|
|
130
|
+
break
|
|
131
|
+
case "right":
|
|
132
|
+
frame.x = settings.maxInt - perpendicularLength
|
|
133
|
+
frame.y = minY
|
|
134
|
+
frame.width = perpendicularLength
|
|
135
|
+
frame.height = maxY - minY
|
|
136
|
+
break
|
|
137
|
+
case "top":
|
|
138
|
+
frame.x = minX
|
|
139
|
+
frame.y = 0
|
|
140
|
+
frame.width = maxX - minX
|
|
141
|
+
frame.height = perpendicularLength
|
|
142
|
+
break
|
|
143
|
+
case "bottom":
|
|
144
|
+
frame.x = minX
|
|
145
|
+
frame.y = settings.maxInt - perpendicularLength
|
|
146
|
+
frame.width = maxX - minX
|
|
147
|
+
frame.height = perpendicularLength
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}
|
|
131
151
|
|
|
132
|
-
return { modified:
|
|
152
|
+
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
133
153
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn"
|
|
2
|
+
import { walk } from "@alanscodelog/utils/walk"
|
|
3
|
+
|
|
4
|
+
import { applyFrameChanges } from "./applyFrameChanges.js"
|
|
5
|
+
import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js"
|
|
6
|
+
|
|
7
|
+
import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js"
|
|
8
|
+
import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js"
|
|
9
|
+
import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js"
|
|
10
|
+
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
11
|
+
import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js"
|
|
12
|
+
import { LAYOUT_ERROR } from "../types/index.js"
|
|
13
|
+
import { KnownError } from "../utils/KnownError.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a {@link LayoutChange} with the information necessary to expand a frame touching a window edge by a given amount.
|
|
17
|
+
*
|
|
18
|
+
* Changes can be applied to a window with {@link applyFrameChanges}.
|
|
19
|
+
*
|
|
20
|
+
* It expands the frame by redistributing space from neighboring frames.
|
|
21
|
+
*/
|
|
22
|
+
export function getFrameExpandInfo(
|
|
23
|
+
win: LayoutWindow,
|
|
24
|
+
frameId: string,
|
|
25
|
+
amount: number,
|
|
26
|
+
expandSide: EdgeSide
|
|
27
|
+
): LayoutChange
|
|
28
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS>
|
|
29
|
+
| KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE>
|
|
30
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>
|
|
31
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME> {
|
|
32
|
+
win = walk(win, undefined, { save: true })
|
|
33
|
+
const frame = win.frames[frameId]
|
|
34
|
+
if (!frame) { throw new Error(`Unknown frame ${frameId}`) }
|
|
35
|
+
if (!frame.docked) { throw new Error(`Frame ${frameId} is not docked.`) }
|
|
36
|
+
const toExtract = [frame.id]
|
|
37
|
+
|
|
38
|
+
if (Object.keys(win.frames).length === 1) {
|
|
39
|
+
return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot expand.`, { frame })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// verify the frame actually touches the specified edge
|
|
43
|
+
if (!(expandSide in findEdgesTouchingWindow(frame))) {
|
|
44
|
+
throw new Error(`Frame ${frameId} does not touch the ${expandSide} edge.`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
const isVertical = frame.docked === "left" || frame.docked === "right"
|
|
49
|
+
const sizeKey = isVertical ? "width" : "height" as const
|
|
50
|
+
const posKey = isVertical ? "x" : "y"
|
|
51
|
+
const currentSize = frame[sizeKey]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
const dockedSide = frame.docked as EdgeSide
|
|
55
|
+
|
|
56
|
+
const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "expand")
|
|
57
|
+
|
|
58
|
+
// note fully collapsed frames without an area are already excluded by getFramesRedistributeInfo
|
|
59
|
+
const otherFrameIds = Object.keys(win.frames).filter(id => id !== frameId)
|
|
60
|
+
|
|
61
|
+
const redistributeSide = oppositeSide(frame.docked)
|
|
62
|
+
|
|
63
|
+
const pinnedEdgeCoordinates: number[] = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey)
|
|
64
|
+
|
|
65
|
+
const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, amount, { pinnedEdgeCoordinates })
|
|
66
|
+
|
|
67
|
+
if (changes instanceof KnownError) {
|
|
68
|
+
return changes
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
applyFrameChanges(win, changes)
|
|
72
|
+
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
73
|
+
|
|
74
|
+
applyFixes()
|
|
75
|
+
pushIfNotIn(toExtract, toFix)
|
|
76
|
+
|
|
77
|
+
frame[sizeKey] = currentSize + amount
|
|
78
|
+
|
|
79
|
+
if (frame.docked === "right" || frame.docked === "bottom") {
|
|
80
|
+
frame[posKey] -= amount
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
84
|
+
}
|
|
@@ -7,7 +7,7 @@ import { getSideTouching } from "../helpers/getSideTouching.js"
|
|
|
7
7
|
import { isEdgeEqual } from "../helpers/isEdgeEqual.js"
|
|
8
8
|
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
9
9
|
import { sideToDirection } from "../helpers/sideToDirection.js"
|
|
10
|
-
import type {
|
|
10
|
+
import type { LayoutChange, LayoutWindow, Zone } from "../types/index.js"
|
|
11
11
|
import { LAYOUT_ERROR } from "../types/index.js"
|
|
12
12
|
import { KnownError } from "../utils/KnownError.js"
|
|
13
13
|
|
|
@@ -150,9 +150,9 @@ import { KnownError } from "../utils/KnownError.js"
|
|
|
150
150
|
|
|
151
151
|
export function getFrameRearrangeInfo(
|
|
152
152
|
win: LayoutWindow,
|
|
153
|
-
|
|
153
|
+
movingFrameId: string,
|
|
154
154
|
hoveredFrameId: string,
|
|
155
|
-
zoneSide:
|
|
155
|
+
zoneSide: Zone["side"]
|
|
156
156
|
): LayoutChange<"split" | "swap" | "rearrange">
|
|
157
157
|
| KnownError<typeof LAYOUT_ERROR.CANT_SWAP_WITH_SELF>
|
|
158
158
|
| KnownError<typeof LAYOUT_ERROR.CANT_SPLIT_FRAME_TOO_SMALL>
|
|
@@ -161,10 +161,10 @@ export function getFrameRearrangeInfo(
|
|
|
161
161
|
| KnownError<typeof LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED>
|
|
162
162
|
| KnownError<typeof LAYOUT_ERROR.CANT_SPLIT_DOCKED_FRAME>
|
|
163
163
|
| KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES> {
|
|
164
|
-
const draggingFrame = { ...win.frames[
|
|
164
|
+
const draggingFrame = { ...win.frames[movingFrameId] }
|
|
165
165
|
const hoveredFrame = { ...win.frames[hoveredFrameId] }
|
|
166
166
|
|
|
167
|
-
if (
|
|
167
|
+
if (movingFrameId === hoveredFrameId) {
|
|
168
168
|
if (zoneSide === "center") return new KnownError(LAYOUT_ERROR.CANT_SWAP_WITH_SELF, `Can't swap frame with self.`, { frame: hoveredFrame, zoneSide })
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -180,12 +180,12 @@ export function getFrameRearrangeInfo(
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
if (draggingFrame.docked && !hoveredFrame.docked) { // center swapping IS allowed above
|
|
183
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED, `Can't rearrange docked frame ${
|
|
183
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_DOCKED_WITH_NON_DOCKED, `Can't rearrange docked frame ${movingFrameId} with non-docked frame ${hoveredFrameId}, can only swap.`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// block edge rearrange on docked frames
|
|
187
187
|
if (hoveredFrame.docked) { // again center swapping docked to undocked and vice versa is allowed
|
|
188
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_WITH_DOCKED_EDGES, `Can't rearrange with docked frame ${hoveredFrameId} edge, can only swap.`, {
|
|
188
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_WITH_DOCKED_EDGES, `Can't rearrange with docked frame ${hoveredFrameId} edge, can only swap.`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
const touchingSide = getSideTouching(draggingFrame, hoveredFrame)
|
|
@@ -206,7 +206,7 @@ export function getFrameRearrangeInfo(
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
if (hoverOppositeSide === zoneSide) {
|
|
209
|
-
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_TO_SAME_RELATIVE_POSITION, `Frame ${
|
|
209
|
+
return new KnownError(LAYOUT_ERROR.CANT_REARRANGE_TO_SAME_RELATIVE_POSITION, `Frame ${movingFrameId} is already on the ${zoneSide} of ${hoveredFrameId}`, { movingFrameId, hoveredFrameId, zoneSide })
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -224,7 +224,7 @@ export function getFrameRearrangeInfo(
|
|
|
224
224
|
const splitFrame = splitResult.modified[0]
|
|
225
225
|
const newFrame = splitResult.created[0]
|
|
226
226
|
|
|
227
|
-
if (
|
|
227
|
+
if (movingFrameId === hoveredFrameId) {
|
|
228
228
|
return { ...splitResult, deleted: [], info: "split" }
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -249,7 +249,7 @@ export function getFrameRearrangeInfo(
|
|
|
249
249
|
[draggingFrame.id]: draggingFrame
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
-
const changes = getFillEmptySpaceInfo(winCopy, emptySpace, [hoveredFrameId,
|
|
252
|
+
const changes = getFillEmptySpaceInfo(winCopy, emptySpace, [hoveredFrameId, movingFrameId])
|
|
253
253
|
if (changes instanceof KnownError) return changes
|
|
254
254
|
|
|
255
255
|
// it will also be missing said changes unless it further moved the frames which is not alway
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn"
|
|
2
|
+
import { walk } from "@alanscodelog/utils/walk"
|
|
3
|
+
|
|
4
|
+
import { applyFrameChanges } from "./applyFrameChanges.js"
|
|
5
|
+
import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js"
|
|
6
|
+
|
|
7
|
+
import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js"
|
|
8
|
+
import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js"
|
|
9
|
+
import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js"
|
|
10
|
+
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
11
|
+
import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js"
|
|
12
|
+
import { LAYOUT_ERROR } from "../types/index.js"
|
|
13
|
+
import { KnownError } from "../utils/KnownError.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a {@link LayoutChange} with the information necessary to shrink a frame touching a window edge by a given amount.
|
|
17
|
+
*
|
|
18
|
+
* Changes can be applied to a window with {@link applyFrameChanges}.
|
|
19
|
+
*
|
|
20
|
+
* It shrinks the frame by redistributing the freed space to neighboring frames.
|
|
21
|
+
*/
|
|
22
|
+
export function getFrameShrinkInfo(
|
|
23
|
+
win: LayoutWindow,
|
|
24
|
+
frameId: string,
|
|
25
|
+
amount: number,
|
|
26
|
+
shrinkSide: EdgeSide,
|
|
27
|
+
{
|
|
28
|
+
allowOutOfBounds = false
|
|
29
|
+
}: {
|
|
30
|
+
/** Allow the resize span to exceed window bounds. */
|
|
31
|
+
allowOutOfBounds?: boolean
|
|
32
|
+
} = {}
|
|
33
|
+
): LayoutChange
|
|
34
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>
|
|
35
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME> {
|
|
36
|
+
win = walk(win, undefined, { save: true }) as typeof win
|
|
37
|
+
const frame = win.frames[frameId]
|
|
38
|
+
if (!frame) throw new Error(`Unknown frame ${frameId}`)
|
|
39
|
+
|
|
40
|
+
if (Object.keys(win.frames).length === 1) {
|
|
41
|
+
return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot shrink.`, { frame })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// verify the frame actually touches the specified edge
|
|
45
|
+
if (!(shrinkSide in findEdgesTouchingWindow(frame))) {
|
|
46
|
+
throw new Error(`Frame ${frameId} does not touch the ${shrinkSide} edge.`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const isVertical = shrinkSide === "left" || shrinkSide === "right"
|
|
50
|
+
const sizeKey = isVertical ? "width" : "height" as const
|
|
51
|
+
const posKey = isVertical ? "x" : "y"
|
|
52
|
+
|
|
53
|
+
const toExtract = [frame.id]
|
|
54
|
+
|
|
55
|
+
const { applyFixes, toFix } = framesRedistributeFix(win, frame, shrinkSide, posKey, sizeKey, "shrink")
|
|
56
|
+
|
|
57
|
+
// note fully collapsed frames without an area are already excluded by getFramesRedistributeInfo
|
|
58
|
+
const otherFrameIds = Object.keys(win.frames).filter(id => id !== frameId)
|
|
59
|
+
|
|
60
|
+
const redistributeSide = oppositeSide(shrinkSide)
|
|
61
|
+
|
|
62
|
+
const pinnedEdgeCoordinates: number[] = getPinnedEdgesForCollapsedFrames(win, frame, shrinkSide, posKey, sizeKey)
|
|
63
|
+
|
|
64
|
+
const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, -amount, { allowOutOfBounds, pinnedEdgeCoordinates })
|
|
65
|
+
|
|
66
|
+
if (changes instanceof KnownError) {
|
|
67
|
+
// we should never get out of bounds and because this is a shrink there should always be space
|
|
68
|
+
if (changes.code === LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS || changes.code === LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE) {
|
|
69
|
+
changes.message = `This error should never happen, please file a bug report: ${changes.message}`
|
|
70
|
+
throw changes
|
|
71
|
+
}
|
|
72
|
+
return changes
|
|
73
|
+
}
|
|
74
|
+
applyFrameChanges(win, changes)
|
|
75
|
+
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
applyFixes()
|
|
79
|
+
pushIfNotIn(toExtract, toFix)
|
|
80
|
+
|
|
81
|
+
if (shrinkSide === "right" || shrinkSide === "bottom") {
|
|
82
|
+
frame[posKey] = frame[posKey] + amount
|
|
83
|
+
}
|
|
84
|
+
frame[sizeKey] = frame[sizeKey] - amount
|
|
85
|
+
|
|
86
|
+
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
87
|
+
}
|
|
@@ -21,7 +21,7 @@ import { KnownError } from "../utils/KnownError.js"
|
|
|
21
21
|
export function getFrameSplitInfo(
|
|
22
22
|
frame: LayoutFrame,
|
|
23
23
|
dir: Direction,
|
|
24
|
-
|
|
24
|
+
movePointOrPosition: Point | number | "midpoint" = "midpoint",
|
|
25
25
|
minSize: Size = settings.minSizeScaled,
|
|
26
26
|
snapAmount: Point = settings.snapPointScaled
|
|
27
27
|
): LayoutChange
|
|
@@ -42,11 +42,11 @@ export function getFrameSplitInfo(
|
|
|
42
42
|
const sizeKey: "width" | "height" = isHorz ? "width" : "height"
|
|
43
43
|
const posKey: "x" | "y" = isHorz ? "x" : "y"
|
|
44
44
|
|
|
45
|
-
const position =
|
|
45
|
+
const position = movePointOrPosition === "midpoint"
|
|
46
46
|
? (isHorz ? frame.x + (frame.width / 2) : frame.y + (frame.height / 2))
|
|
47
|
-
: typeof
|
|
48
|
-
?
|
|
49
|
-
:
|
|
47
|
+
: typeof movePointOrPosition === "number"
|
|
48
|
+
? movePointOrPosition
|
|
49
|
+
: movePointOrPosition[isHorz ? "x" : "y"]
|
|
50
50
|
|
|
51
51
|
const safePosition = findSafeSplitEdgeAndPosition(
|
|
52
52
|
frame,
|