pendingRoots.put(Integer.valueOf(id),
new PendingRootRequest(request));
} else {
- root.init(request);
+ root.doInit(request);
}
}
} else if (pendingRootRequest != null) {
// We have a root, but the init has been pending
- root.init(request);
+ root.doInit(request);
}
}
public BrowserDetails getBrowserDetails() {
return new BrowserDetails() {
public String getUriFragmet() {
- return secondRequest.getParameter("f");
+ String fragment = secondRequest.getParameter("f");
+ if (fragment == null || fragment.length() == 0) {
+ return "";
+ } else {
+ // Trim the initial # char
+ return fragment.substring(1);
+ }
}
public String getWindowName() {
import com.google.gwt.event.dom.client.DomEvent.Type;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
public class VView extends SimplePanel implements Container, ResizeHandler,
Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable {
+ public static final String FRAGMENT_VARIABLE = "fragment";
+
private static final String CLASSNAME = "v-view";
public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain";
*/
private Element parentFrame;
+ private HandlerRegistration historyHandlerRegistration;
+
+ /**
+ * The current URI fragment, used to avoid sending updates if nothing has
+ * changed.
+ */
+ private String currentFragment;
+
+ /**
+ * Listener for URI fragment changes. Notifies the server of the new value
+ * whenever the value changes.
+ */
+ private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {
+ public void onValueChange(ValueChangeEvent<String> event) {
+ String newFragment = event.getValue();
+
+ // Send the new fragment to the server if it has changed
+ if (!newFragment.equals(currentFragment) && connection != null) {
+ currentFragment = newFragment;
+ connection.updateVariable(id, FRAGMENT_VARIABLE, newFragment,
+ true);
+ }
+ }
+ };
+
private ClickEventHandler clickEventHandler = new ClickEventHandler(this,
VPanel.CLICK_EVENT_IDENTIFIER) {
getElement().setTabIndex(-1);
}
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+ historyHandlerRegistration = History
+ .addValueChangeHandler(historyChangeHandler);
+ currentFragment = History.getToken();
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+ historyHandlerRegistration.removeHandler();
+ historyHandlerRegistration = null;
+ }
+
/**
* Called when the window might have been resized.
*
scrollIntoView(uidl);
+ if (uidl.hasAttribute(FRAGMENT_VARIABLE)) {
+ currentFragment = uidl.getStringAttribute(FRAGMENT_VARIABLE);
+ if (!currentFragment.equals(History.getToken())) {
+ History.newItem(currentFragment, true);
+ }
+ } else {
+ // Initial request for which the server doesn't yet have a fragment
+ // (and haven't shown any interest in getting one)
+ currentFragment = History.getToken();
+
+ // Include current fragment in the next request
+ client.updateVariable(id, FRAGMENT_VARIABLE, currentFragment, false);
+ }
+
rendering = false;
}
package com.vaadin.ui;
import java.io.Serializable;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class Root extends AbstractComponentContainer implements
Action.Container, Action.Notifier {
+ /**
+ * Listener that listens changes in URI fragment.
+ */
+ public interface FragmentChangedListener extends Serializable {
+ public void fragmentChanged(FragmentChangedEvent event);
+ }
+
+ /**
+ * Event fired when uri fragment changes.
+ */
+ public class FragmentChangedEvent extends Component.Event {
+
+ /**
+ * The new uri fragment
+ */
+ private final String fragment;
+
+ /**
+ * Creates a new instance of UriFragmentReader change event.
+ *
+ * @param source
+ * the Source of the event.
+ */
+ public FragmentChangedEvent(Root source, String fragment) {
+ super(source);
+ this.fragment = fragment;
+ }
+
+ /**
+ * Gets the root in which the fragment has changed.
+ *
+ * @return the root in which the fragment has changed
+ */
+ public Root getRoot() {
+ return (Root) getComponent();
+ }
+
+ /**
+ * Get the new fragment
+ *
+ * @return the new fragment
+ */
+ public String getFragment() {
+ return fragment;
+ }
+ }
+
+ private static final Method FRAGMENT_CHANGED_METHOD;
+
+ static {
+ try {
+ FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
+ .getDeclaredMethod("fragmentChanged",
+ new Class[] { FragmentChangedEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error finding methods in FragmentChangedListener");
+ }
+ }
+
/**
* A border style used for opening resources in a window without a border.
*/
if (actionManager != null) {
actionManager.paintActions(null, target);
}
+
+ if (fragment != null) {
+ target.addAttribute(VView.FRAGMENT_VARIABLE, fragment);
+ }
}
@Override
if (actionManager != null) {
actionManager.handleActions(variables, this);
}
+
+ if (variables.containsKey(VView.FRAGMENT_VARIABLE)) {
+ String fragment = (String) variables.get(VView.FRAGMENT_VARIABLE);
+ setFragment(fragment, true);
+ }
}
public Iterator<Component> getComponentIterator() {
*/
private Focusable pendingFocus;
+ /**
+ * The current URI fragment.
+ */
+ private String fragment;
+
/**
* This method is used by Component.Focusable objects to request focus to
* themselves. Focus renders must be handled at window level (instead of
}
}
+ /**
+ * Internal initialization method, should not be overridden. This method is
+ * not declared as final because that would break compatibility with e.g.
+ * CDI.
+ *
+ * @param request
+ * the initialization request
+ */
+ public void doInit(WrappedRequest request) {
+ BrowserDetails browserDetails = request.getBrowserDetails();
+ if (browserDetails != null) {
+ fragment = browserDetails.getUriFragmet();
+ }
+
+ // Call the init overridden by the application developer
+ init(request);
+ }
+
/**
* Initializes this root. This method is intended to be overridden by
* subclasses to build the view and configure non-component functionality.
* @param request
* the wrapped request that caused this root to be created
*/
- public void init(WrappedRequest request) {
+ protected void init(WrappedRequest request) {
// Default implementation doesn't do anything
}
listener);
}
+ public void addListener(FragmentChangedListener listener) {
+ addListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ public void removeListener(FragmentChangedListener listener) {
+ removeListener(FragmentChangedEvent.class, listener,
+ FRAGMENT_CHANGED_METHOD);
+ }
+
+ /**
+ * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @param fireEvent
+ * true to fire event
+ * @see FragmentChangedEvent
+ * @see FragmentChangedListener
+ */
+ public void setFragment(String newFragment, boolean fireEvents) {
+ if (newFragment == null) {
+ throw new NullPointerException("The fragment may not be null");
+ }
+ if (!newFragment.equals(fragment)) {
+ fragment = newFragment;
+ if (fireEvents) {
+ fireEvent(new FragmentChangedEvent(this, newFragment));
+ }
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
+ *
+ * @param newFragment
+ * id of the new fragment
+ * @see FragmentChangedEvent
+ * @see FragmentChangedListener
+ */
+ public void setFragment(String newFragment) {
+ setFragment(newFragment, true);
+ }
+
+ /**
+ * Gets currently set URI fragment.
+ * <p>
+ * To listen changes in fragment, hook a {@link FragmentChangedListener}.
+ *
+ * @return the current fragment in browser uri or null if not known
+ */
+ public String getFragment() {
+ return fragment;
+ }
+
public void addListener(ResizeListener resizeListener) {
throw new RuntimeException("Not yet implemented");
}
+++ /dev/null
-/*
-@ITMillApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.util.Map;
-
-import com.vaadin.terminal.PaintException;
-import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility;
-import com.vaadin.ui.ClientWidget.LoadStyle;
-
-/**
- * Experimental web browser dependent component for URI fragment (part after
- * hash mark "#") reading and writing.
- *
- * Component can be used to workaround common ajax web applications pitfalls:
- * bookmarking a program state and back button.
- *
- */
-@SuppressWarnings("serial")
-@ClientWidget(value = VUriFragmentUtility.class, loadStyle = LoadStyle.EAGER)
-public class UriFragmentUtility extends AbstractComponent {
-
- /**
- * Listener that listens changes in URI fragment.
- */
- public interface FragmentChangedListener extends Serializable {
-
- public void fragmentChanged(FragmentChangedEvent source);
-
- }
-
- /**
- * Event fired when uri fragment changes.
- */
- public class FragmentChangedEvent extends Component.Event {
-
- /**
- * Creates a new instance of UriFragmentReader change event.
- *
- * @param source
- * the Source of the event.
- */
- public FragmentChangedEvent(Component source) {
- super(source);
- }
-
- /**
- * Gets the UriFragmentReader where the event occurred.
- *
- * @return the Source of the event.
- */
- public UriFragmentUtility getUriFragmentUtility() {
- return (UriFragmentUtility) getSource();
- }
- }
-
- private static final Method FRAGMENT_CHANGED_METHOD;
-
- static {
- try {
- FRAGMENT_CHANGED_METHOD = FragmentChangedListener.class
- .getDeclaredMethod("fragmentChanged",
- new Class[] { FragmentChangedEvent.class });
- } catch (final java.lang.NoSuchMethodException e) {
- // This should never happen
- throw new java.lang.RuntimeException(
- "Internal error finding methods in FragmentChangedListener");
- }
- }
-
- public void addListener(FragmentChangedListener listener) {
- addListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
- }
-
- public void removeListener(FragmentChangedListener listener) {
- removeListener(FragmentChangedEvent.class, listener,
- FRAGMENT_CHANGED_METHOD);
- }
-
- private String fragment;
-
- public UriFragmentUtility() {
- // immediate by default
- setImmediate(true);
- }
-
- @Override
- public void paintContent(PaintTarget target) throws PaintException {
- super.paintContent(target);
- target.addVariable(this, "fragment", fragment);
- }
-
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- super.changeVariables(source, variables);
- fragment = (String) variables.get("fragment");
- fireEvent(new FragmentChangedEvent(this));
- }
-
- /**
- * Gets currently set URI fragment.
- * <p>
- * To listen changes in fragment, hook a {@link FragmentChangedListener}.
- * <p>
- * Note that initial URI fragment that user used to enter the application
- * will be read after application init. It fires FragmentChangedEvent only
- * if it is not the same as on server side.
- *
- * @return the current fragment in browser uri or null if not known
- */
- public String getFragment() {
- return fragment;
- }
-
- /**
- * Sets URI fragment. Optionally fires a {@link FragmentChangedEvent}
- *
- * @param newFragment
- * id of the new fragment
- * @param fireEvent
- * true to fire event
- * @see FragmentChangedEvent
- * @see FragmentChangedListener
- */
- public void setFragment(String newFragment, boolean fireEvent) {
- if ((newFragment == null && fragment != null)
- || (newFragment != null && !newFragment.equals(fragment))) {
- fragment = newFragment;
- if (fireEvent) {
- fireEvent(new FragmentChangedEvent(this));
- }
- requestRepaint();
- }
- }
-
- /**
- * Sets URI fragment. This method fires a {@link FragmentChangedEvent}
- *
- * @param newFragment
- * id of the new fragment
- * @see FragmentChangedEvent
- * @see FragmentChangedListener
- */
- public void setFragment(String newFragment) {
- setFragment(newFragment, true);
- }
-
-}
package com.vaadin.tests.server.component.urifragmentutility;\r
\r
import com.vaadin.tests.server.component.AbstractListenerMethodsTest;\r
-import com.vaadin.ui.UriFragmentUtility;\r
-import com.vaadin.ui.UriFragmentUtility.FragmentChangedEvent;\r
-import com.vaadin.ui.UriFragmentUtility.FragmentChangedListener;\r
+import com.vaadin.ui.Root;\r
+import com.vaadin.ui.Root.FragmentChangedEvent;\r
+import com.vaadin.ui.Root.FragmentChangedListener;\r
\r
public class UriFragmentUtilityListeners extends AbstractListenerMethodsTest {\r
public void testFragmentChangedListenerAddGetRemove() throws Exception {\r
- testListenerAddGetRemove(UriFragmentUtility.class,\r
- FragmentChangedEvent.class, FragmentChangedListener.class);\r
+ testListenerAddGetRemove(Root.class, FragmentChangedEvent.class,\r
+ FragmentChangedListener.class);\r
}\r
}\r
import com.vaadin.ui.Link;
import com.vaadin.ui.Panel;
import com.vaadin.ui.Root;
+import com.vaadin.ui.Root.FragmentChangedEvent;
import com.vaadin.ui.Tree;
-import com.vaadin.ui.UriFragmentUtility;
-import com.vaadin.ui.UriFragmentUtility.FragmentChangedEvent;
import com.vaadin.ui.VerticalLayout;
/**
VerticalLayout lo = new VerticalLayout();
lo.addComponent(menu);
- UriFragmentUtility uri = new UriFragmentUtility();
- lo.addComponent(uri);
-
- uri.addListener(new UriFragmentUtility.FragmentChangedListener() {
+ mainWindow.addListener(new Root.FragmentChangedListener() {
public void fragmentChanged(FragmentChangedEvent source) {
- String fragment = source.getUriFragmentUtility().getFragment();
+ String fragment = source.getFragment();
if (fragment != null && !"".equals(fragment)) {
// try to find a proper test class
--- /dev/null
+package com.vaadin.tests.components.root;
+
+import com.vaadin.annotations.RootInitRequiresBrowserDetals;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Label;
+
+@RootInitRequiresBrowserDetals
+public class UriFragmentTest extends AbstractTestRoot {
+
+ private final Label fragmentLabel = new Label();
+
+ @Override
+ protected void setup(WrappedRequest request) {
+ addComponent(fragmentLabel);
+ updateLabel();
+ addListener(new FragmentChangedListener() {
+ public void fragmentChanged(FragmentChangedEvent event) {
+ updateLabel();
+ }
+ });
+ addComponent(new Button("Navigate to #test",
+ new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ setFragment("test");
+ }
+ }));
+ }
+
+ private void updateLabel() {
+ String fragment = getFragment();
+ if (fragment == null) {
+ fragmentLabel.setValue("No URI fragment set");
+ } else {
+ fragmentLabel.setValue("Current URI fragment: " + fragment);
+ }
+ }
+
+ @Override
+ public String getTestDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.Root;
+import com.vaadin.ui.Root.FragmentChangedEvent;
import com.vaadin.ui.TextField;
-import com.vaadin.ui.UriFragmentUtility;
-import com.vaadin.ui.UriFragmentUtility.FragmentChangedEvent;
import com.vaadin.ui.VerticalLayout;
public class Ticket34 extends Application.LegacyApplication {
private Map<String, Component> views = new HashMap<String, Component>();
private VerticalLayout mainLayout;
private Component currentView;
- private UriFragmentUtility reader;
@Override
public void init() {
buildViews(new String[] { "main", "view2", "view3" });
- reader = new UriFragmentUtility();
- reader.addListener(new UriFragmentUtility.FragmentChangedListener() {
-
- public void fragmentChanged(FragmentChangedEvent event) {
- getMainWindow().showNotification(
- "Fragment now: "
- + event.getUriFragmentUtility().getFragment());
- // try to change to view mapped by fragment string
- setView(event.getUriFragmentUtility().getFragment());
- }
- });
-
mainLayout = new VerticalLayout();
mainLayout.setSizeFull();
final Root mainWin = new Root(
"Test app for URI fragment management/reading", mainLayout);
setMainWindow(mainWin);
- // UriFragmentReader is 0px size by default, so it will not render
- // anything on screen
- mainLayout.addComponent(reader);
+ mainWin.addListener(new Root.FragmentChangedListener() {
+
+ public void fragmentChanged(FragmentChangedEvent event) {
+ getMainWindow().showNotification(
+ "Fragment now: " + event.getFragment());
+ // try to change to view mapped by fragment string
+ setView(event.getFragment());
+ }
+ });
setView("main");
public void buttonClick(ClickEvent event) {
String viewName = tf.getValue().toString();
// fragmentChangedListener will change the view if possible
- reader.setFragment(viewName);
+ event.getButton().getRoot().setFragment(viewName);
}
});