flexlayout-react 0.7.15 → 0.8.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/ChangeLog.txt +23 -0
- package/README.md +157 -330
- package/Screenshot_light.png +0 -0
- package/Screenshot_rounded.png +0 -0
- package/declarations/Attribute.d.ts +1 -1
- package/declarations/AttributeDefinitions.d.ts +1 -1
- package/declarations/DockLocation.d.ts +12 -12
- package/declarations/DropInfo.d.ts +12 -12
- package/declarations/I18nLabel.d.ts +12 -14
- package/declarations/Orientation.d.ts +7 -7
- package/declarations/PopupMenu.d.ts +1 -1
- package/declarations/Rect.d.ts +41 -28
- package/declarations/Types.d.ts +95 -79
- package/declarations/examples/demo/Utils.d.ts +4 -0
- package/declarations/index.d.ts +21 -22
- package/declarations/model/Action.d.ts +5 -5
- package/declarations/model/Actions.d.ts +127 -110
- package/declarations/model/BorderNode.d.ts +30 -34
- package/declarations/model/BorderSet.d.ts +3 -4
- package/declarations/model/ICloseType.d.ts +5 -5
- package/declarations/model/IDraggable.d.ts +2 -2
- package/declarations/model/IDropTarget.d.ts +2 -2
- package/declarations/model/IJsonModel.d.ts +811 -149
- package/declarations/model/LayoutWindow.d.ts +28 -0
- package/declarations/model/Model.d.ts +91 -86
- package/declarations/model/Node.d.ts +17 -17
- package/declarations/model/RowNode.d.ts +10 -11
- package/declarations/model/TabNode.d.ts +44 -37
- package/declarations/model/TabSetNode.d.ts +44 -41
- package/declarations/model/Utils.d.ts +1 -1
- package/declarations/model/WindowLayout.d.ts +24 -0
- package/declarations/src/Attribute.d.ts +1 -0
- package/declarations/src/AttributeDefinitions.d.ts +1 -0
- package/declarations/src/DockLocation.d.ts +12 -0
- package/declarations/src/DropInfo.d.ts +12 -0
- package/declarations/src/I18nLabel.d.ts +10 -0
- package/declarations/src/Orientation.d.ts +7 -0
- package/declarations/src/PopupMenu.d.ts +1 -0
- package/declarations/src/Rect.d.ts +31 -0
- package/declarations/src/Types.d.ts +92 -0
- package/declarations/src/index.d.ts +20 -0
- package/declarations/src/model/Action.d.ts +5 -0
- package/declarations/src/model/Actions.d.ts +110 -0
- package/declarations/src/model/BorderNode.d.ts +28 -0
- package/declarations/src/model/BorderSet.d.ts +3 -0
- package/declarations/src/model/ICloseType.d.ts +5 -0
- package/declarations/src/model/IDraggable.d.ts +2 -0
- package/declarations/src/model/IDropTarget.d.ts +2 -0
- package/declarations/src/model/IJsonModel.d.ts +153 -0
- package/declarations/src/model/Model.d.ts +98 -0
- package/declarations/src/model/Node.d.ts +16 -0
- package/declarations/src/model/RowNode.d.ts +11 -0
- package/declarations/src/model/TabNode.d.ts +36 -0
- package/declarations/src/model/TabSetNode.d.ts +37 -0
- package/declarations/src/model/Utils.d.ts +1 -0
- package/declarations/src/view/BorderButton.d.ts +1 -0
- package/declarations/src/view/BorderTab.d.ts +2 -0
- package/declarations/src/view/BorderTabSet.d.ts +1 -0
- package/declarations/src/view/DragContainer.d.ts +1 -0
- package/declarations/src/view/ErrorBoundary.d.ts +1 -0
- package/declarations/src/view/FloatingWindow.d.ts +1 -0
- package/declarations/src/view/Icons.d.ts +7 -0
- package/declarations/src/view/Layout.d.ts +113 -0
- package/declarations/src/view/Overlay.d.ts +1 -0
- package/declarations/src/view/PopupMenu.d.ts +1 -0
- package/declarations/src/view/Row.d.ts +1 -0
- package/declarations/src/view/Splitter.d.ts +1 -0
- package/declarations/src/view/Tab.d.ts +1 -0
- package/declarations/src/view/TabButton.d.ts +1 -0
- package/declarations/src/view/TabButtonStamp.d.ts +1 -0
- package/declarations/src/view/TabOverflowHook.d.ts +1 -0
- package/declarations/src/view/TabSet.d.ts +1 -0
- package/declarations/src/view/Utils.d.ts +4 -0
- package/declarations/view/BorderButton.d.ts +1 -1
- package/declarations/view/BorderTab.d.ts +2 -0
- package/declarations/view/BorderTabSet.d.ts +1 -1
- package/declarations/view/DragContainer.d.ts +1 -0
- package/declarations/view/ErrorBoundary.d.ts +1 -1
- package/declarations/view/ExtendedResizeObserver.d.ts +23 -0
- package/declarations/view/FloatingWindow.d.ts +1 -1
- package/declarations/view/Icons.d.ts +8 -7
- package/declarations/view/Layout.d.ts +139 -161
- package/declarations/view/Overlay.d.ts +1 -0
- package/declarations/view/PopoutWindow.d.ts +1 -0
- package/declarations/view/PopupMenu.d.ts +1 -0
- package/declarations/view/Row.d.ts +1 -0
- package/declarations/view/SizeTracker.d.ts +10 -0
- package/declarations/view/Splitter.d.ts +1 -1
- package/declarations/view/Tab.d.ts +1 -1
- package/declarations/view/TabButton.d.ts +1 -1
- package/declarations/view/TabButtonStamp.d.ts +1 -1
- package/declarations/view/TabOverflowHook.d.ts +1 -1
- package/declarations/view/TabSet.d.ts +1 -1
- package/declarations/view/Utils.d.ts +11 -1
- package/dist/bundles/demo.js +232052 -0
- package/dist/bundles/demo.js.map +1 -0
- package/dist/flexlayout.js +122 -92
- package/dist/flexlayout_min.js +1 -1
- package/lib/Attribute.js +42 -31
- package/lib/Attribute.js.map +1 -1
- package/lib/AttributeDefinitions.js +131 -108
- package/lib/AttributeDefinitions.js.map +1 -1
- package/lib/DockLocation.js +120 -124
- package/lib/DockLocation.js.map +1 -1
- package/lib/DropInfo.js +9 -13
- package/lib/DropInfo.js.map +1 -1
- package/lib/I18nLabel.js +13 -18
- package/lib/I18nLabel.js.map +1 -1
- package/lib/Orientation.js +22 -26
- package/lib/Orientation.js.map +1 -1
- package/lib/Rect.js +104 -72
- package/lib/Rect.js.map +1 -1
- package/lib/Types.js +96 -83
- package/lib/Types.js.map +1 -1
- package/lib/index.js +21 -38
- package/lib/index.js.map +1 -1
- package/lib/model/Action.js +6 -10
- package/lib/model/Action.js.map +1 -1
- package/lib/model/Actions.js +169 -155
- package/lib/model/Actions.js.map +1 -1
- package/lib/model/BorderNode.js +385 -406
- package/lib/model/BorderNode.js.map +1 -1
- package/lib/model/BorderSet.js +66 -121
- package/lib/model/BorderSet.js.map +1 -1
- package/lib/model/ICloseType.js +6 -9
- package/lib/model/ICloseType.js.map +1 -1
- package/lib/model/IDraggable.js +1 -2
- package/lib/model/IDropTarget.js +1 -2
- package/lib/model/IJsonModel.js +1 -2
- package/lib/model/LayoutWindow.js +83 -0
- package/lib/model/LayoutWindow.js.map +1 -0
- package/lib/model/Model.js +614 -496
- package/lib/model/Model.js.map +1 -1
- package/lib/model/Node.js +217 -228
- package/lib/model/Node.js.map +1 -1
- package/lib/model/RowNode.js +491 -504
- package/lib/model/RowNode.js.map +1 -1
- package/lib/model/TabNode.js +289 -184
- package/lib/model/TabNode.js.map +1 -1
- package/lib/model/TabSetNode.js +457 -446
- package/lib/model/TabSetNode.js.map +1 -1
- package/lib/model/Utils.js +47 -82
- package/lib/model/Utils.js.map +1 -1
- package/lib/view/BorderButton.js +124 -138
- package/lib/view/BorderButton.js.map +1 -1
- package/lib/view/BorderTab.js +47 -0
- package/lib/view/BorderTab.js.map +1 -0
- package/lib/view/BorderTabSet.js +134 -128
- package/lib/view/BorderTabSet.js.map +1 -1
- package/lib/view/DragContainer.js +16 -0
- package/lib/view/DragContainer.js.map +1 -0
- package/lib/view/ErrorBoundary.js +23 -27
- package/lib/view/ErrorBoundary.js.map +1 -1
- package/lib/view/Icons.js +40 -45
- package/lib/view/Icons.js.map +1 -1
- package/lib/view/Layout.js +918 -907
- package/lib/view/Layout.js.map +1 -1
- package/lib/view/Overlay.js +9 -0
- package/lib/view/Overlay.js.map +1 -0
- package/lib/view/PopoutWindow.js +129 -0
- package/lib/view/PopoutWindow.js.map +1 -0
- package/lib/view/PopupMenu.js +71 -0
- package/lib/view/PopupMenu.js.map +1 -0
- package/lib/view/Row.js +45 -0
- package/lib/view/Row.js.map +1 -0
- package/lib/view/SizeTracker.js +11 -0
- package/lib/view/SizeTracker.js.map +1 -0
- package/lib/view/Splitter.js +191 -147
- package/lib/view/Splitter.js.map +1 -1
- package/lib/view/Tab.js +86 -60
- package/lib/view/Tab.js.map +1 -1
- package/lib/view/TabButton.js +122 -135
- package/lib/view/TabButton.js.map +1 -1
- package/lib/view/TabButtonStamp.js +16 -21
- package/lib/view/TabButtonStamp.js.map +1 -1
- package/lib/view/TabOverflowHook.js +150 -149
- package/lib/view/TabOverflowHook.js.map +1 -1
- package/lib/view/TabSet.js +267 -234
- package/lib/view/TabSet.js.map +1 -1
- package/lib/view/Utils.js +126 -68
- package/lib/view/Utils.js.map +1 -1
- package/package.json +36 -30
- package/src/Attribute.ts +23 -0
- package/src/AttributeDefinitions.ts +38 -15
- package/src/DockLocation.ts +13 -13
- package/src/I18nLabel.ts +7 -9
- package/src/Rect.ts +53 -1
- package/src/Types.ts +16 -0
- package/src/index.ts +1 -2
- package/src/model/Actions.ts +49 -29
- package/src/model/BorderNode.ts +208 -214
- package/src/model/BorderSet.ts +42 -91
- package/src/model/IJsonModel.ts +883 -103
- package/src/model/LayoutWindow.ts +121 -0
- package/src/model/Model.ts +488 -366
- package/src/model/Node.ts +98 -111
- package/src/model/RowNode.ts +323 -319
- package/src/model/TabNode.ts +294 -110
- package/src/model/TabSetNode.ts +300 -242
- package/src/model/Utils.ts +6 -32
- package/src/view/BorderButton.tsx +32 -52
- package/src/view/BorderTab.tsx +70 -0
- package/src/view/BorderTabSet.tsx +64 -52
- package/src/view/DragContainer.tsx +32 -0
- package/src/view/Icons.tsx +6 -0
- package/src/view/Layout.tsx +1051 -1046
- package/src/view/Overlay.tsx +22 -0
- package/src/view/PopoutWindow.tsx +152 -0
- package/src/{PopupMenu.tsx → view/PopupMenu.tsx} +36 -31
- package/src/view/Row.tsx +68 -0
- package/src/view/SizeTracker.tsx +20 -0
- package/src/view/Splitter.tsx +167 -112
- package/src/view/Tab.tsx +76 -42
- package/src/view/TabButton.tsx +36 -55
- package/src/view/TabButtonStamp.tsx +5 -9
- package/src/view/TabOverflowHook.tsx +14 -9
- package/src/view/TabSet.tsx +217 -176
- package/src/view/Utils.tsx +119 -39
- package/style/_base.scss +140 -34
- package/style/dark.css +685 -580
- package/style/dark.css.map +1 -1
- package/style/dark.scss +3 -1
- package/style/gray.css +668 -563
- package/style/gray.css.map +1 -1
- package/style/gray.scss +2 -0
- package/style/light.css +669 -564
- package/style/light.css.map +1 -1
- package/style/light.scss +4 -2
- package/style/rounded.css +697 -0
- package/style/rounded.css.map +1 -0
- package/style/rounded.scss +194 -0
- package/style/underline.css +690 -585
- package/style/underline.css.map +1 -1
- package/style/underline.scss +2 -0
- package/cypress.config.ts +0 -16
- package/lib/DragDrop.js +0 -316
- package/lib/DragDrop.js.map +0 -1
- package/lib/PopupMenu.js +0 -68
- package/lib/PopupMenu.js.map +0 -1
- package/lib/model/SplitterNode.js +0 -72
- package/lib/model/SplitterNode.js.map +0 -1
- package/lib/view/FloatingWindow.js +0 -123
- package/lib/view/FloatingWindow.js.map +0 -1
- package/lib/view/FloatingWindowTab.js +0 -19
- package/lib/view/FloatingWindowTab.js.map +0 -1
- package/lib/view/TabFloating.js +0 -66
- package/lib/view/TabFloating.js.map +0 -1
- package/src/DragDrop.ts +0 -392
- package/src/model/SplitterNode.ts +0 -78
- package/src/view/FloatingWindow.tsx +0 -140
- package/src/view/FloatingWindowTab.tsx +0 -29
- package/src/view/TabFloating.tsx +0 -101
package/src/model/Model.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Attribute } from "../Attribute";
|
|
|
2
2
|
import { AttributeDefinitions } from "../AttributeDefinitions";
|
|
3
3
|
import { DockLocation } from "../DockLocation";
|
|
4
4
|
import { DropInfo } from "../DropInfo";
|
|
5
|
-
import { Orientation } from "../Orientation";
|
|
6
5
|
import { Rect } from "../Rect";
|
|
7
6
|
import { Action } from "./Action";
|
|
8
7
|
import { Actions } from "./Actions";
|
|
@@ -10,281 +9,73 @@ import { BorderNode } from "./BorderNode";
|
|
|
10
9
|
import { BorderSet } from "./BorderSet";
|
|
11
10
|
import { IDraggable } from "./IDraggable";
|
|
12
11
|
import { IDropTarget } from "./IDropTarget";
|
|
13
|
-
import { IJsonModel, ITabSetAttributes } from "./IJsonModel";
|
|
12
|
+
import { IJsonModel, IJsonPopout, ITabSetAttributes } from "./IJsonModel";
|
|
14
13
|
import { Node } from "./Node";
|
|
15
14
|
import { RowNode } from "./RowNode";
|
|
16
15
|
import { TabNode } from "./TabNode";
|
|
17
16
|
import { TabSetNode } from "./TabSetNode";
|
|
18
|
-
import {
|
|
17
|
+
import { randomUUID } from "./Utils";
|
|
18
|
+
import { LayoutWindow } from "./LayoutWindow";
|
|
19
|
+
import { isOnScreen } from "../view/Utils";
|
|
19
20
|
|
|
20
21
|
/** @internal */
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
borderBarSize: number;
|
|
25
|
-
}
|
|
22
|
+
export const DefaultMin = 0;
|
|
23
|
+
/** @internal */
|
|
24
|
+
export const DefaultMax = 99999;
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
27
|
* Class containing the Tree of Nodes used by the FlexLayout component
|
|
29
28
|
*/
|
|
30
29
|
export class Model {
|
|
31
|
-
|
|
32
|
-
* Loads the model from the given json object
|
|
33
|
-
* @param json the json model to load
|
|
34
|
-
* @returns {Model} a new Model object
|
|
35
|
-
*/
|
|
36
|
-
static fromJson(json: IJsonModel) {
|
|
37
|
-
const model = new Model();
|
|
38
|
-
Model._attributeDefinitions.fromJson(json.global, model._attributes);
|
|
39
|
-
|
|
40
|
-
if (json.borders) {
|
|
41
|
-
model._borders = BorderSet._fromJson(json.borders, model);
|
|
42
|
-
}
|
|
43
|
-
model._root = RowNode._fromJson(json.layout, model);
|
|
44
|
-
model._tidy(); // initial tidy of node tree
|
|
45
|
-
return model;
|
|
46
|
-
}
|
|
47
|
-
/** @internal */
|
|
48
|
-
private static _attributeDefinitions: AttributeDefinitions = Model._createAttributeDefinitions();
|
|
30
|
+
static MAIN_WINDOW_ID = "__main_window_id__";
|
|
49
31
|
|
|
50
32
|
/** @internal */
|
|
51
|
-
private static
|
|
52
|
-
const attributeDefinitions = new AttributeDefinitions();
|
|
53
|
-
|
|
54
|
-
attributeDefinitions.add("legacyOverflowMenu", false).setType(Attribute.BOOLEAN);
|
|
55
|
-
attributeDefinitions.add("enableEdgeDock", true).setType(Attribute.BOOLEAN);
|
|
56
|
-
attributeDefinitions.add("rootOrientationVertical", false).setType(Attribute.BOOLEAN);
|
|
57
|
-
attributeDefinitions.add("marginInsets", { top: 0, right: 0, bottom: 0, left: 0 })
|
|
58
|
-
.setType("IInsets");
|
|
59
|
-
attributeDefinitions.add("enableUseVisibility", false).setType(Attribute.BOOLEAN);
|
|
60
|
-
attributeDefinitions.add("enableRotateBorderIcons", true).setType(Attribute.BOOLEAN);
|
|
61
|
-
|
|
62
|
-
// splitter
|
|
63
|
-
attributeDefinitions.add("splitterSize", -1).setType(Attribute.NUMBER);
|
|
64
|
-
attributeDefinitions.add("splitterExtra", 0).setType(Attribute.NUMBER);
|
|
65
|
-
|
|
66
|
-
// tab
|
|
67
|
-
attributeDefinitions.add("tabEnableClose", true).setType(Attribute.BOOLEAN);
|
|
68
|
-
attributeDefinitions.add("tabCloseType", 1).setType("ICloseType");
|
|
69
|
-
attributeDefinitions.add("tabEnableFloat", false).setType(Attribute.BOOLEAN);
|
|
70
|
-
attributeDefinitions.add("tabEnableDrag", true).setType(Attribute.BOOLEAN);
|
|
71
|
-
attributeDefinitions.add("tabEnableRename", true).setType(Attribute.BOOLEAN);
|
|
72
|
-
attributeDefinitions.add("tabContentClassName", undefined).setType(Attribute.STRING);
|
|
73
|
-
attributeDefinitions.add("tabClassName", undefined).setType(Attribute.STRING);
|
|
74
|
-
attributeDefinitions.add("tabIcon", undefined).setType(Attribute.STRING);
|
|
75
|
-
attributeDefinitions.add("tabEnableRenderOnDemand", true).setType(Attribute.BOOLEAN);
|
|
76
|
-
attributeDefinitions.add("tabDragSpeed", 0.3).setType(Attribute.NUMBER);
|
|
77
|
-
attributeDefinitions.add("tabBorderWidth", -1).setType(Attribute.NUMBER);
|
|
78
|
-
attributeDefinitions.add("tabBorderHeight", -1).setType(Attribute.NUMBER);
|
|
79
|
-
|
|
80
|
-
// tabset
|
|
81
|
-
attributeDefinitions.add("tabSetEnableDeleteWhenEmpty", true).setType(Attribute.BOOLEAN);
|
|
82
|
-
attributeDefinitions.add("tabSetEnableDrop", true).setType(Attribute.BOOLEAN);
|
|
83
|
-
attributeDefinitions.add("tabSetEnableDrag", true).setType(Attribute.BOOLEAN);
|
|
84
|
-
attributeDefinitions.add("tabSetEnableDivide", true).setType(Attribute.BOOLEAN);
|
|
85
|
-
attributeDefinitions.add("tabSetEnableMaximize", true).setType(Attribute.BOOLEAN);
|
|
86
|
-
attributeDefinitions.add("tabSetEnableClose", false).setType(Attribute.BOOLEAN);
|
|
87
|
-
attributeDefinitions.add("tabSetEnableSingleTabStretch", false).setType(Attribute.BOOLEAN);
|
|
88
|
-
attributeDefinitions.add("tabSetAutoSelectTab", true).setType(Attribute.BOOLEAN);
|
|
89
|
-
attributeDefinitions.add("tabSetClassNameTabStrip", undefined).setType(Attribute.STRING);
|
|
90
|
-
attributeDefinitions.add("tabSetClassNameHeader", undefined).setType(Attribute.STRING);
|
|
91
|
-
attributeDefinitions.add("tabSetEnableTabStrip", true).setType(Attribute.BOOLEAN);
|
|
92
|
-
attributeDefinitions.add("tabSetHeaderHeight", 0).setType(Attribute.NUMBER);
|
|
93
|
-
attributeDefinitions.add("tabSetTabStripHeight", 0).setType(Attribute.NUMBER);
|
|
94
|
-
attributeDefinitions.add("tabSetMarginInsets", { top: 0, right: 0, bottom: 0, left: 0 })
|
|
95
|
-
.setType("IInsets");
|
|
96
|
-
attributeDefinitions.add("tabSetBorderInsets", { top: 0, right: 0, bottom: 0, left: 0 })
|
|
97
|
-
.setType("IInsets");
|
|
98
|
-
attributeDefinitions.add("tabSetTabLocation", "top").setType("ITabLocation");
|
|
99
|
-
attributeDefinitions.add("tabSetMinWidth", 0).setType(Attribute.NUMBER);
|
|
100
|
-
attributeDefinitions.add("tabSetMinHeight", 0).setType(Attribute.NUMBER);
|
|
101
|
-
|
|
102
|
-
// border
|
|
103
|
-
attributeDefinitions.add("borderSize", 200).setType(Attribute.NUMBER);
|
|
104
|
-
attributeDefinitions.add("borderMinSize", 0).setType(Attribute.NUMBER);
|
|
105
|
-
attributeDefinitions.add("borderBarSize", 0).setType(Attribute.NUMBER);
|
|
106
|
-
attributeDefinitions.add("borderEnableDrop", true).setType(Attribute.BOOLEAN);
|
|
107
|
-
attributeDefinitions.add("borderAutoSelectTabWhenOpen", true).setType(Attribute.BOOLEAN);
|
|
108
|
-
attributeDefinitions.add("borderAutoSelectTabWhenClosed", false).setType(Attribute.BOOLEAN);
|
|
109
|
-
attributeDefinitions.add("borderClassName", undefined).setType(Attribute.STRING);
|
|
110
|
-
attributeDefinitions.add("borderEnableAutoHide", false).setType(Attribute.BOOLEAN);
|
|
111
|
-
|
|
112
|
-
return attributeDefinitions;
|
|
113
|
-
}
|
|
33
|
+
private static attributeDefinitions: AttributeDefinitions = Model.createAttributeDefinitions();
|
|
114
34
|
|
|
115
35
|
/** @internal */
|
|
116
|
-
private
|
|
117
|
-
/** @internal */
|
|
118
|
-
private _idMap: Record<string, Node>;
|
|
119
|
-
/** @internal */
|
|
120
|
-
private _changeListener?: (action: Action) => void;
|
|
121
|
-
/** @internal */
|
|
122
|
-
private _root?: RowNode;
|
|
123
|
-
/** @internal */
|
|
124
|
-
private _borders: BorderSet;
|
|
36
|
+
private attributes: Record<string, any>;
|
|
125
37
|
/** @internal */
|
|
126
|
-
private
|
|
38
|
+
private idMap: Map<string, Node>;
|
|
127
39
|
/** @internal */
|
|
128
|
-
private
|
|
40
|
+
private changeListeners: ((action: Action) => void)[];
|
|
129
41
|
/** @internal */
|
|
130
|
-
private
|
|
42
|
+
private borders: BorderSet;
|
|
131
43
|
/** @internal */
|
|
132
|
-
private
|
|
44
|
+
private onAllowDrop?: (dragNode: Node, dropInfo: DropInfo) => boolean;
|
|
133
45
|
/** @internal */
|
|
134
|
-
private
|
|
46
|
+
private onCreateTabSet?: (tabNode?: TabNode) => ITabSetAttributes;
|
|
135
47
|
/** @internal */
|
|
136
|
-
private
|
|
48
|
+
private windows: Map<string, LayoutWindow>;
|
|
137
49
|
/** @internal */
|
|
138
|
-
private
|
|
139
|
-
|
|
50
|
+
private rootWindow: LayoutWindow;
|
|
140
51
|
|
|
141
52
|
/**
|
|
142
53
|
* 'private' constructor. Use the static method Model.fromJson(json) to create a model
|
|
143
54
|
* @internal
|
|
144
55
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
/** @internal */
|
|
155
|
-
_setChangeListener(listener: ((action: Action) => void) | undefined) {
|
|
156
|
-
this._changeListener = listener;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get the currently active tabset node
|
|
161
|
-
*/
|
|
162
|
-
getActiveTabset() {
|
|
163
|
-
if (this._activeTabSet && this.getNodeById(this._activeTabSet.getId())) {
|
|
164
|
-
return this._activeTabSet;
|
|
165
|
-
} else {
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** @internal */
|
|
171
|
-
_getShowHiddenBorder() {
|
|
172
|
-
return this._showHiddenBorder;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** @internal */
|
|
176
|
-
_setShowHiddenBorder(location: DockLocation) {
|
|
177
|
-
this._showHiddenBorder = location;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** @internal */
|
|
181
|
-
_setActiveTabset(tabsetNode: TabSetNode | undefined) {
|
|
182
|
-
this._activeTabSet = tabsetNode;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get the currently maximized tabset node
|
|
187
|
-
*/
|
|
188
|
-
getMaximizedTabset() {
|
|
189
|
-
return this._maximizedTabSet;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** @internal */
|
|
193
|
-
_setMaximizedTabset(tabsetNode: (TabSetNode | undefined)) {
|
|
194
|
-
this._maximizedTabSet = tabsetNode;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Gets the root RowNode of the model
|
|
199
|
-
* @returns {RowNode}
|
|
200
|
-
*/
|
|
201
|
-
getRoot() {
|
|
202
|
-
return this._root as RowNode;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
isRootOrientationVertical() {
|
|
206
|
-
return this._attributes.rootOrientationVertical as boolean;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
isUseVisibility() {
|
|
210
|
-
return this._attributes.enableUseVisibility as boolean;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
isEnableRotateBorderIcons() {
|
|
214
|
-
return this._attributes.enableRotateBorderIcons as boolean;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Gets the
|
|
219
|
-
* @returns {BorderSet|*}
|
|
220
|
-
*/
|
|
221
|
-
getBorderSet() {
|
|
222
|
-
return this._borders;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** @internal */
|
|
226
|
-
_getOuterInnerRects() {
|
|
227
|
-
return this._borderRects;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** @internal */
|
|
231
|
-
_getPointerFine() {
|
|
232
|
-
return this._pointerFine;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** @internal */
|
|
236
|
-
_setPointerFine(pointerFine: boolean) {
|
|
237
|
-
this._pointerFine = pointerFine;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Visits all the nodes in the model and calls the given function for each
|
|
242
|
-
* @param fn a function that takes visited node and a integer level as parameters
|
|
243
|
-
*/
|
|
244
|
-
visitNodes(fn: (node: Node, level: number) => void) {
|
|
245
|
-
this._borders._forEachNode(fn);
|
|
246
|
-
(this._root as RowNode)._forEachNode(fn, 0);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Gets a node by its id
|
|
251
|
-
* @param id the id to find
|
|
252
|
-
*/
|
|
253
|
-
getNodeById(id: string): Node | undefined {
|
|
254
|
-
return this._idMap[id];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Finds the first/top left tab set of the given node.
|
|
259
|
-
* @param node The top node you want to begin searching from, deafults to the root node
|
|
260
|
-
* @returns The first Tab Set
|
|
261
|
-
*/
|
|
262
|
-
getFirstTabSet(node = this._root as Node): Node
|
|
263
|
-
{
|
|
264
|
-
const child = node.getChildren()[0];
|
|
265
|
-
if (child instanceof TabSetNode)
|
|
266
|
-
{
|
|
267
|
-
return child;
|
|
268
|
-
}
|
|
269
|
-
else
|
|
270
|
-
{
|
|
271
|
-
return this.getFirstTabSet(child);
|
|
272
|
-
}
|
|
56
|
+
protected constructor() {
|
|
57
|
+
this.attributes = {};
|
|
58
|
+
this.idMap = new Map();
|
|
59
|
+
this.borders = new BorderSet(this);
|
|
60
|
+
this.windows = new Map<string, LayoutWindow>();
|
|
61
|
+
this.rootWindow = new LayoutWindow(Model.MAIN_WINDOW_ID, Rect.empty());
|
|
62
|
+
this.windows.set(Model.MAIN_WINDOW_ID, this.rootWindow);
|
|
63
|
+
this.changeListeners = [];
|
|
273
64
|
}
|
|
274
65
|
|
|
275
66
|
/**
|
|
276
67
|
* Update the node tree by performing the given action,
|
|
277
68
|
* Actions should be generated via static methods on the Actions class
|
|
278
69
|
* @param action the action to perform
|
|
279
|
-
* @returns added Node for Actions.addNode
|
|
70
|
+
* @returns added Node for Actions.addNode, windowId for createWindow
|
|
280
71
|
*/
|
|
281
|
-
doAction(action: Action):
|
|
72
|
+
doAction(action: Action): any {
|
|
282
73
|
let returnVal = undefined;
|
|
283
74
|
// console.log(action);
|
|
284
75
|
switch (action.type) {
|
|
285
76
|
case Actions.ADD_NODE: {
|
|
286
77
|
const newNode = new TabNode(this, action.data.json, true);
|
|
287
|
-
const toNode = this.
|
|
78
|
+
const toNode = this.idMap.get(action.data.toNode) as Node & IDraggable;
|
|
288
79
|
if (toNode instanceof TabSetNode || toNode instanceof BorderNode || toNode instanceof RowNode) {
|
|
289
80
|
toNode.drop(newNode, DockLocation.getByName(action.data.location), action.data.index, action.data.select);
|
|
290
81
|
returnVal = newNode;
|
|
@@ -292,24 +83,31 @@ export class Model {
|
|
|
292
83
|
break;
|
|
293
84
|
}
|
|
294
85
|
case Actions.MOVE_NODE: {
|
|
295
|
-
const fromNode = this.
|
|
296
|
-
|
|
297
|
-
|
|
86
|
+
const fromNode = this.idMap.get(action.data.fromNode) as Node & IDraggable;
|
|
87
|
+
|
|
88
|
+
if (fromNode instanceof TabNode || fromNode instanceof TabSetNode || fromNode instanceof RowNode) {
|
|
89
|
+
if (fromNode === this.getMaximizedTabset(fromNode.getWindowId())) {
|
|
90
|
+
const fromWindow = this.windows.get(fromNode.getWindowId())!;
|
|
91
|
+
fromWindow.maximizedTabSet = undefined;
|
|
92
|
+
}
|
|
93
|
+
const toNode = this.idMap.get(action.data.toNode) as Node & IDropTarget;
|
|
298
94
|
if (toNode instanceof TabSetNode || toNode instanceof BorderNode || toNode instanceof RowNode) {
|
|
299
95
|
toNode.drop(fromNode, DockLocation.getByName(action.data.location), action.data.index, action.data.select);
|
|
300
96
|
}
|
|
301
97
|
}
|
|
98
|
+
this.removeEmptyWindows();
|
|
302
99
|
break;
|
|
303
100
|
}
|
|
304
101
|
case Actions.DELETE_TAB: {
|
|
305
|
-
const node = this.
|
|
102
|
+
const node = this.idMap.get(action.data.node);
|
|
306
103
|
if (node instanceof TabNode) {
|
|
307
|
-
node.
|
|
104
|
+
node.delete();
|
|
308
105
|
}
|
|
106
|
+
this.removeEmptyWindows();
|
|
309
107
|
break;
|
|
310
108
|
}
|
|
311
109
|
case Actions.DELETE_TABSET: {
|
|
312
|
-
const node = this.
|
|
110
|
+
const node = this.idMap.get(action.data.node);
|
|
313
111
|
|
|
314
112
|
if (node instanceof TabSetNode) {
|
|
315
113
|
// first delete all child tabs that are closeable
|
|
@@ -317,273 +115,597 @@ export class Model {
|
|
|
317
115
|
for (let i = 0; i < children.length; i++) {
|
|
318
116
|
const child = children[i];
|
|
319
117
|
if ((child as TabNode).isEnableClose()) {
|
|
320
|
-
(child as TabNode).
|
|
118
|
+
(child as TabNode).delete();
|
|
321
119
|
}
|
|
322
120
|
}
|
|
323
121
|
|
|
324
122
|
if (node.getChildren().length === 0) {
|
|
325
|
-
node.
|
|
123
|
+
node.delete();
|
|
326
124
|
}
|
|
327
|
-
this.
|
|
125
|
+
this.tidy();
|
|
328
126
|
}
|
|
127
|
+
this.removeEmptyWindows();
|
|
329
128
|
break;
|
|
330
129
|
}
|
|
331
|
-
case Actions.
|
|
332
|
-
const node = this.
|
|
333
|
-
if (node instanceof
|
|
334
|
-
node.
|
|
335
|
-
|
|
130
|
+
case Actions.POPOUT_TABSET: {
|
|
131
|
+
const node = this.idMap.get(action.data.node);
|
|
132
|
+
if (node instanceof TabSetNode) {
|
|
133
|
+
const isMaximized = node.isMaximized();
|
|
134
|
+
const oldLayoutWindow = this.windows.get(node.getWindowId())!;
|
|
135
|
+
const windowId = randomUUID()
|
|
136
|
+
const layoutWindow = new LayoutWindow(windowId, oldLayoutWindow.toScreenRectFunction(node.getRect()));
|
|
137
|
+
const json = {
|
|
138
|
+
type: "row",
|
|
139
|
+
children: []
|
|
140
|
+
}
|
|
141
|
+
const row = RowNode.fromJson(json, this, layoutWindow);
|
|
142
|
+
layoutWindow.root = row;
|
|
143
|
+
this.windows.set(windowId, layoutWindow);
|
|
144
|
+
row.drop(node, DockLocation.CENTER, 0);
|
|
145
|
+
|
|
146
|
+
if (isMaximized) {
|
|
147
|
+
this.rootWindow.maximizedTabSet = undefined;
|
|
148
|
+
}
|
|
336
149
|
}
|
|
150
|
+
this.removeEmptyWindows();
|
|
337
151
|
break;
|
|
338
152
|
}
|
|
339
|
-
case Actions.
|
|
340
|
-
const node = this.
|
|
153
|
+
case Actions.POPOUT_TAB: {
|
|
154
|
+
const node = this.idMap.get(action.data.node);
|
|
341
155
|
if (node instanceof TabNode) {
|
|
342
|
-
|
|
343
|
-
|
|
156
|
+
const windowId = randomUUID()
|
|
157
|
+
let r = Rect.empty();
|
|
158
|
+
if (node.getParent() instanceof TabSetNode) {
|
|
159
|
+
r = node.getParent()!.getRect();
|
|
160
|
+
} else {
|
|
161
|
+
r = (node.getParent() as BorderNode).getContentRect();
|
|
162
|
+
}
|
|
163
|
+
const oldLayoutWindow = this.windows.get(node.getWindowId())!;
|
|
164
|
+
const layoutWindow = new LayoutWindow(windowId, oldLayoutWindow.toScreenRectFunction(r));
|
|
165
|
+
const tabsetId = randomUUID();
|
|
166
|
+
const json = {
|
|
167
|
+
type: "row",
|
|
168
|
+
children: [
|
|
169
|
+
{ type: "tabset", id: tabsetId }
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
const row = RowNode.fromJson(json, this, layoutWindow);
|
|
173
|
+
layoutWindow.root = row;
|
|
174
|
+
this.windows.set(windowId, layoutWindow);
|
|
175
|
+
|
|
176
|
+
const tabset = this.idMap.get(tabsetId) as TabSetNode & IDropTarget;
|
|
177
|
+
tabset.drop(node, DockLocation.CENTER, 0, true);
|
|
178
|
+
}
|
|
179
|
+
this.removeEmptyWindows();
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case Actions.CLOSE_WINDOW: {
|
|
183
|
+
const window = this.windows.get(action.data.windowId);
|
|
184
|
+
if (window) {
|
|
185
|
+
this.rootWindow.root?.drop(window?.root!, DockLocation.CENTER, -1);
|
|
186
|
+
this.rootWindow.visitNodes((node, level) => {
|
|
187
|
+
if (node instanceof RowNode) {
|
|
188
|
+
node.setWindowId(Model.MAIN_WINDOW_ID);
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// this.getFirstTabSet().drop(window?.root!,DockLocation.CENTER, -1);
|
|
193
|
+
|
|
194
|
+
this.windows.delete(action.data.windowId);
|
|
344
195
|
}
|
|
345
196
|
break;
|
|
346
197
|
}
|
|
198
|
+
case Actions.CREATE_WINDOW: {
|
|
199
|
+
const windowId = randomUUID();
|
|
200
|
+
const layoutWindow = new LayoutWindow(windowId, Rect.fromJson(action.data.rect));
|
|
201
|
+
const row = RowNode.fromJson(action.data.layout, this, layoutWindow);
|
|
202
|
+
layoutWindow.root = row;
|
|
203
|
+
this.windows.set(windowId, layoutWindow);
|
|
204
|
+
returnVal = windowId;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
347
207
|
case Actions.RENAME_TAB: {
|
|
348
|
-
const node = this.
|
|
208
|
+
const node = this.idMap.get(action.data.node);
|
|
349
209
|
if (node instanceof TabNode) {
|
|
350
|
-
node.
|
|
210
|
+
node.setName(action.data.text);
|
|
351
211
|
}
|
|
352
212
|
break;
|
|
353
213
|
}
|
|
354
214
|
case Actions.SELECT_TAB: {
|
|
355
|
-
const tabNode = this.
|
|
215
|
+
const tabNode = this.idMap.get(action.data.tabNode);
|
|
216
|
+
const windowId = action.data.windowId ? action.data.windowId : Model.MAIN_WINDOW_ID;
|
|
217
|
+
const window = this.windows.get(windowId)!;
|
|
356
218
|
if (tabNode instanceof TabNode) {
|
|
357
219
|
const parent = tabNode.getParent() as Node;
|
|
358
220
|
const pos = parent.getChildren().indexOf(tabNode);
|
|
359
221
|
|
|
360
222
|
if (parent instanceof BorderNode) {
|
|
361
223
|
if (parent.getSelected() === pos) {
|
|
362
|
-
parent.
|
|
224
|
+
parent.setSelected(-1);
|
|
363
225
|
} else {
|
|
364
|
-
parent.
|
|
226
|
+
parent.setSelected(pos);
|
|
365
227
|
}
|
|
366
228
|
} else if (parent instanceof TabSetNode) {
|
|
367
229
|
if (parent.getSelected() !== pos) {
|
|
368
|
-
parent.
|
|
230
|
+
parent.setSelected(pos);
|
|
369
231
|
}
|
|
370
|
-
|
|
232
|
+
window.activeTabSet = parent;
|
|
371
233
|
}
|
|
372
234
|
}
|
|
373
235
|
break;
|
|
374
236
|
}
|
|
375
237
|
case Actions.SET_ACTIVE_TABSET: {
|
|
238
|
+
const windowId = action.data.windowId ? action.data.windowId : Model.MAIN_WINDOW_ID;
|
|
239
|
+
const window = this.windows.get(windowId)!;
|
|
376
240
|
if (action.data.tabsetNode === undefined) {
|
|
377
|
-
|
|
241
|
+
window.activeTabSet = undefined;
|
|
378
242
|
} else {
|
|
379
|
-
const tabsetNode = this.
|
|
243
|
+
const tabsetNode = this.idMap.get(action.data.tabsetNode);
|
|
380
244
|
if (tabsetNode instanceof TabSetNode) {
|
|
381
|
-
|
|
245
|
+
window.activeTabSet = tabsetNode;
|
|
382
246
|
}
|
|
383
247
|
}
|
|
384
248
|
break;
|
|
385
249
|
}
|
|
386
|
-
case Actions.
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
this._adjustSplitSide(node2, action.data.weight2, action.data.pixelWidth2);
|
|
250
|
+
case Actions.ADJUST_WEIGHTS: {
|
|
251
|
+
const row = this.idMap.get(action.data.nodeId) as RowNode;
|
|
252
|
+
const c = row.getChildren();
|
|
253
|
+
for (let i = 0; i < c.length; i++) {
|
|
254
|
+
const n = c[i] as TabSetNode | RowNode;
|
|
255
|
+
n.setWeight(action.data.weights[i]);
|
|
393
256
|
}
|
|
394
257
|
break;
|
|
395
258
|
}
|
|
396
259
|
case Actions.ADJUST_BORDER_SPLIT: {
|
|
397
|
-
const node = this.
|
|
260
|
+
const node = this.idMap.get(action.data.node);
|
|
398
261
|
if (node instanceof BorderNode) {
|
|
399
|
-
node.
|
|
262
|
+
node.setSize(action.data.pos);
|
|
400
263
|
}
|
|
401
264
|
break;
|
|
402
265
|
}
|
|
403
266
|
case Actions.MAXIMIZE_TOGGLE: {
|
|
404
|
-
const
|
|
267
|
+
const windowId = action.data.windowId ? action.data.windowId : Model.MAIN_WINDOW_ID;
|
|
268
|
+
const window = this.windows.get(windowId)!;
|
|
269
|
+
const node = this.idMap.get(action.data.node);
|
|
405
270
|
if (node instanceof TabSetNode) {
|
|
406
|
-
if (node ===
|
|
407
|
-
|
|
271
|
+
if (node === window.maximizedTabSet) {
|
|
272
|
+
window.maximizedTabSet = undefined;
|
|
408
273
|
} else {
|
|
409
|
-
|
|
410
|
-
|
|
274
|
+
window.maximizedTabSet = node;
|
|
275
|
+
window.activeTabSet = node;
|
|
411
276
|
}
|
|
412
277
|
}
|
|
413
278
|
|
|
414
279
|
break;
|
|
415
280
|
}
|
|
416
281
|
case Actions.UPDATE_MODEL_ATTRIBUTES: {
|
|
417
|
-
this.
|
|
282
|
+
this.updateAttrs(action.data.json);
|
|
418
283
|
break;
|
|
419
284
|
}
|
|
420
285
|
|
|
421
286
|
case Actions.UPDATE_NODE_ATTRIBUTES: {
|
|
422
|
-
const node = this.
|
|
423
|
-
node.
|
|
287
|
+
const node = this.idMap.get(action.data.node)!;
|
|
288
|
+
node.updateAttrs(action.data.json);
|
|
424
289
|
break;
|
|
425
290
|
}
|
|
426
291
|
default:
|
|
427
292
|
break;
|
|
428
293
|
}
|
|
429
294
|
|
|
430
|
-
this.
|
|
295
|
+
this.updateIdMap();
|
|
431
296
|
|
|
432
|
-
|
|
433
|
-
|
|
297
|
+
for (const listener of this.changeListeners) {
|
|
298
|
+
listener(action);
|
|
434
299
|
}
|
|
435
300
|
|
|
436
301
|
return returnVal;
|
|
437
302
|
}
|
|
438
303
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get the currently active tabset node
|
|
308
|
+
*/
|
|
309
|
+
getActiveTabset(windowId: string = Model.MAIN_WINDOW_ID) {
|
|
310
|
+
const window = this.windows.get(windowId);
|
|
311
|
+
if (window && window.activeTabSet && this.getNodeById(window.activeTabSet.getId())) {
|
|
312
|
+
return window.activeTabSet;
|
|
313
|
+
} else {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
445
316
|
}
|
|
446
317
|
|
|
447
|
-
/**
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Get the currently maximized tabset node
|
|
320
|
+
*/
|
|
321
|
+
getMaximizedTabset(windowId: string = Model.MAIN_WINDOW_ID) {
|
|
322
|
+
return this.windows.get(windowId)!.maximizedTabSet;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Gets the root RowNode of the model
|
|
327
|
+
* @returns {RowNode}
|
|
328
|
+
*/
|
|
329
|
+
getRoot(windowId: string = Model.MAIN_WINDOW_ID) {
|
|
330
|
+
return this.windows.get(windowId)!.root!;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
isRootOrientationVertical() {
|
|
334
|
+
return this.attributes.rootOrientationVertical as boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
isEnableRotateBorderIcons() {
|
|
338
|
+
return this.attributes.enableRotateBorderIcons as boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Gets the
|
|
343
|
+
* @returns {BorderSet|*}
|
|
344
|
+
*/
|
|
345
|
+
getBorderSet() {
|
|
346
|
+
return this.borders;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
getwindowsMap() {
|
|
350
|
+
return this.windows;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Visits all the nodes in the model and calls the given function for each
|
|
355
|
+
* @param fn a function that takes visited node and a integer level as parameters
|
|
356
|
+
*/
|
|
357
|
+
visitNodes(fn: (node: Node, level: number) => void) {
|
|
358
|
+
this.borders.forEachNode(fn);
|
|
359
|
+
for (const [_, w] of this.windows) {
|
|
360
|
+
w.root!.forEachNode(fn, 0);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
visitWindowNodes(windowId: string, fn: (node: Node, level: number) => void) {
|
|
365
|
+
if (this.windows.has(windowId)) {
|
|
366
|
+
if (windowId === Model.MAIN_WINDOW_ID) {
|
|
367
|
+
this.borders.forEachNode(fn);
|
|
368
|
+
}
|
|
369
|
+
this.windows.get(windowId)!.visitNodes(fn);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Gets a node by its id
|
|
375
|
+
* @param id the id to find
|
|
376
|
+
*/
|
|
377
|
+
getNodeById(id: string): Node | undefined {
|
|
378
|
+
return this.idMap.get(id);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Finds the first/top left tab set of the given node.
|
|
383
|
+
* @param node The top node you want to begin searching from, deafults to the root node
|
|
384
|
+
* @returns The first Tab Set
|
|
385
|
+
*/
|
|
386
|
+
getFirstTabSet(node = this.windows.get(Model.MAIN_WINDOW_ID)!.root as Node): TabSetNode {
|
|
387
|
+
const child = node.getChildren()[0];
|
|
388
|
+
if (child instanceof TabSetNode) {
|
|
389
|
+
return child;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
return this.getFirstTabSet(child);
|
|
454
393
|
}
|
|
455
394
|
}
|
|
456
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Loads the model from the given json object
|
|
398
|
+
* @param json the json model to load
|
|
399
|
+
* @returns {Model} a new Model object
|
|
400
|
+
*/
|
|
401
|
+
static fromJson(json: IJsonModel) {
|
|
402
|
+
const model = new Model();
|
|
403
|
+
Model.attributeDefinitions.fromJson(json.global, model.attributes);
|
|
404
|
+
|
|
405
|
+
if (json.borders) {
|
|
406
|
+
model.borders = BorderSet.fromJson(json.borders, model);
|
|
407
|
+
}
|
|
408
|
+
if (json.popouts) {
|
|
409
|
+
let i= 0;
|
|
410
|
+
let top = 100;
|
|
411
|
+
let left = 100;
|
|
412
|
+
for (const windowId in json.popouts) {
|
|
413
|
+
const windowJson = json.popouts[windowId];
|
|
414
|
+
const layoutWindow = LayoutWindow.fromJson(windowJson, model, windowId);
|
|
415
|
+
model.windows.set(windowId, layoutWindow);
|
|
416
|
+
// offscreen windows will reload cascaded (since cannot reposition)
|
|
417
|
+
if (!isOnScreen(layoutWindow.rect)) {
|
|
418
|
+
layoutWindow.rect = new Rect(top + i*50, left+ i*50, 600, 400);
|
|
419
|
+
i++;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
model.rootWindow.root = RowNode.fromJson(json.layout, model, model.getwindowsMap().get(Model.MAIN_WINDOW_ID)!);
|
|
425
|
+
model.tidy(); // initial tidy of node tree
|
|
426
|
+
return model;
|
|
427
|
+
}
|
|
428
|
+
|
|
457
429
|
/**
|
|
458
430
|
* Converts the model to a json object
|
|
459
431
|
* @returns {IJsonModel} json object that represents this model
|
|
460
432
|
*/
|
|
461
433
|
toJson(): IJsonModel {
|
|
462
434
|
const global: any = {};
|
|
463
|
-
Model.
|
|
435
|
+
Model.attributeDefinitions.toJson(global, this.attributes);
|
|
464
436
|
|
|
465
437
|
// save state of nodes
|
|
466
438
|
this.visitNodes((node) => {
|
|
467
|
-
node.
|
|
439
|
+
node.fireEvent("save", {});
|
|
468
440
|
});
|
|
469
441
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (splitterSize === -1) {
|
|
476
|
-
// use defaults
|
|
477
|
-
splitterSize = this._pointerFine ? 8 : 12; // larger for mobile
|
|
442
|
+
const windows: Record<string, IJsonPopout> = {};
|
|
443
|
+
for (const [id, window] of this.windows) {
|
|
444
|
+
if (id !== Model.MAIN_WINDOW_ID) {
|
|
445
|
+
windows[id] = window.toJson();
|
|
446
|
+
}
|
|
478
447
|
}
|
|
479
|
-
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
global,
|
|
451
|
+
borders: this.borders.toJson(),
|
|
452
|
+
layout: this.rootWindow.root!.toJson(),
|
|
453
|
+
popouts: windows
|
|
454
|
+
};
|
|
480
455
|
}
|
|
481
456
|
|
|
482
|
-
|
|
483
|
-
return this.
|
|
457
|
+
getSplitterSize() {
|
|
458
|
+
return this.attributes.splitterSize as number;
|
|
484
459
|
}
|
|
485
460
|
|
|
486
461
|
getSplitterExtra() {
|
|
487
|
-
return this.
|
|
462
|
+
return this.attributes.splitterExtra as number;
|
|
488
463
|
}
|
|
489
464
|
|
|
490
465
|
isEnableEdgeDock() {
|
|
491
|
-
return this.
|
|
466
|
+
return this.attributes.enableEdgeDock as boolean;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
isSplitterEnableHandle() {
|
|
470
|
+
return this.attributes.splitterEnableHandle as boolean;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Sets a function to allow/deny dropping a node
|
|
475
|
+
* @param onAllowDrop function that takes the drag node and DropInfo and returns true if the drop is allowed
|
|
476
|
+
*/
|
|
477
|
+
setOnAllowDrop(onAllowDrop: (dragNode: Node, dropInfo: DropInfo) => boolean) {
|
|
478
|
+
this.onAllowDrop = onAllowDrop;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* set callback called when a new TabSet is created.
|
|
483
|
+
* The tabNode can be undefined if it's the auto created first tabset in the root row (when the last
|
|
484
|
+
* tab is deleted, the root tabset can be recreated)
|
|
485
|
+
* @param onCreateTabSet
|
|
486
|
+
*/
|
|
487
|
+
setOnCreateTabSet(onCreateTabSet: (tabNode?: TabNode) => ITabSetAttributes) {
|
|
488
|
+
this.onCreateTabSet = onCreateTabSet;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
addChangeListener(listener: ((action: Action) => void)) {
|
|
492
|
+
this.changeListeners.push(listener);
|
|
492
493
|
}
|
|
493
494
|
|
|
495
|
+
removeChangeListener(listener: ((action: Action) => void)) {
|
|
496
|
+
const pos = this.changeListeners.findIndex(l => l === listener);
|
|
497
|
+
if (pos !== -1) {
|
|
498
|
+
this.changeListeners.splice(pos, 1);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
toString() {
|
|
503
|
+
return JSON.stringify(this.toJson());
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/***********************internal ********************************/
|
|
507
|
+
|
|
494
508
|
/** @internal */
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
509
|
+
removeEmptyWindows() {
|
|
510
|
+
const emptyWindows = new Set<string>();
|
|
511
|
+
for (const [windowId] of this.windows) {
|
|
512
|
+
if (windowId !== Model.MAIN_WINDOW_ID) {
|
|
513
|
+
let count = 0;
|
|
514
|
+
this.visitWindowNodes(windowId, (node) => {
|
|
515
|
+
if (node instanceof TabNode) {
|
|
516
|
+
count++;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
if (count === 0) {
|
|
520
|
+
emptyWindows.add(windowId);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
499
523
|
}
|
|
500
524
|
|
|
501
|
-
|
|
502
|
-
this.
|
|
525
|
+
for (const windowId of emptyWindows) {
|
|
526
|
+
this.windows.delete(windowId);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/** @internal */
|
|
530
|
+
setActiveTabset(tabsetNode: TabSetNode | undefined, windowId: string) {
|
|
531
|
+
const window = this.windows.get(windowId);
|
|
532
|
+
if (window) {
|
|
533
|
+
if (tabsetNode) {
|
|
534
|
+
window.activeTabSet = tabsetNode;
|
|
535
|
+
} else {
|
|
536
|
+
window.activeTabSet = undefined;
|
|
537
|
+
}
|
|
503
538
|
}
|
|
504
539
|
}
|
|
505
540
|
|
|
506
541
|
/** @internal */
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
542
|
+
setMaximizedTabset(tabsetNode: (TabSetNode | undefined), windowId: string) {
|
|
543
|
+
const window = this.windows.get(windowId);
|
|
544
|
+
if (window) {
|
|
545
|
+
if (tabsetNode) {
|
|
546
|
+
window.maximizedTabSet = tabsetNode;
|
|
547
|
+
} else {
|
|
548
|
+
window.maximizedTabSet = undefined;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
511
552
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
//
|
|
515
|
-
|
|
553
|
+
/** @internal */
|
|
554
|
+
updateIdMap() {
|
|
555
|
+
// regenerate idMap to stop it building up
|
|
556
|
+
this.idMap.clear();
|
|
557
|
+
this.visitNodes((node) => {
|
|
558
|
+
this.idMap.set(node.getId(), node)
|
|
559
|
+
// if (node instanceof RowNode) {
|
|
560
|
+
// node.normalizeWeights();
|
|
561
|
+
// }
|
|
562
|
+
});
|
|
563
|
+
// console.log(JSON.stringify(Object.keys(this._idMap)));
|
|
516
564
|
}
|
|
517
565
|
|
|
518
566
|
/** @internal */
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (
|
|
522
|
-
|
|
567
|
+
addNode(node: Node) {
|
|
568
|
+
const id = node.getId();
|
|
569
|
+
if (this.idMap.has(id)) {
|
|
570
|
+
throw new Error(`Error: each node must have a unique id, duplicate id:${node.getId()}`);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
this.idMap.set(id, node);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** @internal */
|
|
577
|
+
findDropTargetNode(windowId: string, dragNode: Node & IDraggable, x: number, y: number) {
|
|
578
|
+
let node = (this.windows.get(windowId)!.root as RowNode).findDropTargetNode(windowId, dragNode, x, y);
|
|
579
|
+
if (node === undefined && windowId === Model.MAIN_WINDOW_ID) {
|
|
580
|
+
node = this.borders.findDropTargetNode(dragNode, x, y);
|
|
523
581
|
}
|
|
524
582
|
return node;
|
|
525
583
|
}
|
|
526
584
|
|
|
527
585
|
/** @internal */
|
|
528
|
-
|
|
586
|
+
tidy() {
|
|
529
587
|
// console.log("before _tidy", this.toString());
|
|
530
|
-
(
|
|
588
|
+
for (const [_, window] of this.windows) {
|
|
589
|
+
window.root!.tidy();
|
|
590
|
+
}
|
|
531
591
|
// console.log("after _tidy", this.toString());
|
|
532
592
|
}
|
|
533
593
|
|
|
534
594
|
/** @internal */
|
|
535
|
-
|
|
536
|
-
Model.
|
|
595
|
+
updateAttrs(json: any) {
|
|
596
|
+
Model.attributeDefinitions.update(json, this.attributes);
|
|
537
597
|
}
|
|
538
598
|
|
|
539
599
|
/** @internal */
|
|
540
|
-
|
|
600
|
+
nextUniqueId() {
|
|
541
601
|
return '#' + randomUUID();
|
|
542
602
|
}
|
|
543
603
|
|
|
544
604
|
/** @internal */
|
|
545
|
-
|
|
546
|
-
return this.
|
|
605
|
+
getAttribute(name: string): any {
|
|
606
|
+
return this.attributes[name];
|
|
547
607
|
}
|
|
548
608
|
|
|
549
|
-
/**
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
*/
|
|
553
|
-
setOnAllowDrop(onAllowDrop: (dragNode: Node, dropInfo: DropInfo) => boolean) {
|
|
554
|
-
this._onAllowDrop = onAllowDrop;
|
|
609
|
+
/** @internal */
|
|
610
|
+
getOnAllowDrop() {
|
|
611
|
+
return this.onAllowDrop;
|
|
555
612
|
}
|
|
556
613
|
|
|
557
614
|
/** @internal */
|
|
558
|
-
|
|
559
|
-
return this.
|
|
615
|
+
getOnCreateTabSet() {
|
|
616
|
+
return this.onCreateTabSet;
|
|
560
617
|
}
|
|
561
618
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
619
|
+
static toTypescriptInterfaces() {
|
|
620
|
+
Model.attributeDefinitions.pairAttributes("RowNode", RowNode.getAttributeDefinitions());
|
|
621
|
+
Model.attributeDefinitions.pairAttributes("TabSetNode", TabSetNode.getAttributeDefinitions());
|
|
622
|
+
Model.attributeDefinitions.pairAttributes("TabNode", TabNode.getAttributeDefinitions());
|
|
623
|
+
Model.attributeDefinitions.pairAttributes("BorderNode", BorderNode.getAttributeDefinitions());
|
|
624
|
+
|
|
625
|
+
let sb = [];
|
|
626
|
+
sb.push(Model.attributeDefinitions.toTypescriptInterface("Global", undefined));
|
|
627
|
+
sb.push(RowNode.getAttributeDefinitions().toTypescriptInterface("Row", Model.attributeDefinitions));
|
|
628
|
+
sb.push(TabSetNode.getAttributeDefinitions().toTypescriptInterface("TabSet", Model.attributeDefinitions));
|
|
629
|
+
sb.push(TabNode.getAttributeDefinitions().toTypescriptInterface("Tab", Model.attributeDefinitions));
|
|
630
|
+
sb.push(BorderNode.getAttributeDefinitions().toTypescriptInterface("Border", Model.attributeDefinitions));
|
|
631
|
+
console.log(sb.join("\n"));
|
|
570
632
|
}
|
|
571
633
|
|
|
572
634
|
/** @internal */
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
635
|
+
private static createAttributeDefinitions(): AttributeDefinitions {
|
|
636
|
+
const attributeDefinitions = new AttributeDefinitions();
|
|
576
637
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
638
|
+
attributeDefinitions.add("enableEdgeDock", true).setType(Attribute.BOOLEAN).setDescription(
|
|
639
|
+
`enable docking to the edges of the layout, this will show the edge indicators`
|
|
640
|
+
);
|
|
641
|
+
attributeDefinitions.add("rootOrientationVertical", false).setType(Attribute.BOOLEAN).setDescription(
|
|
642
|
+
`the top level 'row' will layout horizontally by default, set this option true to make it layout vertically`
|
|
643
|
+
);
|
|
644
|
+
attributeDefinitions.add("enableRotateBorderIcons", true).setType(Attribute.BOOLEAN).setDescription(
|
|
645
|
+
`boolean indicating if tab icons should rotate with the text in the left and right borders`
|
|
646
|
+
);
|
|
584
647
|
|
|
585
|
-
|
|
586
|
-
|
|
648
|
+
// splitter
|
|
649
|
+
attributeDefinitions.add("splitterSize", 8).setType(Attribute.NUMBER).setDescription(
|
|
650
|
+
`width in pixels of all splitters between tabsets/borders`
|
|
651
|
+
);
|
|
652
|
+
attributeDefinitions.add("splitterExtra", 0).setType(Attribute.NUMBER).setDescription(
|
|
653
|
+
`additional width in pixels of the splitter hit test area`
|
|
654
|
+
);
|
|
655
|
+
attributeDefinitions.add("splitterEnableHandle", false).setType(Attribute.BOOLEAN).setDescription(
|
|
656
|
+
`enable a small centralized handle on all splitters`
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
// tab
|
|
660
|
+
attributeDefinitions.add("tabEnableClose", true).setType(Attribute.BOOLEAN);
|
|
661
|
+
attributeDefinitions.add("tabCloseType", 1).setType("ICloseType");
|
|
662
|
+
attributeDefinitions.add("tabEnablePopout", false).setType(Attribute.BOOLEAN).setAlias("tabEnableFloat");
|
|
663
|
+
attributeDefinitions.add("tabEnablePopoutIcon", true).setType(Attribute.BOOLEAN);
|
|
664
|
+
attributeDefinitions.add("tabEnablePopoutOverlay", false).setType(Attribute.BOOLEAN);
|
|
665
|
+
attributeDefinitions.add("tabEnableDrag", true).setType(Attribute.BOOLEAN);
|
|
666
|
+
attributeDefinitions.add("tabEnableRename", true).setType(Attribute.BOOLEAN);
|
|
667
|
+
attributeDefinitions.add("tabContentClassName", undefined).setType(Attribute.STRING);
|
|
668
|
+
attributeDefinitions.add("tabClassName", undefined).setType(Attribute.STRING);
|
|
669
|
+
attributeDefinitions.add("tabIcon", undefined).setType(Attribute.STRING);
|
|
670
|
+
attributeDefinitions.add("tabEnableRenderOnDemand", true).setType(Attribute.BOOLEAN);
|
|
671
|
+
attributeDefinitions.add("tabDragSpeed", 0.3).setType(Attribute.NUMBER);
|
|
672
|
+
attributeDefinitions.add("tabBorderWidth", -1).setType(Attribute.NUMBER);
|
|
673
|
+
attributeDefinitions.add("tabBorderHeight", -1).setType(Attribute.NUMBER);
|
|
674
|
+
|
|
675
|
+
// tabset
|
|
676
|
+
attributeDefinitions.add("tabSetEnableDeleteWhenEmpty", true).setType(Attribute.BOOLEAN);
|
|
677
|
+
attributeDefinitions.add("tabSetEnableDrop", true).setType(Attribute.BOOLEAN);
|
|
678
|
+
attributeDefinitions.add("tabSetEnableDrag", true).setType(Attribute.BOOLEAN);
|
|
679
|
+
attributeDefinitions.add("tabSetEnableDivide", true).setType(Attribute.BOOLEAN);
|
|
680
|
+
attributeDefinitions.add("tabSetEnableMaximize", true).setType(Attribute.BOOLEAN);
|
|
681
|
+
attributeDefinitions.add("tabSetEnableClose", false).setType(Attribute.BOOLEAN);
|
|
682
|
+
attributeDefinitions.add("tabSetEnableSingleTabStretch", false).setType(Attribute.BOOLEAN);
|
|
683
|
+
attributeDefinitions.add("tabSetAutoSelectTab", true).setType(Attribute.BOOLEAN);
|
|
684
|
+
attributeDefinitions.add("tabSetEnableActiveIcon", false).setType(Attribute.BOOLEAN);
|
|
685
|
+
attributeDefinitions.add("tabSetClassNameTabStrip", undefined).setType(Attribute.STRING);
|
|
686
|
+
attributeDefinitions.add("tabSetEnableTabStrip", true).setType(Attribute.BOOLEAN);
|
|
687
|
+
attributeDefinitions.add("tabSetEnableTabWrap", false).setType(Attribute.BOOLEAN);
|
|
688
|
+
attributeDefinitions.add("tabSetTabLocation", "top").setType("ITabLocation");
|
|
689
|
+
attributeDefinitions.add("tabMinWidth", DefaultMin).setType(Attribute.NUMBER);
|
|
690
|
+
attributeDefinitions.add("tabMinHeight", DefaultMin).setType(Attribute.NUMBER);
|
|
691
|
+
attributeDefinitions.add("tabSetMinWidth", DefaultMin).setType(Attribute.NUMBER);
|
|
692
|
+
attributeDefinitions.add("tabSetMinHeight", DefaultMin).setType(Attribute.NUMBER);
|
|
693
|
+
attributeDefinitions.add("tabMaxWidth", DefaultMax).setType(Attribute.NUMBER);
|
|
694
|
+
attributeDefinitions.add("tabMaxHeight", DefaultMax).setType(Attribute.NUMBER);
|
|
695
|
+
attributeDefinitions.add("tabSetMaxWidth", DefaultMax).setType(Attribute.NUMBER);
|
|
696
|
+
attributeDefinitions.add("tabSetMaxHeight", DefaultMax).setType(Attribute.NUMBER);
|
|
697
|
+
|
|
698
|
+
// border
|
|
699
|
+
attributeDefinitions.add("borderSize", 200).setType(Attribute.NUMBER);
|
|
700
|
+
attributeDefinitions.add("borderMinSize", DefaultMin).setType(Attribute.NUMBER);
|
|
701
|
+
attributeDefinitions.add("borderMaxSize", DefaultMax).setType(Attribute.NUMBER);
|
|
702
|
+
attributeDefinitions.add("borderEnableDrop", true).setType(Attribute.BOOLEAN);
|
|
703
|
+
attributeDefinitions.add("borderAutoSelectTabWhenOpen", true).setType(Attribute.BOOLEAN);
|
|
704
|
+
attributeDefinitions.add("borderAutoSelectTabWhenClosed", false).setType(Attribute.BOOLEAN);
|
|
705
|
+
attributeDefinitions.add("borderClassName", undefined).setType(Attribute.STRING);
|
|
706
|
+
attributeDefinitions.add("borderEnableAutoHide", false).setType(Attribute.BOOLEAN);
|
|
707
|
+
|
|
708
|
+
return attributeDefinitions;
|
|
587
709
|
}
|
|
588
710
|
}
|
|
589
711
|
|