Dear reader of Juri's TechBlog,
I moved my blog to a new domain and a new hosting solution as well. I'm now blogging on juristr.com.

GWT Button with image AND text

GWT just provides the basic widgets like check boxes, hyperlinks, buttons etc...and leave the more complex ones to the developer or to 3rd party providers. For instance they don't have lists. Another thing which quite surprised me, their implementation of a button doesn't allow to have images AND text at the same time although a lot of Google products have it (Wave, GDocs, Gmail...).
With GWT buttons you have mainly two possibilities, using Button or PushButton. The first is just the standard one while the latter allows to assign an image which is passed in its constructor. But also the PushButton doesn't allow to have both, image AND text visualized...which somehow seems to be a use case which is quite requested. A search on the web brought me to the GWT's JavaDoc describing a CustomButton widget which can be used like this:

<g:PushButton ui:field='pushButton' enabled='true'>
 <g:upFace>
  <b>click me</b>
 </g:upFace>
 <g:upHoveringFace>
  <b>Click ME!</b>
 </g:upHoveringFace>
 <g:downFace image='{res.save}' />
</g:PushButton>

But also this kind of implementation doesn't allow you to have an image and text declaration...* uff *.

So after that, similar as for the Hyperlink, I decided to implement one by myself. The implementation wasn't that difficult after all. Here's the source code:
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Image;

public class CustomButton extends Button {
 private String text;
 
 public CustomButton(){
  super();
 }
 
 public void setResource(ImageResource imageResource){
  Image img = new Image(imageResource);
  String definedStyles = img.getElement().getAttribute("style");
  img.getElement().setAttribute("style", definedStyles + "; vertical-align:middle;");
  DOM.insertBefore(getElement(), img.getElement(), DOM.getFirstChild(getElement()));
 }
 
 @Override
 public void setText(String text) {
  this.text = text;
  Element span = DOM.createElement("span");
  span.setInnerText(text);
  span.setAttribute("style", "padding-left:3px; vertical-align:middle;");
  
  DOM.insertChild(getElement(), span, 0);
 }
 
 @Override
 public String getText() {
  return this.text;
 }
}
The according usage with the GWT UiBinder is the following:
...
<!-- Declaration of your ImageBundle -->
<ui:with field="res" type="com.sample.client.IDevbookImageBundle" />
...
<d:CustomButton ui:field="buttonSave" text="Save" resource="{res.save}"></d:CustomButton>
Pretty simple, isn't it? And it doesn't alter the button's behavior rather than adding the image. Some explanations might be needed as for instance you may wonder why I've overridden the setText(String), getText() of the standard Button widget. This was needed in order to wrap the text inside a span element which I can then identify when adding the image in order to position the image before the text. This has the drawback that a definition like
...
<d:CustomButton ui:field="buttonSave"resource="{res.save}">Save</d:CustomButton>
...won't work.

Moreover this widget may be enhanced by making it more configurable like adding the image after the text etc. I've also hard-coded some styles as you see in order to make the widget easier to use. The final outcome:

Posts you might also be interested in..

Credits: Hoctro | Jack Book

8 Comments:

Thomas Broyer said...

Well, first, both Button and Hyperlink are HasHTML, so you can really use whatever HTML snippet you want, including one with an image and some text.

As for your code (which makes it easier to use and makes your whole app more consistent as to how your image is displayed), I'd rather use img.getElement().getStyle().setVerticalAlignment(VerticalAlign.MIDDLE)than your setAttribute("style") technique (well, actually, I'd even rather use a CSS class); and the DOM class will soon be deprecated in favor of the Node and Element's own methods. Finally, you don't have to instantiate an Image widget, you can just use AbstractImagePrototype.create(imageResource).createElement() which is a bit lghter-weight.

Juri Strumpflohner said...

Hei,
thx for your infos. I certainly will use the approach of using "img.getElement().getStyle().setVerticalAlignment(VerticalAlign.MIDDLE)", didn't know about that as well as your suggestion regarding the AbstractImagePrototype.

As I also mentioned in the post, this is just a sample implementation which makes it really easy to create a button with image + text. And sure, one should always prefer CSS classes over direct styles :)

thx

b.ringaling said...

Thanks so much for this posting! I'm developing my first GWT app and coming from a Flex background so I'm used to a very firm ui / code distinction and having a lot of trouble with UiBinder not natively supporting a lot of attributes. Your code worked great and your other examples have also been very useful for me - thanks again!

skrat said...

Good job, using your class after little cleanup.

Juri Strumpflohner said...

Just for curiosity: what did you clean? (and of course there was lots of space to clean up in my sample; it was more a proof of concept :) )

Plutarco Gonzalez Aguilar said...

Thanks so much for sharing this.
It's works fine!

Anonymous said...

Thanks, Used your code but in my case the image ends up behind the text in stead of in front of it!

majun8cn said...

Hi Juri
First, good work.

I found that you can also use setHTML method of Button class to do the trick.

sorry, the blog does not allow me to add span tag. So. have to use SPAN
For example,
addStockButton.setHTML("SPAN class=\"gwt-PushButton-add\"\>Add_Icon SPAN");

You can control the class to show the icon. For example:
.gwt-PushButton-add {

background:url(image/add.png) no-repeat 0px 0px;
text-indent:30px;
display:block;
}

If you like to hide this trick, you can still create a custom class to wrap this trick and expose the text/class to users

Thanks


Jun

Post a Comment