From: Matti Tahvonen Date: Thu, 13 Nov 2008 09:46:16 +0000 (+0000) Subject: Added new component to read and manipulate URI fragment. (#34) X-Git-Tag: 6.7.0.beta1~3811 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a2f158bdd83c79f8e65d64479fe83bbd37d7c059;p=vaadin-framework.git Added new component to read and manipulate URI fragment. (#34) svn changeset:5883/svn branch:trunk --- diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java new file mode 100644 index 0000000000..c659b40500 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IUriFragmentUtility.java @@ -0,0 +1,66 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.History; +import com.google.gwt.user.client.HistoryListener; +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.BrowserInfo; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; + +/** + * Client side implementation for UriFragmentUtility. Uses GWT's History object + * as an implementation. + * + */ +public class IUriFragmentUtility extends Widget implements Paintable, + HistoryListener { + + private String fragment; + private ApplicationConnection client; + private String paintableId; + private boolean immediate; + + public IUriFragmentUtility() { + setElement(Document.get().createDivElement()); + if (BrowserInfo.get().isIE6()) { + getElement().getStyle().setProperty("overflow", "hidden"); + getElement().getStyle().setProperty("height", "0"); + } + History.addHistoryListener(this); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, false)) { + return; + } + String uidlFragment = uidl.getStringVariable("fragment"); + immediate = uidl.getBooleanAttribute("immediate"); + if (this.client == null) { + // initial paint has some special logic + this.client = client; + paintableId = uidl.getId(); + if ("".equals(uidlFragment)) { + // empty fragment, send initial fragment to server + History.fireCurrentHistoryState(); + } else { + // fragment initially exists server side, set that + History.newItem(uidlFragment, false); + } + } else { + if (uidlFragment != null && !uidlFragment.equals(fragment)) { + // normal fragment change from server, add new history item + History.newItem(uidlFragment, false); + } + } + } + + public void onHistoryChanged(String historyToken) { + fragment = historyToken; + if (client != null) { + client.updateVariable(paintableId, "fragment", fragment, immediate); + } + } + +} diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket34.java b/src/com/itmill/toolkit/tests/tickets/Ticket34.java new file mode 100644 index 0000000000..9568c6dfc1 --- /dev/null +++ b/src/com/itmill/toolkit/tests/tickets/Ticket34.java @@ -0,0 +1,110 @@ +package com.itmill.toolkit.tests.tickets; + +import java.util.HashMap; +import java.util.Map; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.ui.Button; +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.Label; +import com.itmill.toolkit.ui.OrderedLayout; +import com.itmill.toolkit.ui.Panel; +import com.itmill.toolkit.ui.TextField; +import com.itmill.toolkit.ui.UriFragmentUtility; +import com.itmill.toolkit.ui.Window; +import com.itmill.toolkit.ui.Button.ClickEvent; +import com.itmill.toolkit.ui.UriFragmentUtility.FragmentChangedEvent; + +public class Ticket34 extends Application { + + private Map views = new HashMap(); + private OrderedLayout mainLayout; + private Component currentView; + private UriFragmentUtility reader; + + 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 OrderedLayout(); + mainLayout.setSizeFull(); + final Window mainWin = new Window( + "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); + + setView("main"); + + } + + private void setView(String string) { + Component component = views.get(string); + if (component == null) { + getMainWindow().showNotification( + "View called " + string + " not found!"); + } else if (component != currentView) { + if (currentView != null) { + mainLayout.replaceComponent(currentView, component); + } else { + mainLayout.addComponent(component); + } + // give all extra space for view + mainLayout.setExpandRatio(component, 1); + currentView = component; + } + } + + private void buildViews(String[] strings) { + for (String string : strings) { + Panel p = new Panel(string); + p.setSizeFull(); + ((OrderedLayout) p.getLayout()).setSpacing(true); + p.addComponent(new Label("This is a simple test case for " + + "UriFragmentReader that can be used for" + + " adding linking, back/forward button " + + "and history support for ajax application. ")); + StringBuffer sb = new StringBuffer(); + sb.append("Available views : "); + for (String key : strings) { + sb.append(key); + sb.append(" "); + } + sb.append("Application will change to them from uri " + + "fragment or server initiated via textfield below."); + p.addComponent(new Label(sb.toString())); + + final TextField tf = new TextField( + "Type view name (will change to that " + + "view and change the uri fragment)"); + p.addComponent(tf); + Button b = new Button("Go!"); + p.addComponent(b); + b.addListener(new Button.ClickListener() { + + public void buttonClick(ClickEvent event) { + String viewName = tf.getValue().toString(); + // fragmentChangedListener will change the view if possible + reader.setFragment(viewName); + } + }); + + views.put(string, p); + } + } + +} diff --git a/src/com/itmill/toolkit/ui/UriFragmentUtility.java b/src/com/itmill/toolkit/ui/UriFragmentUtility.java new file mode 100644 index 0000000000..633d8da7a8 --- /dev/null +++ b/src/com/itmill/toolkit/ui/UriFragmentUtility.java @@ -0,0 +1,137 @@ +package com.itmill.toolkit.ui; + +import java.lang.reflect.Method; +import java.util.Map; + +import com.itmill.toolkit.service.ApplicationContext.TransactionListener; +import com.itmill.toolkit.terminal.PaintException; +import com.itmill.toolkit.terminal.PaintTarget; + +/** + * 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. + * + */ +public class UriFragmentUtility extends AbstractComponent { + + /** + * Listener that listens changes in URI fragment. + */ + public interface FragmentChangedListener { + + public void fragmentChanged(FragmentChangedEvent source); + + } + + /** + * Event fired when uri fragment changes. + */ + public class FragmentChangedEvent extends Component.Event { + + /** + * Serial generated by eclipse + */ + private static final long serialVersionUID = -4142140007700263197L; + + /** + * 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 String getTag() { + return "urifragment"; + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + target.addVariable(this, "fragment", fragment); + } + + @Override + public void changeVariables(Object source, Map variables) { + super.changeVariables(source, variables); + fragment = (String) variables.get("fragment"); + fireEvent(new FragmentChangedEvent(this)); + } + + /** + * Gets currently set URI fragment. + * + * Note that initial URI fragment that user used to enter the application + * will be read after application init. If you absolutely need that you must + * hook to {@link TransactionListener} + * + * 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; + } + + /** + * Sets URI fragment. + * + * @param newFragment + */ + public void setFragment(String newFragment) { + if ((newFragment == null && fragment != null) + || (newFragment != null && !newFragment.equals(fragment))) { + fragment = newFragment; + fireEvent(new FragmentChangedEvent(this)); + requestRepaint(); + } + } + +}