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

com.gwtplatform.mvp.client.PresenterWidget Maven / Gradle / Ivy

There is a newer version: 1.6
Show newest version
/**
 * Copyright 2011 ArcBees 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.gwtplatform.mvp.client;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.gwtplatform.mvp.client.proxy.ResetPresentersEvent;

/**
 * A presenter that does not have to be a singleton. Pages from your
 * application will usually be singletons and extend the {@link Presenter} class.
 * 

* Choosing between a {@link Presenter} and {@link PresenterWidget} is a design decision that * requires some thought. For example, a {@link PresenterWidget} is useful when * you need a custom widget with extensive logic. For example, a chat box that can be instantiated * multiple times and needs to communicate with the server would be a good candidate for * a {@link PresenterWidget}. The drawback of a {@link PresenterWidget} is that it is managed by its * parent presenter, which increases coupling. Therefore, you should use a {@link Presenter} when * the parent is not expected to know its child. Moreover, only {@link Presenter} can be attached * to name tokens in order to support browser history. *

* {@link PresenterWidget}s and {@link Presenter}s are organized in a hierarchy. * Internally, parent presenters have links to their currently attached children presenters. A * parent {@link Presenter} can contain either {@link Presenter}s or {@link PresenterWidget}s, * but a {@link PresenterWidget} can only contain {@link PresenterWidget}s. *

* To reveal a {@link PresenterWidget} you should insert it within a {@link HasSlots slot} of its * containing presenter using one of the following methods: *

    *
  • {@link #setInSlot(Object, PresenterWidget)} *
  • {@link #setInSlot(Object, PresenterWidget, boolean)} *
  • {@link #addToSlot(Object, PresenterWidget)} *
  • {@link #addToPopupSlot(PresenterWidget)} *
  • {@link #addToPopupSlot(PresenterWidget, boolean)} *
* Revealing a {@link Presenter} is done differently, refer to the class documentation for more details. *

* To hide a {@link PresenterWidget} or a {@link Presenter} you can use {@link #setInSlot} to place * another presenter in the same slot, or you can call one of the following methods: *

    *
  • {@link #removeFromSlot(Object, PresenterWidget)} *
  • {@link #clearSlot(Object)} *
  • {@link PopupView#hide()} if the presenter is a popup or a dialog box. *
* Hide a {@link Presenter} using these methods, but *

* A presenter has a number of lifecycle methods that you can hook on to: *

    *
  • {@link #onBind()} *
  • {@link #onReveal()} *
  • {@link #onReset()} *
  • {@link #onHide()} *
  • {@link #onUnbind()} *
* Revealing or hiding a {@link PresenterWidget} triggers an internal chain of events that result in * these lifecycle methods being called. For an example, here is what happens following * a call to {@link #setInSlot(Object, PresenterWidget)}: *
    *
  • If a presenter already occupies this slot it is removed.
  • *
    • If the presenter owning the slot is currently visible then * {@link #onHide()} is called on the removed presenter and, recursively, * on its children (bottom-up: first the children, then the parent)
    • *
    • If the parent is not visible and is a {@link Presenter}, it asks to be * set in one of its parent slot by firing a * {@link com.gwtplatform.mvp.client.proxy.RevealContentEvent RevealContentEvent}. * For more details, see the documentation for {@link Presenter}.
    • *
    *
  • If, at this point, the presenter owning the slot is not visible, then the * chain stops. Otherwise, {@link #onReveal()} is called on the {@link PresenterWidget} that * was just added.
  • *
  • {@link #onReveal()} is called recursively on that presenter's children * (top-down: first the parent, then the children).
  • *
  • If {@link #setInSlot(Object, PresenterWidget, boolean)} was called with {@code false} * as the third parameter then the process stops. Otherwise, {@link #onReset()} is * called on all the currently visible presenters (top-down: first the parent, then * the children).
  • *
* * @param The {@link View} type. */ public abstract class PresenterWidget extends HandlerContainerImpl implements HasHandlers, HasSlots, HasPopupSlot, IsWidget { private static class HandlerInformation { private final Type type; private final H eventHandler; private HandlerInformation(Type type, H eventHandler) { this.type = type; this.eventHandler = eventHandler; } } private static final Object POPUP_SLOT = new Object(); boolean visible; private final EventBus eventBus; private final V view; private final List> visibleHandlers = new ArrayList>(); private final List visibleHandlerRegistrations = new ArrayList(); private final Set> children = new HashSet>(); private PresenterWidget parent; private Object slot; /** * Creates a {@link PresenterWidget} that is not necessarily using automatic * binding. Automatic binding will only work when instantiating this object using * Guice/GIN dependency injection. See * {@link HandlerContainerImpl#HandlerContainerImpl(boolean)} for * more details on automatic binding. * * @param autoBind {@code true} to request automatic binding, {@code false} otherwise. * @param eventBus The {@link EventBus}. * @param view The {@link View}. */ public PresenterWidget(boolean autoBind, EventBus eventBus, V view) { super(autoBind); assert view != null : "presenter view cannot be null"; this.eventBus = eventBus; this.view = view; } /** * Creates a {@link PresenterWidget} that uses automatic binding. This will * only work when instantiating this object using Guice/GIN dependency injection. * See {@link HandlerContainerImpl#HandlerContainerImpl()} for more details on * automatic binding. * * @param eventBus The {@link EventBus}. * @param view The {@link View}. */ public PresenterWidget(EventBus eventBus, V view) { this(true, eventBus, view); } @Override public void addToPopupSlot(PresenterWidget child) { addToSlot(POPUP_SLOT, child); } @Override public void addToPopupSlot(PresenterWidget child, boolean center) { addToPopupSlot(child); } @Override public void addToSlot(Object slot, PresenterWidget child) { assert child != null : "cannot add null to a slot"; if (child.slot == slot && child.parent == this) { return; } adoptChild(slot, child); if (!child.isPopup()) { getView().addToSlot(slot, child); } if (isVisible()) { child.internalReveal(); } } @Override public Widget asWidget() { return getView().asWidget(); } @Override public void clearSlot(Object slot) { internalClearSlot(slot, null); getView().setInSlot(slot, null); } private void internalClearSlot(Object slot, PresenterWidget dontRemove) { // use new set to prevent concurrent modification for (PresenterWidget child: new HashSet>(children)) { if (child.slot == slot && !child.equals(dontRemove)) { child.orphan(); } } } /** * PresenterWidgets may only be equal to the same instance. * To ensure this contract you may not override this method. * @param obj - the object to compare * @return whether the obj is the same instance as this presenter. */ @Override public final boolean equals(Object obj) { return super.equals(obj); } @Override public void fireEvent(GwtEvent event) { getEventBus().fireEventFromSource(event, this); } /** * Returns the {@link View} for the current presenter. * * @return The view. */ public V getView() { return view; } /** * Makes it possible to access the {@link Widget} object associated with that * presenter. * * @return The Widget associated with that presenter. */ @Deprecated public Widget getWidget() { return asWidget(); } @Override public final int hashCode() { return super.hashCode(); } /** * Verifies if the presenter is currently visible on the screen. A presenter * should be visible if it successfully revealed itself and was not hidden * later. * * @return {@code true} if the presenter is visible, {@code false} otherwise. */ public boolean isVisible() { return visible; } /** * Removes this presenter from its parent. * If this presenter has no parent, this method does nothing. */ public void removeFromParentSlot() { if (parent == null) { return; } parent.removeFromSlot(slot, this); } @Override public void removeFromPopupSlot(PresenterWidget child) { removeFromSlot(POPUP_SLOT, child); } @Override public void removeFromSlot(Object slot, PresenterWidget child) { if (child == null || child.slot != slot) { return; } if (!child.isPopup()) { getView().removeFromSlot(slot, child); } child.orphan(); } @Override public void setInSlot(Object slot, PresenterWidget child) { setInSlot(slot, child, true); } @Override public void setInSlot(Object slot, PresenterWidget child, boolean performReset) { if (child == null) { clearSlot(slot); return; } adoptChild(slot, child); internalClearSlot(slot, child); getView().setInSlot(slot, child); if (isVisible()) { child.internalReveal(); if (performReset) { ResetPresentersEvent.fire(this); } } } /** * Registers an event handler towards the {@link EventBus}. * Use this only in the rare situations where you want to manually * control when the handler is unregistered, otherwise call * {@link #addRegisteredHandler(com.google.gwt.event.shared.GwtEvent.Type, EventHandler)}. * * @param The handler type. * @param type See {@link com.google.gwt.event.shared.GwtEvent.Type}. * @param handler The handler to register. * @return The {@link HandlerRegistration} you should use to unregister the handler. */ @Deprecated protected HandlerRegistration addHandler(Type type, H handler) { return getEventBus().addHandler(type, handler); } /** * Registers an event handler towards the {@link EventBus} and * registers it to be automatically removed when {@link #unbind()} * is called. This is usually the desired behavior, but if you * want to unregister handlers manually use {@link #addHandler} * instead. * * @param The handler type. * @param type See {@link com.google.gwt.event.shared.GwtEvent.Type}. * @param handler The handler to register. * @see #addHandler(com.google.gwt.event.shared.GwtEvent.Type, EventHandler) */ protected void addRegisteredHandler(Type type, H handler) { registerHandler(getEventBus().addHandler(type, handler)); } /** * Registers an event handler towards the {@link EventBus} and * registers it to be only active when the presenter is visible * is called. * * @param The handler type. * @param type See {@link com.google.gwt.event.shared.GwtEvent.Type}. * @param handler The handler to register. * @see #addRegisteredHandler(com.google.gwt.event.shared.GwtEvent.Type, com.google.gwt.event.shared.EventHandler) * @see #addHandler(com.google.gwt.event.shared.GwtEvent.Type, EventHandler) */ protected void addVisibleHandler(Type type, H handler) { HandlerInformation handlerInformation = new HandlerInformation(type, handler); visibleHandlers.add(handlerInformation); if (visible) { registerVisibleHandler(handlerInformation); } } /** * Registers a handler so that it is automatically removed when * {@link #onHide()} is called. This provides an easy way to track event * handler registrations. * * @param handlerRegistration The registration of handler to track. */ protected void registerVisibleHandler(HandlerRegistration handlerRegistration) { visibleHandlerRegistrations.add(handlerRegistration); } /** * Access the {@link EventBus} object associated with that presenter. * You should not usually use this method to interact with the event bus. * Instead call {@link #fireEvent}, {@link #addRegisteredHandler} or * {@link #addHandler}. * * @return The EventBus associated with that presenter. */ protected final EventBus getEventBus() { return eventBus; } /** * Lifecycle method called whenever this presenter is about to be * hidden. *

* Important: Make sure you call your superclass {@link #onHide()} if * you override. Also, do not call directly, see {@link PresenterWidget} * for more details on lifecycle methods. *

* You should override this method to dispose of any object * created directly or indirectly during the call to {@link #onReveal()}. *

* This method will not be invoked a multiple times without {@link #onReveal()} * being called. *

* In a presenter hierarchy, this method is called bottom-up: first on the * child presenters, then on the parent. */ protected void onHide() { } /** * Lifecycle method called on all visible presenters whenever a * presenter is revealed anywhere in the presenter hierarchy. *

* Important: Make sure you call your superclass {@link #onReset()} if * you override. Also, do not call directly, fire a {@link ResetPresentersEvent} * to perform a reset manually. See {@link PresenterWidget} for more details on * lifecycle methods. *

* This is one of the most frequently used lifecycle method. This is usually a good * place to refresh any information displayed by your presenter. *

* Note that {@link #onReset()} is not called only when using * {@link #addToSlot(Object, PresenterWidget)}, {@link #addToPopupSlot(PresenterWidget)} * or #setInSlot(Object, PresenterWidget, boolean)} with {@code false} as the third * parameter. *

* In a presenter hierarchy, this method is called top-down: first on the * parent presenters, then on the children. */ protected void onReset() { } /** * Lifecycle method called whenever this presenter is about to be * revealed. *

* Important: Make sure you call your superclass {@link #onReveal()} if * you override. Also, do not call directly, see {@link PresenterWidget} * for more details on lifecycle methods. *

* You should override this method to perform any action or initialisation * that needs to be done when the presenter is revealed. Any initialisation * you perform here should be taken down in {@link #onHide()}. *

* Information that needs to be updated whenever the user navigates should * be refreshed in {@link #onReset()}. *

* In a presenter hierarchy, this method is called top-down: first on the * parent presenters, then on the children. */ protected void onReveal() { } /** * Internal method called to hide a presenter. * See {@link PresenterWidget} for ways to hide a presenter. */ void internalHide() { if (!isVisible()) { return; } for (PresenterWidget child : children) { child.internalHide(); } if (isPopup()) { ((PopupView) this.getView()).setCloseHandler(null); ((PopupView) this.getView()).hide(); } unregisterVisibleHandlers(); visible = false; onHide(); } /** * Internal method called to reset a presenter. Instead of using that method, * fire a {@link ResetPresentersEvent} to perform a reset manually. */ void internalReset() { if (!isVisible()) { return; } onReset(); // use new set to prevent concurrent modification for (PresenterWidget child: new HashSet>(children)) { child.internalReset(); } if (isPopup()) { ((PopupView) getView()).show(); } } /** * Internal method called to reveal a presenter. * See {@link PresenterWidget} and {@link Presenter} for ways to reveal a * presenter. */ @SuppressWarnings("unchecked") void internalReveal() { if (isVisible()) { return; } onReveal(); visible = true; // use new set to prevent concurrent modification for (PresenterWidget child: new HashSet>(children)) { child.internalReveal(); } if (isPopup()) { monitorCloseEvent((PresenterWidget) this); ((PopupView) getView()).showAndReposition(); } registerVisibleHandlers(); } /** * Make a child a child of this presenter. * @param slot * @param child */ private void adoptChild(Object slot, PresenterWidget child) { if (child.parent != this) { if (child.parent != null) { child.parent.children.remove(child); } child.parent = this; children.add(child); } child.slot = slot; } private boolean isPopup() { return slot == POPUP_SLOT; } /** * Monitors the specified popup presenter so that we know when it * is closing. This allows us to make sure it doesn't receive * future messages. * * @param popupPresenter The {@link PresenterWidget} to monitor. */ private void monitorCloseEvent(final PresenterWidget popupPresenter) { PopupView popupView = popupPresenter.getView(); popupView.setCloseHandler(new PopupViewCloseHandler() { @Override public void onClose() { popupPresenter.removeFromParentSlot(); } }); } /** * Disconnects a child from its parent. */ private void orphan() { if (parent != null) { internalHide(); parent.children.remove(this); parent = null; } slot = null; } private void registerVisibleHandler(HandlerInformation handlerInformation) { HandlerRegistration handlerRegistration = addHandler(handlerInformation.type, handlerInformation.eventHandler); visibleHandlerRegistrations.add(handlerRegistration); } private void registerVisibleHandlers() { for (HandlerInformation handlerInformation : visibleHandlers) { registerVisibleHandler(handlerInformation); } } private void unregisterVisibleHandlers() { for (HandlerRegistration handlerRegistration : visibleHandlerRegistrations) { handlerRegistration.removeHandler(); } visibleHandlerRegistrations.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy