- Adds UiBinder parsers for DockLayoutPanel and StackLayoutPanel. - Changes DockLayoutPanel interface to be more direct (e.g., addNorth(w) rather than add(w, NORTH)). - Changes Mail sample to use UiBinder throughout. Review: http://gwt-code-reviews.appspot.com/68805 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6192 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/mail/src/com/google/gwt/sample/mail/Mail.gwt.xml b/samples/mail/src/com/google/gwt/sample/mail/Mail.gwt.xml index 6cca2a8..9225cee 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/Mail.gwt.xml +++ b/samples/mail/src/com/google/gwt/sample/mail/Mail.gwt.xml
@@ -14,5 +14,6 @@ <module rename-to="mail"> <inherits name='com.google.gwt.user.User'/> + <inherits name="com.google.gwt.uibinder.UiBinder" /> <entry-point class='com.google.gwt.sample.mail.client.Mail'/> </module>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.java b/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.java index f371ee0..12d1e62 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.java
@@ -15,57 +15,53 @@ */ package com.google.gwt.sample.mail.client; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; /** * A simple example of an 'about' dialog box. */ public class AboutDialog extends DialogBox { + interface Binder extends UiBinder<Widget, AboutDialog> { } + private static final Binder binder = GWT.create(Binder.class); + + @UiField Button closeButton; + public AboutDialog() { // Use this opportunity to set the dialog's caption. setText("About the Mail Sample"); - - // Create a VerticalPanel to contain the 'about' label and the 'OK' button. - VerticalPanel outer = new VerticalPanel(); - - // Create the 'about' text and set a style name so we can style it with CSS. - - HTML text = new HTML("This sample application demonstrates the " - + "construction of a complex user interface using GWT's built-in " - + "widgets. Have a look at the code to see how easy it is to build " - + "your own apps!"); - text.setStyleName("mail-AboutText"); - outer.add(text); - - // Create the 'OK' button, along with a handler that hides the dialog - // when the button is clicked. - outer.add(new Button("Close", new ClickHandler() { - public void onClick(ClickEvent event) { - hide(); - } - })); - - setWidget(outer); + setWidget(binder.createAndBindUi(this)); } @Override - public boolean onKeyDownPreview(char key, int modifiers) { - // Use the popup's key preview hooks to close the dialog when either - // enter or escape is pressed. - switch (key) { - case KeyCodes.KEY_ENTER: - case KeyCodes.KEY_ESCAPE: - hide(); - break; - } + protected void onPreviewNativeEvent(NativePreviewEvent preview) { + super.onPreviewNativeEvent(preview); - return true; + NativeEvent evt = preview.getNativeEvent(); + if (evt.getType().equals("keydown")) { + // Use the popup's key preview hooks to close the dialog when either + // enter or escape is pressed. + switch (evt.getKeyCode()) { + case KeyCodes.KEY_ENTER: + case KeyCodes.KEY_ESCAPE: + hide(); + break; + } + } + } + + @UiHandler("closeButton") + void onSignOutClicked(ClickEvent event) { + hide(); } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.ui.xml new file mode 100644 index 0000000..3aa9afc --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/AboutDialog.ui.xml
@@ -0,0 +1,27 @@ +<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .aboutText { + width: 24em; + padding: 10px; + text-align: left; + } + </ui:style> + + <g:DockPanel> + <g:Dock direction='SOUTH'> + <g:Button text='Close' ui:field='closeButton' /> + </g:Dock> + <g:Dock direction='CENTER'> + <g:HTML styleName='{style.aboutText}'> + This sample application demonstrates the + construction of a complex user interface using GWT's built-in + widgets. Have a look at the code to see how easy it is to build + your own apps! + </g:HTML> + </g:Dock> + </g:DockPanel> + +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/ContactPopup.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/ContactPopup.ui.xml new file mode 100644 index 0000000..fc5b2a0 --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/ContactPopup.ui.xml
@@ -0,0 +1,41 @@ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .popup { + background: #fff; + border: 1px solid #666; + padding: 0.5em; + width: 14em; + height: 2.5em; + } + + .photo { + float: left; + margin-right: 4px; + + background: url(default_photo.jpg); + width: 32px; + height: 32px; + } + + .right { + white-space: nowrap; + } + + .email { + font-style:italic; + } + </ui:style> + + <g:HTMLPanel styleName='{style.popup}'> + <div class='{style.photo}'/> + <div class='{style.right}'> + <div ui:field='nameDiv' /> + <div ui:field='emailDiv' class='{style.email}'/> + </div> + </g:HTMLPanel> + +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.java b/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.java index 738b1fb..df69bdf 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.java
@@ -15,17 +15,18 @@ */ package com.google.gwt.sample.mail.client; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiTemplate; +import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.ImageBundle; -import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; /** * A component that displays a list of contacts. @@ -33,14 +34,6 @@ public class Contacts extends Composite { /** - * An image bundle for this widget and an example of the use of @Resource. - */ - public interface Images extends ImageBundle { - @Resource("default_photo.jpg") - AbstractImagePrototype defaultPhoto(); - } - - /** * Simple data structure representing a contact. */ private static class Contact { @@ -56,31 +49,28 @@ /** * A simple popup that displays a contact's information. */ - private class ContactPopup extends PopupPanel { + static class ContactPopup extends PopupPanel { + @UiTemplate("ContactPopup.ui.xml") + interface Binder extends UiBinder<Widget, ContactPopup> { } + private static final Binder binder = GWT.create(Binder.class); + + @UiField Element nameDiv; + @UiField Element emailDiv; public ContactPopup(Contact contact) { // The popup's constructor's argument is a boolean specifying that it // auto-close itself when the user clicks outside of it. super(true); + add(binder.createAndBindUi(this)); - VerticalPanel inner = new VerticalPanel(); - Label nameLabel = new Label(contact.name); - Label emailLabel = new Label(contact.email); - inner.add(nameLabel); - inner.add(emailLabel); - - HorizontalPanel hp = new HorizontalPanel(); - hp.setSpacing(4); - hp.add(images.defaultPhoto().createImage()); - hp.add(inner); - - add(hp); - setStyleName("mail-ContactPopup"); - nameLabel.setStyleName("mail-ContactPopupName"); - emailLabel.setStyleName("mail-ContactPopupEmail"); + nameDiv.setInnerText(contact.name); + emailDiv.setInnerText(contact.email); } } + interface Binder extends UiBinder<VerticalPanel, Contacts> { } + private static final Binder binder = GWT.create(Binder.class); + private Contact[] contacts = new Contact[] { new Contact("Benoit Mandelbrot", "benoit@example.com"), new Contact("Albert Einstein", "albert@example.com"), @@ -91,26 +81,19 @@ new Contact("Alan Turing", "alan@example.com"), new Contact("John von Neumann", "john@example.com")}; - private VerticalPanel panel = new VerticalPanel(); - private final Images images; + private VerticalPanel panel; - public Contacts(Images images) { - SimplePanel outer = new SimplePanel(); - outer.setWidget(panel); + public Contacts() { + initWidget(panel = binder.createAndBindUi(this)); - this.images = images; // Add all the contacts to the list. for (int i = 0; i < contacts.length; ++i) { addContact(contacts[i]); } - - initWidget(outer); - setStyleName("mail-Contacts"); } private void addContact(final Contact contact) { - final HTML link = new HTML("<a href='javascript:;'>" + contact.name - + "</a>"); + final Anchor link = new Anchor(contact.name); panel.add(link); // Add a click handler that displays a ContactPopup when it is clicked.
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.ui.xml new file mode 100644 index 0000000..5349613 --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Contacts.ui.xml
@@ -0,0 +1,13 @@ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .contacts { + padding: 0.5em; + } + </ui:style> + + <g:VerticalPanel styleName='{style.contacts}'/> +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java index 1b7f652..a639a12 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java
@@ -17,128 +17,58 @@ import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; -import com.google.gwt.event.logical.shared.ResizeEvent; -import com.google.gwt.event.logical.shared.ResizeHandler; -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.DockPanel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.RootLayoutPanel; /** * This application demonstrates how to construct a relatively complex user * interface, similar to many common email readers. It has no back-end, * populating its components with hard-coded data. */ -public class Mail implements EntryPoint, ResizeHandler { +public class Mail implements EntryPoint { - private static Mail singleton; + interface Binder extends UiBinder<DockLayoutPanel, Mail> { } + private static final Binder binder = GWT.create(Binder.class); - /** - * Instantiate an application-level image bundle. This object will provide - * programmatic access to all the images needed by widgets. - */ - private static final Images images = GWT.create(Images.class); - - /** - * An aggregate image bundle that pulls together all the images for this - * application into a single bundle. - */ - public interface Images extends Shortcuts.Images, TopPanel.Images { - } - - /** - * Gets the singleton Mail instance. - */ - public static Mail get() { - return singleton; - } - - private TopPanel topPanel = new TopPanel(images); - private VerticalPanel rightPanel = new VerticalPanel(); - private MailList mailList; - private MailDetail mailDetail = new MailDetail(); - private Shortcuts shortcuts = new Shortcuts(images); - - /** - * Displays the specified item. - * - * @param item - */ - public void displayItem(MailItem item) { - mailDetail.setItem(item); - } + @UiField TopPanel topPanel; + @UiField MailList mailList; + @UiField MailDetail mailDetail; + @UiField Shortcuts shortcuts; /** * This method constructs the application user interface by instantiating * controls and hooking up event handler. */ public void onModuleLoad() { - singleton = this; - - topPanel.setWidth("100%"); - - // MailList uses Mail.get() in its constructor, so initialize it after - // 'singleton'. - mailList = new MailList(); - mailList.setWidth("100%"); - - // Create the right panel, containing the email list & details. - rightPanel.add(mailList); - rightPanel.add(mailDetail); - mailList.setWidth("100%"); - mailDetail.setWidth("100%"); - - // Create a dock panel that will contain the menu bar at the top, - // the shortcuts to the left, and the mail list & details taking the rest. - DockPanel outer = new DockPanel(); - outer.add(topPanel, DockPanel.NORTH); - outer.add(shortcuts, DockPanel.WEST); - outer.add(rightPanel, DockPanel.CENTER); - outer.setWidth("100%"); - - outer.setSpacing(4); - outer.setCellWidth(rightPanel, "100%"); - - // Hook the window resize event, so that we can adjust the UI. - Window.addResizeHandler(this); + DockLayoutPanel outer = binder.createAndBindUi(this); // Get rid of scrollbars, and clear out the window's built-in margin, // because we want to take advantage of the entire client area. Window.enableScrolling(false); Window.setMargin("0px"); - // Finally, add the outer panel to the RootPanel, so that it will be - // displayed. - RootPanel.get().add(outer); + // Special-case stuff to make topPanel overhang a bit. + Element topElem = outer.getContainerElementFor(topPanel); + topElem.getStyle().setZIndex(2); + topElem.getStyle().setOverflow(Overflow.VISIBLE); - // Call the window resized handler to get the initial sizes setup. Doing - // this in a deferred command causes it to occur after all widgets' sizes - // have been computed by the browser. - DeferredCommand.addCommand(new Command() { - public void execute() { - onWindowResized(Window.getClientWidth(), Window.getClientHeight()); + // Listen for item selection, displaying the currently-selected item in + // the detail area. + mailList.setListener(new MailList.Listener() { + public void onItemSelected(MailItem item) { + mailDetail.setItem(item); } }); - onWindowResized(Window.getClientWidth(), Window.getClientHeight()); - } - - public void onResize(ResizeEvent event) { - onWindowResized(event.getWidth(), event.getHeight()); - } - - public void onWindowResized(int width, int height) { - // Adjust the shortcut panel and detail area to take up the available room - // in the window. - int shortcutHeight = height - shortcuts.getAbsoluteTop() - 8; - if (shortcutHeight < 1) { - shortcutHeight = 1; - } - shortcuts.setHeight(shortcutHeight + "px"); - - // Give the mail detail widget a chance to resize itself as well. - mailDetail.adjustSize(width, height); + // Add the outer panel to the RootLayoutPanel, so that it will be + // displayed. + RootLayoutPanel root = RootLayoutPanel.get(); + root.add(outer); + root.layout(); } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Mail.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.ui.xml new file mode 100644 index 0000000..08abf6f --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.ui.xml
@@ -0,0 +1,28 @@ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <g:DockLayoutPanel unit='EM'> + <g:north size='5'> + <mail:TopPanel ui:field='topPanel' /> + </g:north> + + <g:center> + <g:SplitLayoutPanel> + <g:west size='192'> + <mail:Shortcuts ui:field='shortcuts' /> + </g:west> + + <g:north size='200'> + <mail:MailList ui:field='mailList' /> + </g:north> + + <g:center> + <mail:MailDetail ui:field='mailDetail' /> + </g:center> + </g:SplitLayoutPanel> + </g:center> + </g:DockLayoutPanel> + +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.java b/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.java index 42901ba..b8697ec 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.java
@@ -15,74 +15,39 @@ */ package com.google.gwt.sample.mail.client; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.DockPanel; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.ScrollPanel; -import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.LayoutComposite; +import com.google.gwt.user.client.ui.Widget; /** * A composite for displaying the details of an email message. */ -public class MailDetail extends Composite { +public class MailDetail extends LayoutComposite { - private VerticalPanel panel = new VerticalPanel(); - private VerticalPanel headerPanel = new VerticalPanel(); - private HTML subject = new HTML(); - private HTML sender = new HTML(); - private HTML recipient = new HTML(); - private HTML body = new HTML(); - private ScrollPanel scroller = new ScrollPanel(body); + interface Binder extends UiBinder<Widget, MailDetail> { } + private static final Binder binder = GWT.create(Binder.class); + + @UiField Element subject; + @UiField Element sender; + @UiField Element recipient; + @UiField HTML body; public MailDetail() { - body.setWordWrap(true); - - headerPanel.add(subject); - headerPanel.add(sender); - headerPanel.add(recipient); - headerPanel.setWidth("100%"); - - DockPanel innerPanel = new DockPanel(); - innerPanel.add(headerPanel, DockPanel.NORTH); - innerPanel.add(scroller, DockPanel.CENTER); - - innerPanel.setCellHeight(scroller, "100%"); - panel.add(innerPanel); - innerPanel.setSize("100%", "100%"); - scroller.setSize("100%", "100%"); - initWidget(panel); - - setStyleName("mail-Detail"); - headerPanel.setStyleName("mail-DetailHeader"); - innerPanel.setStyleName("mail-DetailInner"); - subject.setStyleName("mail-DetailSubject"); - sender.setStyleName("mail-DetailSender"); - recipient.setStyleName("mail-DetailRecipient"); - body.setStyleName("mail-DetailBody"); - } - - /** - * Adjusts the widget's size such that it fits within the window's client - * area. - */ - public void adjustSize(int windowWidth, int windowHeight) { - int scrollWidth = windowWidth - scroller.getAbsoluteLeft() - 9; - if (scrollWidth < 1) { - scrollWidth = 1; - } - - int scrollHeight = windowHeight - scroller.getAbsoluteTop() - 9; - if (scrollHeight < 1) { - scrollHeight = 1; - } - - scroller.setPixelSize(scrollWidth, scrollHeight); + initWidget(binder.createAndBindUi(this)); } public void setItem(MailItem item) { - subject.setHTML(item.subject); - sender.setHTML("<b>From:</b> " + item.sender); - recipient.setHTML("<b>To:</b> foo@example.com"); + subject.setInnerText(item.subject); + sender.setInnerText(item.sender); + recipient.setInnerHTML("foo@example.com"); + + // WARNING: For the purposes of this demo, we're using HTML directly, on + // the assumption that the "server" would have appropriately scrubbed the + // HTML. Failure to do so would open your application to XSS attacks. body.setHTML(item.body); } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.ui.xml new file mode 100644 index 0000000..d5872ff --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/MailDetail.ui.xml
@@ -0,0 +1,44 @@ +<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .detail { + border: 1px solid #666; + background-color: white; + } + + .header { + background: #eee; + border-bottom: 1px solid #666; + padding: 0.5em; + } + + .headerItem { + margin-bottom:0.5em; + } + + .body { + line-height: 150%; + padding: 20px 40px 20px 10px; + font-family: 'Times New Roman', Times, serif; + } + </ui:style> + + <g:DockLayoutPanel unit='EM' styleName='{style.detail}'> + <g:north size='6'> + <g:HTMLPanel styleName='{style.header}'> + <div class='{style.headerItem}' ui:field='subject'/> + <div class='{style.headerItem}'><b>From:</b> <span ui:field='sender'/></div> + <div class='{style.headerItem}'><b>To:</b> <span ui:field='recipient'/></div> + </g:HTMLPanel> + </g:north> + + <g:center> + <g:ScrollPanel> + <g:HTML styleName='{style.body}' ui:field='body' wordWrap='true'/> + </g:ScrollPanel> + </g:center> + </g:DockLayoutPanel> + +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/MailItems.java b/samples/mail/src/com/google/gwt/sample/mail/client/MailItems.java index d36b789..3b4828c 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/MailItems.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/MailItems.java
@@ -22,7 +22,7 @@ */ public class MailItems { - private static final int NUM_ITEMS = 37; + private static final int NUM_ITEMS = 64; private static final int FRAGMENTS_PER_EMAIL = 10; private static final String[] senders = new String[] {
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java b/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java index 43fa86a..f6345a0 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java
@@ -15,20 +15,29 @@ */ package com.google.gwt.sample.mail.client; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.LayoutComposite; +import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.HTMLTable.Cell; /** * A composite that displays a list of emails that can be selected. */ -public class MailList extends Composite implements ClickHandler { +public class MailList extends LayoutComposite implements ClickHandler { - private static final int VISIBLE_EMAIL_COUNT = 10; + private static final int VISIBLE_EMAIL_COUNT = 20; + + public interface Listener { + void onItemSelected(MailItem item); + } + + private Listener listener; private HTML countLabel = new HTML(); private HTML newerButton = new HTML("<a href='javascript:;'>< newer</a>", @@ -36,6 +45,7 @@ private HTML olderButton = new HTML("<a href='javascript:;'>older ></a>", true); private int startIndex, selectedRow = -1; + private FlexTable header = new FlexTable(); private FlexTable table = new FlexTable(); private HorizontalPanel navBar = new HorizontalPanel(); @@ -61,7 +71,13 @@ navBar.add(innerNavBar); navBar.setWidth("100%"); - initWidget(table); + DockLayoutPanel dock = new DockLayoutPanel(Unit.EM); + dock.addNorth(header, 2); + dock.add(new ScrollPanel(table)); + header.setWidth("100%"); + table.setWidth("100%"); + dock.layout(); + initWidget(dock); setStyleName("mail-List"); initTable(); @@ -95,34 +111,60 @@ Cell cell = table.getCellForEvent(event); if (cell != null) { int row = cell.getRowIndex(); - if (row > 0) { - selectRow(row - 1); - } + selectRow(row); } } } /** + * Sets the listener that will be notified when an item is selected. + */ + public void setListener(Listener listener) { + this.listener = listener; + } + + @Override + protected void onLoad() { + // Select the first row if none is selected. + if (selectedRow == -1) { + selectRow(0); + } + } + + /** * Initializes the table so that it contains enough rows for a full page of * emails. Also creates the images that will be used as 'read' flags. */ private void initTable() { - // Create the header row. - table.setText(0, 0, "Sender"); - table.setText(0, 1, "Email"); - table.setText(0, 2, "Subject"); - table.setWidget(0, 3, navBar); - table.getRowFormatter().setStyleName(0, "mail-ListHeader"); + // Create the header. + header.getRowFormatter().setStyleName(0, "mail-ListHeader"); + header.getElement().getStyle().setProperty("tableLayout", "fixed"); + header.setCellSpacing(0); - // Initialize the rest of the rows. + header.getColumnFormatter().setWidth(0, "96px"); + header.getColumnFormatter().setWidth(1, "160px"); + header.getColumnFormatter().setWidth(3, "256px"); + + header.setText(0, 0, "Sender"); + header.setText(0, 1, "Email"); + header.setText(0, 2, "Subject"); + header.setWidget(0, 3, navBar); + + // Initialize the table. + table.getElement().getStyle().setProperty("tableLayout", "fixed"); + header.setCellSpacing(0); + + table.getColumnFormatter().setWidth(0, "96px"); + table.getColumnFormatter().setWidth(1, "160px"); + for (int i = 0; i < VISIBLE_EMAIL_COUNT; ++i) { - table.setText(i + 1, 0, ""); - table.setText(i + 1, 1, ""); - table.setText(i + 1, 2, ""); - table.getCellFormatter().setWordWrap(i + 1, 0, false); - table.getCellFormatter().setWordWrap(i + 1, 1, false); - table.getCellFormatter().setWordWrap(i + 1, 2, false); - table.getFlexCellFormatter().setColSpan(i + 1, 2, 2); + table.setText(i, 0, ""); + table.setText(i, 1, ""); + table.setText(i, 2, ""); + table.getCellFormatter().setWordWrap(i, 0, false); + table.getCellFormatter().setWordWrap(i, 1, false); + table.getCellFormatter().setWordWrap(i, 2, false); + table.getFlexCellFormatter().setColSpan(i, 2, 2); } } @@ -144,15 +186,18 @@ item.read = true; selectedRow = row; - Mail.get().displayItem(item); + + if (listener != null) { + listener.onItemSelected(item); + } } private void styleRow(int row, boolean selected) { if (row != -1) { if (selected) { - table.getRowFormatter().addStyleName(row + 1, "mail-SelectedRow"); + table.getRowFormatter().addStyleName(row, "mail-SelectedRow"); } else { - table.getRowFormatter().removeStyleName(row + 1, "mail-SelectedRow"); + table.getRowFormatter().removeStyleName(row, "mail-SelectedRow"); } } } @@ -181,21 +226,16 @@ // Add a new row to the table, then set each of its columns to the // email's sender and subject values. - table.setText(i + 1, 0, item.sender); - table.setText(i + 1, 1, item.email); - table.setText(i + 1, 2, item.subject); + table.setText(i, 0, item.sender); + table.setText(i, 1, item.email); + table.setText(i, 2, item.subject); } // Clear any remaining slots. for (; i < VISIBLE_EMAIL_COUNT; ++i) { - table.setHTML(i + 1, 0, " "); - table.setHTML(i + 1, 1, " "); - table.setHTML(i + 1, 2, " "); - } - - // Select the first row if none is selected. - if (selectedRow == -1) { - selectRow(0); + table.setHTML(i, 0, " "); + table.setHTML(i, 1, " "); + table.setHTML(i, 2, " "); } } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Mailboxes.java b/samples/mail/src/com/google/gwt/sample/mail/client/Mailboxes.java index f0e852d..27fb3c5 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/Mailboxes.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Mailboxes.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.sample.mail.client; +import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.ui.AbstractImagePrototype; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.ImageBundle; @@ -55,7 +56,9 @@ * * @param images a bundle that provides the images for this widget */ - public Mailboxes(Images images) { + public Mailboxes() { + Images images = GWT.create(Images.class); + tree = new Tree(images); TreeItem root = new TreeItem( imageItemHTML(images.home(), "foo@example.com"));
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java index c58b71a..28687a1 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
@@ -15,10 +15,11 @@ */ package com.google.gwt.sample.mail.client; -import com.google.gwt.user.client.ui.AbstractImagePrototype; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.DecoratedStackPanel; -import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.LayoutComposite; +import com.google.gwt.user.client.ui.StackLayoutPanel; /** * A composite that contains the shortcut stack panel on the left side. The @@ -27,67 +28,29 @@ * {@link com.google.gwt.user.client.ui.StackPanel}, * {@link com.google.gwt.user.client.ui.Tree}, and other custom widgets. */ -public class Shortcuts extends Composite { +public class Shortcuts extends LayoutComposite { - /** - * An image bundle specifying the images for this Widget and aggragating - * images needed in child widgets. - */ - public interface Images extends Contacts.Images, Mailboxes.Images { - AbstractImagePrototype contactsgroup(); + interface Binder extends UiBinder<StackLayoutPanel, Shortcuts> { } + private static final Binder binder = GWT.create(Binder.class); - AbstractImagePrototype mailgroup(); + private StackLayoutPanel stackPanel; - AbstractImagePrototype tasksgroup(); - } - - private int nextHeaderIndex = 0; - private DecoratedStackPanel stackPanel = new DecoratedStackPanel(); + @UiField Mailboxes mailboxes; + @UiField Tasks tasks; + @UiField Contacts contacts; /** * Constructs a new shortcuts widget using the specified images. * * @param images a bundle that provides the images for this widget */ - public Shortcuts(Images images) { - // Create the groups within the stack panel. - add(new Mailboxes(images), images.mailgroup(), "Mail"); - add(new Tasks(), images.tasksgroup(), "Tasks"); - add(new Contacts(images), images.contactsgroup(), "Contacts"); - - initWidget(stackPanel); + public Shortcuts() { + initWidget(stackPanel = binder.createAndBindUi(this)); } @Override protected void onLoad() { // Show the mailboxes group by default. - stackPanel.showStack(0); - } - - private void add(Widget widget, AbstractImagePrototype imageProto, - String caption) { - widget.addStyleName("mail-StackContent"); - stackPanel.add(widget, createHeaderHTML(imageProto, caption), true); - } - - /** - * Creates an HTML fragment that places an image & caption together, for use - * in a group header. - * - * @param imageProto an image prototype for an image - * @param caption the group caption - * @return the header HTML fragment - */ - private String createHeaderHTML(AbstractImagePrototype imageProto, - String caption) { - nextHeaderIndex++; - - String captionHTML = "<table class='caption' cellpadding='0' cellspacing='0'>" - + "<tr><td class='lcaption'>" - + imageProto.getHTML() - + "</td><td class='rcaption'><b style='white-space:nowrap'>" - + caption - + "</b></td></tr></table>"; - return captionHTML; + stackPanel.showWidget(mailboxes); } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml new file mode 100644 index 0000000..2cfa4eb --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.ui.xml
@@ -0,0 +1,45 @@ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .shortcuts { + border-left: 1px solid #666; + border-right: 1px solid #666; + } + + .stackHeader { + background: #c1eec8 url(gradient.gif) repeat-x 0px -1px; + padding-top: 1em; + font-weight: bold; + text-align: center; + border-top: 1px solid #666; + border-bottom: 1px solid #666; + } + </ui:style> + + <g:StackLayoutPanel styleName='{style.shortcuts}' unit='EM'> + <g:stack> + <g:header size='4'> + <g:Label styleName='{style.stackHeader}'>Mailboxes</g:Label> + </g:header> + <mail:Mailboxes ui:field='mailboxes'/> + </g:stack> + + <g:stack> + <g:header size='4'> + <g:Label styleName='{style.stackHeader}'>Tasks</g:Label> + </g:header> + <mail:Tasks ui:field='tasks'/> + </g:stack> + + <g:stack> + <g:header size='4'> + <g:Label styleName='{style.stackHeader}'>Contacts</g:Label> + </g:header> + <mail:Contacts ui:field='contacts'/> + </g:stack> + </g:StackLayoutPanel> + +</ui:UiBinder>
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Tasks.java b/samples/mail/src/com/google/gwt/sample/mail/client/Tasks.java index b8e79d4..73a4fe7 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/Tasks.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/Tasks.java
@@ -17,7 +17,6 @@ import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; /** @@ -26,17 +25,14 @@ public class Tasks extends Composite { public Tasks() { - SimplePanel panel = new SimplePanel(); VerticalPanel list = new VerticalPanel(); - panel.setWidget(list); list.add(new CheckBox("Get groceries")); list.add(new CheckBox("Walk the dog")); list.add(new CheckBox("Start Web 2.0 company")); list.add(new CheckBox("Write cool app in GWT")); list.add(new CheckBox("Get funding")); list.add(new CheckBox("Take a vacation")); - initWidget(panel); + initWidget(list); setStyleName("mail-Tasks"); } - }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.java b/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.java index 0d6d178..ad5bc84 100644 --- a/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.java +++ b/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.java
@@ -15,71 +15,43 @@ */ package com.google.gwt.sample.mail.client; +import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Image; -import com.google.gwt.user.client.ui.ImageBundle; -import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; /** * The top panel, which contains the 'welcome' message and various links. */ -public class TopPanel extends Composite implements ClickHandler { +public class TopPanel extends Composite { - /** - * An image bundle for this widgets images. - */ - public interface Images extends ImageBundle { - AbstractImagePrototype logo(); + interface Binder extends UiBinder<Widget, TopPanel> { } + private static final Binder binder = GWT.create(Binder.class); + + @UiField Anchor signOutLink; + @UiField Anchor aboutLink; + + public TopPanel() { + initWidget(binder.createAndBindUi(this)); } - private HTML signOutLink = new HTML("<a href='javascript:;'>Sign Out</a>"); - private HTML aboutLink = new HTML("<a href='javascript:;'>About</a>"); - - public TopPanel(Images images) { - HorizontalPanel outer = new HorizontalPanel(); - VerticalPanel inner = new VerticalPanel(); - - outer.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT); - inner.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT); - - HorizontalPanel links = new HorizontalPanel(); - links.setSpacing(4); - links.add(signOutLink); - links.add(aboutLink); - - final Image logo = images.logo().createImage(); - outer.add(logo); - outer.setCellHorizontalAlignment(logo, HorizontalPanel.ALIGN_LEFT); - - outer.add(inner); - inner.add(new HTML("<b>Welcome back, foo@example.com</b>")); - inner.add(links); - - signOutLink.addClickHandler(this); - aboutLink.addClickHandler(this); - - initWidget(outer); - setStyleName("mail-TopPanel"); - links.setStyleName("mail-TopPanelLinks"); + @UiHandler("aboutLink") + void onAboutClicked(ClickEvent event) { + // When the 'About' item is selected, show the AboutDialog. + // Note that showing a dialog box does not block -- execution continues + // normally, and the dialog fires an event when it is closed. + AboutDialog dlg = new AboutDialog(); + dlg.show(); + dlg.center(); } - public void onClick(ClickEvent event) { - Object sender = event.getSource(); - if (sender == signOutLink) { - Window.alert("If this were implemented, you would be signed out now."); - } else if (sender == aboutLink) { - // When the 'About' item is selected, show the AboutDialog. - // Note that showing a dialog box does not block -- execution continues - // normally, and the dialog fires an event when it is closed. - AboutDialog dlg = new AboutDialog(); - dlg.show(); - dlg.center(); - } + @UiHandler("signOutLink") + void onSignOutClicked(ClickEvent event) { + Window.alert("If this were implemented, you would be signed out now."); } }
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.ui.xml b/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.ui.xml new file mode 100644 index 0000000..1981a59 --- /dev/null +++ b/samples/mail/src/com/google/gwt/sample/mail/client/TopPanel.ui.xml
@@ -0,0 +1,39 @@ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> + + <ui:style> + .statusDiv { + text-align: right; + margin: 1em; + } + + .linksDiv { + text-align: right; + } + + .logo { + background: url(logo.png); + position: absolute; + width: 140px; + height: 75px; + } + </ui:style> + + <g:HTMLPanel> + <div class='{style.logo}'/> + + <div class="{style.statusDiv}"> + <div> + <b>Welcome back, foo@example.com</b> + </div> + + <div class='{style.linksDiv}'> + <g:Anchor href='javascript:;' ui:field='signOutLink'>Sign Out</g:Anchor> + <g:Anchor href='javascript:;' ui:field='aboutLink'>About</g:Anchor> + </div> + </div> + </g:HTMLPanel> + +</ui:UiBinder>
diff --git a/samples/mail/war/Mail.css b/samples/mail/war/Mail.css index 2d045a4..023ced9 100644 --- a/samples/mail/war/Mail.css +++ b/samples/mail/war/Mail.css
@@ -1,11 +1,11 @@ -body, html { - height: 100%; +body, table { + font-size: small; } + body { background: #fff; color: black; font-family: Helvetica, Arial, sans-serif; - font-size: small; margin: 8px; margin-top: 3px; } @@ -24,7 +24,7 @@ .gwt-DialogBox .Caption { background: url(gradient.gif) repeat-x 0px -1px; - font-weight: bold; + font-weight: normal; cursor: default; padding: 5px 10px; border: 1px solid #666; @@ -44,26 +44,10 @@ margin: 10px; } -.gwt-MenuBar { - background: #c3d9ff; - cursor: default; -} - -.gwt-MenuItem { - font-size: 80%; - margin: 1px; - cursor: default; -} - -.gwt-MenuItem-selected { - background: #e8eef7; -} - .gwt-Tree { } .gwt-Tree .gwt-TreeItem { - font-size: 80%; padding: 1px 3px 0 3px; cursor: hand; cursor: pointer; @@ -74,142 +58,10 @@ background: #ccc; } -.gwt-DecoratedStackPanel { - width: 15em; - border-bottom: 1px solid #666; -} -.gwt-DecoratedStackPanel .lcaption { - width: 32px; - padding: 0 0 4px 5px; -} -.gwt-DecoratedStackPanel .rcaption { - padding: 0 0 4px 5px; -} -.gwt-DecoratedStackPanel .gwt-StackPanelContent { - border: 1px solid #666; - border-bottom: 0px; - background: white; - padding: 2px 2px 10px 5px; -} -.gwt-DecoratedStackPanel .gwt-StackPanelItem { - cursor: pointer; - cursor: hand; -} -.gwt-DecoratedStackPanel .stackItemTopLeft, -.gwt-DecoratedStackPanel .stackItemTopRight { - width: 4px; - height: 4px; - zoom: 1; -} -html>body .gwt-DecoratedStackPanel .stackItemTopLeft { - background: #c1eec8 url(leftCorner.gif) no-repeat; - border-left: 1px solid #666; -} -html>body .gwt-DecoratedStackPanel .stackItemTopRight { - background: #c1eec8 url(rightCorner.gif) no-repeat; - border-right: 1px solid #666; -} -.gwt-DecoratedStackPanel .stackItemTopLeftInner, -.gwt-DecoratedStackPanel .stackItemTopRightInner { - width: 4px; - height: 4px; -} -* html .gwt-DecoratedStackPanel .stackItemTopLeftInner { - overflow: hidden; - border-left: 1px solid #666; - background-color: #d3def6; - filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='leftCorner.gif',sizingMethod='crop'); -} -* html .gwt-DecoratedStackPanel .stackItemTopRightInner { - overflow: hidden; - border-right: 1px solid #666; - background-color: #d3def6; - filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='rightCorner.gif',sizingMethod='crop'); -} -.gwt-DecoratedStackPanel .stackItemTopCenter { - background: #ddefde url(gradient.gif) repeat-x 0px 0px; -} -.gwt-DecoratedStackPanel .stackItemMiddleLeft { - background: #d3def6 url(gradient.gif) repeat-x 0px -1px; - border-left: 1px solid #666; -} -.gwt-DecoratedStackPanel .stackItemMiddleLeftInner, -.gwt-DecoratedStackPanel .stackItemMiddleRightInner { - width: 1px; - height: 1px; -} -.gwt-DecoratedStackPanel .stackItemMiddleRight { - background: #d3def6 url(gradient.gif) repeat-x 0px -1px; - border-right: 1px solid #666; -} -.gwt-DecoratedStackPanel .stackItemMiddleCenter { - font-weight: bold; - font-size: 1.3em; - background: #d3def6 url(gradient.gif) repeat-x 0px -1px; -} -html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .stackItemTopRight, -html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .stackItemTopLeft { - border: 0px; - background-color: white; -} -html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .stackItemTopLeft, -html>body .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .stackItemTopRight { - background-color: white; -} -* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .stackItemTopLeftInner, -* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .stackItemTopRightInner { - border: 0px; - background-color: white; -} -* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-first .stackItemTopLeftInner { - padding-left: 1px; -} -* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .stackItemTopLeftInner, -* html .gwt-DecoratedStackPanel .gwt-StackPanelItem-below-selected .stackItemTopRightInner { - background-color: white; -} - -.mail-TopPanel { - height: 60px; -} - -.mail-TopPanel table { - font-size: 80%; -} - -.mail-TopPanel .gwt-Image { - margin-left: 10px; - position: absolute; -} - -.mail-TopPanelLinks { - font-size: 80%; -} - -.mail-AboutText { - width: 24em; - font-size: 80%; - padding: 10px; - text-align: left; -} - -.mail-Contacts td, .mail-Tasks td { - padding: 4px 0 0 0; -} - -.mail-Contacts table { - font-size: 80%; -} - -.mail-Tasks table { - font-size: 80%; -} - .mail-List { border-left: 1px solid #666; border-right: 1px solid #666; border-bottom: 1px solid #666; - font-size: 80%; cursor: pointer; cursor: hand; } @@ -226,11 +78,11 @@ .mail-ListHeader { background: #c1eec8 url(gradient.gif) repeat-x 0px -1px; - font-weight: bold; + font-weight: normal; } .mail-ListHeader .mail-ListNavBar .gwt-HTML { - font-weight: bold; + font-weight: normal; } .mail-ListHeader td { @@ -240,7 +92,6 @@ } .mail-ListNavBar table { - font-size: 80%; } .mail-ListNavBar td { @@ -259,76 +110,3 @@ .mail-SelectedRow { background: #eee; } - -.mail-Toolbar .gwt-Image { -} - -.mail-ToolButton { - font-size: 80%; - width: 10em; -} - -.mail-Detail { - border: 1px solid #666; - margin-top: 4px; -} - -.mail-DetailHeader { - background: #eee; - border-bottom: 1px solid #666; - padding: 6px 4px; -} - -.mail-DetailHeader td { - padding: 0; -} - -.mail-DetailInner { - background-color: white; - font-size: 80%; -} - -.mail-DetailSubject { - padding: 2px 10px; - font-weight: bold; -} - -.mail-DetailSender { - font-size: 80%; - padding: 2px 10px; -} - -.mail-DetailRecipient { - font-size: 80%; - padding: 2px 10px; -} - -.mail-DetailBody { - line-height: 150%; - padding: 20px 40px 20px 10px; - font-family: 'Times New Roman', Times, serif; -} - -.mail-ContactPopup { - background: #fff; - border: 1px solid #666; - padding: 4px; -} - -.mail-ContactPopupName { - font-size: 80%; - font-weight: bold; -} - -.mail-ContactPopupEmail { - font-size: 80%; - font-style: italic; -} - -.mail-StackContent { - height: 100%; -} - -.mail-Contacts { - border-bottom: none; -}
diff --git a/samples/mail/war/default_photo.jpg b/samples/mail/war/default_photo.jpg new file mode 100644 index 0000000..751970d --- /dev/null +++ b/samples/mail/war/default_photo.jpg Binary files differ
diff --git a/samples/mail/war/logo.png b/samples/mail/war/logo.png new file mode 100644 index 0000000..a3123b2 --- /dev/null +++ b/samples/mail/war/logo.png Binary files differ
diff --git a/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java index 31fa1ad..6474fb1 100644 --- a/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java +++ b/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java
@@ -20,7 +20,6 @@ import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.RootLayoutPanel; -import com.google.gwt.user.client.ui.DockLayoutPanel.Direction; public class DockLayoutPanelExample implements EntryPoint { @@ -28,11 +27,11 @@ // Attach five widgets to a DockLayoutPanel, one in each direction. Lay // them out in 'em' units. DockLayoutPanel p = new DockLayoutPanel(Unit.EM); - p.add(new HTML("north"), Direction.NORTH, 2); - p.add(new HTML("south"), Direction.SOUTH, 2); - p.add(new HTML("east"), Direction.EAST, 2); - p.add(new HTML("west"), Direction.WEST, 2); - p.add(new HTML("center"), Direction.CENTER, 2); + p.addNorth(new HTML("north"), 2); + p.addSouth(new HTML("south"), 2); + p.addEast(new HTML("east"), 2); + p.addWest(new HTML("west"), 2); + p.add(new HTML("center")); // Note the explicit call to layout(). This is required for the layout to // take effect.
diff --git a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java new file mode 100644 index 0000000..d3c1a66 --- /dev/null +++ b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
@@ -0,0 +1,124 @@ +/* + * Copyright 2009 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.uibinder.parsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.NotFoundException; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.uibinder.rebind.UiBinderWriter; +import com.google.gwt.uibinder.rebind.XMLElement; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.SplitLayoutPanel; + +import java.util.HashMap; +import java.util.Map; + +/** + * Parses {@link DockLayoutPanel} widgets. + * + * TODO(jgw): The code that explicitly excludes SplitLayoutPanel in a fairly + * awkward way could be greatly simplified if we hoisted the "dock-ness" into an + * interface implemented by both DockLayoutPanel and SplitLayoutPanel, and moved + * most of this code into a parser for that specific interface. This parser + * would then be reduced to a simple special case for the ctor param. + */ +public class DockLayoutPanelParser implements ElementParser { + + private static final Map<String, String> DOCK_NAMES = new HashMap<String, String>(); + + static { + DOCK_NAMES.put("north", "addNorth"); + DOCK_NAMES.put("south", "addSouth"); + DOCK_NAMES.put("east", "addEast"); + DOCK_NAMES.put("west", "addWest"); + DOCK_NAMES.put("center", "add"); + } + + /** + * TODO(jgw): This will be moved into EnumAttributeParser as soon as I get + * around to building it. + */ + static String getFullyQualifiedEnumName(Enum<?> e) { + Class<?> cls = e.getClass(); + String clsName = cls.getCanonicalName(); + if (clsName == null) { + // A synthesized enum subtype (e.g., Unit$3) will have no canonical name + // (yet will not be marked as synthetic). Its superclass will be the + // one we want (e.g., Unit). + clsName = cls.getSuperclass().getCanonicalName(); + } + return clsName + "." + e.name(); + } + + public void parse(XMLElement elem, String fieldName, JClassType type, + UiBinderWriter writer) throws UnableToCompleteException { + // Generate instantiation (requires a 'unit' ctor param). + // (Don't generate a ctor for the SplitLayoutPanel; it's implicitly PX). + if (type != getSplitLayoutPanelType(writer)) { + Unit unit = elem.consumeEnumAttribute("unit", Unit.class); + writer.setFieldInitializerAsConstructor(fieldName, writer.getOracle() + .findType(DockLayoutPanel.class.getName()), + getFullyQualifiedEnumName(unit)); + } + + // Parse children. + for (XMLElement child : elem.consumeChildElements()) { + // Make sure the element is one of the fixed set of valid directions. + if (!isValidChildElement(elem, child)) { + writer.die("In %s, child must be one of {north, south, east, west, center}", elem); + } + + // Consume the single widget element. + XMLElement widget = child.consumeSingleChildElement(); + String childFieldName = writer.parseElementToField(widget); + + if (requiresSize(child)) { + double size = child.consumeDoubleAttribute("size"); + writer.addStatement("%s.%s(%s, %f);", fieldName, addMethodName(child), + childFieldName, size); + } else { + writer.addStatement("%s.%s(%s);", fieldName, addMethodName(child), + childFieldName); + } + } + + // Emit the layout() call. + writer.addStatement("%s.layout();", fieldName); + } + + private String addMethodName(XMLElement elem) { + return DOCK_NAMES.get(elem.getLocalName()); + } + + private JClassType getSplitLayoutPanelType(UiBinderWriter writer) + throws UnableToCompleteException { + try { + return writer.getOracle().getType(SplitLayoutPanel.class.getName()); + } catch (NotFoundException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + + private boolean isValidChildElement(XMLElement parent, XMLElement child) { + return child.getNamespaceUri().equals(parent.getNamespaceUri()) + && DOCK_NAMES.containsKey(child.getLocalName()); + } + + private boolean requiresSize(XMLElement elem) { + return !elem.getLocalName().equals("center"); + } +}
diff --git a/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java new file mode 100644 index 0000000..ca773de --- /dev/null +++ b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
@@ -0,0 +1,83 @@ +/* + * Copyright 2009 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.uibinder.parsers; + +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.uibinder.rebind.UiBinderWriter; +import com.google.gwt.uibinder.rebind.XMLElement; +import com.google.gwt.user.client.ui.StackLayoutPanel; + +/** + * Parses {@link StackLayoutPanel} widgets. + */ +public class StackLayoutPanelParser implements ElementParser { + + private static final String HEADER_ELEM = "header"; + private static final String STACK_ELEM = "stack"; + + public void parse(XMLElement elem, String fieldName, JClassType type, + UiBinderWriter writer) throws UnableToCompleteException { + // StackLayoutPanel requires a unit ctor. + Unit unit = elem.consumeEnumAttribute("unit", Unit.class); + writer.setFieldInitializerAsConstructor(fieldName, writer.getOracle() + .findType(StackLayoutPanel.class.getName()), DockLayoutPanelParser + .getFullyQualifiedEnumName(unit)); + + // Parse children. + for (XMLElement child : elem.consumeChildElements()) { + // Get the stack element. + if (!isElementType(elem, child, STACK_ELEM)) { + writer.die("In %s, Only <stack> children are allowed.", elem); + } + + XMLElement headerElem = null, widgetElem = null; + for (XMLElement stackChild : child.consumeChildElements()) { + // Get the header. + if (isElementType(elem, stackChild, HEADER_ELEM)) { + if (headerElem != null) { + writer.die("In %s, Only one <header> allowed per <stack>", elem); + } + headerElem = stackChild; + continue; + } + + // Get the widget. + if (widgetElem != null) { + writer.die("In %s, Only one child widget allowed per <stack>", elem); + } + widgetElem = stackChild; + } + + double size = headerElem.consumeDoubleAttribute("size"); + XMLElement headerWidgetElem = headerElem.consumeSingleChildElement(); + String headerFieldName = writer.parseElementToField(headerWidgetElem); + String childFieldName = writer.parseElementToField(widgetElem); + + writer.addStatement("%s.add(%s, %s, %f);", fieldName, childFieldName, + headerFieldName, size); + } + + // Emit the layout() call. + writer.addStatement("%s.layout();", fieldName); + } + + private boolean isElementType(XMLElement parent, XMLElement child, String type) { + return child.getNamespaceUri().equals(parent.getNamespaceUri()) + && type.equals(child.getLocalName()); + } +}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java index 743ee19..739f365 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -341,6 +341,7 @@ // something like that, but in FieldManager. fieldName = ("f_" + elem.getLocalName() + (++fieldIndex)); } + fieldName = normalizeFieldName(fieldName); fieldManager.registerField(type, fieldName); return fieldName; } @@ -874,6 +875,13 @@ } } + private String normalizeFieldName(String fieldName) { + // If a field name has a '.' in it, replace it with '$' to make it a legal + // identifier. This can happen with the field names associated with nested + // classes. + return fieldName.replace('.', '$'); + } + /** * Parse the document element and return the source of the Java class that * will implement its UiBinder. @@ -979,6 +987,9 @@ addWidgetParser("CellPanel"); addWidgetParser("CustomButton"); + addWidgetParser("DockLayoutPanel"); + addWidgetParser("StackLayoutPanel"); + addAttributeParser("boolean", "com.google.gwt.uibinder.parsers.BooleanAttributeParser");
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java index 0ff9000..a1c8055 100644 --- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java +++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -206,6 +206,54 @@ } /** + * Consumes the given attribute as a double value. + * + * @param attr the attribute's full name (including prefix) + * @return the attribute's value as a double + * @throws UnableToCompleteException + */ + public double consumeDoubleAttribute(String attr) + throws UnableToCompleteException { + try { + return Double.parseDouble(consumeAttribute(attr)); + } catch (NumberFormatException e) { + writer.die(String.format("Error parsing \"%s\" attribute of \"%s\" " + + "as a double value", attr, this)); + return 0; // unreachable line for happy compiler + } + } + + /** + * Consumes the given attribute as an enum value. + * + * @param attr the attribute's full name (including prefix) + * @param type the enumerated type of which this attribute must be a member + * @return the attribute's value + * @throws UnableToCompleteException + */ + public <T extends Enum<T>> T consumeEnumAttribute(String attr, Class<T> type) + throws UnableToCompleteException { + String strValue = consumeAttribute(attr); + + // Get the enum value. Enum.valueOf() throws IAE if the specified string is + // not valid. + T value = null; + try { + // Enum.valueOf() doesn't accept null arguments. + if (strValue != null) { + value = Enum.valueOf(type, strValue); + } + } catch (IllegalArgumentException e) { + } + + if (value == null) { + writer.die(String.format("Error parsing \"%s\" attribute of \"%s\" " + + "as a %s enum", attr, this, type.getSimpleName())); + } + return value; + } + + /** * Consumes all child elements, and returns an HTML interpretation of them. * Trailing and leading whitespace is trimmed. * <p> @@ -287,6 +335,23 @@ } /** + * Consumes the given attribute as an int value. + * + * @param attr the attribute's full name (including prefix) + * @return the attribute's value as an int + * @throws UnableToCompleteException + */ + public int consumeIntAttribute(String attr) throws UnableToCompleteException { + try { + return Integer.parseInt(consumeAttribute(attr)); + } catch (NumberFormatException e) { + writer.die(String.format("Error parsing \"%s\" attribute of \"%s\" " + + "as an int value", attr, this)); + return 0; // unreachable line for happy compiler + } + } + + /** * Consumes all attributes, and returns a string representing the entire * opening tag. E.g., "<div able='baker'>" */ @@ -306,7 +371,7 @@ throws UnableToCompleteException { String value = consumeAttribute(name); if ("".equals(value)) { - writer.die("In %s, missing required attribute name\"%s\"", this, name); + writer.die("In %s, missing required attribute name \"%s\"", this, name); } return value; }
diff --git a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java index 58e15d3..9c2883f 100644 --- a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java +++ b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
@@ -17,6 +17,7 @@ import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.layout.client.Layout; import com.google.gwt.layout.client.Layout.Layer; @@ -95,23 +96,69 @@ } /** - * Adds a widget to the specified edge of the dock. If the widget is already a - * child of this panel, this method behaves as though {@link #remove(Widget)} - * had already been called. + * Adds a widget at the center of the dock. No further widgets may be added + * after this one. * * @param widget the widget to be added - * @param direction the widget's direction in the dock - * @param size the child widget's size - * - * @throws IllegalArgumentException when adding to the {@link #CENTER} and - * there is already a different widget there */ - public void add(Widget widget, Direction direction, double size) { - insert(widget, direction, size, null); + @Override + public void add(Widget widget) { + insert(widget, Direction.CENTER, 0, null); } /** + * Adds a widget to the east edge of the dock. + * + * @param widget the widget to be added + * @param size the child widget's size + */ + public void addEast(Widget widget, double size) { + insert(widget, Direction.EAST, size, null); + } + + /** + * Adds a widget to the north edge of the dock. + * + * @param widget the widget to be added + * @param size the child widget's size + */ + public void addNorth(Widget widget, double size) { + insert(widget, Direction.NORTH, size, null); + } + + /** + * Adds a widget to the south edge of the dock. + * + * @param widget the widget to be added + * @param size the child widget's size + */ + public void addSouth(Widget widget, double size) { + insert(widget, Direction.SOUTH, size, null); + } + + /** + * Adds a widget to the west edge of the dock. + * + * @param widget the widget to be added + * @param size the child widget's size + */ + public void addWest(Widget widget, double size) { + insert(widget, Direction.WEST, size, null); + } + + /** + * Gets the container element associated with the given child widget. + * + * <p> + * The container element is created by the {@link Layout} class. This should + * be used with certain styles, such as {@link Style#setZIndex(int)}, that + * must be applied to the container, rather than directly to the child widget. + * </p> + * * TODO(jgw): Is this really the best way to do this? + * + * @param widget the widget whose container element is to be retrieved + * @return the widget's container element */ public Element getContainerElementFor(Widget widget) { assertIsChild(widget); @@ -133,46 +180,55 @@ } /** - * Adds a widget to the specified edge of the dock. If the widget is already a - * child of this panel, this method behaves as though {@link #remove(Widget)} - * had already been called. + * Adds a widget to the east edge of the dock, inserting it before an + * existing widget. * * @param widget the widget to be added - * @param direction the widget's direction in the dock + * @param size the child widget's size * @param before the widget before which to insert the new child, or * <code>null</code> to append - * - * @throws IllegalArgumentException when adding to the {@link #CENTER} and - * there is already a different widget there */ - public void insert(Widget widget, Direction direction, double size, - Widget before) { - assertIsChild(before); + public void insertEast(Widget widget, double size, Widget before) { + insert(widget, Direction.EAST, size, before); + } - // Validation. - if (before == null) { - assert center == null : "No widget may be added after the CENTER widget"; - } else { - assert direction != Direction.CENTER : "A CENTER widget must always be added last"; - } + /** + * Adds a widget to the north edge of the dock, inserting it before an + * existing widget. + * + * @param widget the widget to be added + * @param size the child widget's size + * @param before the widget before which to insert the new child, or + * <code>null</code> to append + */ + public void insertNorth(Widget widget, double size, Widget before) { + insert(widget, Direction.NORTH, size, before); + } - // Detach new child. - widget.removeFromParent(); + /** + * Adds a widget to the south edge of the dock, inserting it before an + * existing widget. + * + * @param widget the widget to be added + * @param size the child widget's size + * @param before the widget before which to insert the new child, or + * <code>null</code> to append + */ + public void insertSouth(Widget widget, double size, Widget before) { + insert(widget, Direction.SOUTH, size, before); + } - // Logical attach. - getChildren().add(widget); - if (direction == Direction.CENTER) { - center = widget; - } - - // Physical attach. - Layer layer = layout.attachChild(widget.getElement(), - (before != null) ? before.getElement() : null, widget); - LayoutData data = new LayoutData(direction, size, layer); - widget.setLayoutData(data); - - // Adopt. - adopt(widget); + /** + * Adds a widget to the west edge of the dock, inserting it before an + * existing widget. + * + * @param widget the widget to be added + * @param size the child widget's size + * @param before the widget before which to insert the new child, or + * <code>null</code> to append + */ + public void insertWest(Widget widget, double size, Widget before) { + insert(widget, Direction.WEST, size, before); } public void layout() { @@ -284,6 +340,46 @@ return unit; } + /** + * Adds a widget to the specified edge of the dock. If the widget is already a + * child of this panel, this method behaves as though {@link #remove(Widget)} + * had already been called. + * + * @param widget the widget to be added + * @param direction the widget's direction in the dock + * @param before the widget before which to insert the new child, or + * <code>null</code> to append + */ + protected void insert(Widget widget, Direction direction, double size, + Widget before) { + assertIsChild(before); + + // Validation. + if (before == null) { + assert center == null : "No widget may be added after the CENTER widget"; + } else { + assert direction != Direction.CENTER : "A CENTER widget must always be added last"; + } + + // Detach new child. + widget.removeFromParent(); + + // Logical attach. + getChildren().add(widget); + if (direction == Direction.CENTER) { + center = widget; + } + + // Physical attach. + Layer layer = layout.attachChild(widget.getElement(), + (before != null) ? before.getElement() : null, widget); + LayoutData data = new LayoutData(direction, size, layer); + widget.setLayoutData(data); + + // Adopt. + adopt(widget); + } + @Override protected void onLoad() { layout.onAttach(); @@ -295,6 +391,6 @@ } private void assertIsChild(Widget widget) { - assert (widget == null) || (widget.getParent() == this) : "TODO"; + assert (widget == null) || (widget.getParent() == this) : "The specified widget is not a child of this panel"; } }
diff --git a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java index 46a43e8..db42600 100644 --- a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java +++ b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
@@ -212,10 +212,10 @@ } @Override - public void add(Widget child, Direction direction, double size) { - super.add(child, direction, size); + public void insert(Widget child, Direction direction, double size, Widget before) { + super.insert(child, direction, size, before); if (direction != Direction.CENTER) { - addSplitter(); + insertSplitter(before); } } @@ -251,13 +251,13 @@ splitter.setMinSize(minSize); } - private void addSplitter() { + private void insertSplitter(Widget before) { assert getChildren().size() > 0 : "Can't add a splitter before any children"; assert getCenter() == null : "Can't add a splitter after the CENTER widget"; Widget lastChild = getChildren().get(getChildren().size() - 1); LayoutData lastChildLayout = (LayoutData) lastChild.getLayoutData(); - Splitter splitter; + Splitter splitter = null; switch (lastChildLayout.direction) { case WEST: splitter = new HSplitter(lastChild, false); @@ -273,10 +273,9 @@ break; default: assert false : "Unexpected direction"; - return; } - super.add(splitter, lastChildLayout.direction, SPLITTER_SIZE); + super.insert(splitter, lastChildLayout.direction, SPLITTER_SIZE, before); } private Splitter getAssociatedSplitter(Widget child) {