I'm using Eclipse 3.7, GWT 2.5, Java 1.6, and the latest GPE. I've used UiBinder quite a bit, and now we're looking into using GWT Designer for the next version of our app. I created a basic web app project and let it generate its sample code. Then did New->GWT Designer->GWT UiBinder->Dialog Box. Then I added a button to spawn the popup, and a size and title for the dialog. Here's the widget code:
public class TestPopup extends DialogBox {
private static final Binder binder = GWT.create(Binder.class);
#UiField FlowPanel flowPanel;
interface Binder extends UiBinder<Widget, TestPopup> {
}
public TestPopup() {
setWidget(binder.createAndBindUi(this));
setTitle("Test Popup Title");
setPixelSize(400, 300);
}
}
and the corresponding *ui.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
.panel {
padding: 5px;
border: 5px solid cornflowerblue;
background-color: Menu;
}
</ui:style>
<g:FlowPanel styleName="{style.panel}" ui:field="flowPanel" width="100%" height="100%"/>
</ui:UiBinder>
and this is the result. Can anyone explain how these tools can hack a simple configuration into this:
This is a known issue. The DecoratorPanel and DecoratedPopupPanel Widgets have problems like this when you attempt to set their size specifically. Don't specify the size of the DialogBox. Instead, specify the size of the DialogBox's child Widget and the DialogBox should match it fine.
Related
UiBinder is used to lay out GWT components in a declarative way, with XML markup, as opposed to programmatically, with Java code.
A new XML element in a UiBinder tree means a new instance of that class should be created. Thus, this example from the GWT docs instantiates a new HorizontalPanel and two Labels:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:HorizontalPanel>
<g:Label>Keep your ducks</g:Label>
<g:Label>in a row</g:Label>
</g:HorizontalPanel>
</ui:UiBinder>
There's also this other example, with a DockLayoutPanel:
<g:DockLayoutPanel unit='EM'>
<g:north size='5'>
<g:Label>Top</g:Label>
</g:north>
<g:center>
<g:Label>Body</g:Label>
</g:center>
<g:west size='10'>
<g:HTML>
<ul>
<li>Sidebar</li>
<li>Sidebar</li>
<li>Sidebar</li>
</ul>
</g:HTML>
</g:west>
</g:DockLayoutPanel>
In this case, the elements are 'north', 'west', 'center', but those are not new instances of classes, but a configuration of the new DockLayoutPanel.
How do I write a component that, like DockLayoutPanel, accepts custom UiBinder XML elements ?
Where in the source of class DockLayoutPanel, or in its configuration files, is it marked as using special markup, and what to do with the inner content of the special markup elements ?
What other widgets accept special UiBinder markup ?
Seems like you'll have to introduce a custom UiBinder parser for your custom widget.
There's the com.google.gwt.uibinder.elementparsers.DockLayoutPanelParser class which has the following static final map defined:
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("lineStart", "addLineStart");
DOCK_NAMES.put("lineEnd", "addLineEnd");
DOCK_NAMES.put("center", "add");
}
Haven't searched for all of them but am guessing that any widget that has a custom parser like DockLayoutPanel can process whichever inner XML elements you program it to process.
I am new to GWT and trying to making a page which is trying to inherit a composite widget but the value of the composite widget is dynamic.
My main page is somehting like:
.....
.....
<g:Button>Open composite widget</g:button>
.....
.....
which is opening an another popup panel which is something like:
.....
<table>
<tr><td>Top: <myCompany:userMeasurementUnit/> </td></tr>
<tr><td>Bottom: <myCompany:userMeasurementUnit/> </td></tr>
<tr><td>Left: <myCompany:userMeasurementUnit/> </td></tr>
<tr><td>Right: <myCompany:userMeasurementUnit/> </td></tr>
</table>
the above should show us
top (cm)
bottom (cm)
left (cm)
right (cm)
But I don't know how to pass the values from main page to custom widget i.e usermeasurementunit
<myCompany:userMeasurementUnit/>
My usermeasurementunit is something like:
UIBinder:
<htmlpanel tag="span" ui:field="unit"/>
and the composit widget is
usermeasurementunit extends Composite {
public usermeasurementunit(){
initwidget...
}
public onload() {
...
}
}
Now I want to pass any measurement unit cm or inches or meters upon clicking button. I tried it using the event bus but it didnt help because when I click the button popuppanel is not on the screen and its not catching the event. If any one of you can help me regarding this I would be really thankful as I am really struggling with this thing.
kind regards
First of all, you need to understand the object instantiation flow in GWT.
They call it "delayed binding", not "dynamic binding".
Uibinder xml file is a layout template. And the JAva source bean behind it is known in general programming terms as the "code-behind".
The role or purpose of the uibinder layout template is to off-load the laying-out (on the internet many non-English speaking programmers write "lay-outing" which, though syntax-wise amusing, is the same thing) so that the code-behind could be focused on controlling the layout's responses.
It's akin to the MVP attitude. View implementation separated from presentation control. You can write the code-behind error free without even knowing exactly the positions where those fields are laid out. You could even simply supply a template where the ui elements are not properly laid out so as to concentrate on your code-behind first. Perhaps after that. one uibinder template for mobile while another for desktop - but they can share the same code-behind.
The values displayed effected by the uibinder template is determined once-and-for-all during uibind. There is no dynamic binding of a uibinder field to the ever changing value of an object/variable declared in the code-behind.
To dynamically change/propagate the values of a uibinder field after uibind, you have to deliberately set its value in the code-behind or write a listener to detect its change.
public class Graceland {
#UiField
Label pressure;
#UiField
Button reset;
public void setPressure(int value) {
pressure.setText(value);
}
#UiHandler("reset")
void nameDoesNotMatter(ClickEvent ev) {
pressure.setText(default);
}
}
GWT.create() generates the Java source for the template during compile time. GWT.create is not a run-time function.
#UiField and #UiHandler are bound to the uifield in the template during uibind.
The role of uibind() is mostly not run-time but compile time too. Though, its finality is realised during run-time, all the javascript code and respective values to construct the objects are generated during compile time and executed once and only once during uibind at run-time.
Therefore, the intention is not to be able to completely replace the dynamic role of the code-behind but simply to free it from the task of laying-out, so that we the programmer could have a clean piece of code-behind being smudged as little as possible with the spaghetti source of the layout.
However, if you wish to "dynamically" affect the value of a uibinder field during bind time,then Ui:with is your friend.
package z.mambazo;
public class Graceland {
....
String initialPressure(){
/* "dynamically" obtain the pressure from the
* pressure gauge in the petroleum distillation stack
* during uibind
*/
}
}
Graceland.ui.xml:
<ui:UiBinder blah...blah>
<ui:with type="z.mambazo" field="zulu"/>
<g:VerticalPanel>
<g:Label
ui:field="pressure"
text="the temperature is :{zulu.initialPressure}"/>
</g:VerticalPanel>
</ui:UiBinder>
The ui:with bean does not have to be the template's code-behind. Either the ui:with bean has an no-argument constructor or you have to supply ui:with tag with attributes corresponding to the constructor arguments.
You have to take note that in order to use ui:with, the init value must be declared in the value attribute not in the tag text.
<g:Label
ui:field="pressure"
text="the temperature is : {zulu.initialPressure}"/>
Not
<g:Label ui:field="pressure">
the temperature is : {zulu.initialPressure}
</g:Label>
The second way, would simply reproduce the text as is.
However, you could also do it this way:
<g:HtmlPanel>
the temperature is :
<g:Label ui:field="pressure"
text="{zulu.initialPressure}"/>
</g:HtmlPanel>
Also, be reminded that all GWT UI Java code, even the interim generated ones, are all translated into browser Javascript. So, whatever class you reference with ui:with must be in Java source code not Java byte code. And those source code must not at any time down the calling chain call byte code.
What you need are shared resources. Here is an example:
MeasurementConstants.java:
package com.acme.client;
public class MeasurementConstants {
private final String measurementUnit;
public MeasurementConstants(String measurementUnit) {
this.measurementUnit = measurementUnit;
}
public String measurementUnit() {
return measurementUnit;
}
}
UiBinderMeasurement.java:
package com.acme.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
public class UiBinderMeasurement extends Composite {
private static UiBinderI18nUiBinder uiBinder = GWT
.create(UiBinderI18nUiBinder.class);
private MeasurementConstants constants;
interface UiBinderI18nUiBinder extends UiBinder<Widget, UiBinderMeasurement> {
}
public UiBinderMeasurement(MeasurementConstants constants) {
this.constants = constants;
initWidget(uiBinder.createAndBindUi(this));
}
#UiFactory
public MeasurementConstants getConstants() {
return constants;
}
}
UiBinderMeasurement.ui.xml:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:with type="com.acme.client.MeasurementConstants" field="constants"></ui:with>
<g:HTMLPanel>
<table>
<tr><td><g:Label text="Top ({constants.measurementUnit}):" /> </td></tr>
<tr><td><g:Label text="Bottom ({constants.measurementUnit}):" /> </td></tr>
<tr><td><g:Label text="Left ({constants.measurementUnit}):" /> </td></tr>
<tr><td><g:Label text="Right ({constants.measurementUnit}):" /> </td></tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>
Now you can call it like this:
new UiBinderMeasurement(new MeasurementConstants("cm"))
Here is a miminal UI demonstrating my problem. It is the usual UIBinder boilerplate, plus the three widgets: TabLayoutPanel, ScrollPanel, TextArea. I want the TextArea to take up all the available space of the tab, and I want it to have a scroll bar if it can't fit. But this code yields a TextArea that is two lines tall. How do you fix this? Why is it ignoring the height?
In the ui.xml file:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:style>
.scrollPanel {
height: 100%;
}
.textArea {
height: 100%;
}
</ui:style>
<g:TabLayoutPanel barHeight="20" barUnit='PX'>
<g:tab>
<g:header>Text Area</g:header>
<g:ScrollPanel styleName='{style.scrollPanel}'>
<g:TextArea ui:field='textArea' styleName='{style.textArea}'></g:TextArea>
</g:ScrollPanel>
</g:tab>
</g:TabLayoutPanel>
</ui:UiBinder>
And in the Java file:
package com....client;
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.ResizeComposite;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
public class BugDemoLayout extends ResizeComposite {
private static BugDemoLayoutUiBinder uiBinder = GWT.create(BugDemoLayoutUiBinder.class);
interface BugDemoLayoutUiBinder extends UiBinder<Widget, BugDemoLayout> {}
#UiField TextArea textArea;
public BugDemoLayout() {
initWidget(uiBinder.createAndBindUi(this));
StringBuilder junk = new StringBuilder();
for (int i=1; i<300; i++) {
junk.append("Line " + i + "\n");
}
textArea.setText(junk.toString());
}
}
The module file simply adds the ui to the root:
public void onModuleLoad() {
BugDemoLayout bd = new BugDemoLayout();
RootLayoutPanel.get().add(bd);
TabLayoutPanel and ScrollPanel both implement the RequireResize interface and automatically resize to the available space using absolute positioning.
You specified a relative height (100%) for the content inside the ScrollPanel. This doesn't work because the size in the parent isn't explicitly set (see here for more details).
So you can either:
Set an explicit size in the ScrollPanel in pixel and then set the Textarea height to 100%.
Extend the TextArea and implement the RequiresResize interface (implement the onResize() method where you set the height/width of the TextArea
The second approach is the cleaner recommend one as it also resizes the TextArea when you resize the browser window.
It would look something like that:
TextArea:
public class ResizableTextArea extends TextArea implements RequiresResize {
public void onResize() {
int height = getParent().getOffsetHeight();
int width = getParent().getOffsetWidth();
setSize(width+"px",height+"px");
}
}
You have to put your TabLayoutPanel into a RootLayoutPanel. This will ensure that there is an unbroken chain of LayoutPanels or Widgets that implement RequiresResize/ProvidesResize interfaces all the way down to your custom TextArea.
You're placing a TextArea inside of a ScrollPanel, but the ScrollPanel isn't necessary as the TextArea already has scrollability built-in. If you take out the ScrollPanel, it should work fine (in my testing it works).
I'm going to skip the sizing part of this question, but answer the scrolling part (as that never seemed to be resolved?).
I had the same problem, so I placed a ScrollPanel inside the tab, and nested an HTMLPanel within it, sized to 100% the width and height of that parent ScrollPanel. Then, I used panel.add( new HTML("my text" ) ); to populate it.
I also replaced "\n" with "<br />" in my actual long text string. (That was applicable for my needs).
Now, that's not quite the same thing as a TextArea, of course, but it does allow you to display long running scrolling text inside of a TabLayoutPanel.
I have a task to add a composite widget into the main ui.xml file, but it's not working for some reason. Here is my widget code:
public class MyClass extends Composite {
#UiTemplate("MyClass .ui.xml")
interface MyClassUiBinder extends UiBinder<Widget, MyClass > {
}
private static MyClassUiBinder uiBinder = GWT.create(MyClassUiBinder.class);
#UiField Label label;
public MyClass() {
initWidget(uiBinder.createAndBindUi(this));
} ...
Then in my main viewImpl.ui.xml class:
I declare the class package:
... xmlns:u="urn:com... client.view">
and then the widget itself:
<g:HTMLPanel ui:field="mainPanel" styleName="{style.mainPanel}">
<table align="center">
<tr>
<td align="center">
<u:MyClass/>
</td>
</tr>
I also tried setting up a ui:field declaration in the viewImpl class, but I got an error thrown at compile time:
ERROR] In #UiField myClass, template field and owner field types don't match: com.google.gwt.dom.client.Element is not assignable to com... client.view.MyClass
When I took the #UiField declaration out of the viewImpl and the ui.xml it compiled, but the widget wasn't displaying when the page loaded.
I created another composite widget class and tried to duplicate this with just a button widget.
Using firebug I see that the element has been added to the main ui.xml page, but it also is not displaying, so my binding isn't totally complete somehow.
What am I missing here?
I found the problem, GWT was telling me that I didn't do my declaration properly, but the error wasn't as descriptive as I would have liked.
In the main.ui.xml file I used:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:u="urn:com.abc.client.view">
However, the last line should have had the word import in it:
xmlns:u="urn:import.com.abc.client.view">
This is where I found the answer.
I hope this helps someone, this cost me a lot of time!
You didn't show more UI binder markup around where you are inserting the MyClass widget, but if you have your widget inside some raw HTML you need to have all this HTML in an HTMLPanel.
I would like to implement menus (MenuBar, MenuItem) using the declarative approach via UiBinder in GWT 2.0.
I have run into two problems:
Is there a way to add MenuItemSeparators in the .ui.xml file? So
far, I have only managed to put MenuBar- and MenuItem-tags into the
file.
Using #UiHandler, GWT writes the boilerplate code for event
handlers for me. For menus, I need to write commands. How am I
supposed to do this using the UiBinder approach? Is there a command
tag to put in the .ui.xml file? Do I have to write the boilerplate
code for the command handlers myself?
Thanks for thinking about these questions!
I agree, if you try to put a MenuItemSeparator in, it will complain stating only a MenuItem can be a child when GWT tries to create the widget . Since this is not currently supported, I suggest that you request this as a future enhancement to the GWT team.
In the meantime, you can add a separator programmatically and add a command in the following manner:
The XML file:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
<g:MenuBar ui:field="menuBar">
<g:MenuItem ui:field="helpMenuItem">Help</g:MenuItem>
<g:MenuItem ui:field="aboutMenuItem">About</g:MenuItem>
<g:MenuItem ui:field="siteMapMenuItem">Site Map</g:MenuItem>
</g:MenuBar>
</g:HTMLPanel>
The Java file(s):
public class Menu extends Composite {
...
#UiField MenuBar menuBar;
#UiField MenuItem helpMenuItem;
...
public Menu() {
initWidget(uiBinder.createAndBindUi(this));
// insert a separator
menuBar.insertSeparator(1);
// attach commands to a menu item
helpMenuItem.setCommand(new MenuCommand(HistoryToken.Help));
...
}
public class MenuCommand implements Command {
final HistoryToken historyToken;
public MenuCommand(HistoryToken historyToken) {
this.historyToken = historyToken;
}
#Override
public void execute() {
historyToken.fire();
}
}
public enum HistoryToken {
Help,About,SiteMap;
public void fire(){
History.newItem(this.toString());
}
}
Elsewhere in my code, I implemented a HistoryListener to catch any changes, i.e.
class HistoryManager implements ValueChangeHandler<String> {
// 1. get token
// 2. change it into a HistoryToken
// 3. perform switch statement
// 4. change contents based upon HistoryToken found
...
}
For (1) JavaDoc says:
Use in UiBinder Templates
MenuBar elements in UiBinder template files can have a vertical boolean attribute (which defaults to false), and may have only MenuItem elements as children. MenuItems may contain HTML and MenuBars.
For example:
<g:MenuBar>
<g:MenuItem>Higgledy
<g:MenuBar vertical="true">
<g:MenuItem>able</g:MenuItem>
<g:MenuItem>baker</g:MenuItem>
<g:MenuItem>charlie</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
<g:MenuItem>Piggledy
<g:MenuBar vertical="true">
<g:MenuItem>foo</g:MenuItem>
<g:MenuItem>bar</g:MenuItem>
<g:MenuItem>baz</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
<g:MenuItem><b>Pop!</b>
<g:MenuBar vertical="true">
<g:MenuItem>uno</g:MenuItem>
<g:MenuItem>dos</g:MenuItem>
<g:MenuItem>tres</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
</g:MenuBar>
Taking the hint from the words "only MenuItem elements as children", my guess is that MenuItemSeparators are not supported
Here's an example of my solution to this, which seems to work pretty well with GWT 2.4.0.
UiBinder:
<g:MenuBar vertical='true' ui:field='mainMenu'>
<g:MenuItem ui:field='item1'>Item 1</g:MenuItem>
<g:MenuItem ui:field='item2'>Item 2</g:MenuItem>
<g:MenuItemSeparator />
<g:MenuItem ui:field='sub' enabled='false'>
Submenu
<g:MenuBar vertical='true' ui:field='subMenu' />
</g:MenuItem>
</g:MenuBar>
Java:
#UiField MenuItem item1;
#UiField MenuItem item2;
#UiField MenuBar subMenu;
#UiField MenuItem sub;
...
this.setWidget(uiBinder.createAndBindUi(this));
item1.setCommand(new Command() {
public void execute() {
History.newItem("item1");
}
});
Overall not too bad.
I know this question is OLD, but I keep running across this question in my google searches, so i thought it would be important to note that although i haven't seen it documented anywhere yet, i have been using:
<g:MenuItemSeparator/>
successfully in my uibinder template. The gwt eclipse plugin gives me a red error marker, but the MenuItemSeparator compiles and shows up ok. I'm using gwt 2.1.
Just thought someone might be interested to know.
Unfortunately I haven't seen a solution for #2 yet - but I hope they give us something soon.
It is possible to add a menuItemSeparator in the ui.xml file. Here is an example with separator and submenu from the official GWT-API page.
Well, i think i found a way to implement this. (This is a solution if you want to declare the separator inside the *.ui.xml file. )
HocusView.java
...
#UiField MenuBar menuBar;
#UiField MenuItem item1;
#UiField MenuItem item2;
#UiField MenuItem item3;
#UiField MenuItem item4;
...
private static HocusViewUiBinder uiBinder = GWT.create(HocusViewUiBinder.class);
#UiTemplate("Hocus.ui.xml")
interface HocusViewUiBinder extends UiBinder<Widget, HocusView>{}
public HocusView()
{
initWidget(uiBinder.createAndBindUi(this));
// Attach commands to menu items
menuItem1.setScheduledCommand(cmd_menuItem1);
menuItem2.setScheduledCommand(cmd_menuItem2);
menuItem3.setScheduledCommand(cmd_menuItem3);
menuItem4.setScheduledCommand(cmd_menuItem4);
}
Command cmd_menuItem1= new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem2 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem3 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem4 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
});
HocusView.ui.xml
<gwt:MenuBar ui:field="menuBar" >
<gwt:MenuItem ui:field="menuItem1">Search</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem2">Ingestion</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem3">Analysis</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem4">About</gwt:MenuItem>
</gwt:MenuBar>
Its as simple as that.This will add a separator between the menu items.
Cheers!