blob: 6aa07c8d4f781838524cca97c6b785f1a0b7b7c3 [file] [log] [blame]
/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.client.ui;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import java.util.Iterator;
/**
* Abstract base class for all panels, which are widgets that can contain other
* widgets.
*/
public abstract class Panel extends Widget implements HasWidgets.ForIsWidget {
/**
* Adds a child widget.
*
* <p>
* <b>How to Override this Method</b>
* </p>
* <p>
* There are several important things that must take place in the correct
* order to properly add or insert a Widget to a Panel. Not all of these steps
* will be relevant to every Panel, but all of the steps must be considered.
* <ol>
* <li><b>Validate:</b> Perform any sanity checks to ensure the Panel can
* accept a new Widget. Examples: checking for a valid index on insertion;
* checking that the Panel is not full if there is a max capacity.</li>
* <li><b>Adjust for Reinsertion:</b> Some Panels need to handle the case
* where the Widget is already a child of this Panel. Example: when performing
* a reinsert, the index might need to be adjusted to account for the Widget's
* removal. See {@link ComplexPanel#adjustIndex(Widget, int)}.</li>
* <li><b>Detach Child:</b> Remove the Widget from its existing parent, if
* any. Most Panels will simply call {@link Widget#removeFromParent()} on the
* Widget.</li>
* <li><b>Logical Attach:</b> Any state variables of the Panel should be
* updated to reflect the addition of the new Widget. Example: the Widget is
* added to the Panel's {@link WidgetCollection} at the appropriate index.</li>
* <li><b>Physical Attach:</b> The Widget's Element must be physically
* attached to the Panel's Element, either directly or indirectly.</li>
* <li><b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
* very last step.</li>
* </ol>
* </p>
*
* @param child the widget to be added
* @throws UnsupportedOperationException if this method is not supported (most
* often this means that a specific overload must be called)
* @see HasWidgets#add(Widget)
*/
public void add(Widget child) {
throw new UnsupportedOperationException(
"This panel does not support no-arg add()");
}
public void add(IsWidget child) {
this.add(asWidgetOrNull(child));
}
public void clear() {
Iterator<Widget> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
/**
* Removes a child widget.
*
* <p>
* <b>How to Override this Method</b>
* </p>
* <p>
* There are several important things that must take place in the correct
* order to properly remove a Widget from a Panel. Not all of these steps will
* be relevant to every Panel, but all of the steps must be considered.
* <ol>
* <li><b>Validate:</b> Make sure this Panel is actually the parent of the
* child Widget; return <code>false</code> if it is not.</li>
* <li><b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
* Widget is still attached.</li>
* <li><b>Physical Detach:</b> Adjust the DOM to account for the removal of
* the child Widget. The Widget's Element must be physically removed from the
* DOM.</li>
* <li><b>Logical Detach:</b> Update the Panel's state variables to reflect
* the removal of the child Widget. Example: the Widget is removed from the
* Panel's {@link WidgetCollection}.</li>
* </ol>
* </p>
*
* @param child the widget to be removed
* @return <code>true</code> if the child was present
*/
public abstract boolean remove(Widget child);
public boolean remove(IsWidget child) {
return remove(asWidgetOrNull(child));
}
/**
* Finalize the attachment of a Widget to this Panel. This method is the
* <b>last</b> step in adding or inserting a Widget into a Panel, and should
* be called after physical attachment in the DOM is complete. This Panel
* becomes the parent of the child Widget, and the child will now fire its
* {@link Widget#onAttach()} event if this Panel is currently attached.
*
* @param child the widget to be adopted
* @see #add(Widget)
*/
protected final void adopt(Widget child) {
assert (child.getParent() == null);
child.setParent(this);
}
/**
* This method was formerly part of the process of adding a Widget to a Panel
* but has been deprecated in favor of {@link #adopt(Widget)}.
*
* @deprecated Use {@link #adopt(Widget)}.
*/
@Deprecated
protected void adopt(Widget w, Element container) {
// Remove the widget from its current parent, if any.
w.removeFromParent();
// Attach it at the DOM and GWT levels.
if (container != null) {
DOM.appendChild(container, w.getElement());
}
w.setParent(this);
}
/**
* This method was formerly part of the process of removing a Widget from a
* Panel but has been deprecated in favor of {@link #orphan(Widget)}.
*
* @deprecated Use {@link #orphan(Widget)}.
*/
@Deprecated
protected void disown(Widget w) {
// Only disown it if it's actually contained in this panel.
if (w.getParent() != this) {
throw new IllegalArgumentException("w is not a child of this panel");
}
// setParent() must be called before removeChild() to ensure that the
// element is still attached when onDetach()/onUnload() are called.
Element elem = w.getElement();
w.setParent(null);
DOM.removeChild(DOM.getParent(elem), elem);
}
@Override
protected void doAttachChildren() {
AttachDetachException.tryCommand(this, AttachDetachException.attachCommand);
}
@Override
protected void doDetachChildren() {
AttachDetachException.tryCommand(this, AttachDetachException.detachCommand);
}
/**
* A Panel's onLoad method will be called after all of its children are
* attached.
*
* @see Widget#onLoad()
*/
@Override
protected void onLoad() {
}
/**
* A Panel's onUnload method will be called before its children become
* detached themselves.
*
* @see Widget#onLoad()
*/
@Override
protected void onUnload() {
}
/**
* <p>
* This method must be called as part of the remove method of any Panel. It
* ensures that the Widget's parent is cleared. This method should be called
* after verifying that the child Widget is an existing child of the Panel,
* but before physically removing the child Widget from the DOM. The child
* will now fire its {@link Widget#onDetach()} event if this Panel is
* currently attached.
* </p>
* <p>
* Calls to {@link #orphan(Widget)} should be wrapped in a try/finally block
* to ensure that the widget is physically detached even if orphan throws an
* exception.
* </p>
*
* @param child the widget to be disowned
* @see #add(Widget)
*/
protected final void orphan(Widget child) {
assert (child.getParent() == this);
child.setParent(null);
}
}