From 7e80369a74187c96ace452a1deaeceffd0251eae Mon Sep 17 00:00:00 2001 From: =?utf8?q?Risto=20Yrj=C3=A4n=C3=A4?= Date: Thu, 21 Aug 2008 13:36:43 +0000 Subject: [PATCH] Copied PopupView from incubator to trunk svn changeset:5240/svn branch:trunk --- .../themes/default/popupview/popupview.css | 11 + WebContent/ITMILL/themes/default/styles.css | 14 +- .../terminal/gwt/client/DefaultWidgetSet.java | 6 + .../terminal/gwt/client/ui/IPopupView.java | 337 ++++++++++++++++++ .../toolkit/tests/tickets/Ticket1397.java | 147 ++++++++ src/com/itmill/toolkit/ui/PopupView.java | 235 ++++++++++++ 6 files changed, 749 insertions(+), 1 deletion(-) create mode 100644 WebContent/ITMILL/themes/default/popupview/popupview.css create mode 100644 src/com/itmill/toolkit/terminal/gwt/client/ui/IPopupView.java create mode 100644 src/com/itmill/toolkit/tests/tickets/Ticket1397.java create mode 100644 src/com/itmill/toolkit/ui/PopupView.java diff --git a/WebContent/ITMILL/themes/default/popupview/popupview.css b/WebContent/ITMILL/themes/default/popupview/popupview.css new file mode 100644 index 0000000000..d1024c4f38 --- /dev/null +++ b/WebContent/ITMILL/themes/default/popupview/popupview.css @@ -0,0 +1,11 @@ +/* + * PopupView styles + */ +.i-popupview-popup{ + border: 1px solid #babfc0; + border-bottom: 1px solid #dee2e3; + background-color: white; + overflow: auto; + padding: 3px; + margin: 3px; +} \ No newline at end of file diff --git a/WebContent/ITMILL/themes/default/styles.css b/WebContent/ITMILL/themes/default/styles.css index fa5c072c9a..ba97b407d1 100644 --- a/WebContent/ITMILL/themes/default/styles.css +++ b/WebContent/ITMILL/themes/default/styles.css @@ -210,7 +210,6 @@ input.i-modified, line-height: 13px; font-family: arial, helvetica, tahoma, verdana, sans-serif; color: #5d5444; - overflow: auto; } .i-tooltip-text { @@ -218,6 +217,7 @@ input.i-modified, border: none; border-top: 1px solid #fffef5; border-bottom: 1px solid #fbf8d9; + overflow: auto; } .i-tooltip .i-errormessage { @@ -227,6 +227,7 @@ input.i-modified, border: none; border-top: 1px solid #fff3dc; border-bottom: 1px solid #ead7b1; + overflow: auto; } .i-tooltip .i-errormessage h2 { @@ -1035,6 +1036,17 @@ i-orderedlayout-margin-top { background: #c1c6cc; border: none; } +/* + * PopupView styles + */ +.i-popupview-popup{ + border: 1px solid #babfc0; + border-bottom: 1px solid #dee2e3; + background-color: white; + overflow: auto; + padding: 3px; + margin: 3px; +} .i-progressindicator { background: #dfe2e4 url(progressindicator/img/base.gif); height: 9px; diff --git a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java index 0cd7348c5e..3f672a3ce0 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java @@ -32,6 +32,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayout; import com.itmill.toolkit.terminal.gwt.client.ui.IPanel; import com.itmill.toolkit.terminal.gwt.client.ui.IPasswordField; import com.itmill.toolkit.terminal.gwt.client.ui.IPopupCalendar; +import com.itmill.toolkit.terminal.gwt.client.ui.IPopupView; import com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator; import com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable; import com.itmill.toolkit.terminal.gwt.client.ui.ISlider; @@ -186,6 +187,9 @@ public class DefaultWidgetSet implements WidgetSet { } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar" .equals(className)) { return new IMenuBar(); + } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IPopupView" + .equals(className)) { + return new IPopupView(); } return new IUnknownComponent(); @@ -306,6 +310,8 @@ public class DefaultWidgetSet implements WidgetSet { } } else if ("menubar".equals(tag)) { return "com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar"; + } else if ("popupview".equals(tag)) { + return "com.itmill.toolkit.terminal.gwt.client.ui.IPopupView"; } return "com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent"; diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IPopupView.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPopupView.java new file mode 100644 index 0000000000..303767faa1 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPopupView.java @@ -0,0 +1,337 @@ +package com.itmill.toolkit.terminal.gwt.client.ui; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HasFocus; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; +import com.itmill.toolkit.terminal.gwt.client.Caption; +import com.itmill.toolkit.terminal.gwt.client.CaptionWrapper; +import com.itmill.toolkit.terminal.gwt.client.Container; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; + +public class IPopupView extends HTML implements Paintable, Container { + + public static final String CLASSNAME = "i-popupview"; + + /** For server-client communication */ + private String uidlId; + private ApplicationConnection client; + + /** For inner classes */ + private final IPopupView hostReference = this; + + /** This variable helps to communicate popup visibility to the server */ + private boolean hostPopupVisible; + + private CustomPopup popup; + private final Label loading = new Label("Loading..."); + + // Browser window sizes + int windowTop; + int windowLeft; + int windowRight; + int windowBottom; + + /** + * loading constructor + */ + public IPopupView() { + super(); + popup = new CustomPopup(); + + setStyleName(CLASSNAME); + popup.setStylePrimaryName(CLASSNAME + "-popup"); + + this.setHTML("PopupPanel"); + popup.setWidget(loading); + + // When we click to open the popup... + this.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + updateState(true); + } + }); + + // ..and when we close it + popup.addPopupListener(new PopupListener() { + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + updateState(false); + } + }); + + } + + /** + * + * + * @see com.itmill.toolkit.terminal.gwt.client.Paintable#updateFromUIDL(com.itmill.toolkit.terminal.gwt.client.UIDL, + * com.itmill.toolkit.terminal.gwt.client.ApplicationConnection) + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // This call should be made first. Ensure correct implementation, + // and don't let the containing layout manage caption. + if (client.updateComponent(this, uidl, false)) { + return; + } + + // These are for future server connections + this.client = client; + uidlId = uidl.getId(); + + updateWindowSize(); + + hostPopupVisible = uidl.getBooleanAttribute("popupVisible"); + this.setHTML(uidl.getStringAttribute("html")); + + if (uidl.hasAttribute("description")) { + this.setTitle(uidl.getStringAttribute("description")); + } + + // Render the popup if visible and show it. The component inside can + // change dynamically. + if (hostPopupVisible) { + UIDL popupUIDL = uidl.getChildUIDL(0); + + popup.updateFromUIDL(popupUIDL, client); + showPopupOnTop(popup, hostReference); + + } else { // The popup isn't visible so we should remove its child + popup.setWidget(null); + } + }// updateFromUIDL + + /** + * + * @param visibility + */ + private void updateState(boolean visibility) { + // If we know the server connection + // then update the current situation + if (uidlId != null && client != null) { + client.updateVariable(uidlId, "popupVisibility", visibility, true); + } + } + + /** + * This shows the popup on top of the widget below. This function allows us + * to position the popup before making it visible. + * + * @param popup + * the popup to show + * @param host + * the widget to draw the popup on + */ + private void showPopupOnTop(final CustomPopup popup, final Widget host) { + popup.setPopupPositionAndShow(new PopupPanel.PositionCallback() { + public void setPosition(int offsetWidth, int offsetHeight) { + int hostHorizontalCenter = host.getAbsoluteLeft() + + host.getOffsetWidth() / 2; + int hostVerticalCenter = host.getAbsoluteTop() + + host.getOffsetHeight() / 2; + + int left = hostHorizontalCenter - offsetWidth / 2; + int top = hostVerticalCenter - offsetHeight / 2; + + // Superclass takes care of top and left + if ((left + offsetWidth) > windowRight) { + left -= (left + offsetWidth) - windowRight; + } + + if ((top + offsetHeight) > windowBottom) { + top -= (top + offsetHeight) - windowBottom; + } + + popup.setPopupPosition(left, top); + } + }); + } + + public void updateWindowSize() { + windowTop = RootPanel.get().getAbsoluteTop(); + windowLeft = RootPanel.get().getAbsoluteLeft(); + windowRight = windowLeft + RootPanel.get().getOffsetWidth(); + windowBottom = windowTop + RootPanel.get().getOffsetHeight(); + } + + public boolean hasChildComponent(Widget component) { + return popup.equals(component); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + if (newComponent == null || newComponent instanceof CustomPopup) { + popup.hide(); + + if (popup != null) { + client.unregisterPaintable((Paintable) popup); + } + popup = (CustomPopup) newComponent; + + } else { + throw new IllegalArgumentException( + "PopupPanel only supports components of type CustomPopup"); + } + + } + + public void updateCaption(Paintable component, UIDL uidl) { + + } + + public static native void nativeBlur(Element e) /*-{ + if(e.focus) { + e.blur(); + } + }-*/; + + private class CustomPopup extends ToolkitOverlay implements Container { + + private Paintable popupComponentPaintable = null; + private Widget popupComponentWidget = null; + private CaptionWrapper captionWrapper = null; + + private boolean hasHadMouseOver = false; + private Set activeChildren; + + public CustomPopup() { + super(true, false, true); // autoHide, not modal, dropshadow + activeChildren = new HashSet(); + } + + // For some reason ONMOUSEOUT events are not always recieved, so we have + // to use ONMOUSEMOVE that doesn't target the popup + public boolean onEventPreview(Event event) { + + Element target = DOM.eventGetTarget(event); + boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target); + int type = DOM.eventGetType(event); + + // Catch children that use keyboard, so we can unfocus them when + // hiding + if (type == Event.ONKEYPRESS) { + activeChildren.add(target); + } + + if (eventTargetsPopup & type == Event.ONMOUSEMOVE) { + hasHadMouseOver = true; + } + + if (!eventTargetsPopup & type == Event.ONMOUSEMOVE) { + if (hasHadMouseOver) { + hide(); + hasHadMouseOver = false; + return true; + } + } + + return super.onEventPreview(event); + } + + public void hide() { + // Notify children with focus + if ((popupComponentWidget instanceof HasFocus)) { + ((HasFocus) popupComponentWidget).setFocus(false); + } + + for (Iterator iterator = activeChildren.iterator(); iterator + .hasNext();) { + nativeBlur((Element) iterator.next()); + } + activeChildren.clear(); + + super.hide(); + } + + public void setWidget(Widget w) { + super.setWidget(w); + + if (w == null) { + + unregisterPaintables(); + + popupComponentPaintable = null; + popupComponentWidget = null; + captionWrapper = null; + } + } + + public boolean hasChildComponent(Widget component) { + if (popupComponentWidget != null) { + return popupComponentWidget.equals(component); + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, + Widget newComponent) { + System.out.println("CustomPopup replacechildcomponent"); + if (oldComponent != null) { + client.unregisterPaintable((Paintable) oldComponent); + } + + popupComponentWidget = (Widget) newComponent; + + setWidget(popupComponentWidget); + } + + public void updateCaption(Paintable component, UIDL uidl) { + if (Caption.isNeeded(uidl)) { + if (captionWrapper != null) { + captionWrapper.updateCaption(uidl); + } else { + captionWrapper = new CaptionWrapper(component, client); + setWidget(captionWrapper); + captionWrapper.updateCaption(uidl); + } + } else { + if (captionWrapper != null) { + setWidget(popupComponentWidget); + } + } + + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, false)) { + return; + } + + Paintable newPopupComponent = client.getPaintable(uidl + .getChildUIDL(0)); + + if (newPopupComponent != popupComponentPaintable) { + + unregisterPaintables(); + + popupComponentWidget = (Widget) newPopupComponent; + + popup.setWidget(popupComponentWidget); + popupComponentPaintable = newPopupComponent; + popupComponentPaintable.updateFromUIDL(uidl.getChildUIDL(0), + client); + } + + } + + private void unregisterPaintables() { + if (popupComponentPaintable != null) { + client.unregisterPaintable(popupComponentPaintable); + } + } + + }// class CustomPopup + +}// class IPopupView diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket1397.java b/src/com/itmill/toolkit/tests/tickets/Ticket1397.java new file mode 100644 index 0000000000..3fb7f8633a --- /dev/null +++ b/src/com/itmill/toolkit/tests/tickets/Ticket1397.java @@ -0,0 +1,147 @@ +package com.itmill.toolkit.tests.tickets; + +import java.util.Date; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.data.util.ObjectProperty; +import com.itmill.toolkit.ui.Button; +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.InlineDateField; +import com.itmill.toolkit.ui.Label; +import com.itmill.toolkit.ui.Panel; +import com.itmill.toolkit.ui.PopupView; +import com.itmill.toolkit.ui.Table; +import com.itmill.toolkit.ui.TextField; +import com.itmill.toolkit.ui.Window; + +public class Ticket1397 extends Application { + + Window main; + + public void init() { + setTheme("default"); + main = new Window("PopupView test"); + setMainWindow(main); + Panel panel = new Panel("PopupTest"); + + // First test component + final ObjectProperty prop = new ObjectProperty("fooTextField"); + + PopupView.Content content = new PopupView.Content() { + public String getMinimizedValueAsHTML() { + return prop.toString(); + } + + public Component getPopupComponent() { + return new TextField("Edit foo", prop); + } + }; + + PopupView pe = new PopupView(content); + pe.setDescription("Click to edit"); + panel.addComponent(pe); + + // Second test component + PopupView pe2 = new PopupView("fooLabel", new Label( + "Foooooooooo...")); + pe2.setDescription("Click to view"); + panel.addComponent(pe2); + + // Third test component + final ObjectProperty prop2 = new ObjectProperty(new StringBuffer( + "Text for button")); + + class myButton extends Button { + public myButton() { + super("Reverse the property"); + this.addListener(new Button.ClickListener() { + public void buttonClick(Button.ClickEvent event) { + StringBuffer getContents = (StringBuffer) prop2 + .getValue(); + getContents.reverse(); + + } + }); + } + } + + final Panel panel2 = new Panel("Editor with a button"); + panel2.addComponent(new myButton()); + PopupView.Content content2 = new PopupView.Content() { + public String getMinimizedValueAsHTML() { + return prop2.toString(); + } + + public Component getPopupComponent() { + return panel2; + } + }; + + PopupView p3 = new PopupView(content2); + panel.addComponent(p3); + + // Fourth test component + final Panel panel3 = new Panel("Editor popup for a property"); + TextField tf2 = new TextField("TextField for editing a property"); + final ObjectProperty op = new ObjectProperty("This is property text."); + tf2.setPropertyDataSource(op); + panel3.addComponent(tf2); + PopupView.Content content3 = new PopupView.Content() { + + public String getMinimizedValueAsHTML() { + return op.toString(); + } + + public Component getPopupComponent() { + return panel3; + } + + }; + PopupView p4 = new PopupView(content3); + panel.addComponent(p4); + + // Fifth test component + Table table = new Table("Table for testing purposes"); + for (int i = 0; i < 5; i++) + table.addContainerProperty("" + (i + 1), String.class, ""); + table.addContainerProperty("" + 6, PopupView.class, null); + table.addContainerProperty("" + 7, PopupView.class, null); + table.setPageLength(20); + for (int i = 0; i < 1000; i++) { + + final InlineDateField df = new InlineDateField("", new Date()); + PopupView pp = new PopupView(new PopupView.Content() { + public String getMinimizedValueAsHTML() { + return df.toString(); + } + + public Component getPopupComponent() { + return df; + } + }); + final int lineNum = i; + PopupView pp2 = new PopupView(new PopupView.Content() { + + TextField tf = new TextField("Editor for line " + lineNum, + + "Try to edit the contents for this textfield on line " + + lineNum + + " and see how the overview-version changes."); + + public String getMinimizedValueAsHTML() { + return "" + tf.toString().length() + " characters of info"; + } + + public Component getPopupComponent() { + return tf; + } + + }); + table.addItem(new Object[] { "1 " + i, "2 " + i, "3 " + i, + "4 " + i, "5 " + i, pp, pp2 }, new Integer(i)); + } + + main.addComponent(table); + main.addComponent(panel); + } +} diff --git a/src/com/itmill/toolkit/ui/PopupView.java b/src/com/itmill/toolkit/ui/PopupView.java new file mode 100644 index 0000000000..c3ccb8f088 --- /dev/null +++ b/src/com/itmill/toolkit/ui/PopupView.java @@ -0,0 +1,235 @@ +package com.itmill.toolkit.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import com.itmill.toolkit.terminal.PaintException; +import com.itmill.toolkit.terminal.PaintTarget; + +public class PopupView extends AbstractComponentContainer { + + Content itsContent; + boolean popupVisible; + ArrayList componentList; + + /* Constructors */ + + /** + * A simple way to create a PopupPanel. Note that the minimal representation + * may not be dynamically updated, to achieve this create your own Content + * object. + * + * @param small + * the minimal textual representation as HTML + * @param large + * the full, Component-type representation + */ + public PopupView(final java.lang.String small, final Component large) { + this(new PopupView.Content() { + public java.lang.String getMinimizedValueAsHTML() { + return small; + } + + public Component getPopupComponent() { + return large; + } + }); + + } + + /** + * Creates a PopupView through the PopupView.Content interface. This allows + * the creator to dynamically change the contents of the PopupView. + * + * @param content + * the PopupView.Content that contains the information for this + */ + public PopupView(final PopupView.Content content) { + super(); + itsContent = content; + popupVisible = false; + componentList = new ArrayList(1); + } + + /** + * This method will replace the current content of the panel with a new one. + * Give null to remove current content. + * + * @param newContent + * PopupView.Content object containing new information for the + * PopupView + * + */ + public void setContent(PopupView.Content newContent) { + itsContent = newContent; + requestRepaint(); + } + + /** + * Returns the content-package for this PopupView. Returns null if the + * PopupView has no content. + * + * @return the PopupView.Content for this object or null + */ + public PopupView.Content getContent() { + return itsContent; + } + + /** + * Return whether the popup is visible. + * + * @return true if the popup is showing + */ + public boolean getPopupVisibility() { + return popupVisible; + } + + /* + * Methods inherited from AbstractComponentContainer. These are unnecessary + * (but mandatory). They are not supported in this implementation. + */ + + /** + * Not supported in this implementation. Will return an empty iterator. + * + * @see com.itmill.toolkit.ui.ComponentContainer#getComponentIterator() + */ + public Iterator getComponentIterator() { + return componentList.iterator(); + + } + + /** + * Not supported in this implementation. + * + * @see com.itmill.toolkit.ui.AbstractComponentContainer#removeAllComponents() + * @throws UnsupportedOperationException + */ + public void removeAllComponents() { + throw new UnsupportedOperationException(); + } + + /** + * In this implementation, moveComponents is not supported. It always throws + * UnsupportedOperationException. + * + * @see com.itmill.toolkit.ui.AbstractComponentContainer#moveComponentsFrom(com.itmill.toolkit.ui.ComponentContainer) + * @throws UnsupportedOperationException + */ + public void moveComponentsFrom(ComponentContainer source) + throws UnsupportedOperationException { + + throw new UnsupportedOperationException(); + } + + /** + * Not supported in this implementation. + * + * @see com.itmill.toolkit.ui.AbstractComponentContainer#addComponent(com.itmill.toolkit.ui.Component) + * @throws UnsupportedOperationException + */ + public void addComponent(Component c) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + + } + + /** + * Not supported in this implementation. Always throws + * UnsupportedOperationException. + * + * @see com.itmill.toolkit.ui.ComponentContainer#replaceComponent(com.itmill.toolkit.ui.Component, + * com.itmill.toolkit.ui.Component) + * @throws UnsupportedOperationException + */ + public void replaceComponent(Component oldComponent, Component newComponent) + throws UnsupportedOperationException { + + throw new UnsupportedOperationException(); + } + + /** + * Not supported in this implementation + * + * @see com.itmill.toolkit.ui.AbstractComponentContainer#removeComponent(com.itmill.toolkit.ui.Component) + */ + public void removeComponent(Component c) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + + } + + /* + * Methods for server-client communications. + */ + + /** + * @see com.itmill.toolkit.ui.AbstractComponent#getTag() + */ + public java.lang.String getTag() { + return "popupview"; + } + + /** + * Paint (serialize) the component for the client. + * + * @see com.itmill.toolkit.ui.AbstractComponent#paintContent(com.itmill.toolkit.terminal.PaintTarget) + */ + public void paintContent(PaintTarget target) throws PaintException { + // Superclass writes any common attributes in the paint target. + super.paintContent(target); + + target.addAttribute("html", itsContent.getMinimizedValueAsHTML()); + target.addAttribute("popupVisible", popupVisible); + + if (popupVisible) { + Component c = itsContent.getPopupComponent(); + + target.startTag("popupComponent"); + c.paint(target); + target.endTag("popupComponent"); + } + + } + + /** + * Deserialize changes received from client. + * + * @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, + * java.util.Map) + */ + public void changeVariables(Object source, Map variables) { + if (variables.containsKey("popupVisibility")) { + popupVisible = ((Boolean) variables.get("popupVisibility")) + .booleanValue(); + + if (popupVisible) { + componentList.add(itsContent.getPopupComponent()); + } else { + componentList.clear(); + } + requestRepaint(); + } + } + + /** + * Used to deliver customized content-packages to the PopupView. These are + * dynamically loaded when they are redrawn. + */ + public interface Content { + + /** + * This should return a small view of the full data. + * + * @return value in HTML format + */ + public String getMinimizedValueAsHTML(); + + /** + * This should return the full Component representing the data + * + * @return a Component for the value + */ + public Component getPopupComponent(); + } +} -- 2.39.5