UNPKG

5.67 kBPlain TextView Raw
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|----------------------------------------------------------------------------*/
10import { each, empty, IIterator, once } from '@lumino/algorithm';
11
12import { MessageLoop } from '@lumino/messaging';
13
14import { Layout } from './layout';
15
16import { 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 */
25export 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}