@witchcraft/layout 0.4.2 → 0.4.4

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.
Files changed (65) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/LayoutDragEdgeGrabbed.vue +1 -1
  3. package/dist/runtime/components/LayoutDragEdgeHandle.vue +1 -2
  4. package/dist/runtime/components/LayoutWindow.d.vue.ts +12 -0
  5. package/dist/runtime/components/LayoutWindow.vue +5 -4
  6. package/dist/runtime/components/LayoutWindow.vue.d.ts +12 -0
  7. package/dist/runtime/composables/useFrames.d.ts +38 -22
  8. package/dist/runtime/composables/useFrames.js +44 -8
  9. package/dist/runtime/demo/tailwind.css +1 -1
  10. package/dist/runtime/drag/CloseAction.d.ts +3 -0
  11. package/dist/runtime/drag/CloseAction.js +5 -0
  12. package/dist/runtime/drag/DragActionHandler.d.ts +4 -1
  13. package/dist/runtime/drag/DragActionHandler.js +2 -2
  14. package/dist/runtime/drag/FrameDragAction.d.ts +3 -0
  15. package/dist/runtime/drag/FrameDragAction.js +5 -0
  16. package/dist/runtime/drag/SplitAction.d.ts +3 -0
  17. package/dist/runtime/drag/SplitAction.js +8 -3
  18. package/dist/runtime/helpers/findEdgesTouchingWindow.d.ts +7 -0
  19. package/dist/runtime/helpers/findEdgesTouchingWindow.js +20 -0
  20. package/dist/runtime/helpers/index.d.ts +8 -1
  21. package/dist/runtime/helpers/index.js +8 -1
  22. package/dist/runtime/layout/debugFrame.js +5 -1
  23. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +1 -1
  24. package/dist/runtime/layout/getFrameCollapseInfo.js +9 -29
  25. package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
  26. package/dist/runtime/layout/getFrameDockInfo.js +61 -44
  27. package/dist/runtime/layout/getFrameExpandInfo.d.ts +11 -0
  28. package/dist/runtime/layout/getFrameExpandInfo.js +49 -0
  29. package/dist/runtime/layout/getFrameShrinkInfo.d.ts +14 -0
  30. package/dist/runtime/layout/getFrameShrinkInfo.js +48 -0
  31. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +1 -1
  32. package/dist/runtime/layout/getFrameUncollapseInfo.js +6 -20
  33. package/dist/runtime/layout/getUpdateWindowSizeInfo.js +10 -12
  34. package/dist/runtime/layout/index.d.ts +3 -0
  35. package/dist/runtime/layout/index.js +3 -0
  36. package/dist/runtime/settings.js +1 -1
  37. package/dist/runtime/types/index.d.ts +72 -0
  38. package/dist/runtime/types/index.js +2 -1
  39. package/dist/runtime/types/vue.d.ts +12 -3
  40. package/package.json +3 -2
  41. package/src/runtime/components/LayoutDragEdgeGrabbed.vue +1 -1
  42. package/src/runtime/components/LayoutDragEdgeHandle.vue +1 -2
  43. package/src/runtime/components/LayoutWindow.vue +8 -6
  44. package/src/runtime/composables/useFrames.ts +61 -32
  45. package/src/runtime/demo/tailwind.css +4 -0
  46. package/src/runtime/drag/CloseAction.ts +7 -0
  47. package/src/runtime/drag/DragActionHandler.ts +3 -3
  48. package/src/runtime/drag/FrameDragAction.ts +9 -0
  49. package/src/runtime/drag/SplitAction.ts +11 -3
  50. package/src/runtime/helpers/findEdgesTouchingWindow.ts +30 -0
  51. package/src/runtime/helpers/index.ts +8 -1
  52. package/src/runtime/layout/debugFrame.ts +6 -1
  53. package/src/runtime/layout/getFrameCollapseInfo.ts +12 -41
  54. package/src/runtime/layout/getFrameDockInfo.ts +77 -57
  55. package/src/runtime/layout/getFrameExpandInfo.ts +84 -0
  56. package/src/runtime/layout/getFrameShrinkInfo.ts +87 -0
  57. package/src/runtime/layout/getFrameUncollapseInfo.ts +8 -30
  58. package/src/runtime/layout/getUpdateWindowSizeInfo.ts +16 -22
  59. package/src/runtime/layout/index.ts +3 -0
  60. package/src/runtime/settings.ts +1 -1
  61. package/src/runtime/types/index.ts +41 -1
  62. package/src/runtime/types/vue.ts +5 -3
  63. package/dist/runtime/drag/defaultDragActions.d.ts +0 -9
  64. package/dist/runtime/drag/defaultDragActions.js +0 -10
  65. package/src/runtime/drag/defaultDragActions.ts +0 -20
@@ -6,6 +6,7 @@ import { createSplitDecoFromDrag } from "../layout/createSplitDecoFromDrag.js";
6
6
  import { getFrameSplitInfo } from "../layout/getFrameSplitInfo.js";
