All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.user.client.ui.Widget Maven / Gradle / Ivy

There is a newer version: 2.12.1
Show newest version
/*
 * Copyright 2008 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.dom.client.Element;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.event.logical.shared.HasAttachHandlers;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;

/**
 * The base class for the majority of user-interface objects. Widget adds
 * support for receiving events from the browser and being added directly to
 * {@link com.google.gwt.user.client.ui.Panel panels}.
 */
public class Widget extends UIObject implements EventListener, HasAttachHandlers,
    IsWidget {

  /**
   * This convenience method makes a null-safe call to
   * {@link IsWidget#asWidget()}.
   *
   * @return the widget aspect, or null if w is null
   */
  public static Widget asWidgetOrNull(IsWidget w) {
    return w == null ? null : w.asWidget();
  }
  /**
   * A bit-map of the events that should be sunk when the widget is attached to
   * the DOM. (We delay the sinking of events to improve startup performance.)
   * When the widget is attached, this is set to -1
   * 

* Package protected to allow Composite to see it. */ int eventsToSink; private boolean attached; private HandlerManager handlerManager; private Object layoutData; private Widget parent; public HandlerRegistration addAttachHandler(Handler handler) { return addHandler(handler, AttachEvent.getType()); } /** * For browsers which do not leak, adds a native event handler to the widget. * Note that, unlike the * {@link #addDomHandler(EventHandler, com.google.gwt.event.dom.client.DomEvent.Type)} * implementation, there is no need to attach the widget to the DOM in order * to cause the event handlers to be attached. * * @param the type of handler to add * @param type the event key * @param handler the handler * @return {@link HandlerRegistration} used to remove the handler */ public final HandlerRegistration addBitlessDomHandler( final H handler, DomEvent.Type type) { assert handler != null : "handler must not be null"; assert type != null : "type must not be null"; sinkBitlessEvent(type.getName()); return ensureHandlers().addHandler(type, handler); } /** * Adds a native event handler to the widget and sinks the corresponding * native event. If you do not want to sink the native event, use the generic * addHandler method instead. * * @param the type of handler to add * @param type the event key * @param handler the handler * @return {@link HandlerRegistration} used to remove the handler */ public final HandlerRegistration addDomHandler( final H handler, DomEvent.Type type) { assert handler != null : "handler must not be null"; assert type != null : "type must not be null"; int typeInt = Event.getTypeInt(type.getName()); if (typeInt == -1) { sinkBitlessEvent(type.getName()); } else { sinkEvents(typeInt); } return ensureHandlers().addHandler(type, handler); } /** * Adds this handler to the widget. * * @param the type of handler to add * @param type the event type * @param handler the handler * @return {@link HandlerRegistration} used to remove the handler */ public final HandlerRegistration addHandler( final H handler, GwtEvent.Type type) { return ensureHandlers().addHandler(type, handler); } public Widget asWidget() { return this; } public void fireEvent(GwtEvent event) { if (handlerManager != null) { handlerManager.fireEvent(event); } } /** * Gets the panel-defined layout data associated with this widget. * * @return the widget's layout data * @see #setLayoutData */ public Object getLayoutData() { return layoutData; } /** * Gets this widget's parent panel. * * @return the widget's parent panel */ public Widget getParent() { return parent; } /** * Determines whether this widget is currently attached to the browser's * document (i.e., there is an unbroken chain of widgets between this widget * and the underlying browser document). * * @return true if the widget is attached */ public boolean isAttached() { return attached; } public void onBrowserEvent(Event event) { switch (DOM.eventGetType(event)) { case Event.ONMOUSEOVER: // Only fire the mouse over event if it's coming from outside this // widget. case Event.ONMOUSEOUT: // Only fire the mouse out event if it's leaving this // widget. Element related = event.getRelatedEventTarget().cast(); if (related != null && getElement().isOrHasChild(related)) { return; } break; } DomEvent.fireNativeEvent(event, this, this.getElement()); } /** * Removes this widget from its parent widget, if one exists. * *

* If it has no parent, this method does nothing. If it is a "root" widget * (meaning it's been added to the detach list via * {@link RootPanel#detachOnWindowClose(Widget)}), it will be removed from the * detached immediately. This makes it possible for Composites and Panels to * adopt root widgets. *

* * @throws IllegalStateException if this widget's parent does not support * removal (e.g. {@link Composite}) */ public void removeFromParent() { if (parent == null) { // If the widget had no parent, check to see if it was in the detach list // and remove it if necessary. if (RootPanel.isInDetachList(this)) { RootPanel.detachNow(this); } } else if (parent instanceof HasWidgets) { ((HasWidgets) parent).remove(this); } else if (parent != null) { throw new IllegalStateException( "This widget's parent does not implement HasWidgets"); } } /** * Sets the panel-defined layout data associated with this widget. Only the * panel that currently contains a widget should ever set this value. It * serves as a place to store layout bookkeeping data associated with a * widget. * * @param layoutData the widget's layout data */ public void setLayoutData(Object layoutData) { this.layoutData = layoutData; } /** * Overridden to defer the call to super.sinkEvents until the first time this * widget is attached to the dom, as a performance enhancement. Subclasses * wishing to customize sinkEvents can preserve this deferred sink behavior by * putting their implementation behind a check of * isOrWasAttached(): * *
   * {@literal @}Override
   * public void sinkEvents(int eventBitsToAdd) {
   *   if (isOrWasAttached()) {
   *     /{@literal *} customized sink code goes here {@literal *}/
   *   } else {
   *     super.sinkEvents(eventBitsToAdd);
   *  }
   *} 
*/ @Override public void sinkEvents(int eventBitsToAdd) { if (isOrWasAttached()) { super.sinkEvents(eventBitsToAdd); } else { eventsToSink |= eventBitsToAdd; } } @Override public void unsinkEvents(int eventBitsToRemove) { if (isOrWasAttached()) { super.unsinkEvents(eventBitsToRemove); } else { eventsToSink &= ~eventBitsToRemove; } } /** * Creates the {@link HandlerManager} used by this Widget. You can override * this method to create a custom {@link HandlerManager}. * * @return the {@link HandlerManager} you want to use */ protected HandlerManager createHandlerManager() { return new HandlerManager(this); } /** * Fires an event on a child widget. Used to delegate the handling of an event * from one widget to another. * * @param event the event * @param target fire the event on the given target */ protected void delegateEvent(Widget target, GwtEvent event) { target.fireEvent(event); } /** * If a widget contains one or more child widgets that are not in the logical * widget hierarchy (the child is physically connected only on the DOM level), * it must override this method and call {@link #onAttach()} for each of its * child widgets. * * @see #onAttach() */ protected void doAttachChildren() { } /** * If a widget contains one or more child widgets that are not in the logical * widget hierarchy (the child is physically connected only on the DOM level), * it must override this method and call {@link #onDetach()} for each of its * child widgets. * * @see #onDetach() */ protected void doDetachChildren() { } /** * Gets the number of handlers listening to the event type. * * @param type the event type * @return the number of registered handlers */ protected int getHandlerCount(GwtEvent.Type type) { return handlerManager == null ? 0 : handlerManager.getHandlerCount(type); } /** * Has this widget ever been attached? * * @return true if this widget ever been attached to the DOM, false otherwise */ protected final boolean isOrWasAttached() { return eventsToSink == -1; } /** *

* This method is called when a widget is attached to the browser's document. * To receive notification after a Widget has been added to the document, * override the {@link #onLoad} method or use {@link #addAttachHandler}. *

*

* It is strongly recommended that you override {@link #onLoad()} or * {@link #doAttachChildren()} instead of this method to avoid inconsistencies * between logical and physical attachment states. *

*

* Subclasses that override this method must call * super.onAttach() to ensure that the Widget has been attached * to its underlying Element. *

* * @throws IllegalStateException if this widget is already attached * @see #onLoad() * @see #doAttachChildren() */ protected void onAttach() { if (isAttached()) { throw new IllegalStateException( "Should only call onAttach when the widget is detached from the browser's document"); } attached = true; // Event hookup code DOM.setEventListener(getElement(), this); int bitsToAdd = eventsToSink; eventsToSink = -1; if (bitsToAdd > 0) { sinkEvents(bitsToAdd); } doAttachChildren(); // onLoad() gets called only *after* all of the children are attached and // the attached flag is set. This allows widgets to be notified when they // are fully attached, and panels when all of their children are attached. onLoad(); AttachEvent.fire(this, true); } /** *

* This method is called when a widget is detached from the browser's * document. To receive notification before a Widget is removed from the * document, override the {@link #onUnload} method or use {@link #addAttachHandler}. *

*

* It is strongly recommended that you override {@link #onUnload()} or * {@link #doDetachChildren()} instead of this method to avoid inconsistencies * between logical and physical attachment states. *

*

* Subclasses that override this method must call * super.onDetach() to ensure that the Widget has been detached * from the underlying Element. Failure to do so will result in application * memory leaks due to circular references between DOM Elements and JavaScript * objects. *

* * @throws IllegalStateException if this widget is already detached * @see #onUnload() * @see #doDetachChildren() */ protected void onDetach() { if (!isAttached()) { throw new IllegalStateException( "Should only call onDetach when the widget is attached to the browser's document"); } try { // onUnload() gets called *before* everything else (the opposite of // onLoad()). onUnload(); AttachEvent.fire(this, false); } finally { // Put this in a finally, just in case onUnload throws an exception. try { doDetachChildren(); } finally { // Put this in a finally, in case doDetachChildren throws an exception. DOM.setEventListener(getElement(), null); attached = false; } } } /** * This method is called immediately after a widget becomes attached to the * browser's document. */ protected void onLoad() { } /** * This method is called immediately before a widget will be detached from the * browser's document. */ protected void onUnload() { } /** * Ensures the existence of the handler manager. * * @return the handler manager * */ HandlerManager ensureHandlers() { return handlerManager == null ? handlerManager = createHandlerManager() : handlerManager; } HandlerManager getHandlerManager() { return handlerManager; } @Override void replaceElement(com.google.gwt.dom.client.Element elem) { if (isAttached()) { // Remove old event listener to avoid leaking. onDetach will not do this // for us, because it is only called when the widget itself is detached // from the document. DOM.setEventListener(getElement(), null); } super.replaceElement(elem); if (isAttached()) { // Hook the event listener back up on the new element. onAttach will not // do this for us, because it is only called when the widget itself is // attached to the document. DOM.setEventListener(getElement(), this); } } /** * Sets this widget's parent. This method should only be called by * {@link Panel} and {@link Composite}. * * @param parent the widget's new parent * @throws IllegalStateException if parent is non-null and the * widget already has a parent */ void setParent(Widget parent) { Widget oldParent = this.parent; if (parent == null) { try { if (oldParent != null && oldParent.isAttached()) { onDetach(); assert !isAttached() : "Failure of " + this.getClass().getName() + " to call super.onDetach()"; } } finally { // Put this in a finally in case onDetach throws an exception. this.parent = null; } } else { if (oldParent != null) { throw new IllegalStateException( "Cannot set a new parent without first clearing the old parent"); } this.parent = parent; if (parent.isAttached()) { onAttach(); assert isAttached() : "Failure of " + this.getClass().getName() + " to call super.onAttach()"; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy