1 | // Copyright (c) Jupyter Development Team.
|
2 | // Distributed under the terms of the Modified BSD License.
|
3 | /*-----------------------------------------------------------------------------
|
4 | | Copyright (c) 2014-2017, PhosphorJS Contributors
|
5 | |
|
6 | | Distributed under the terms of the BSD 3-Clause License.
|
7 | |
|
8 | | The full license is in the file LICENSE, distributed with this software.
|
9 | |----------------------------------------------------------------------------*/
|
10 | import { each, empty, IIterator, once } from '@lumino/algorithm';
|
11 |
|
12 | import { MessageLoop } from '@lumino/messaging';
|
13 |
|
14 | import { Layout } from './layout';
|
15 |
|
16 | import { Widget } from './widget';
|
17 |
|
18 | /**
|
19 | * A concrete layout implementation which holds a single widget.
|
20 | *
|
21 | * #### Notes
|
22 | * This class is useful for creating simple container widgets which
|
23 | * hold a single child. The child should be positioned with CSS.
|
24 | */
|
25 | export class SingletonLayout extends Layout {
|
26 | /**
|
27 | * Dispose of the resources held by the layout.
|
28 | */
|
29 | dispose(): void {
|
30 | if (this._widget) {
|
31 | let widget = this._widget;
|
32 | this._widget = null;
|
33 | widget.dispose();
|
34 | }
|
35 | super.dispose();
|
36 | }
|
37 |
|
38 | /**
|
39 | * Get the child widget for the layout.
|
40 | */
|
41 | get widget(): Widget | null {
|
42 | return this._widget;
|
43 | }
|
44 |
|
45 | /**
|
46 | * Set the child widget for the layout.
|
47 | *
|
48 | * #### Notes
|
49 | * Setting the child widget will cause the old child widget to be
|
50 | * automatically disposed. If that is not desired, set the parent
|
51 | * of the old child to `null` before assigning a new child.
|
52 | */
|
53 | set widget(widget: Widget | null) {
|
54 | // Remove the widget from its current parent. This is a no-op
|
55 | // if the widget's parent is already the layout parent widget.
|
56 | if (widget) {
|
57 | widget.parent = this.parent;
|
58 | }
|
59 |
|
60 | // Bail early if the widget does not change.
|
61 | if (this._widget === widget) {
|
62 | return;
|
63 | }
|
64 |
|
65 | // Dispose of the old child widget.
|
66 | if (this._widget) {
|
67 | this._widget.dispose();
|
68 | }
|
69 |
|
70 | // Update the internal widget.
|
71 | this._widget = widget;
|
72 |
|
73 | // Attach the new child widget if needed.
|
74 | if (this.parent && widget) {
|
75 | this.attachWidget(widget);
|
76 | }
|
77 | }
|
78 |
|
79 | /**
|
80 | * Create an iterator over the widgets in the layout.
|
81 | *
|
82 | * @returns A new iterator over the widgets in the layout.
|
83 | */
|
84 | iter(): IIterator<Widget> {
|
85 | return this._widget ? once(this._widget) : empty<Widget>();
|
86 | }
|
87 |
|
88 | /**
|
89 | * Remove a widget from the layout.
|
90 | *
|
91 | * @param widget - The widget to remove from the layout.
|
92 | *
|
93 | * #### Notes
|
94 | * A widget is automatically removed from the layout when its `parent`
|
95 | * is set to `null`. This method should only be invoked directly when
|
96 | * removing a widget from a layout which has yet to be installed on a
|
97 | * parent widget.
|
98 | *
|
99 | * This method does *not* modify the widget's `parent`.
|
100 | */
|
101 | removeWidget(widget: Widget): void {
|
102 | // Bail early if the widget does not exist in the layout.
|
103 | if (this._widget !== widget) {
|
104 | return;
|
105 | }
|
106 |
|
107 | // Clear the internal widget.
|
108 | this._widget = null;
|
109 |
|
110 | // If the layout is parented, detach the widget from the DOM.
|
111 | if (this.parent) {
|
112 | this.detachWidget(widget);
|
113 | }
|
114 | }
|
115 |
|
116 | /**
|
117 | * Perform layout initialization which requires the parent widget.
|
118 | */
|
119 | protected init(): void {
|
120 | super.init();
|
121 | each(this, widget => {
|
122 | this.attachWidget(widget);
|
123 | });
|
124 | }
|
125 |
|
126 | /**
|
127 | * Attach a widget to the parent's DOM node.
|
128 | *
|
129 | * @param index - The current index of the widget in the layout.
|
130 | *
|
131 | * @param widget - The widget to attach to the parent.
|
132 | *
|
133 | * #### Notes
|
134 | * This method is called automatically by the single layout at the
|
135 | * appropriate time. It should not be called directly by user code.
|
136 | *
|
137 | * The default implementation adds the widgets's node to the parent's
|
138 | * node at the proper location, and sends the appropriate attach
|
139 | * messages to the widget if the parent is attached to the DOM.
|
140 | *
|
141 | * Subclasses may reimplement this method to control how the widget's
|
142 | * node is added to the parent's node.
|
143 | */
|
144 | protected attachWidget(widget: Widget): void {
|
145 | // Send a `'before-attach'` message if the parent is attached.
|
146 | if (this.parent!.isAttached) {
|
147 | MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
|
148 | }
|
149 |
|
150 | // Add the widget's node to the parent.
|
151 | this.parent!.node.appendChild(widget.node);
|
152 |
|
153 | // Send an `'after-attach'` message if the parent is attached.
|
154 | if (this.parent!.isAttached) {
|
155 | MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
|
156 | }
|
157 | }
|
158 |
|
159 | /**
|
160 | * Detach a widget from the parent's DOM node.
|
161 | *
|
162 | * @param widget - The widget to detach from the parent.
|
163 | *
|
164 | * #### Notes
|
165 | * This method is called automatically by the single layout at the
|
166 | * appropriate time. It should not be called directly by user code.
|
167 | *
|
168 | * The default implementation removes the widget's node from the
|
169 | * parent's node, and sends the appropriate detach messages to the
|
170 | * widget if the parent is attached to the DOM.
|
171 | *
|
172 | * Subclasses may reimplement this method to control how the widget's
|
173 | * node is removed from the parent's node.
|
174 | */
|
175 | protected detachWidget(widget: Widget): void {
|
176 | // Send a `'before-detach'` message if the parent is attached.
|
177 | if (this.parent!.isAttached) {
|
178 | MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
|
179 | }
|
180 |
|
181 | // Remove the widget's node from the parent.
|
182 | this.parent!.node.removeChild(widget.node);
|
183 |
|
184 | // Send an `'after-detach'` message if the parent is attached.
|
185 | if (this.parent!.isAttached) {
|
186 | MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
|
187 | }
|
188 | }
|
189 |
|
190 | private _widget: Widget | null = null;
|
191 | }
|