7
7
  export class SplitAction {
8
8
  name = "split";
9
+ minDragDistance = 5;
9
10
  state = {};
10
11
  // this is initialized by `this.reset()`
11
12
  debug = false;
@@ -24,6 +25,7 @@ export class SplitAction {
24
25
  this.hooks = hooks;
25
26
  this.reset();
26
27
  if (config?.debug) this.debug = true;
28
+ if (config?.minDragDistance !== void 0) this.minDragDistance = config.minDragDistance;
27
29
  if (config?.splitHints?.actions) this.splitHints.actions = config.splitHints.actions;
28
30
  if (config?.splitHints?.transformError) this.splitHints.transformError = config.splitHints.transformError;
29
31
  }
@@ -62,7 +64,7 @@ export class SplitAction {
62
64
  canHandleRequest(e, state) {
63
65
  const { draggingEdges } = state;
64
66
  if (draggingEdges.length !== 1) return false;
65
- this.setTextHints(state.isDragging === "edge" ? true : void 0);
67
+ this.setTextHints(state.isDragging === "edge" && !state.isDraggingFromWindowEdge ? true : void 0);
66
68
  if (this.handleEvent(e, state)) {
67
69
  this.hooks.onStart?.();
68
70
  return true;
@@ -103,7 +105,7 @@ export class SplitAction {
103
105
  dragDirections[oppositeOrientation],
104
106
  dragPoint
105
107
  );
106
- this.setTextHints(canSplit);
108
+ this.setTextHints(state.isDragging === "edge" && !state.isDraggingFromWindowEdge ? canSplit : void 0);
107
109
  if (!(canSplit instanceof Error)) {
108
110
  this.state.allowed = true;
109
111
  this.state.res = canSplit;
@@ -117,7 +119,10 @@ export class SplitAction {
117
119
  }
118
120
  }
119
121
  onDragChange(_type, _e, state) {
120
- const { dragHoveredFrame } = state;
122
+ const { dragHoveredFrame, dragDistance } = state;
123
+ if (dragDistance <= this.minDragDistance) {
124
+ return { updateEdges: false, shapes: [], showDragging: false };
125
+ }
121
126
  let ok = false;
122
127
  if (dragHoveredFrame) {
123
128
  if (this.state.lastPoint?.x === state.dragPoint?.x && this.state.lastPoint?.y === state.dragPoint?.y && this.state.lastReturn) {
@@ -0,0 +1,7 @@
1
+ import type { EdgeSide, LayoutFrame } from "../types/index.js";
2
+ /**
3
+ * Checks which window edges a frame touches and how.
4
+ *
5
+ * @returns Record mapping touched sides to `"full"` (spans the entire edge) or `"partial"` (touches but doesn't span fully). Only touched sides are included as keys.
6
+ */
7
+ export declare function findEdgesTouchingWindow(frame: LayoutFrame): Partial<Record<EdgeSide, "full" | "partial">>;
@@ -0,0 +1,20 @@
1
+ import { settings } from "../settings.js";
2
+ export function findEdgesTouchingWindow(frame) {
3
+ const maxInt = settings.maxInt;
4
+ const result = {};
5
+ const spansVertical = frame.y === 0 && frame.y + frame.height === maxInt;
6
+ const spansHorizontal = frame.x === 0 && frame.x + frame.width === maxInt;
7
+ if (frame.x === 0) {
8
+ result.left = spansVertical ? "full" : "partial";
9
+ }
10
+ if (frame.x + frame.width === maxInt) {
11
+ result.right = spansVertical ? "full" : "partial";
12
+ }
13
+ if (frame.y === 0) {
14
+ result.top = spansHorizontal ? "full" : "partial";
15
+ }
16
+ if (frame.y + frame.height === maxInt) {
17
+ result.bottom = spansHorizontal ? "full" : "partial";
18
+ }
19
+ return result;
20
+ }
@@ -9,6 +9,7 @@ export { assertValidWinAndFrameIds } from "./assertValidWinAndFrameIds.js";
9
9
  export { assertWindowHasActiveFrame } from "./assertWindowHasActiveFrame.js";
10
10
  export { cloneFrame } from "./cloneFrame.js";
11
11
  export { cloneFrames } from "./cloneFrames.js";
12
+ export { consoleDebugWindow } from "./consoleDebugWindow.js";
12
13
  export { containsEdge } from "./containsEdge.js";
13
14
  export { convertLayoutWindowToWorkspace } from "./convertLayoutWindowToWorkspace.js";
14
15
  export { copySize } from "./copySize.js";
@@ -21,17 +22,21 @@ export { doesEdgeContinueEdge } from "./doesEdgeContinueEdge.js";
21
22
  export { edgeToPoints } from "./edgeToPoints.js";
22
23
  export { findDraggableEdge } from "./findDraggableEdge.js";
23
24
  export { findFrameDraggableEdges } from "./findFrameDraggableEdges.js";
25
+ export { framesRedistributeFix } from "./framesRedistributeFix.js";
24
26
  export { frameToEdges } from "./frameToEdges.js";
25
27
  export { frameToPoints } from "./frameToPoints.js";
28
+ export { getDockBoundaries } from "./getDockBoundaries.js";
26
29
  export { getEdgeLength } from "./getEdgeLength.js";
27
30
  export { getEdgeOrientation } from "./getEdgeOrientation.js";
28
31
  export { getEdgeSharedDirection } from "./getEdgeSharedDirection.js";
29
32
  export { getEdgeSide } from "./getEdgeSide.js";
30
33
  export { getFrameById } from "./getFrameById.js";
31
34
  export { getFrameConstant } from "./getFrameConstant.js";
35
+ export { findEdgesTouchingWindow } from "./findEdgesTouchingWindow.js";
32
36
  export { getIntersections } from "./getIntersections.js";
33
37
  export { getIntersectionsCss } from "./getIntersectionsCss.js";
34
38
  export { getMoveEdgeInfo } from "./getMoveEdgeInfo.js";
39
+ export { getPinnedEdgesForCollapsedFrames } from "./getPinnedEdgesForCollapsedFrames.js";
35
40
  export { getResizeLimit } from "./getResizeLimit.js";
36
41
  export { getShapeRectCss } from "./getShapeRectCss.js";
37
42
  export { getSideTouching } from "./getSideTouching.js";
@@ -52,9 +57,10 @@ export { isWindowEdge } from "./isWindowEdge.js";
52
57
  export { isWindowEdgePoint } from "./isWindowEdgePoint.js";
53
58
  export { moveEdge } from "./moveEdge.js";
54
59
  export { numberToScaledPercent } from "./numberToScaledPercent.js";
55
- export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js";
56
60
  export { oppositeSide } from "./oppositeSide.js";
61
+ export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js";
57
62
  export { resizeByEdge } from "./resizeByEdge.js";
63
+ export { rotateLayout } from "./rotateFrames.js";
58
64
  export { scaledPointToPx } from "./scaledPointToPx.js";
59
65
  export { sideToDirection } from "./sideToDirection.js";
60
66
  export { sideToOrientation } from "./sideToOrientation.js";
@@ -64,3 +70,4 @@ export { toId } from "./toId.js";
64
70
  export { toWindowCoord } from "./toWindowCoord.js";
65
71
  export { unionEdges } from "./unionEdges.js";
66
72
  export { updateWindowWithEvent } from "./updateWindowSizeWithEvent.js";
73
+ export { validateLayoutShape } from "./validateLayoutShape.js";
@@ -9,6 +9,7 @@ export { assertValidWinAndFrameIds } from "./assertValidWinAndFrameIds.js";
9
9
  export { assertWindowHasActiveFrame } from "./assertWindowHasActiveFrame.js";
10
10
  export { cloneFrame } from "./cloneFrame.js";
11
11
  export { cloneFrames } from "./cloneFrames.js";
12
+ export { consoleDebugWindow } from "./consoleDebugWindow.js";
12
13
  export { containsEdge } from "./containsEdge.js";
13
14
  export { convertLayoutWindowToWorkspace } from "./convertLayoutWindowToWorkspace.js";
14
15
  export { copySize } from "./copySize.js";
@@ -21,17 +22,21 @@ export { doesEdgeContinueEdge } from "./doesEdgeContinueEdge.js";
21
22
  export { edgeToPoints } from "./edgeToPoints.js";
22
23
  export { findDraggableEdge } from "./findDraggableEdge.js";
23
24
  export { findFrameDraggableEdges } from "./findFrameDraggableEdges.js";
25
+ export { framesRedistributeFix } from "./framesRedistributeFix.js";
24
26
  export { frameToEdges } from "./frameToEdges.js";
25
27
  export { frameToPoints } from "./frameToPoints.js";
28
+ export { getDockBoundaries } from "./getDockBoundaries.js";
26
29
  export { getEdgeLength } from "./getEdgeLength.js";
27
30
  export { getEdgeOrientation } from "./getEdgeOrientation.js";
28
31
  export { getEdgeSharedDirection } from "./getEdgeSharedDirection.js";
29
32
  export { getEdgeSide } from "./getEdgeSide.js";
30
33
  export { getFrameById } from "./getFrameById.js";
31
34
  export { getFrameConstant } from "./getFrameConstant.js";
35
+ export { findEdgesTouchingWindow } from "./findEdgesTouchingWindow.js";
32
36
  export { getIntersections } from "./getIntersections.js";
33
37
  export { getIntersectionsCss } from "./getIntersectionsCss.js";
34
38
  export { getMoveEdgeInfo } from "./getMoveEdgeInfo.js";
39
+ export { getPinnedEdgesForCollapsedFrames } from "./getPinnedEdgesForCollapsedFrames.js";
35
40
  export { getResizeLimit } from "./getResizeLimit.js";
36
41
  export { getShapeRectCss } from "./getShapeRectCss.js";
37
42
  export { getSideTouching } from "./getSideTouching.js";
@@ -52,9 +57,10 @@ export { isWindowEdge } from "./isWindowEdge.js";
52
57
  export { isWindowEdgePoint } from "./isWindowEdgePoint.js";
53
58
  export { moveEdge } from "./moveEdge.js";
54
59
  export { numberToScaledPercent } from "./numberToScaledPercent.js";
55
- export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js";
56
60
  export { oppositeSide } from "./oppositeSide.js";
61
+ export { pxSizeToScaledSize } from "./pxSizeToScaledSize.js";
57
62
  export { resizeByEdge } from "./resizeByEdge.js";
63
+ export { rotateLayout } from "./rotateFrames.js";
58
64
  export { scaledPointToPx } from "./scaledPointToPx.js";
59
65
  export { sideToDirection } from "./sideToDirection.js";
60
66
  export { sideToOrientation } from "./sideToOrientation.js";
@@ -64,3 +70,4 @@ export { toId } from "./toId.js";
64
70
  export { toWindowCoord } from "./toWindowCoord.js";
65
71
  export { unionEdges } from "./unionEdges.js";
66
72
  export { updateWindowWithEvent } from "./updateWindowSizeWithEvent.js";
73
+ export { validateLayoutShape } from "./validateLayoutShape.js";
@@ -1,5 +1,9 @@
1
+ import { keys } from "@alanscodelog/utils/keys";
2
+ const builtinProperties = ["id", "x", "y", "width", "height", "docked", "collapsed"];
1
3
  export function debugFrame(frame) {
2
4
  const f = frame;
5
+ const otherProperties = keys(f).filter((k) => !builtinProperties.includes(k)).map((k) => `${k}: ${f[k]}`);
6
+ const otherPropertiesString = otherProperties.length > 0 ? `, ${otherProperties.join(", ")}` : "";
3
7
  return `id: ${f.id.slice(0, 4)}, x: ${f.x}, y: ${f.y}, w: ${f.width}, h: ${f.height}
4
- docked: ${f.docked}, collapsed: ${f.collapsed}`;
8
+ docked: ${f.docked}, collapsed: ${f.collapsed}${otherPropertiesString}`;
5
9
  }
@@ -13,4 +13,4 @@ import { KnownError } from "../utils/KnownError.js";
13
13
  export declare function getFrameCollapseInfo(win: LayoutWindow, frameId: string, { collapseSizeScaled }?: {
14
14
  /** Override the target collapse size. If provided, the frame will collapse to this size instead of settings.collapseSizeScaled. */
15
15
  collapseSizeScaled?: Size;
16
- }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_COLLAPSE_NOT_DOCKED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>;
16
+ }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_COLLAPSE_NOT_DOCKED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -1,10 +1,7 @@
1
1
  import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "./applyFrameChanges.js";
4
- import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
- import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
6
- import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
7
- import { oppositeSide } from "../helpers/oppositeSide.js";
4
+ import { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
8
5
  import { settings } from "../settings.js";
9
6
  import { LAYOUT_ERROR } from "../types/index.js";
10
7
  import { KnownError } from "../utils/KnownError.js";
@@ -27,35 +24,18 @@ export function getFrameCollapseInfo(win, frameId, {
27
24
  }
28
25
  const isVertical = frame.docked === "left" || frame.docked === "right";
29
26
  const sizeKey = isVertical ? "width" : "height";
30
- const posKey = isVertical ? "x" : "y";
31
- const oppositePosKey = isVertical ? "y" : "x";
32
- const oppositeSizeKey = isVertical ? "height" : "width";
33
27
  const currentSize = frame[sizeKey];
34
- const collapseAmount = collapseSizeScaled[sizeKey];
35
- const shrinkAmount = currentSize - collapseAmount;
36
- const dockedSide = frame.docked;
37
- const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "shrink");
38
- const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
39
- const redistributeSide = oppositeSide(dockedSide);
40
- const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey);
41
- const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, -shrinkAmount, { allowOutOfBounds: true, pinnedEdgeCoordinates });
42
- if (changes instanceof KnownError) {
43
- if (changes.code === LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS || changes.code === LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE) {
44
- changes.message = `This error should never happen, please file a bug report: ${changes.message}`;
45
- throw changes;
46
- }
47
- return changes;
28
+ const shrinkAmount = currentSize - collapseSizeScaled[sizeKey];
29
+ const shrinkResult = getFrameShrinkInfo(win, frameId, shrinkAmount, frame.docked, { allowOutOfBounds: true });
30
+ if (shrinkResult instanceof KnownError) {
31
+ return shrinkResult;
48
32
  }
49
- applyFrameChanges(win, changes);
50
- pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
51
- applyFixes();
52
- pushIfNotIn(toExtract, toFix);
53
- if (frame.docked === "right" || frame.docked === "bottom") {
54
- frame[posKey] = frame[posKey] + (frame[sizeKey] - collapseSizeScaled[sizeKey]);
55
- }
56
- frame[sizeKey] = collapseSizeScaled[sizeKey];
33
+ applyFrameChanges(win, shrinkResult);
34
+ pushIfNotIn(toExtract, shrinkResult.modified.map((_) => _.id));
57
35
  frame.collapsed = currentSize;
58
36
  if (collapseSizeScaled[sizeKey] === 0) {
37
+ const oppositePosKey = isVertical ? "y" : "x";
38
+ const oppositeSizeKey = isVertical ? "height" : "width";
59
39
  frame[oppositePosKey] = 0;
60
40
  frame[oppositeSizeKey] = settings.maxInt;
61
41
  }
@@ -6,4 +6,4 @@ import { KnownError } from "../utils/KnownError.js";
6
6
  *
7
7
  * Changes can be applied to a window with {@link applyFrameChanges}.
8
8
  */
9
- export declare function getFrameDockInfo(win: LayoutWindow, frameId: string, side: EdgeSide, maxPerpendicularLength?: number): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES> | KnownError<typeof LAYOUT_ERROR.FRAME_ALREADY_DOCKED_ON_SIDE> | KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME> | KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES>;
9
+ export declare function getFrameDockInfo(win: LayoutWindow, frameId: string, side: EdgeSide, maxPerpendicularLength?: number): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES> | KnownError<typeof LAYOUT_ERROR.FRAME_ALREADY_DOCKED_ON_SIDE> | KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME> | KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -2,9 +2,11 @@ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "./applyFrameChanges.js";
4
4
  import { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
5
+ import { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
5
6
  import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
6
7
  import { getFrameUndockInfo } from "./getFrameUndockInfo.js";
7
8
  import { cloneFrame } from "../helpers/cloneFrame.js";
9
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
8
10
  import { getDockBoundaries } from "../helpers/getDockBoundaries.js";
9
11
  import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
10
12
  import { oppositeSide } from "../helpers/oppositeSide.js";
@@ -29,58 +31,73 @@ export function getFrameDockInfo(win, frameId, side, maxPerpendicularLength) {
29
31
  }
30
32
  const isHorizontal = side === "left" || side === "right";
31
33
  const perpendicular = isHorizontal ? "width" : "height";
32
- const oldFrame = cloneFrame(frame);
34
+ const posKey = isHorizontal ? "x" : "y";
35
+ const sizeKey = isHorizontal ? "width" : "height";
33
36
  const otherFrameIds = Object.keys(win.frames).filter((_) => _ !== frameId);
34
37
  const nonDockedFrameIds = otherFrameIds.filter((id) => !win.frames[id].docked);
35
38
  if (nonDockedFrameIds.length === 0) {
36
39
  return new KnownError(LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES, `Can't dock frame ${frameId} because there are no other undocked frames.`, { frameId });
37
40
  }
38
- const effectiveMaxPerpendicular = maxPerpendicularLength ?? settings.maxPerpendicularLengthScaled.width;
39
- const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular], effectiveMaxPerpendicular) : frame[perpendicular];
41
+ const maxPerpendicular = maxPerpendicularLength ?? settings.maxPerpendicularLengthScaled.width;
42
+ const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular], maxPerpendicular) : frame[perpendicular];
40
43
  frame.docked = side;
41
44
  frame.collapsed = void 0;
42
45
  const toExtract = [frame.id];
43
- const changes = getFillEmptySpaceInfo(win, oldFrame, [], [frameId]);
44
- if (changes instanceof Error) return changes;
45
- applyFrameChanges(win, changes);
46
- pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
47
- const sideToPushTowards = oppositeSide(side);
48
- const posKey = isHorizontal ? "x" : "y";
49
- const sizeKey = isHorizontal ? "width" : "height";
50
- const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey);
51
- const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates });
52
- if (redistributeChanges instanceof KnownError) {
53
- return redistributeChanges;
46
+ if (frame[perpendicular] === perpendicularLength) {
47
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
54
48
  }
55
- applyFrameChanges(win, redistributeChanges);
56
- pushIfNotIn(toExtract, redistributeChanges.modified.map((_) => _.id));
57
- const { minX, maxX, minY, maxY } = getDockBoundaries(win);
58
- switch (side) {
59
- case "left":
60
- frame.x = 0;
61
- frame.y = minY;
62
- frame.width = perpendicularLength;
63
- frame.height = maxY - minY;
64
- break;
65
- case "right":
66
- frame.x = settings.maxInt - perpendicularLength;
67
- frame.y = minY;
68
- frame.width = perpendicularLength;
69
- frame.height = maxY - minY;
70
- break;
71
- case "top":
72
- frame.x = minX;
73
- frame.y = 0;
74
- frame.width = maxX - minX;
75
- frame.height = perpendicularLength;
76
- break;
77
- case "bottom":
78
- frame.x = minX;
79
- frame.y = settings.maxInt - perpendicularLength;
80
- frame.width = maxX - minX;
81
- frame.height = perpendicularLength;
82
- break;
49
+ const touchesEntireEdge = findEdgesTouchingWindow(frame)[side] === "full";
50
+ if (touchesEntireEdge) {
51
+ const shrinkAmount = frame[sizeKey] - perpendicularLength;
52
+ if (shrinkAmount > 0) {
53
+ const shrinkResult = getFrameShrinkInfo(win, frameId, shrinkAmount, side);
54
+ if (shrinkResult instanceof KnownError) {
55
+ return shrinkResult;
56
+ }
57
+ applyFrameChanges(win, shrinkResult);
58
+ pushIfNotIn(toExtract, shrinkResult.modified.map((_) => _.id));
59
+ }
60
+ } else {
61
+ const oldFrame = cloneFrame(frame);
62
+ const changes = getFillEmptySpaceInfo(win, oldFrame, [], [frameId]);
63
+ if (changes instanceof Error) return changes;
64
+ applyFrameChanges(win, changes);
65
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
66
+ const sideToPushTowards = oppositeSide(side);
67
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey);
68
+ const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates });
69
+ if (redistributeChanges instanceof KnownError) {
70
+ return redistributeChanges;
71
+ }
72
+ applyFrameChanges(win, redistributeChanges);
73
+ pushIfNotIn(toExtract, redistributeChanges.modified.map((_) => _.id));
74
+ const { minX, maxX, minY, maxY } = getDockBoundaries(win);
75
+ switch (side) {
76
+ case "left":
77
+ frame.x = 0;
78
+ frame.y = minY;
79
+ frame.width = perpendicularLength;
80
+ frame.height = maxY - minY;
81
+ break;
82
+ case "right":
83
+ frame.x = settings.maxInt - perpendicularLength;
84
+ frame.y = minY;
85
+ frame.width = perpendicularLength;
86
+ frame.height = maxY - minY;
87
+ break;
88
+ case "top":
89
+ frame.x = minX;
90
+ frame.y = 0;
91
+ frame.width = maxX - minX;
92
+ frame.height = perpendicularLength;
93
+ break;
94
+ case "bottom":
95
+ frame.x = minX;
96
+ frame.y = settings.maxInt - perpendicularLength;
97
+ frame.width = maxX - minX;
98
+ frame.height = perpendicularLength;
99
+ break;
100
+ }
83
101
  }
84
- const res = toExtract.map((_) => win.frames[_]);
85
- return { modified: res, created: [], deleted: [] };
102
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
86
103
  }
@@ -0,0 +1,11 @@
1
+ import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js";
2
+ import { LAYOUT_ERROR } from "../types/index.js";
3
+ import { KnownError } from "../utils/KnownError.js";
4
+ /**
5
+ * Returns a {@link LayoutChange} with the information necessary to expand a frame touching a window edge by a given amount.
6
+ *
7
+ * Changes can be applied to a window with {@link applyFrameChanges}.
8
+ *
9
+ * It expands the frame by redistributing space from neighboring frames.
10
+ */
11
+ export declare function getFrameExpandInfo(win: LayoutWindow, frameId: string, amount: number, expandSide: EdgeSide): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -0,0 +1,49 @@
1
+ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
+ import { walk } from "@alanscodelog/utils/walk";
3
+ import { applyFrameChanges } from "./applyFrameChanges.js";
4
+ import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
6
+ import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
7
+ import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
8
+ import { oppositeSide } from "../helpers/oppositeSide.js";
9
+ import { LAYOUT_ERROR } from "../types/index.js";
10
+ import { KnownError } from "../utils/KnownError.js";
11
+ export function getFrameExpandInfo(win, frameId, amount, expandSide) {
12
+ win = walk(win, void 0, { save: true });
13
+ const frame = win.frames[frameId];
14
+ if (!frame) {
15
+ throw new Error(`Unknown frame ${frameId}`);
16
+ }
17
+ if (!frame.docked) {
18
+ throw new Error(`Frame ${frameId} is not docked.`);
19
+ }
20
+ const toExtract = [frame.id];
21
+ if (Object.keys(win.frames).length === 1) {
22
+ return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot expand.`, { frame });
23
+ }
24
+ if (!(expandSide in findEdgesTouchingWindow(frame))) {
25
+ throw new Error(`Frame ${frameId} does not touch the ${expandSide} edge.`);
26
+ }
27
+ const isVertical = frame.docked === "left" || frame.docked === "right";
28
+ const sizeKey = isVertical ? "width" : "height";
29
+ const posKey = isVertical ? "x" : "y";
30
+ const currentSize = frame[sizeKey];
31
+ const dockedSide = frame.docked;
32
+ const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "expand");
33
+ const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
34
+ const redistributeSide = oppositeSide(frame.docked);
35
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey);
36
+ const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, amount, { pinnedEdgeCoordinates });
37
+ if (changes instanceof KnownError) {
38
+ return changes;
39
+ }
40
+ applyFrameChanges(win, changes);
41
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
42
+ applyFixes();
43
+ pushIfNotIn(toExtract, toFix);
44
+ frame[sizeKey] = currentSize + amount;
45
+ if (frame.docked === "right" || frame.docked === "bottom") {
46
+ frame[posKey] -= amount;
47
+ }
48
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
49
+ }
@@ -0,0 +1,14 @@
1
+ import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js";
2
+ import { LAYOUT_ERROR } from "../types/index.js";
3
+ import { KnownError } from "../utils/KnownError.js";
4
+ /**
5
+ * Returns a {@link LayoutChange} with the information necessary to shrink a frame touching a window edge by a given amount.
6
+ *
7
+ * Changes can be applied to a window with {@link applyFrameChanges}.
8
+ *
9
+ * It shrinks the frame by redistributing the freed space to neighboring frames.
10
+ */
11
+ export declare function getFrameShrinkInfo(win: LayoutWindow, frameId: string, amount: number, shrinkSide: EdgeSide, { allowOutOfBounds }?: {
12
+ /** Allow the resize span to exceed window bounds. */
13
+ allowOutOfBounds?: boolean;
14
+ }): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -0,0 +1,48 @@
1
+ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
+ import { walk } from "@alanscodelog/utils/walk";
3
+ import { applyFrameChanges } from "./applyFrameChanges.js";
4
+ import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
6
+ import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
7
+ import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
8
+ import { oppositeSide } from "../helpers/oppositeSide.js";
9
+ import { LAYOUT_ERROR } from "../types/index.js";
10
+ import { KnownError } from "../utils/KnownError.js";
11
+ export function getFrameShrinkInfo(win, frameId, amount, shrinkSide, {
12
+ allowOutOfBounds = false
13
+ } = {}) {
14
+ win = walk(win, void 0, { save: true });
15
+ const frame = win.frames[frameId];
16
+ if (!frame) throw new Error(`Unknown frame ${frameId}`);
17
+ if (Object.keys(win.frames).length === 1) {
18
+ return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot shrink.`, { frame });
19
+ }
20
+ if (!(shrinkSide in findEdgesTouchingWindow(frame))) {
21
+ throw new Error(`Frame ${frameId} does not touch the ${shrinkSide} edge.`);
22
+ }
23
+ const isVertical = shrinkSide === "left" || shrinkSide === "right";
24
+ const sizeKey = isVertical ? "width" : "height";
25
+ const posKey = isVertical ? "x" : "y";
26
+ const toExtract = [frame.id];
27
+ const { applyFixes, toFix } = framesRedistributeFix(win, frame, shrinkSide, posKey, sizeKey, "shrink");
28
+ const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
29
+ const redistributeSide = oppositeSide(shrinkSide);
30
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, shrinkSide, posKey, sizeKey);
31
+ const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, -amount, { allowOutOfBounds, pinnedEdgeCoordinates });
32
+ if (changes instanceof KnownError) {
33
+ if (changes.code === LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS || changes.code === LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE) {
34
+ changes.message = `This error should never happen, please file a bug report: ${changes.message}`;
35
+ throw changes;
36
+ }
37
+ return changes;
38
+ }
39
+ applyFrameChanges(win, changes);
40
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
41
+ applyFixes();
42
+ pushIfNotIn(toExtract, toFix);
43
+ if (shrinkSide === "right" || shrinkSide === "bottom") {
44
+ frame[posKey] = frame[posKey] + amount;
45
+ }
46
+ frame[sizeKey] = frame[sizeKey] - amount;
47
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
48
+ }
@@ -12,4 +12,4 @@ import { KnownError } from "../utils/KnownError.js";
12
12
  export declare function getFrameUncollapseInfo(win: LayoutWindow, frameId: string, { restoreSize }?: {
13
13
  /** Override the size to restore the frame to. */
14
14
  restoreSize?: Size;
15
- }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>;
15
+ }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -1,10 +1,7 @@
1
1
  import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "./applyFrameChanges.js";
