@datagrok-libraries/dock-spawn-dg 0.0.1
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/.eslintrc.json +47 -0
- package/CHANGELOG.md +355 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/css/dock-manager-context-menu.css +19 -0
- package/css/dock-manager-style.css +190 -0
- package/css/dock-manager.css +411 -0
- package/images/close.svg +6 -0
- package/images/dock_bottom.png +0 -0
- package/images/dock_bottom_sel.png +0 -0
- package/images/dock_fill.png +0 -0
- package/images/dock_fill_sel.png +0 -0
- package/images/dock_left.png +0 -0
- package/images/dock_left_sel.png +0 -0
- package/images/dock_right.png +0 -0
- package/images/dock_right_sel.png +0 -0
- package/images/dock_top.png +0 -0
- package/images/dock_top_sel.png +0 -0
- package/index.ts +32 -0
- package/lib/BrowserDialogHelper.d.ts +8 -0
- package/lib/BrowserDialogHelper.d.ts.map +1 -0
- package/lib/BrowserDialogHelper.js +60 -0
- package/lib/ContainerType.d.ts +7 -0
- package/lib/ContainerType.d.ts.map +1 -0
- package/lib/ContainerType.js +7 -0
- package/lib/Dialog.d.ts +51 -0
- package/lib/Dialog.d.ts.map +1 -0
- package/lib/Dialog.js +209 -0
- package/lib/DockConfig.d.ts +9 -0
- package/lib/DockConfig.d.ts.map +1 -0
- package/lib/DockConfig.js +14 -0
- package/lib/DockGraphDeserializer.d.ts +19 -0
- package/lib/DockGraphDeserializer.d.ts.map +1 -0
- package/lib/DockGraphDeserializer.js +114 -0
- package/lib/DockGraphSerializer.d.ts +14 -0
- package/lib/DockGraphSerializer.d.ts.map +1 -0
- package/lib/DockGraphSerializer.js +40 -0
- package/lib/DockLayoutEngine.d.ts +36 -0
- package/lib/DockLayoutEngine.d.ts.map +1 -0
- package/lib/DockLayoutEngine.js +323 -0
- package/lib/DockManager.d.ts +155 -0
- package/lib/DockManager.d.ts.map +1 -0
- package/lib/DockManager.js +752 -0
- package/lib/DockManagerContext.d.ts +10 -0
- package/lib/DockManagerContext.d.ts.map +1 -0
- package/lib/DockManagerContext.js +12 -0
- package/lib/DockModel.d.ts +9 -0
- package/lib/DockModel.d.ts.map +1 -0
- package/lib/DockModel.js +8 -0
- package/lib/DockNode.d.ts +16 -0
- package/lib/DockNode.d.ts.map +1 -0
- package/lib/DockNode.js +64 -0
- package/lib/DockWheel.d.ts +41 -0
- package/lib/DockWheel.d.ts.map +1 -0
- package/lib/DockWheel.js +208 -0
- package/lib/DockWheelItem.d.ts +16 -0
- package/lib/DockWheelItem.d.ts.map +1 -0
- package/lib/DockWheelItem.js +34 -0
- package/lib/DocumentManagerContainer.d.ts +16 -0
- package/lib/DocumentManagerContainer.d.ts.map +1 -0
- package/lib/DocumentManagerContainer.js +28 -0
- package/lib/DocumentTabPage.d.ts +11 -0
- package/lib/DocumentTabPage.d.ts.map +1 -0
- package/lib/DocumentTabPage.js +26 -0
- package/lib/DraggableContainer.d.ts +51 -0
- package/lib/DraggableContainer.d.ts.map +1 -0
- package/lib/DraggableContainer.js +145 -0
- package/lib/EventHandler.d.ts +8 -0
- package/lib/EventHandler.d.ts.map +1 -0
- package/lib/EventHandler.js +14 -0
- package/lib/FillDockContainer.d.ts +34 -0
- package/lib/FillDockContainer.d.ts.map +1 -0
- package/lib/FillDockContainer.js +80 -0
- package/lib/HorizontalDockContainer.d.ts +7 -0
- package/lib/HorizontalDockContainer.d.ts.map +1 -0
- package/lib/HorizontalDockContainer.js +9 -0
- package/lib/PanelContainer.d.ts +114 -0
- package/lib/PanelContainer.d.ts.map +1 -0
- package/lib/PanelContainer.js +517 -0
- package/lib/Point.d.ts +6 -0
- package/lib/Point.d.ts.map +1 -0
- package/lib/Point.js +8 -0
- package/lib/ResizableContainer.d.ts +56 -0
- package/lib/ResizableContainer.d.ts.map +1 -0
- package/lib/ResizableContainer.js +254 -0
- package/lib/ResizeHandle.d.ts +16 -0
- package/lib/ResizeHandle.d.ts.map +1 -0
- package/lib/ResizeHandle.js +58 -0
- package/lib/SplitterBar.d.ts +33 -0
- package/lib/SplitterBar.d.ts.map +1 -0
- package/lib/SplitterBar.js +137 -0
- package/lib/SplitterDockContainer.d.ts +36 -0
- package/lib/SplitterDockContainer.d.ts.map +1 -0
- package/lib/SplitterDockContainer.js +75 -0
- package/lib/SplitterPanel.d.ts +27 -0
- package/lib/SplitterPanel.d.ts.map +1 -0
- package/lib/SplitterPanel.js +195 -0
- package/lib/TabHandle.d.ts +56 -0
- package/lib/TabHandle.d.ts.map +1 -0
- package/lib/TabHandle.js +292 -0
- package/lib/TabHost.d.ts +47 -0
- package/lib/TabHost.d.ts.map +1 -0
- package/lib/TabHost.js +238 -0
- package/lib/TabPage.d.ts +20 -0
- package/lib/TabPage.d.ts.map +1 -0
- package/lib/TabPage.js +81 -0
- package/lib/UndockInitiator.d.ts +32 -0
- package/lib/UndockInitiator.d.ts.map +1 -0
- package/lib/UndockInitiator.js +152 -0
- package/lib/Utils.d.ts +15 -0
- package/lib/Utils.d.ts.map +1 -0
- package/lib/Utils.js +69 -0
- package/lib/VerticalDockContainer.d.ts +7 -0
- package/lib/VerticalDockContainer.d.ts.map +1 -0
- package/lib/VerticalDockContainer.js +9 -0
- package/lib/enums/PanelType.d.ts +5 -0
- package/lib/enums/PanelType.d.ts.map +1 -0
- package/lib/enums/PanelType.js +5 -0
- package/lib/enums/TabHostDirection.d.ts +7 -0
- package/lib/enums/TabHostDirection.d.ts.map +1 -0
- package/lib/enums/TabHostDirection.js +7 -0
- package/lib/enums/WheelTypes.d.ts +12 -0
- package/lib/enums/WheelTypes.d.ts.map +1 -0
- package/lib/enums/WheelTypes.js +14 -0
- package/lib/i18n/Defaults.d.ts +12 -0
- package/lib/i18n/Defaults.d.ts.map +1 -0
- package/lib/i18n/Defaults.js +9 -0
- package/lib/i18n/Localizer.d.ts +7 -0
- package/lib/i18n/Localizer.d.ts.map +1 -0
- package/lib/i18n/Localizer.js +16 -0
- package/lib/index.d.ts +33 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +32 -0
- package/lib/interfaces/IDockContainer.d.ts +26 -0
- package/lib/interfaces/IDockContainer.d.ts.map +1 -0
- package/lib/interfaces/IDockContainer.js +1 -0
- package/lib/interfaces/IDockContainerWithSize.d.ts +6 -0
- package/lib/interfaces/IDockContainerWithSize.d.ts.map +1 -0
- package/lib/interfaces/IDockContainerWithSize.js +1 -0
- package/lib/interfaces/ILayoutEventListener.d.ts +27 -0
- package/lib/interfaces/ILayoutEventListener.d.ts.map +1 -0
- package/lib/interfaces/ILayoutEventListener.js +1 -0
- package/lib/interfaces/IMouseOrTouchEvent.d.ts +7 -0
- package/lib/interfaces/IMouseOrTouchEvent.d.ts.map +1 -0
- package/lib/interfaces/IMouseOrTouchEvent.js +1 -0
- package/lib/interfaces/INodeInfo.d.ts +8 -0
- package/lib/interfaces/INodeInfo.d.ts.map +1 -0
- package/lib/interfaces/INodeInfo.js +1 -0
- package/lib/interfaces/IPanelInfo.d.ts +10 -0
- package/lib/interfaces/IPanelInfo.d.ts.map +1 -0
- package/lib/interfaces/IPanelInfo.js +1 -0
- package/lib/interfaces/IRectangle.d.ts +7 -0
- package/lib/interfaces/IRectangle.d.ts.map +1 -0
- package/lib/interfaces/IRectangle.js +1 -0
- package/lib/interfaces/ISize.d.ts +5 -0
- package/lib/interfaces/ISize.d.ts.map +1 -0
- package/lib/interfaces/ISize.js +1 -0
- package/lib/interfaces/IState.d.ts +12 -0
- package/lib/interfaces/IState.d.ts.map +1 -0
- package/lib/interfaces/IState.js +1 -0
- package/lib/interfaces/IThickness.d.ts +7 -0
- package/lib/interfaces/IThickness.d.ts.map +1 -0
- package/lib/interfaces/IThickness.js +1 -0
- package/lib/webcomponent/DockSpawnTsWebcomponent.d.ts +35 -0
- package/lib/webcomponent/DockSpawnTsWebcomponent.d.ts.map +1 -0
- package/lib/webcomponent/DockSpawnTsWebcomponent.js +209 -0
- package/lib/webcomponent/styles.d.ts +5 -0
- package/lib/webcomponent/styles.d.ts.map +1 -0
- package/lib/webcomponent/styles.js +537 -0
- package/package.json +36 -0
- package/src/BrowserDialogHelper.ts +76 -0
- package/src/ContainerType.ts +6 -0
- package/src/Dialog.ts +253 -0
- package/src/DockConfig.ts +15 -0
- package/src/DockGraphDeserializer.ts +129 -0
- package/src/DockGraphSerializer.ts +53 -0
- package/src/DockLayoutEngine.ts +370 -0
- package/src/DockManager.ts +880 -0
- package/src/DockManagerContext.ts +16 -0
- package/src/DockModel.ts +12 -0
- package/src/DockNode.ts +81 -0
- package/src/DockWheel.ts +215 -0
- package/src/DockWheelItem.ts +41 -0
- package/src/DocumentManagerContainer.ts +39 -0
- package/src/DocumentTabPage.ts +35 -0
- package/src/DraggableContainer.ts +177 -0
- package/src/EventHandler.ts +17 -0
- package/src/FillDockContainer.ts +98 -0
- package/src/HorizontalDockContainer.ts +13 -0
- package/src/PanelContainer.ts +596 -0
- package/src/Point.ts +10 -0
- package/src/ResizableContainer.ts +293 -0
- package/src/ResizeHandle.ts +59 -0
- package/src/SplitterBar.ts +157 -0
- package/src/SplitterDockContainer.ts +95 -0
- package/src/SplitterPanel.ts +228 -0
- package/src/TabHandle.ts +347 -0
- package/src/TabHost.ts +267 -0
- package/src/TabPage.ts +98 -0
- package/src/UndockInitiator.ts +181 -0
- package/src/Utils.ts +85 -0
- package/src/VerticalDockContainer.ts +13 -0
- package/src/enums/PanelType.ts +4 -0
- package/src/enums/TabHostDirection.ts +6 -0
- package/src/enums/WheelTypes.ts +14 -0
- package/src/i18n/Defaults.ts +20 -0
- package/src/i18n/Localizer.ts +23 -0
- package/src/index.ts +32 -0
- package/src/interfaces/IDockContainer.ts +27 -0
- package/src/interfaces/IDockContainerWithSize.ts +6 -0
- package/src/interfaces/ILayoutEventListener.ts +28 -0
- package/src/interfaces/IMouseOrTouchEvent.ts +6 -0
- package/src/interfaces/INodeInfo.ts +8 -0
- package/src/interfaces/IPanelInfo.ts +10 -0
- package/src/interfaces/IRectangle.ts +6 -0
- package/src/interfaces/ISize.ts +4 -0
- package/src/interfaces/IState.ts +12 -0
- package/src/interfaces/IThickness.ts +6 -0
- package/src/webcomponent/DockSpawnTsWebcomponent.ts +248 -0
- package/src/webcomponent/styles.ts +544 -0
- package/tsconfig.json +74 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
import {DockWheel} from './DockWheel.js';
|
|
2
|
+
import {Utils} from './Utils.js';
|
|
3
|
+
import {Point} from './Point.js';
|
|
4
|
+
import {DockManagerContext} from './DockManagerContext.js';
|
|
5
|
+
import {DockNode} from './DockNode.js';
|
|
6
|
+
import {DockLayoutEngine} from './DockLayoutEngine.js';
|
|
7
|
+
import {EventHandler} from './EventHandler.js';
|
|
8
|
+
import {Dialog} from './Dialog.js';
|
|
9
|
+
import {DockGraphSerializer} from './DockGraphSerializer.js';
|
|
10
|
+
import {DockGraphDeserializer} from './DockGraphDeserializer.js';
|
|
11
|
+
import {IDockContainer} from './interfaces/IDockContainer.js';
|
|
12
|
+
import {TabPage} from './TabPage.js';
|
|
13
|
+
import {SplitterDockContainer} from './SplitterDockContainer.js';
|
|
14
|
+
import {PanelContainer} from './PanelContainer.js';
|
|
15
|
+
import {FillDockContainer} from './FillDockContainer.js';
|
|
16
|
+
import {ILayoutEventListener} from './interfaces/ILayoutEventListener.js';
|
|
17
|
+
import {DockModel} from './DockModel.js';
|
|
18
|
+
import {IDockContainerWithSize} from './interfaces/IDockContainerWithSize.js';
|
|
19
|
+
import {DockConfig} from './DockConfig.js';
|
|
20
|
+
import {PanelType} from './enums/PanelType.js';
|
|
21
|
+
import {IState} from './interfaces/IState.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Dock manager manages all the dock panels in a hierarchy, similar to visual studio.
|
|
25
|
+
* It owns a Html Div element inside which all panels are docked
|
|
26
|
+
* Initially the document manager takes up the central space and acts as the root node
|
|
27
|
+
*/
|
|
28
|
+
export class DockManager {
|
|
29
|
+
public element: HTMLElement;
|
|
30
|
+
public context: DockManagerContext;
|
|
31
|
+
public dockWheel: DockWheel;
|
|
32
|
+
public layoutEngine: DockLayoutEngine;
|
|
33
|
+
public mouseMoveHandler: EventHandler;
|
|
34
|
+
public touchMoveHandler: EventHandler;
|
|
35
|
+
public layoutEventListeners: ILayoutEventListener[];
|
|
36
|
+
public closePanelContainerCallback: (panelContainer: PanelContainer) => Promise<boolean>;
|
|
37
|
+
public defaultDialogPosition: Point;
|
|
38
|
+
public backgroundContext: HTMLElement;
|
|
39
|
+
public zIndexCounter: number;
|
|
40
|
+
public zIndexTabHost: number;
|
|
41
|
+
public zIndexTabHandle: number;
|
|
42
|
+
public zIndexDialogCounter: number;
|
|
43
|
+
public onKeyPressBound: any;
|
|
44
|
+
public iframes: HTMLIFrameElement[];
|
|
45
|
+
public _undockEnabled: boolean;
|
|
46
|
+
public getElementCallback: (state: IState) => Promise<{ element: HTMLElement, title: string }>;
|
|
47
|
+
|
|
48
|
+
private _config: DockConfig;
|
|
49
|
+
private _activePanel: PanelContainer;
|
|
50
|
+
private _lastPanelNotADialog: PanelContainer;
|
|
51
|
+
|
|
52
|
+
private _activeDocument: PanelContainer;
|
|
53
|
+
|
|
54
|
+
constructor(element: HTMLElement, config?: DockConfig) {
|
|
55
|
+
if (element === undefined)
|
|
56
|
+
throw new Error('Invalid Dock Manager element provided');
|
|
57
|
+
|
|
58
|
+
this._config = Object.assign(new DockConfig(), config);
|
|
59
|
+
|
|
60
|
+
this.element = element;
|
|
61
|
+
this.context = this.dockWheel = this.layoutEngine = this.mouseMoveHandler = this.touchMoveHandler = undefined;
|
|
62
|
+
this.layoutEventListeners = [];
|
|
63
|
+
|
|
64
|
+
this.defaultDialogPosition = new Point(0, 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get config(): DockConfig {
|
|
68
|
+
return this._config;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
initialize() {
|
|
72
|
+
this.backgroundContext = this.element.children[0] as HTMLElement;
|
|
73
|
+
this.context = new DockManagerContext(this);
|
|
74
|
+
const documentNode = new DockNode(this.context.documentManagerView);
|
|
75
|
+
this.context.model.rootNode = documentNode;
|
|
76
|
+
this.context.model.documentManagerNode = documentNode;
|
|
77
|
+
this.context.model.dialogs = [];
|
|
78
|
+
this.setRootNode(this.context.model.rootNode);
|
|
79
|
+
// Resize the layout
|
|
80
|
+
this.resize(this.element.clientWidth, this.element.clientHeight);
|
|
81
|
+
this.dockWheel = new DockWheel(this);
|
|
82
|
+
this.layoutEngine = new DockLayoutEngine(this);
|
|
83
|
+
this._undockEnabled = true;
|
|
84
|
+
this.rebuildLayout(this.context.model.rootNode);
|
|
85
|
+
this.zIndexCounter = 1001;
|
|
86
|
+
this.zIndexTabHost = 1000;
|
|
87
|
+
this.zIndexTabHandle = 100;
|
|
88
|
+
this.zIndexDialogCounter = 10001;
|
|
89
|
+
if (this.backgroundContext != null) {
|
|
90
|
+
(<FillDockContainer> this.context.model.rootNode.container).tabHost.hostElement
|
|
91
|
+
.insertBefore(this.backgroundContext, (<FillDockContainer> this.context.model.rootNode.container).tabHost.hostElement.firstChild);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.onKeyPressBound = this.onKeyPress.bind(this);
|
|
95
|
+
this.element.addEventListener('keydown', this.onKeyPressBound);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onKeyPress(e: KeyboardEvent) {
|
|
99
|
+
if (e.key == 'Escape' && this.activePanel && !this.activePanel._hideCloseButton) {
|
|
100
|
+
if ((this.activePanel.isDialog && this._config.escClosesDialog) || (!this.activePanel.isDialog && this._config.escClosesWindow)) {
|
|
101
|
+
const panel = this.activePanel;
|
|
102
|
+
this.activePanel = null;
|
|
103
|
+
panel.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
checkXBounds(container: HTMLElement, currentMousePosition: Point, previousMousePosition: Point, resizeWest: boolean, resizeEast: boolean) {
|
|
109
|
+
if (this._config.moveOnlyWithinDockConatiner)
|
|
110
|
+
return this.checkXBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeWest, resizeEast);
|
|
111
|
+
|
|
112
|
+
const rect = this.element.getBoundingClientRect();
|
|
113
|
+
let dx = Math.floor(currentMousePosition.x - previousMousePosition.x);
|
|
114
|
+
const leftBounds = container.offsetLeft + container.offsetWidth + dx + rect.left < 40; // || (container.offsetLeft + container.offsetWidth + dx - 40 ) < 0;
|
|
115
|
+
const rightBounds = container.offsetLeft + dx + rect.left > (window.innerWidth - 40);
|
|
116
|
+
if (leftBounds) {
|
|
117
|
+
previousMousePosition.x = currentMousePosition.x;
|
|
118
|
+
dx = 0;
|
|
119
|
+
const d = 40 - (container.offsetWidth + container.offsetLeft + rect.left);
|
|
120
|
+
if (d > 0)
|
|
121
|
+
dx = d;
|
|
122
|
+
} else if (rightBounds) {
|
|
123
|
+
previousMousePosition.x = currentMousePosition.x;
|
|
124
|
+
dx = 0;
|
|
125
|
+
const d = (window.innerWidth - 40) - container.offsetLeft - rect.left;
|
|
126
|
+
if (d > 0)
|
|
127
|
+
dx = d;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return dx;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
checkXBoundsWithinDockContainer(container: HTMLElement, currentMousePosition: Point, previousMousePosition: Point, resizeWest: boolean, resizeEast: boolean) {
|
|
134
|
+
let dx = currentMousePosition.x - previousMousePosition.x;
|
|
135
|
+
const bbOuter = this.element.getBoundingClientRect();
|
|
136
|
+
const bbInner = container.getBoundingClientRect();
|
|
137
|
+
const leftBounds = dx < 0 && bbInner.left + dx < bbOuter.left && !resizeEast;
|
|
138
|
+
const rightBounds = dx > 0 && bbInner.right + dx > bbOuter.right && !resizeWest;
|
|
139
|
+
|
|
140
|
+
if (leftBounds) {
|
|
141
|
+
currentMousePosition.x -= dx;
|
|
142
|
+
dx = bbOuter.left - bbInner.left;
|
|
143
|
+
currentMousePosition.x -= dx;
|
|
144
|
+
} else if (rightBounds) {
|
|
145
|
+
currentMousePosition.x -= dx;
|
|
146
|
+
dx = bbOuter.right - bbInner.right;
|
|
147
|
+
currentMousePosition.x -= dx;
|
|
148
|
+
}
|
|
149
|
+
return dx;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
checkYBounds(container: HTMLElement, currentMousePosition: Point, previousMousePosition: Point, resizeNorth: boolean, resizeSouth: boolean) {
|
|
153
|
+
if (this._config.moveOnlyWithinDockConatiner)
|
|
154
|
+
return this.checkYBoundsWithinDockContainer(container, currentMousePosition, previousMousePosition, resizeNorth, resizeSouth);
|
|
155
|
+
|
|
156
|
+
const rect = this.element.getBoundingClientRect();
|
|
157
|
+
let dy = Math.floor(currentMousePosition.y - previousMousePosition.y);
|
|
158
|
+
const topBounds = container.offsetTop + dy + rect.top < 0;
|
|
159
|
+
const bottomBounds = container.offsetTop + dy + rect.top > (window.innerHeight - 16);
|
|
160
|
+
if (topBounds) {
|
|
161
|
+
previousMousePosition.y = currentMousePosition.y;
|
|
162
|
+
dy = 0;
|
|
163
|
+
} else if (bottomBounds) {
|
|
164
|
+
previousMousePosition.y = currentMousePosition.y;
|
|
165
|
+
dy = 0;
|
|
166
|
+
const d = (window.innerHeight - 16) - container.offsetTop - rect.top;
|
|
167
|
+
if (d > 0)
|
|
168
|
+
dy = d;
|
|
169
|
+
}
|
|
170
|
+
return dy;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
checkYBoundsWithinDockContainer(container: HTMLElement, currentMousePosition: Point, previousMousePosition: Point, resizeNorth: boolean, resizeSouth: boolean) {
|
|
174
|
+
let dy = currentMousePosition.y - previousMousePosition.y;
|
|
175
|
+
const bbOuter = this.element.getBoundingClientRect();
|
|
176
|
+
const bbInner = container.getBoundingClientRect();
|
|
177
|
+
const topBounds = dy < 0 && bbInner.top + dy < bbOuter.top && !resizeSouth;
|
|
178
|
+
const bottomBounds = dy > 0 && bbInner.bottom + dy > bbOuter.bottom && !resizeNorth;
|
|
179
|
+
|
|
180
|
+
if (topBounds) {
|
|
181
|
+
currentMousePosition.y -= dy;
|
|
182
|
+
dy = bbOuter.top - bbInner.top;
|
|
183
|
+
currentMousePosition.y -= dy;
|
|
184
|
+
} else if (bottomBounds) {
|
|
185
|
+
currentMousePosition.y -= dy;
|
|
186
|
+
dy = bbOuter.bottom - bbInner.bottom;
|
|
187
|
+
currentMousePosition.y -= dy;
|
|
188
|
+
}
|
|
189
|
+
return dy;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
rebuildLayout(node: DockNode) {
|
|
193
|
+
node.children.forEach((child) => {
|
|
194
|
+
this.rebuildLayout(child);
|
|
195
|
+
});
|
|
196
|
+
node.performLayout(false);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
invalidate() {
|
|
200
|
+
this.resize(this.element.clientWidth, this.element.clientHeight);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
resize(width: number, height: number) {
|
|
204
|
+
this.element.style.width = width + 'px';
|
|
205
|
+
this.element.style.height = height + 'px';
|
|
206
|
+
this.context.model.rootNode.container.resize(width, height);
|
|
207
|
+
|
|
208
|
+
let offsetX = 0; let offsetY = 0;
|
|
209
|
+
for (const dialog of this.context.model.dialogs) {
|
|
210
|
+
if (dialog.position.x > this.element.clientWidth || dialog.position.y > this.element.clientHeight) {
|
|
211
|
+
if (offsetX > this.element.clientWidth || offsetY > this.element.clientHeight)
|
|
212
|
+
offsetX = 0, offsetY = 0;
|
|
213
|
+
dialog.setPosition(100 + offsetX, 100 + offsetY);
|
|
214
|
+
offsetX += 100;
|
|
215
|
+
offsetY += 100;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Reset the dock model . This happens when the state is loaded from json
|
|
222
|
+
*/
|
|
223
|
+
setModel(model: DockModel) {
|
|
224
|
+
Utils.removeNode(this.context.documentManagerView.containerElement);
|
|
225
|
+
this.context.model = model;
|
|
226
|
+
this.setRootNode(model.rootNode);
|
|
227
|
+
|
|
228
|
+
this.rebuildLayout(model.rootNode);
|
|
229
|
+
this.loadResize(model.rootNode);
|
|
230
|
+
// this.invalidate();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
loadResize(node: DockNode) {
|
|
234
|
+
node.children.reverse().forEach((child) => {
|
|
235
|
+
this.loadResize(child);
|
|
236
|
+
node.container.setActiveChild(child.container);
|
|
237
|
+
});
|
|
238
|
+
node.children.reverse();
|
|
239
|
+
const container = node.container as IDockContainerWithSize;
|
|
240
|
+
node.container.resize(container.state.width, container.state.height);
|
|
241
|
+
// node.performLayout();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
setRootNode(node: DockNode) {
|
|
246
|
+
// if (this.context.model.rootNode)
|
|
247
|
+
// {
|
|
248
|
+
// // detach it from the dock manager's base element
|
|
249
|
+
// context.model.rootNode.detachFromParent();
|
|
250
|
+
// }
|
|
251
|
+
|
|
252
|
+
// Attach the new node to the dock manager's base element and set as root node
|
|
253
|
+
node.detachFromParent();
|
|
254
|
+
this.context.model.rootNode = node;
|
|
255
|
+
this.element.appendChild(node.container.containerElement);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_onDialogDragStarted(sender: Dialog, e) {
|
|
259
|
+
this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
|
|
260
|
+
this.dockWheel.activeDialog = sender;
|
|
261
|
+
if (sender.noDocking == null || sender.noDocking !== true)
|
|
262
|
+
this.dockWheel.showWheel();
|
|
263
|
+
if (this.mouseMoveHandler) {
|
|
264
|
+
this.mouseMoveHandler.cancel();
|
|
265
|
+
delete this.mouseMoveHandler;
|
|
266
|
+
}
|
|
267
|
+
if (this.touchMoveHandler) {
|
|
268
|
+
this.touchMoveHandler.cancel();
|
|
269
|
+
delete this.touchMoveHandler;
|
|
270
|
+
}
|
|
271
|
+
this.mouseMoveHandler = new EventHandler(window, 'mousemove', this._onMouseMoved.bind(this));
|
|
272
|
+
this.touchMoveHandler = new EventHandler(window, 'touchmove', this._onMouseMoved.bind(this));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_onDialogDragEnded(sender: Dialog, e) {
|
|
276
|
+
if (this.mouseMoveHandler) {
|
|
277
|
+
this.mouseMoveHandler.cancel();
|
|
278
|
+
delete this.mouseMoveHandler;
|
|
279
|
+
}
|
|
280
|
+
if (this.touchMoveHandler) {
|
|
281
|
+
this.touchMoveHandler.cancel();
|
|
282
|
+
delete this.touchMoveHandler;
|
|
283
|
+
}
|
|
284
|
+
this.dockWheel.onDialogDropped(sender);
|
|
285
|
+
this.dockWheel.hideWheel();
|
|
286
|
+
delete this.dockWheel.activeDialog;
|
|
287
|
+
//TODO: not so good
|
|
288
|
+
sender.saveState(sender.elementDialog.offsetLeft, sender.elementDialog.offsetTop);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private _onMouseMoved(e) {
|
|
292
|
+
if (e.changedTouches != null) { // TouchMove Event
|
|
293
|
+
e = e.changedTouches[0];
|
|
294
|
+
}
|
|
295
|
+
this.dockWheel.activeNode = this._findNodeOnPoint(e.clientX, e.clientY);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Perform a DFS (DeepFirstSearch) on the dock model's tree to find the
|
|
300
|
+
* deepest level panel (i.e. the top-most non-overlapping panel)
|
|
301
|
+
* that is under the mouse cursor
|
|
302
|
+
* Retuns null if no node is found under this point
|
|
303
|
+
*/
|
|
304
|
+
private _findNodeOnPoint(x: number, y: number) {
|
|
305
|
+
const stack = [];
|
|
306
|
+
stack.push(this.context.model.rootNode);
|
|
307
|
+
let bestMatch;
|
|
308
|
+
|
|
309
|
+
while (stack.length > 0) {
|
|
310
|
+
const topNode = stack.pop();
|
|
311
|
+
|
|
312
|
+
if (Utils.isPointInsideNode(x, y, topNode)) {
|
|
313
|
+
// This node contains the point.
|
|
314
|
+
bestMatch = topNode;
|
|
315
|
+
|
|
316
|
+
// Keep looking future down
|
|
317
|
+
[].push.apply(stack, topNode.children);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return bestMatch;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Dock the [dialog] to the left of the [referenceNode] node */
|
|
324
|
+
dockDialogLeft(referenceNode: DockNode, dialog: Dialog) {
|
|
325
|
+
return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockLeft.bind(this.layoutEngine));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Dock the [dialog] to the right of the [referenceNode] node */
|
|
329
|
+
dockDialogRight(referenceNode: DockNode, dialog: Dialog) {
|
|
330
|
+
return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockRight.bind(this.layoutEngine));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Dock the [dialog] above the [referenceNode] node */
|
|
334
|
+
dockDialogUp(referenceNode: DockNode, dialog: Dialog) {
|
|
335
|
+
return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockUp.bind(this.layoutEngine));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Dock the [dialog] below the [referenceNode] node */
|
|
339
|
+
dockDialogDown(referenceNode: DockNode, dialog: Dialog) {
|
|
340
|
+
return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockDown.bind(this.layoutEngine));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Dock the [dialog] as a tab inside the [referenceNode] node */
|
|
344
|
+
dockDialogFill(referenceNode: DockNode, dialog: Dialog) {
|
|
345
|
+
return this._requestDockDialog(referenceNode, dialog, this.layoutEngine.dockFill.bind(this.layoutEngine));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Dock the [container] to the left of the [referenceNode] node */
|
|
349
|
+
dockLeft(referenceNode: DockNode, container: PanelContainer, ratio: number) {
|
|
350
|
+
return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockLeft.bind(this.layoutEngine), false, ratio);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Dock the [container] to the right of the [referenceNode] node */
|
|
354
|
+
dockRight(referenceNode: DockNode, container: PanelContainer, ratio: number) {
|
|
355
|
+
return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockRight.bind(this.layoutEngine), true, ratio);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Dock the [container] above the [referenceNode] node */
|
|
359
|
+
dockUp(referenceNode: DockNode, container: PanelContainer, ratio: number) {
|
|
360
|
+
return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockUp.bind(this.layoutEngine), false, ratio);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Dock the [container] below the [referenceNode] node */
|
|
364
|
+
dockDown(referenceNode: DockNode, container: PanelContainer, ratio: number) {
|
|
365
|
+
return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockDown.bind(this.layoutEngine), true, ratio);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Dock the [container] as a tab inside the [referenceNode] node */
|
|
369
|
+
dockFill(referenceNode: DockNode, container: PanelContainer) {
|
|
370
|
+
return this._requestDockContainer(referenceNode, container, this.layoutEngine.dockFill.bind(this.layoutEngine), false);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
floatDialog(container: PanelContainer, x: number, y: number, grayoutParent?: PanelContainer, disableResize?: boolean): Dialog {
|
|
374
|
+
let retdiag = undefined;
|
|
375
|
+
|
|
376
|
+
//check the dialog do not already exist
|
|
377
|
+
this.context.model.dialogs.forEach((dialog) => {
|
|
378
|
+
if (container == dialog.panel) {
|
|
379
|
+
dialog.show();
|
|
380
|
+
dialog.setPosition(x, y);
|
|
381
|
+
retdiag = dialog;
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
if (retdiag)
|
|
385
|
+
return retdiag;
|
|
386
|
+
//try to undock just in case
|
|
387
|
+
try {
|
|
388
|
+
const node = this._findNodeFromContainer(container);
|
|
389
|
+
this.layoutEngine.undock(node);
|
|
390
|
+
} catch (err) { }
|
|
391
|
+
|
|
392
|
+
const panel = container;
|
|
393
|
+
Utils.removeNode(panel.elementPanel);
|
|
394
|
+
panel.isDialog = true;
|
|
395
|
+
const dialog = new Dialog(panel, this, grayoutParent, disableResize);
|
|
396
|
+
dialog.setPosition(x, y);
|
|
397
|
+
return dialog;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private _requestDockDialog(referenceNode: DockNode, dialog: Dialog, layoutDockFunction: (referenceNode: DockNode, newNode: DockNode) => void) {
|
|
401
|
+
// Get the active dialog that was dragged on to the dock wheel
|
|
402
|
+
const panel = dialog.panel;
|
|
403
|
+
const newNode = new DockNode(panel);
|
|
404
|
+
panel.prepareForDocking();
|
|
405
|
+
panel.elementContentContainer.style.zIndex = '';
|
|
406
|
+
dialog.destroy();
|
|
407
|
+
layoutDockFunction(referenceNode, newNode);
|
|
408
|
+
// this.invalidate();
|
|
409
|
+
return newNode;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private _checkShowBackgroundContext() {
|
|
413
|
+
if (this.backgroundContext != null) {
|
|
414
|
+
if (this.context.model.documentManagerNode.children.length > 0)
|
|
415
|
+
this.backgroundContext.style.display = 'none';
|
|
416
|
+
else
|
|
417
|
+
this.backgroundContext.style.display = 'block';
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private _requestDockContainer(referenceNode: DockNode, container: IDockContainer, layoutDockFunction: (referenceNode: DockNode, newNode: DockNode) => void, dockedToPrevious: boolean, ratio?: number) {
|
|
422
|
+
// Get the active dialog that was dragged on to the dock wheel
|
|
423
|
+
const newNode = new DockNode(container);
|
|
424
|
+
if (container.containerType === 'panel') {
|
|
425
|
+
const panel = container as PanelContainer;
|
|
426
|
+
panel.prepareForDocking();
|
|
427
|
+
Utils.removeNode(panel.elementPanel);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let ratios: number[] = null;
|
|
431
|
+
let oldSplitter: SplitterDockContainer;
|
|
432
|
+
if (referenceNode?.parent && referenceNode.parent.container) {
|
|
433
|
+
oldSplitter = referenceNode.parent.container as SplitterDockContainer;
|
|
434
|
+
if (oldSplitter.getRatios)
|
|
435
|
+
ratios = oldSplitter.getRatios();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
layoutDockFunction(referenceNode, newNode);
|
|
439
|
+
|
|
440
|
+
if (ratio && newNode.parent && (newNode.parent.container.containerType === 'vertical' || newNode.parent.container.containerType === 'horizontal')) {
|
|
441
|
+
const splitter = newNode.parent.container as SplitterDockContainer;
|
|
442
|
+
if (ratios && oldSplitter == splitter) {
|
|
443
|
+
if (dockedToPrevious) {
|
|
444
|
+
for (let i = 0; i < ratios.length; i++)
|
|
445
|
+
ratios[i] = ratios[i] - ratios[i] * ratio;
|
|
446
|
+
|
|
447
|
+
ratios.push(ratio);
|
|
448
|
+
} else {
|
|
449
|
+
ratios[0] = ratios[0] - ratio;
|
|
450
|
+
ratios.unshift(ratio);
|
|
451
|
+
}
|
|
452
|
+
splitter.setRatios(ratios);
|
|
453
|
+
} else
|
|
454
|
+
splitter.setContainerRatio(container, ratio);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
this.rebuildLayout(this.context.model.rootNode);
|
|
458
|
+
this.invalidate();
|
|
459
|
+
|
|
460
|
+
this._checkShowBackgroundContext();
|
|
461
|
+
|
|
462
|
+
return newNode;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
_requestTabReorder(container: IDockContainer, e) {
|
|
466
|
+
const node = this._findNodeFromContainer(container);
|
|
467
|
+
this.layoutEngine.reorderTabs(node, e.handle, e.state, e.index);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Undocks a panel and converts it into a floating dialog window
|
|
472
|
+
* It is assumed that only leaf nodes (panels) can be undocked
|
|
473
|
+
*/
|
|
474
|
+
requestUndockToDialog(container: PanelContainer, event, dragOffset: Point) {
|
|
475
|
+
const node = this._findNodeFromContainer(container);
|
|
476
|
+
this.layoutEngine.undock(node);
|
|
477
|
+
|
|
478
|
+
const panelContainer = (<PanelContainer>node.container);
|
|
479
|
+
panelContainer.elementPanel.style.display = 'block';
|
|
480
|
+
|
|
481
|
+
// Create a new dialog window for the undocked panel
|
|
482
|
+
const dialog = new Dialog(panelContainer, this, null);
|
|
483
|
+
|
|
484
|
+
if (panelContainer.lastDialogSize)
|
|
485
|
+
dialog.resize(panelContainer.lastDialogSize.width, panelContainer.lastDialogSize.height);
|
|
486
|
+
|
|
487
|
+
if (event !== undefined) {
|
|
488
|
+
// Adjust the relative position
|
|
489
|
+
const dialogWidth = dialog.elementDialog.clientWidth;
|
|
490
|
+
if (dragOffset.x > dialogWidth)
|
|
491
|
+
dragOffset.x = 0.75 * dialogWidth;
|
|
492
|
+
dialog.setPosition(
|
|
493
|
+
event.clientX - dragOffset.x,
|
|
494
|
+
event.clientY - dragOffset.y);
|
|
495
|
+
dialog.draggable.onMouseDown(event);
|
|
496
|
+
}
|
|
497
|
+
return dialog;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* closes a Panel
|
|
502
|
+
*/
|
|
503
|
+
requestClose(container: PanelContainer) {
|
|
504
|
+
const node = this._findNodeFromContainer(container);
|
|
505
|
+
this.layoutEngine.close(node);
|
|
506
|
+
if (this.activePanel == container)
|
|
507
|
+
this.activePanel = null;
|
|
508
|
+
if (this._activeDocument == container) {
|
|
509
|
+
const last = this._activeDocument;
|
|
510
|
+
this._activeDocument = null;
|
|
511
|
+
this.notifyOnActiveDocumentChange(null, last);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Opens a Element in a Dialog
|
|
517
|
+
* It is assumed that only leaf nodes (panels) can be undocked
|
|
518
|
+
*/
|
|
519
|
+
openInDialog(container: PanelContainer, event, dragOffset: Point, disableResize?: boolean) {
|
|
520
|
+
// Create a new dialog window for the undocked panel
|
|
521
|
+
const dialog = new Dialog(container, this, null, disableResize);
|
|
522
|
+
|
|
523
|
+
if (event != null) {
|
|
524
|
+
// Adjust the relative position
|
|
525
|
+
const dialogWidth = dialog.elementDialog.clientWidth;
|
|
526
|
+
if (dragOffset.x > dialogWidth)
|
|
527
|
+
dragOffset.x = 0.75 * dialogWidth;
|
|
528
|
+
dialog.setPosition(
|
|
529
|
+
event.clientX - dragOffset.x,
|
|
530
|
+
event.clientY - dragOffset.y);
|
|
531
|
+
dialog.draggable.onMouseDown(event);
|
|
532
|
+
}
|
|
533
|
+
return dialog;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** Undocks a panel and converts it into a floating dialog window
|
|
537
|
+
* It is assumed that only leaf nodes (panels) can be undocked
|
|
538
|
+
*/
|
|
539
|
+
requestUndock(container: PanelContainer) {
|
|
540
|
+
const node = this._findNodeFromContainer(container);
|
|
541
|
+
this.layoutEngine.undock(node);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Removes a dock container from the dock layout hierarcy
|
|
546
|
+
* Returns the node that was removed from the dock tree
|
|
547
|
+
*/
|
|
548
|
+
requestRemove(container: PanelContainer) {
|
|
549
|
+
const node = this._findNodeFromContainer(container);
|
|
550
|
+
const parent = node.parent;
|
|
551
|
+
node.detachFromParent();
|
|
552
|
+
if (parent)
|
|
553
|
+
this.rebuildLayout(parent);
|
|
554
|
+
return node;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
getNodeByElementId(id: string): DockNode {
|
|
558
|
+
const stack = [];
|
|
559
|
+
stack.push(this.context.model.rootNode);
|
|
560
|
+
|
|
561
|
+
while (stack.length > 0) {
|
|
562
|
+
const topNode = stack.pop();
|
|
563
|
+
|
|
564
|
+
if (topNode.container instanceof PanelContainer) {
|
|
565
|
+
if (topNode.container.elementContent.id === id)
|
|
566
|
+
return topNode;
|
|
567
|
+
if (topNode.container.elementContent instanceof HTMLSlotElement) {
|
|
568
|
+
if ((<HTMLSlotElement>topNode.container.elementContent).assignedElements()?.[0]?.id === id)
|
|
569
|
+
return topNode;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
[].push.apply(stack, topNode.children);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
getNodeByElement(element: Element): DockNode {
|
|
580
|
+
const stack = [];
|
|
581
|
+
stack.push(this.context.model.rootNode);
|
|
582
|
+
|
|
583
|
+
while (stack.length > 0) {
|
|
584
|
+
const topNode = stack.pop();
|
|
585
|
+
|
|
586
|
+
if (topNode.container instanceof PanelContainer) {
|
|
587
|
+
if (topNode.container.elementContent === element)
|
|
588
|
+
return topNode;
|
|
589
|
+
if (topNode.container.elementContent instanceof HTMLSlotElement) {
|
|
590
|
+
if ((<HTMLSlotElement>topNode.container.elementContent).assignedElements()?.[0] === element)
|
|
591
|
+
return topNode;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
[].push.apply(stack, topNode.children);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/** Finds the node that owns the specified [container] */
|
|
602
|
+
private _findNodeFromContainer(container: IDockContainer): DockNode {
|
|
603
|
+
const stack = [];
|
|
604
|
+
stack.push(this.context.model.rootNode);
|
|
605
|
+
|
|
606
|
+
while (stack.length > 0) {
|
|
607
|
+
const topNode = stack.pop();
|
|
608
|
+
|
|
609
|
+
if (topNode.container === container)
|
|
610
|
+
return topNode;
|
|
611
|
+
[].push.apply(stack, topNode.children);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
findNodeFromContainerElement(containerElement: HTMLElement) {
|
|
618
|
+
const stack: DockNode[] = [];
|
|
619
|
+
stack.push(this.context.model.rootNode);
|
|
620
|
+
|
|
621
|
+
while (stack.length > 0) {
|
|
622
|
+
const topNode = stack.pop();
|
|
623
|
+
|
|
624
|
+
if (topNode.container.containerElement === containerElement)
|
|
625
|
+
return topNode;
|
|
626
|
+
[].push.apply(stack, topNode.children);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
addLayoutListener(listener: ILayoutEventListener) {
|
|
633
|
+
this.layoutEventListeners.push(listener);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
removeLayoutListener(listener: ILayoutEventListener) {
|
|
637
|
+
this.layoutEventListeners.splice(this.layoutEventListeners.indexOf(listener), 1);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
suspendLayout(panel: IDockContainer) {
|
|
641
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
642
|
+
if (listener.onSuspendLayout) listener.onSuspendLayout(this, panel);
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
resumeLayout(panel: IDockContainer) {
|
|
647
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
648
|
+
if (listener.onResumeLayout) listener.onResumeLayout(this, panel);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
notifyOnDock(dockNode: DockNode) {
|
|
653
|
+
this._checkShowBackgroundContext();
|
|
654
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
655
|
+
if (listener.onDock) {
|
|
656
|
+
listener.onDock(this, dockNode);
|
|
657
|
+
dockNode.container.resize(dockNode.container.width, dockNode.container.height);
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
notifyOnTabsReorder(dockNode: DockNode) {
|
|
663
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
664
|
+
if (listener.onTabsReorder)
|
|
665
|
+
listener.onTabsReorder(this, dockNode);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
notifyOnUnDock(dockNode: DockNode) {
|
|
670
|
+
this._checkShowBackgroundContext();
|
|
671
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
672
|
+
if (listener.onUndock)
|
|
673
|
+
listener.onUndock(this, dockNode);
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
notifyOnClosePanel(panel: PanelContainer) {
|
|
678
|
+
this._checkShowBackgroundContext();
|
|
679
|
+
if (this.activePanel == panel)
|
|
680
|
+
this.activePanel = null;
|
|
681
|
+
if (this._activeDocument == panel) {
|
|
682
|
+
const last = this._activeDocument;
|
|
683
|
+
this._activeDocument = null;
|
|
684
|
+
this.notifyOnActiveDocumentChange(null, last);
|
|
685
|
+
}
|
|
686
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
687
|
+
if (listener.onClosePanel)
|
|
688
|
+
listener.onClosePanel(this, panel);
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
notifyOnCreateDialog(dialog: Dialog) {
|
|
693
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
694
|
+
if (listener.onCreateDialog)
|
|
695
|
+
listener.onCreateDialog(this, dialog);
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
notifyOnHideDialog(dialog: Dialog) {
|
|
700
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
701
|
+
if (listener.onHideDialog)
|
|
702
|
+
listener.onHideDialog(this, dialog);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
notifyOnShowDialog(dialog: Dialog) {
|
|
708
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
709
|
+
if (listener.onShowDialog)
|
|
710
|
+
listener.onShowDialog(this, dialog);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
notifyOnChangeDialogPosition(dialog: Dialog, x: number, y: number) {
|
|
715
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
716
|
+
if (listener.onChangeDialogPosition)
|
|
717
|
+
listener.onChangeDialogPosition(this, dialog, x, y);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
notifyOnContainerResized(dockContainer: IDockContainer) {
|
|
722
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
723
|
+
if (listener.onContainerResized)
|
|
724
|
+
listener.onContainerResized(this, dockContainer);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
notifyOnTabChange(tabpage: TabPage) {
|
|
729
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
730
|
+
if (listener.onTabChanged)
|
|
731
|
+
listener.onTabChanged(this, tabpage);
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
notifyOnActivePanelChange(panel: PanelContainer, oldActive: PanelContainer) {
|
|
736
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
737
|
+
if (listener.onActivePanelChange)
|
|
738
|
+
listener.onActivePanelChange(this, panel, oldActive);
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
notifyOnActiveDocumentChange(panel: PanelContainer, oldActive: PanelContainer) {
|
|
743
|
+
this.layoutEventListeners.forEach((listener) => {
|
|
744
|
+
if (listener.onActiveDocumentChange)
|
|
745
|
+
listener.onActiveDocumentChange(this, panel, oldActive);
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
saveState() {
|
|
750
|
+
const serializer = new DockGraphSerializer();
|
|
751
|
+
return serializer.serialize(this.context.model);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async loadState(json: string) {
|
|
755
|
+
const deserializer = new DockGraphDeserializer(this);
|
|
756
|
+
this.context.model = await deserializer.deserialize(json);
|
|
757
|
+
this.setModel(this.context.model);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
getPanels() {
|
|
761
|
+
const panels: PanelContainer[] = [];
|
|
762
|
+
//all visible nodes
|
|
763
|
+
this._allPanels(this.context.model.rootNode, panels);
|
|
764
|
+
|
|
765
|
+
//all visible or not dialogs
|
|
766
|
+
this.context.model.dialogs.forEach((dialog) => {
|
|
767
|
+
//TODO: check visible
|
|
768
|
+
panels.push(dialog.panel);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
return panels;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
undockEnabled(state: boolean) {
|
|
775
|
+
this._undockEnabled = state;
|
|
776
|
+
this.getPanels().forEach((panel) => {
|
|
777
|
+
panel.canUndock(state);
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
lockDockState(state: boolean) {
|
|
782
|
+
this.undockEnabled(!state); // false - not enabled
|
|
783
|
+
this.hideCloseButton(state); //true - hide
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
hideCloseButton(state: boolean) {
|
|
787
|
+
this.getPanels().forEach((panel) => {
|
|
788
|
+
panel.hideCloseButton(state);
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
updatePanels(ids: string[]) {
|
|
793
|
+
const panels: PanelContainer[] = [];
|
|
794
|
+
//all visible nodes
|
|
795
|
+
this._allPanels(this.context.model.rootNode, panels);
|
|
796
|
+
//only remove
|
|
797
|
+
panels.forEach((panel) => {
|
|
798
|
+
if (!Utils.arrayContains(ids, panel.elementContent.id))
|
|
799
|
+
panel.close();
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
this.context.model.dialogs.forEach((dialog) => {
|
|
803
|
+
if (Utils.arrayContains(ids, dialog.panel.elementContent.id))
|
|
804
|
+
dialog.show();
|
|
805
|
+
|
|
806
|
+
else
|
|
807
|
+
dialog.hide();
|
|
808
|
+
});
|
|
809
|
+
return panels;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
getVisiblePanels(): PanelContainer[] {
|
|
813
|
+
const panels: PanelContainer[] = [];
|
|
814
|
+
//all visible nodes
|
|
815
|
+
this._allPanels(this.context.model.rootNode, panels);
|
|
816
|
+
|
|
817
|
+
//all visible
|
|
818
|
+
this.context.model.dialogs.forEach((dialog) => {
|
|
819
|
+
if (!dialog.isHidden)
|
|
820
|
+
panels.push(dialog.panel);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
return panels;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
_allPanels(node: DockNode, panels: PanelContainer[]) {
|
|
827
|
+
node.children.forEach((child) => {
|
|
828
|
+
this._allPanels(child, panels);
|
|
829
|
+
});
|
|
830
|
+
if (node.container.containerType === 'panel')
|
|
831
|
+
panels.push(node.container as PanelContainer);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
get activeDocument(): PanelContainer {
|
|
835
|
+
return this._activeDocument;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
get activePanel(): PanelContainer {
|
|
839
|
+
return this._activePanel;
|
|
840
|
+
}
|
|
841
|
+
set activePanel(value: PanelContainer) {
|
|
842
|
+
if (value !== this._activePanel) {
|
|
843
|
+
if (value && !value.isDialog) //todo store compliete list of panels, remove the closed ones and switch back focus
|
|
844
|
+
this._lastPanelNotADialog = value;
|
|
845
|
+
if (this._lastPanelNotADialog && this.getPanels().indexOf(this._lastPanelNotADialog) < 0)
|
|
846
|
+
this._lastPanelNotADialog = null;
|
|
847
|
+
const oldActive = this.activePanel;
|
|
848
|
+
if (this.activePanel) {
|
|
849
|
+
this.activePanel.elementTitle.classList.remove('dockspan-panel-active');
|
|
850
|
+
this.activePanel.elementTitleText.classList.remove('dockspan-panel-titlebar-text-active');
|
|
851
|
+
if (this.activePanel.tabPage)
|
|
852
|
+
this.activePanel.tabPage.host.setActive(false);
|
|
853
|
+
}
|
|
854
|
+
this._activePanel = value;
|
|
855
|
+
const lastActiveDocument = this._activeDocument;
|
|
856
|
+
if (value && value.panelType == PanelType.document)
|
|
857
|
+
this._activeDocument = value;
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
if (!value && oldActive && oldActive.isDialog && value == null && this._lastPanelNotADialog && this.activePanel != this._lastPanelNotADialog) {
|
|
861
|
+
value = this._lastPanelNotADialog;
|
|
862
|
+
this._lastPanelNotADialog = undefined;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
this.notifyOnActivePanelChange(value, oldActive);
|
|
866
|
+
if (lastActiveDocument != this._activeDocument)
|
|
867
|
+
this.notifyOnActiveDocumentChange(this._activeDocument, lastActiveDocument);
|
|
868
|
+
|
|
869
|
+
if (value) {
|
|
870
|
+
value.elementTitle.classList.add('dockspan-panel-active');
|
|
871
|
+
value.elementTitleText.classList.add('dockspan-panel-titlebar-text-active');
|
|
872
|
+
if (value.tabPage)
|
|
873
|
+
value.tabPage.host.setActive(true);
|
|
874
|
+
}
|
|
875
|
+
} else {
|
|
876
|
+
if (value && value.tabPage)
|
|
877
|
+
value.tabPage.host.setActive(true);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|