4
- import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
- import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
6
- import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
7
- import { oppositeSide } from "../helpers/oppositeSide.js";
4
+ import { getFrameExpandInfo } from "./getFrameExpandInfo.js";
8
5
  import { LAYOUT_ERROR } from "../types/index.js";
9
6
  import { KnownError } from "../utils/KnownError.js";
10
7
  export function getFrameUncollapseInfo(win, frameId, {
@@ -21,7 +18,6 @@ export function getFrameUncollapseInfo(win, frameId, {
21
18
  const toExtract = [frame.id];
22
19
  const isVertical = frame.docked === "left" || frame.docked === "right";
23
20
  const sizeKey = isVertical ? "width" : "height";
24
- const posKey = isVertical ? "x" : "y";
25
21
  const currentSize = frame[sizeKey];
26
22
  const storedSize = restoreSize !== void 0 ? restoreSize[sizeKey] : frame.collapsed;
27
23
  if (frame.collapsed === void 0) {
@@ -33,22 +29,12 @@ export function getFrameUncollapseInfo(win, frameId, {
33
29
  }
34
30
  const expandAmount = storedSize - currentSize;
35
31
  const dockedSide = frame.docked;
36
- const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "expand");
37
- const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
38
- const redistributeSide = oppositeSide(frame.docked);
39
- const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey);
40
- const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, expandAmount, { pinnedEdgeCoordinates });
41
- if (changes instanceof KnownError) {
42
- return changes;
32
+ const expandResult = getFrameExpandInfo(win, frameId, expandAmount, dockedSide);
33
+ if (expandResult instanceof KnownError) {
34
+ return expandResult;
43
35
  }
44
- applyFrameChanges(win, changes);
45
- pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
46
- applyFixes();
47
- pushIfNotIn(toExtract, toFix);
48
- frame[sizeKey] = storedSize;
36
+ applyFrameChanges(win, expandResult);
37
+ pushIfNotIn(toExtract, expandResult.modified.map((_) => _.id));
49
38
  frame.collapsed = void 0;
50
- if (frame.docked === "right" || frame.docked === "bottom") {
51
- frame[posKey] -= expandAmount;
52
- }
53
39
  return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
54
40
  }
@@ -1,8 +1,8 @@
1
1
  import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "../layout/applyFrameChanges.js";
4
- import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js";
5
- import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js";
4
+ import { getFrameExpandInfo } from "../layout/getFrameExpandInfo.js";
5
+ import { getFrameShrinkInfo } from "../layout/getFrameShrinkInfo.js";
6
6
  import { getRelaxFramesInfo } from "../layout/getRelaxFramesInfo.js";
7
7
  import { settings } from "../settings.js";
8
8
  import { KnownError } from "../utils/KnownError.js";
@@ -30,23 +30,21 @@ export function getUpdateWindowSizeInfo(win, event, { relaxPasses = 2 } = {}) {
30
30
  const targetScaled = Math.round(targetPxSize / (orientation === "horizontal" ? win.pxWidth : win.pxHeight) * maxInt);
31
31
  if (frame[sizeKey] === targetScaled) continue;
32
32
  if (frame[sizeKey] === 0) continue;
33
+ const side = frame.docked;
34
+ const diff = targetScaled - frame[sizeKey];
33
35
  let result;
34
- const wasCollapsed = frame.collapsed;
35
- const wasMinSize = { ...settings.minSize };
36
- settings.minSize = 1;
37
- if (targetScaled < frame[sizeKey]) {
38
- frame.collapsed = void 0;
39
- result = getFrameCollapseInfo(win, frame.id, { collapseSizeScaled: { width: targetScaled, height: targetScaled } });
36
+ if (diff < 0) {
37
+ result = getFrameShrinkInfo(win, frame.id, -diff, side, { allowOutOfBounds: true });
40
38
  } else {
41
- frame.collapsed = Infinity;
42
- result = getFrameUncollapseInfo(win, frame.id, { restoreSize: { width: targetScaled, height: targetScaled } });
39
+ const wasMinSize = { ...settings.minSize };
40
+ settings.minSize = 1;
41
+ result = getFrameExpandInfo(win, frame.id, diff, side);
42
+ settings.minSize = wasMinSize;
43
43
  }
44
44
  if (!(result instanceof KnownError)) {
45
45
  applyFrameChanges(win, result);
46
46
  pushIfNotIn(toExtract, result.modified.map((f) => f.id));
47
47
  }
48
- frame.collapsed = wasCollapsed;
49
- settings.minSize = wasMinSize;
50
48
  const result2 = getRelaxFramesInfo(win, relaxPasses);
51
49
  if (!(result2 instanceof KnownError)) {
52
50
  applyFrameChanges(win, result2);
@@ -12,13 +12,16 @@ export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
12
12
  export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js";
13
13
  export { getFrameDockInfo } from "./getFrameDockInfo.js";
14
14
  export { getFrameDragZones } from "./getFrameDragZones.js";
15
+ export { getFrameExpandInfo } from "./getFrameExpandInfo.js";
15
16
  export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js";
17
+ export { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
16
18
  export { getFrameSplitInfo } from "./getFrameSplitInfo.js";
17
19
  export { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
18
20
  export { getFrameSwapInfo } from "./getFrameSwapInfo.js";
19
21
  export { getFrameTo } from "./getFrameTo.js";
20
22
  export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js";
21
23
  export { getFrameUndockInfo } from "./getFrameUndockInfo.js";
24
+ export { getRelaxFramesInfo } from "./getRelaxFramesInfo.js";
22
25
  export { getUpdateWindowSizeInfo } from "./getUpdateWindowSizeInfo.js";
23
26
  export { getWindowDragZones } from "./getWindowDragZones.js";
24
27
  export { isPointInRect } from "./isPointInRect.js";