[RFC] GWT Widgets that ROCK!

532 views
Skip to first unread message

John LaBanca

unread,
Feb 24, 2011, 5:28:57 PM2/24/11
to Google Web Toolkit Contributors, Josh Teague, Joel Webber
GWT Contributors -

I drafted a proposal for a new pattern that the GWT team will use to replace existing GWT widgets with newer, shinier widgets.  The proposal hits on the major pain points that users have with existing widgets:
  • Replace the styles of a widget instance
  • Replace the DOM structure of a widget instance
  • Reskin an entire GWT app (DOM and/or styles)
    • Allow third parties to provide skins
  • Isolate CSS code for each widget
    • Dead strip CSS code that is not used within the app
  • Separate presenter logic from DOM view
  • Offer an identical Cell equivalent of every (most) new widgets
    • Shared code between Cell and Widget
    • Expand Cell library

Please take a look at the design doc and let me know what you think:
http://code.google.com/p/google-web-toolkit/wiki/CellBackedWIdgets

Thanks,
John LaBanca
jlab...@google.com

Thomas Broyer

unread,
Feb 24, 2011, 7:30:05 PM2/24/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber
I support the overall idea.

A few notes though:

I'm worried about the events in the ButtonCell example. If they're just there to provide hooks to inject styles, then:
  1. you probably don't need them if you use a "native button" appearance; which means you're listening to events but doing nothing in response to them; and we now too many registered events are bad for performance (that's what lead to the "event delegation" pattern, taking advantage of event bubbling, right?)
  2. why couldn't you just use :hover, :active and similar pseudo-classes?
OK, maybe the ButtonCell is not the best example, as its appearance and behavior are quite… limited, but still.
Or could, in this case, the Appearance have hasHoverBehavior or similar that the Cell would use to decide which events it's interested in? (so if you want your onHover method to be called, you'd first have to override hasHoverBehavior to return true). As an alternative, a few such behaviours could come in "mixin" interfaces, and the Cell would advertise (in the javadoc) which ones it supports (the ButtonCell would then only say it handles "mouseover" events if the Appearance is an instanceof HasHover).



You wrote:
Finally, note that Appearance is an abstract class. This allows us to add more state logic in the future without breaking existing Appearances. For example, we could add new methods "setRightFlush()" and "setLeftFlush()" which would make the right and left edges of the button flat, such that they could be lined up next to each other. Existing appearances may not support the new feature, but they would still work.
This is not exactly true:
Since the new method is probably something "obvious" I might already have implemented it. This is really evil because my code might still compile but not work any more.




You wrote:
Some Cells support methods to change how the Cell is rendered. For example, ButtonCell could provide a setTabIndex() method to set the tab index of the element. ButtonWidget would expose the same methods and forward through to the Cell.
Does it mean there could finally be a "Button" interface (implemented by both ButtonCell and ButtonWidget) as a few people have asked for: http://code.google.com/p/google-web-toolkit/issues/detail?id=5275


Finally, I don't really like the Widget suffix naming convention, that's what "namespaces" (packages in Java) are for (tell a Button from another Buton), but I sure could live with it.
I already have those "naming conflicts" using Guava and gwt-dev –which includes a rebased, olderguava–, and some other library that transitively depends on a rebased Apache Commons, in the same project (so I have many "Lists" classes to choose from; not to mention java.util.List vs. jaa.awt.List, and java.util.Date vs. java.sql.Date). When I'm angry that eclipse always pick the wrong one by default, I configure it so it ignores the others when suggesting completions. Or maybe the GPE could plug into Eclipse to make it prefer the "new" Button so it's always displayed before the "old" one in such autocomplete or "organize imports" lists?

Isaac Truett

unread,
Feb 25, 2011, 8:05:56 AM2/25/11
to google-web-tool...@googlegroups.com
Thomas Broyer wrote:

> Finally, I don't really like the Widget suffix naming convention, that's
> what "namespaces" (packages in Java) are for (tell a Button from another
> Buton), but I sure could live with it.
> I already have those "naming conflicts" using Guava and gwt-dev –which
> includes a rebased, olderguava–, and some other library that transitively
> depends on a rebased Apache Commons, in the same project (so I have many
> "Lists" classes to choose from; not to mention java.util.List vs.
> jaa.awt.List, and java.util.Date vs. java.sql.Date). When I'm angry that
> eclipse always pick the wrong one by default, I configure it so it ignores
> the others when suggesting completions. Or maybe the GPE could plug into
> Eclipse to make it prefer the "new" Button so it's always displayed before
> the "old" one in such autocomplete or "organize imports" lists?
>

+1 for right name, different package (and aggressive deprecation, please).

> --
> http://groups.google.com/group/Google-Web-Toolkit-Contributors

Jeff Larsen

unread,
Feb 25, 2011, 10:13:30 AM2/25/11
to google-web-tool...@googlegroups.com
I am really, really excited about this. 

+1 for aggressive Ruby style deprecation. It would be nice to get rid of the FooListeners/SourcesFoo stuff that is just clutter now and has been deprecated since 1.6

I have no problem with ButtonWidget naming style however. In eclipse you can just type BW and control-space and eclipse will find all classes with B...W... in their name. I know you can configure eclipse to do all these crazy things, but I never seem to end up doing that for whatever reason. 

Does it mean there could finally be a "Button" interface (implemented by both ButtonCell and ButtonWidget) as a few people have asked for: http://code.google.com/p/google-web-toolkit/issues/detail?id=5275

This would be an incredible feature to have a common interface between the new cell based widgets and their current widget counterpart. 


Stephen Haberman

unread,
Feb 25, 2011, 12:32:29 PM2/25/11
to google-web-tool...@googlegroups.com

> Please take a look at the design doc and let me know what you think:

The introduction mentions this:

The assumptions that current widgets make regarding their structure
prevent us from modernizing existing widgets without breaking
applications that rely on the existing DOM structures.

Which is true, but I don't think the Appearance pattern will alleviate
this? E.g. if ButtonCell's default appearance outputs a certain DOM
structure (whether by SafeHtml/otherwise), aren't users' apps going to
couple themselves to the resulting DOM structure, either via custom
Element-based hacks, or CSS rules that use selectors based on the DOM
output?

Seems like you'll still end up with DefaultAppearances that can never
change?

(Not that this sinks the whole proposal, but I thought I'd point
it out. Though perhaps I'm missing something?)

- Stephen

Stephen Haberman

unread,
Feb 25, 2011, 12:34:02 PM2/25/11
to google-web-tool...@googlegroups.com

> Please take a look at the design doc and let me know what you think:

Out of curiosity, would this allow Panels (all the way up to RootPanel)
to render their initial contents/major changes as one giant SafeHtml +
set innerHTML operation, CellTable-style?

If so, that seems pretty slick.

- Stephen

Ladislav Gazo

unread,
Feb 25, 2011, 1:29:58 PM2/25/11
to Google Web Toolkit Contributors
the idea seems fine but please keep an eye on the resulting size of
JS :)

On 24. Feb, 23:28 h., John LaBanca <jlaba...@google.com> wrote:
> GWT Contributors -
>
> I drafted a proposal for a new pattern that the GWT team will use to replace
> existing GWT widgets with newer, shinier widgets.  The proposal hits on the
> major pain points that users have with existing widgets:
>
>    - Replace the styles of a widget instance
>    - Replace the DOM structure of a widget instance
>    - Reskin an entire GWT app (DOM and/or styles)
>       - Allow third parties to provide skins
>    - Isolate CSS code for each widget
>       - Dead strip CSS code that is not used within the app
>    - Separate presenter logic from DOM view
>    - Offer an identical Cell equivalent of every (most) new widgets
>       - Shared code between Cell and Widget
>       - Expand Cell library
>
> Please take a look at the design doc and let me know what you think:http://code.google.com/p/google-web-toolkit/wiki/CellBackedWIdgets
>
> Thanks,
> John LaBanca
> jlaba...@google.com

John LaBanca

unread,
Feb 25, 2011, 1:30:42 PM2/25/11
to google-web-tool...@googlegroups.com, Stephen Haberman
On Fri, Feb 25, 2011 at 12:34 PM, Stephen Haberman <ste...@exigencecorp.com> wrote:

> Please take a look at the design doc and let me know what you think:

Out of curiosity, would this allow Panels (all the way up to RootPanel)
to render their initial contents/major changes as one giant SafeHtml +
set innerHTML operation, CellTable-style?
We're working toward that.  We could add support for Cells in HtmlPanel, such that a UiBinder backed HtmlPanel containing only HTML and Cells would render as a sstring.

If so, that seems pretty slick.


Which is true, but I don't think the Appearance pattern will alleviate
this? E.g. if ButtonCell's default appearance outputs a certain DOM
structure (whether by SafeHtml/otherwise), aren't users' apps going to
couple themselves to the resulting DOM structure, either via custom
Element-based hacks, or CSS rules that use selectors based on the DOM
output?
Seems like you'll still end up with DefaultAppearances that can never
change?
We can add a new appearance and maintain (or deprecate) the existing DefaultAppearance.  The behavior would depend on the constructor that the user uses.
ButtonCell(); // The default appearance would be replaced
ButtonCell(Appearance); // The user specified appearance remains
ButtonCell(DefaultAppearance.Resources); // The user continues to use the old DefaultAppearance
 

Thomas Broyer

unread,
Feb 25, 2011, 3:07:20 PM2/25/11
to google-web-tool...@googlegroups.com


On Friday, February 25, 2011 7:30:42 PM UTC+1, John LaBanca wrote:
On Fri, Feb 25, 2011 at 12:34 PM, Stephen Haberman <ste...@exigencecorp.com> wrote:

> Please take a look at the design doc and let me know what you think:

Out of curiosity, would this allow Panels (all the way up to RootPanel)
to render their initial contents/major changes as one giant SafeHtml +
set innerHTML operation, CellTable-style?
We're working toward that.  We could add support for Cells in HtmlPanel, such that a UiBinder backed HtmlPanel containing only HTML and Cells would render as a sstring.

That would basically make it an enhanced CompositeCell ;-)

The issue is how to handle events (detect which cell has been targeted):
  • either a Cell must generate a SafeHtml string with a single root element (could it be somehow checked and enforced? at least in DevMode?)
  • or every Cell is automatically wrapped in an element (but then which one? a div? a span? chosen by the Cell, similar to HTMLPanel's constructors? but then how about attributes?)
(i.e. what is the 'parent' element in your ButtonCell example?)

Scott Blum

unread,
Feb 25, 2011, 5:03:29 PM2/25/11
to google-web-tool...@googlegroups.com, John LaBanca, Josh Teague, Joel Webber
Hi John,

If my question is stupid or makes incorrect assumptions, feel free to completely ignore me.  My question relates to render and how the SafeHtmlBuilder is constructed:

    @Override
   
public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
      sb
.appendHtmlConstant("<button type=\"button\" tabindex=\"-1\">");
     
if (data != null) {
        sb
.append(data);
     
}
      sb
.appendHtmlConstant("</button>");
   
}
At a glance, this seems to imply that any inner content has to already be "baked" HTML.  To me, that seems to imply that for a complex hierarchy, you have to construct a bunch of SafeHtmlBuilders as you go, bake them, and pass the result into yet more builders.  I don't know how this fits into the existing model, but would it be possible to do something more visitor like?

    @Override
   
public void render(Context context, SafeHtmlBuilder sb) {
      sb
.appendHtmlConstant("<button type=\"button\" tabindex=\"-1\">");
     
context.doChildren(sb);
      sb.appendHtmlConstant("</button>");
   
}

In effect, you run the SafeHtmlBuilder in 'one shot'.

Just a comment from the peanut gallery, :)
Scott

dflorey

unread,
Feb 26, 2011, 3:35:33 AM2/26/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber
It would be great if localized messages would be moved to ClientBundle so that we can include localized messages as part of the widget styling. This is e.g. very useful for tooltips etc.

Philippe Beaudoin

unread,
Feb 26, 2011, 3:14:37 PM2/26/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber
John, I really like this idea. It's well thought-out and the customization hooks seem at once powerful and easy to use. I like the use of <replace-with> to provide a simple way to perform an app-wide appearance switch. Here are a couple of remarks:

Sorry it's so long. Here is the tl;dnr version:
1) The ButtonCell(DefaultAppearance.Resources) constructor can be confusing, I think it should be dropped.
2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly code.
3) Minor one-time style modifications are hard to make.
4) Contrary to Stephen, I think this design makes it hard to use selector-based CSS that assumes the widget obeys a precise DOM structure.
5) We should make sure GIN can be used to inject Appearance into ButtonCell and Resources into Appearance.
6) I agree with Thomas about:
  - Not being sure making Appearance an abstract class is the right choice
  - Favoring :hover and other pseudoclasses in the DefaultAppearance.

- - - - -

Now the long version...

1) The ButtonCell(DefaultAppearance.Resources) constructor can be confusing, I think it should be dropped.
This constructor gives the impression that you are using the app-wide Appearance where in fact you are using DefaultAppearance. This could lead to the following confusing use case:
- The user has two type of buttons: some initialized with new ButtonCell() and some with new ButtonCell(otherCellButtonResources)
- He changes the style app-wide via <replace-with>
- Buttons initialized with new ButtonCell() change, the ones initialized with new ButtonCell(otherCellButtonResources) don't.
Dropping that constructor would make it explicit you are actually forcing the use of DefaultAppearance.

2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly code.
DefaultAppearance.Resources ties buttonCellStyle() to a specific CSS @Source. Therefore, I believe overriding it means writing something like:
   public interface MyResources extends ButtonCell.DefaultAppearance.Resources {
      @Override
      @Source("com/mygroup/myapp/client/ButtonCell.css")
      Style buttonCellStyle();
    }
Maybe it's just me, but this looks slightly smelly. Also, does GWT.create() like two @Source for the same method?

3) Minor one-time style modifications are hard to make.
Defining a custom Appearance or Resources is good when you want to style all the buttons in your app, but impractical for small changes like adjusting padding for a single button. We should think about a way to allow that (and make sure it works in UiBinder). Ideas:
- Provide an addStyleName() in CellButton and Appearance. I don't like this, however, as it creates the problem mentioned by Stephen (see point 4 below) -- moreover, I really like the idea of Appearance moving us to more "semantic" styling.
- Provide a setProperty(String name, String value) in CellButton and Appearance and let Appearance apply it however it likes
- Provide semantic methods for frequently applied properties, like setMargin(), setPadding()...
I'm really not sure I like any of the above proposals however. Maybe we should just acknowledge that as a limitation of the new styling mechanism? Is it too limiting?

4) Contrary to Stephen, I think this design makes it hard to use selector-based CSS that assumes the widget obeys a precise DOM structure.
This is because:
- There is no public addStyleName on ButtonCell so the user cannot apply a custom CSS class and then rely on CSS selectors to style deeper elements.
- The CSS styles are obfuscated (right?), therefore writing a CSS rule that affects the widget needs an access to DefaultAppearance.Resources.
- DefaultAppearance.Resources is Appearance-specific. So if we modernize the widget look and feel in a way that changes the widget's DOM structure, we simply provide a new Resources type. CSS styles developed for the old DefaultAppearance.Resources cannot be used with the new widget because the types are not compatible.

5) We should make sure GIN can be used to inject Appearance into ButtonCell and Resources into Appearance.
Weak coupling between ButtonCell, Appearance, and Resource seems like a perfect use-case for DI. I have not reviewed the code from that perspective, but it would be nice if we allowed users to subclass ButtonCell and Appearance to provide @Inject-ed versions.

6) I agree with Thomas about:
  - Not being sure making Appearance an abstract class is the right choice.
  - Favoring :hover and other pseudoclasses in the DefaultAppearance.

Cheers,

   Philippe

Stephen Haberman

unread,
Feb 26, 2011, 4:11:24 PM2/26/11
to google-web-tool...@googlegroups.com

> - There is no public addStyleName on ButtonCell so the user cannot
> apply a custom CSS class and then rely on CSS selectors to style
> deeper elements.

True, but ButtonWidget would have addStyleName/removeStyleName.

John is right that appearances could be added/deprecated, but I was
thinking since the default cstr would always have to use the original
version, GWT would always have legacy shackles to it. (Though I also
realize now that, with GWT.create, either new defaults or old defaults
could be enabled/restored by importing a "I want the new appearance" or
"keep me on the old appearance" modules.)

- Stephen

Jeff Larsen

unread,
Feb 26, 2011, 6:12:11 PM2/26/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber

1) The ButtonCell(DefaultAppearance.Resources) constructor can be confusing, I think it should be dropped.
This constructor gives the impression that you are using the app-wide Appearance where in fact you are using DefaultAppearance. This could lead to the following confusing use case:
- The user has two type of buttons: some initialized with new ButtonCell() and some with new ButtonCell(otherCellButtonResources)
- He changes the style app-wide via <replace-with>
- Buttons initialized with new ButtonCell() change, the ones initialized with new ButtonCell(otherCellButtonResources) don't.
Dropping that constructor would make it explicit you are actually forcing the use of DefaultAppearance.

I would prefer the option. Lets say you have a 2 styles of buttons. A blue cancel button and a grey standard type button. With the current proposal, I would be able to use button and just inject different styles into the class. With your proposal, I have to extend button to be able to provide a different style than the norm for my cancel button, and what is worse, is that the style substitution is now buried in the .gwt.xml file. I'd much rather do my programming in the code than in .gwt.xml files. I would definitely like the ability to override styles without being forced to extend the widget classes. 


2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly code.
DefaultAppearance.Resources ties buttonCellStyle() to a specific CSS @Source. Therefore, I believe overriding it means writing something like:
   public interface MyResources extends ButtonCell.DefaultAppearance.Resources {
      @Override
      @Source("com/mygroup/myapp/client/ButtonCell.css")
      Style buttonCellStyle();
    }
Maybe it's just me, but this looks slightly smelly. Also, does GWT.create() like two @Source for the same method?

Annotations on methods aren't inherited. 

You can assign multiple css files in one @Source, so you could have something like this. 

public interface MyResources extends ButtonCell.DefaultAppearance.Resources {
      @Override
      @Source({"com.google.gwt.user.client.ui.ButtonWidgetStyle.css", "com/mygroup/myapp/client/ButtonCell.css"})
      Style buttonCellStyle();
    }

This would also allow you to make minor modifications to the default styling without losing the ability to get updated styles for your widgets. 

Unfortunately the following is a compile error

public interface MyResources extends ButtonCell.DefaultAppearance.Resources {

    String[] SOURCE = {"com.google.gwt.user.client.ui.ButtonWidgetStyle.css"}
      @Source(SOURCE)
      Style buttonCellStyle();
}

otherwise I'd recommend making the DEFAULT_SOURCE a String[] so we could easily add styles to it. 


3) Minor one-time style modifications are hard to make.
Defining a custom Appearance or Resources is good when you want to style all the buttons in your app, but impractical for small changes like adjusting padding for a single button. We should think about a way to allow that (and make sure it works in UiBinder). Ideas:
- Provide an addStyleName() in CellButton and Appearance. I don't like this, however, as it creates the problem mentioned by Stephen (see point 4 below) -- moreover, I really like the idea of Appearance moving us to more "semantic" styling.
- Provide a setProperty(String name, String value) in CellButton and Appearance and let Appearance apply it however it likes
- Provide semantic methods for frequently applied properties, like setMargin(), setPadding()...
I'm really not sure I like any of the above proposals however. Maybe we should just acknowledge that as a limitation of the new styling mechanism? Is it too limiting?

Is that really necessary as ButtonWidget extends CellWidget which extends Widget which gives you access to addStyleName and removeStyle name, making it easy to add padding and margin around widgets without having to get all Appearance.Resouce extendy.
 
5) We should make sure GIN can be used to inject Appearance into ButtonCell and Resources into Appearance.
Weak coupling between ButtonCell, Appearance, and Resource seems like a perfect use-case for DI. I have not reviewed the code from that perspective, but it would be nice if we allowed users to subclass ButtonCell and Appearance to provide @Inject-ed versions.

Agreed. Could/should we add javax.inject to the list of dependencies for GWT allowing for DI on some of the components out of the box? Adding javax.inject wouldn't force anyone to use Gin, but it definitely could provide some api richness to those of us who do use it. I'll admit to not having completely thought this through, but my initial reaction thought is that this is a good idea. 


6) I agree with Thomas about:
  - Not being sure making Appearance an abstract class is the right choice.

I agree, I liked the idea of the Interface that has some method/final constant like _extend_DefaultAppearnce_instead_implement_this interface_at_your_own_peril. 

That way new methods can be added and people who extended the interfaces... they knew the risks and now they're getting burned. 
  - Favoring :hover and other pseudoclasses in the DefaultAppearance.
I also agree with this. 

Philippe Beaudoin

unread,
Feb 26, 2011, 6:59:21 PM2/26/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber
1) The ButtonCell(DefaultAppearance.Resources) constructor can be confusing, I think it should be dropped.
I would prefer the option. Lets say you have a 2 styles of buttons. A blue cancel button and a grey standard type button. With the current proposal, I would be able to use button and just inject different styles into the class. With your proposal, I have to extend button to be able to provide a different style than the norm for my cancel button, and what is worse, is that the style substitution is now buried in the .gwt.xml file. I'd much rather do my programming in the code than in .gwt.xml files. I would definitely like the ability to override styles without being forced to extend the widget classes. 

I thinks there is a misunderstanding. My proposal is just to not provide the convenience constructor, so instead of:
  new ButtonCell(resources);
You'd do:
  new ButtonCell(new DefaultAppearance(resources));
Making it crystal-clear that your ButtonCell is using DefaultAppearance. The convenience constructor leads you to believe you're using "resources" together with the Appearance you bound in the .gwt.xml -- which is not true: you are using DefaultAppearance no matter which <replace-with> you provided.
3) Minor one-time style modifications are hard to make.
Is that really necessary as ButtonWidget extends CellWidget which extends Widget which gives you access to addStyleName and removeStyle name, making it easy to add padding and margin around widgets without having to get all Appearance.Resouce extendy.
 
Somehow I missed that... Which makes my point (4) kind of moot as pointed out by Stephen. 

Now, addStyleName is very limited since you can only style the wrapping <div>. It's good enough to add blank padding, but it wont let you make the internal part of the button wider or taller... Unless you use CSS type selectors but, as Stephen points out, this makes the app dependent on a specific widget style, and the app breaks as soon as this widget DOM structure changes. (So you can't easily upgrade to newer styles.)

In fact, I almost wish CellWidgets did not have a addStyleName given that it's really not that useful. I would rather have a slightly richer set of semantic styling methods in Appearance and having ways to access them from ButtonWidget (and UiBinder).


Could/should we add javax.inject to the list of dependencies for GWT allowing for DI on some of the components out of the box?

Interesting idea.

Jeff Larsen

unread,
Feb 26, 2011, 7:35:31 PM2/26/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber

I thinks there is a misunderstanding. My proposal is just to not provide the convenience constructor, so instead of:
  new ButtonCell(resources);
You'd do:
  new ButtonCell(new DefaultAppearance(resources));
Making it crystal-clear that your ButtonCell is using DefaultAppearance. The convenience constructor leads you to believe you're using "resources" together with the Appearance you bound in the .gwt.xml -- which is not true: you are using DefaultAppearance no matter which <replace-with> you provided.

Sorry, that is far better, but there still is a problem. With that way of doing things is then you've lost your ability to use the .gwt.xml file to swap out the Appearance as you're now using a new instead of a GWT.create.  


Now, addStyleName is very limited since you can only style the wrapping <div>. It's good enough to add blank padding, but it wont let you make the internal part of the button wider or taller... Unless you use CSS type selectors but, as Stephen points out, this makes the app dependent on a specific widget style, and the app breaks as soon as this widget DOM structure changes. (So you can't easily upgrade to newer styles.)
In fact, I almost wish CellWidgets did not have a addStyleName given that it's really not that useful. I would rather have a slightly richer set of semantic styling methods in Appearance and having ways to access them from ButtonWidget (and UiBinder).

All true, I guess if I'm looking to alter the height/width of a cell widget's internals or its internal structure I would expect to extend the bundle. If I'm just looking to do things like set the float, padding/margine addStyleName is the perfect tool. Maybe there is a better way for dealing with boilerplate code, maybe with UiBinder as the hook. I'm not sure I'm still a little foggy on the best practices when dealing with CssResources especially with UiBinder. 

Another option would be an enhancement GEP to make it codegen that boilerplate for you. Admittedly it doesn't get rid of the code smell, but it does save your fingers some work. 

Philippe Beaudoin

unread,
Feb 26, 2011, 7:45:44 PM2/26/11
to google-web-tool...@googlegroups.com, Jeff Larsen, Josh Teague, Joel Webber
On Sat, Feb 26, 2011 at 4:35 PM, Jeff Larsen <lars...@gmail.com> wrote:
>
> Sorry, that is far better, but there still is a problem. With that way of
> doing things is then you've lost your ability to use the .gwt.xml file to
> swap out the Appearance as you're now using a new instead of a GWT.create.

There are still two constructors:
- The default (empty) constructor uses GWT.create()
- A constructor accepting an Appearance. This appearance can be
instantiate with new, if you want to inject a Resources manually, or
with GWT.create if you're happy with the Appearance's default
resource.

> Now, addStyleName is very limited since you can only style the wrapping
> <div>. It's good enough to add blank padding, but it wont let you make the
> internal part of the button wider or taller... Unless you use CSS type
> selectors but, as Stephen points out, this makes the app dependent on a
> specific widget style, and the app breaks as soon as this widget DOM
> structure changes. (So you can't easily upgrade to newer styles.)
> In fact, I almost wish CellWidgets did not have a addStyleName given that
> it's really not that useful. I would rather have a slightly richer set of
> semantic styling methods in Appearance and having ways to access them from
> ButtonWidget (and UiBinder).
>
> All true, I guess if I'm looking to alter the height/width of a cell
> widget's internals or its internal structure I would expect to extend the
> bundle. If I'm just looking to do things like set the float, padding/margine
> addStyleName is the perfect tool. Maybe there is a better way for dealing
> with boilerplate code, maybe with UiBinder as the hook. I'm not sure I'm
> still a little foggy on the best practices when dealing with CssResources
> especially with UiBinder.
> Another option would be an enhancement GEP to make it codegen that
> boilerplate for you. Admittedly it doesn't get rid of the code smell, but it
> does save your fingers some work.

Ok, I think we're on the same page... I guess what I'd like is the
ability to both add semantic styling methods to Appearance (a-la
setFlatLeft, setFlatRight, setInnerWidth...) and to access these
methods from UiBinder.

(Also: GEP?)

Thomas Broyer

unread,
Feb 28, 2011, 6:22:45 AM2/28/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber


On Saturday, February 26, 2011 9:14:37 PM UTC+1, Philippe Beaudoin wrote:
John, I really like this idea. It's well thought-out and the customization hooks seem at once powerful and easy to use. I like the use of <replace-with> to provide a simple way to perform an app-wide appearance switch. Here are a couple of remarks:

Sorry it's so long. Here is the tl;dnr version:
1) The ButtonCell(DefaultAppearance.Resources) constructor can be confusing, I think it should be dropped.
2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly code.

-1 on both the above

(note: ClientBundle only reads the annotations from the method definition, it does not look at the overridden methods)
 
3) Minor one-time style modifications are hard to make.

There's no "one-time style modification"; if you style an instance slightly differently, it's related to the context in which you use it, which means that you're likely to find that context a second time in your app (where you'd want/need to apply the same style modification); if not today, then tomorrow when you'll add a feature.
It's similar to "I won't externalize this value –golden number– into a constant because it's only used once" (until it's used a second time, and you're still not sharing the value definition, so next time you'll want to change the value, you'll miss an instance).
And it's actually not that hard to override a CssResource: 5 lines of Java and a CSS file.
 
4) Contrary to Stephen, I think this design makes it hard to use selector-based CSS that assumes the widget obeys a precise DOM structure.
5) We should make sure GIN can be used to inject Appearance into ButtonCell and Resources into Appearance.
6) I agree with Thomas about:
  - Not being sure making Appearance an abstract class is the right choice

I didn't give an opinion on the subject; I was just dismissing the argument.

I believe in most cases Appearance classes would have only 'public' methods (and it could be documented as being how they would evolve, if ever: only adding 'public' methods), and specific subclasses would only override those methods and add non-public ones (private or protected). If a new 'public' method is added in a later version with a signature that conflicts with one you added in a subclass, because your method would have a lesser visibility, your code would no longer compile.
If you need public to add methods, then it's probably a sign of code smell, and you could still detect conflicts by configuring your IDE to mark as errors an "overridden method missing the @Overrides annotation" (it wouldn't work if the class is a third-party lib you're using though; should there be an equivalent compiler switch?).
Finally, I don't think there would be much inheritance among Appearance classes (even user-made ones), so the case of classes turning into interfaces as we saw in GWT 2.2 is unlikely to happen.

In brief, I'm fine with abstract classes, and I'd be fine interfaces too; it's just that the argument about compatibility when the API evolves is to be taken with care.
Widgets already have see a few methods added in their lifetime and I don't think anyone complained about a new method breaking their code; it's just how OOP works after all.

Philippe Beaudoin

unread,
Feb 28, 2011, 11:24:47 AM2/28/11
to google-web-tool...@googlegroups.com, Thomas Broyer, Josh Teague, Joel Webber
On Mon, Feb 28, 2011 at 3:22 AM, Thomas Broyer <t.br...@gmail.com> wrote:
> On Saturday, February 26, 2011 9:14:37 PM UTC+1, Philippe Beaudoin wrote:
>> 1) The ButtonCell(DefaultAppearance.Resources) constructor can be
>> confusing, I think it should be dropped.
>> 2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly
>> code.
>
> -1 on both the above
> (note: ClientBundle only reads the annotations from the method definition,
> it does not look at the overridden methods)

Agreed on (2), still not sure about (1) even after the discussing it
with Jeff. I'd like to see his reply on this. Also, I'd love to hear
your thoughts on why the extra constructor is not confusing in the use
case I proposed (and why it is needed).

>> 3) Minor one-time style modifications are hard to make.
>
> There's no "one-time style modification"; if you style an instance slightly
> differently, it's related to the context in which you use it, which means
> that you're likely to find that context a second time in your app (where
> you'd want/need to apply the same style modification); if not today, then
> tomorrow when you'll add a feature.
> It's similar to "I won't externalize this value –golden number– into a
> constant because it's only used once" (until it's used a second time, and
> you're still not sharing the value definition, so next time you'll want to
> change the value, you'll miss an instance).
> And it's actually not that hard to override a CssResource: 5 lines of Java
> and a CSS file.

I don't like the comparison with a golden number: I think design is
all about balancing the level of abstractions. Sure, using a golden
number is a smell showing a missing abstraction level, but you can't
say every decision about decreasing abstraction is similar to not
externalizing a golden number.

In that specific case, I disagree with you: in practice most people do
not build webapps by abstracting out all the possible styles that they
will need. The occasional float, the occasion padding, the occasional
wider button to match a margin, the occasional combination of
"flush-left + rounded-right" are typically described on a case-by-case
basis. This is for four reasons:
- Even in a well-styled app, styling is about choosing many components
from a tool box. Abstracting all the styles lead to hard-to-manage
combinatorial explosions (flush-left/rounded-right,
flush-left/flush-right, ...) In that case, 5 lines of Java and a CSS
resource are, in fact, a lot.
- Many people are happy trading good-styling for increased development speed.
- CSS is notoriously hard for most people, and is prone to
context-dependent behaviors. If you find a fix that correct the look
of a specific use of a widget, being forced to fix the global widget
style can prove very hard.
- It's really hard to apply aggressive refactoring to styling because
it's typically not part of automated tests.
Moreover, GWT already acknowledges that local/one-time styling can be
useful by providing support for UiBinder's local <ui:style> (among
other things).

Cheers,

Philippe

> --
> http://groups.google.com/group/Google-Web-Toolkit-Contributors

Jeff Larsen

unread,
Feb 28, 2011, 1:30:50 PM2/28/11
to google-web-tool...@googlegroups.com, Thomas Broyer, Josh Teague, Joel Webber
> On Saturday, February 26, 2011 9:14:37 PM UTC+1, Philippe Beaudoin wrote:
>> 1) The ButtonCell(DefaultAppearance.Resources) constructor can be
>> confusing, I think it should be dropped.
>> 2) Providing a custom DefaultAppearance.Resources lead to somewhat smelly
>> code.
>
> -1 on both the above
> (note: ClientBundle only reads the annotations from the method definition,
> it does not look at the overridden methods)
Agreed on (2), still not sure about (1) even after the discussing it
with Jeff. I'd like to see his reply on this. Also, I'd love to hear
your thoughts on why the extra constructor is not confusing in the use
case I proposed (and why it is needed).

By forcing the user to do 

new DefaultAppearance(Resource) 

you're removing their ability to globally change the appearance. You've now introduced a much tighter coupling than was there previously. As a for instance, lets say I wanted different button styles, I go ahead and extend the ClientBundle and apply then everything gets styled to my liking. 

Now lets say there is some html5 new button hotness that we want to have access to, or we need to add different Aria support etc. I can swap out the Appearance class globally with deferred binding by keeping both constructors in its current form. By getting rid of the constructor, I have to find every instance of my classes and change them programmatically to not use DefaultAppearance, but the new appearance. Now I'm good and I'm in a even more difficult refactor if I need to change the appearance based on locale/browser etc. 

I really think people will grasp injecting the ClientBundle as that is how the new CellWidgets work. The fact that it is Appearance.Resource does add an extra layer of complexity, but the flexibility/decoupling it gives you seems to be more than worth the complexity. 

The use case for changing styles is high. Many/most people will do that on most/all widgets if they're developing to a style guide. So by forcing you to new up an Appearance every time you change the style, you're more or less going to remove the ability to globally change the Appearance classes on your widgets.

Having both constructors gives me extra flexibility which I always like :). As long as there is example code I think people will get the hang of how to change styles, especially since that pattern has already been introduced with the data presentation widgets. 


PS: GEP = Google Eclipse Plugin :)

Philippe Beaudoin

unread,
Feb 28, 2011, 2:23:53 PM2/28/11
to google-web-tool...@googlegroups.com, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber
On Mon, Feb 28, 2011 at 10:30 AM, Jeff Larsen <lars...@gmail.com> wrote:
>
> By forcing the user to do
>
> new DefaultAppearance(Resource)
>
> you're removing their ability to globally change the appearance. You've now
> introduced a much tighter coupling than was there previously. As a for
> instance, lets say I wanted different button styles, I go ahead and extend
> the ClientBundle and apply then everything gets styled to my liking.
> Now lets say there is some html5 new button hotness that we want to have
> access to, or we need to add different Aria support etc. I can swap out the
> Appearance class globally with deferred binding by keeping both constructors
> in its current form. By getting rid of the constructor, I have to find every
> instance of my classes and change them programmatically to not use
> DefaultAppearance, but the new appearance. Now I'm good and I'm in a even
> more difficult refactor if I need to change the appearance based on
> locale/browser etc.

In its current form, I don't think the constructor accepting a
CssResource lets GWT swap the default appearance without impacting the
user's code that relies on it. Let's look at your proposed
implementation for the constructor in question:
// Replace the styles used by this cell instance.
public ButtonCell(DefaultAppearance.Resources resources) {
this(new DefaultAppearance(resources));
}

This constructor is not using deferred binding. Therefore, if you want
user's code relying on it to switch to the new appearance you have to
roll-out a new implementation of the constructor. Let's say you do
that:
// Replace the styles used by this cell instance.
public ButtonCell(DefaultAppearance.Resources resources) {
this(new NewDefaultAppearance(resources));
}

The problem, here, is that you are passing DefaultAppearance.Resources
to NewDefaultAppearance. It means that your new appearance
implementation cannot use CSS classes that did not exist in the
original CssResource. In fact, in the current implementation, the
CssResource is defined by the Appearance _implementation_ (it's
DefaultAppearance.Resource, not Appearance.Resource).

The confusion therefore is:
a) Will ButtonCell(DefaultAppearance.Resources resources) update its
appearance automatically when GWT upgrades to a new default
appearance; OR
b) Will it bind me forever to DefaultAppearance?
My proposition of dropping it was assuming you wanted to go with (b),
now I understand that you want the behavior of (a) -- which I agree is
much preferable -- but the current design does not seem to allow for
that goal. Maybe we should discuss ways to decouple the CssResource
from the Appearance implementations instead?

Also: I agree with the rest of your post and your reasoning.

John LaBanca

unread,
Feb 28, 2011, 2:34:12 PM2/28/11
to google-web-tool...@googlegroups.com, Philippe Beaudoin, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber
Let me clarify what I had in mind for replacing the default GWT appearance.  In the future, we might add a new DefaultAppearance implementation, but would leave the existing one.  We would probably give it some trendy name, like ModernAppearance or DefaultAppearance2013, leaving DefaultAppearance.  The GWT deferred binding for Appearance would be changed to point to DefaultAppearance2013.

Using the default constructor will result in being automatically upgraded to the new appearance:
new ButtonCell(); // Always uses the most recent Apperance.

Using the Resources convenience constructor will use the old Appearance.
new ButtonCell(myResources); // Uses DefaultAppearance.  May be deprecated when new appearances are added.

We would add a new convenience constructor for the new Appearance:
public ButtonCell(DefaultAppearance2013.Resources resources);

There is no way around the fact that DefaultAppearance.Resources are tied to DefaultAppearance and won't carry over to the new DefaultAppearance2013.



Philippe Beaudoin

unread,
Feb 28, 2011, 2:52:15 PM2/28/11
to John LaBanca, google-web-tool...@googlegroups.com, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber
Ok, I understand better now. In that case I see no difference between:
new ButtonCell(myResources)
and
new ButtonCell(new DefaultAppearance(myResources))

I prefer the second one, as the first one does not make it obvious that:
new ButtonCell()
and
new ButtonCell(myResources)
lead to potentially different appearances. The first one using
DefaultAppearance2013 and the second using DefaultAppearance.

You say people will get to injecting the ClientBundle, but even if
they do that they are tied to the old appearance -- unless they change
the type of the injected ClientBundle wherever they use it. It's
clearer in code:

@Inject Provider<ButtonCell> buttonCellProvider;
@Inject Provider<DefaultAppearance.Resouces> resourcesProvider;
...
buttonCellProvider.get(); // Leads to DefaultAppearance2013
new ButtonCell(resourcesProvider.get()); // Leads to DefaultAppearance

If you inject the ClientBundle you cannot change the global appearance
simply by switching one binding. In other words: the convenience
constructor doesn't buy you anything.

(What you can do, however, is inject the appearance itself, and this
should be the recommended pattern given the fact that the ClientBundle
depends on the Appearance implementation.)

Cheers,

Philippe

Jeff Larsen

unread,
Feb 28, 2011, 3:14:45 PM2/28/11
to John LaBanca, google-web-tool...@googlegroups.com, Philippe Beaudoin, Thomas Broyer, Josh Teague, Joel Webber
On Mon, Feb 28, 2011 at 1:34 PM, John LaBanca <jlab...@google.com> wrote:
Let me clarify what I had in mind for replacing the default GWT appearance.  In the future, we might add a new DefaultAppearance implementation, but would leave the existing one.  We would probably give it some trendy name, like ModernAppearance or DefaultAppearance2013, leaving DefaultAppearance.  The GWT deferred binding for Appearance would be changed to point to DefaultAppearance2013.

Using the default constructor will result in being automatically upgraded to the new appearance:
new ButtonCell(); // Always uses the most recent Apperance.

Using the Resources convenience constructor will use the old Appearance.
new ButtonCell(myResources); // Uses DefaultAppearance.  May be deprecated when new appearances are added.

We would add a new convenience constructor for the new Appearance:
public ButtonCell(DefaultAppearance2013.Resources resources);

There is no way around the fact that DefaultAppearance.Resources are tied to DefaultAppearance and won't carry over to the new DefaultAppearance2013.

That wouldn't have to be the case though would it? 

Couldn't we stick the Resource in the Appearance, then if the new DefaultAppearance2013 for Button doesn't need to add new css definitions, there then is no need to add an additional constructor or change up the style definitions. Should something happen where DefaultAppearance2013 needs additional classes you still have the option of creating a new ctor for that new theme, you've just given yourself some more options down the road. The main drawback here is that the Appearance.Resources may have css classes that are unused in descendant appearances. If that burden seems too high, then there still is nothing stopping you from implementing the multiple ctor solution.

We could add a setter for the Resource and do something like which could do something like

public void onAttach(){

  if(resource == null){
    resource = getDefaultResource();
  }
    resource.ensureInjected();
}

thus avoiding injecting unused styles. 

You also could easily be getting into constructor hell here as you get to n themes, you then require a minimum of n constructors and every time you add a theme, you have to touch every FooWidget to add the appropriate constructors. Seems like adding themes could become a nightmare.

John LaBanca

unread,
Feb 28, 2011, 3:42:30 PM2/28/11
to Jeff Larsen, google-web-tool...@googlegroups.com, Philippe Beaudoin, Thomas Broyer, Josh Teague, Joel Webber
On Mon, Feb 28, 2011 at 3:14 PM, Jeff Larsen <lars...@gmail.com> wrote:


On Mon, Feb 28, 2011 at 1:34 PM, John LaBanca <jlab...@google.com> wrote:
Let me clarify what I had in mind for replacing the default GWT appearance.  In the future, we might add a new DefaultAppearance implementation, but would leave the existing one.  We would probably give it some trendy name, like ModernAppearance or DefaultAppearance2013, leaving DefaultAppearance.  The GWT deferred binding for Appearance would be changed to point to DefaultAppearance2013.

Using the default constructor will result in being automatically upgraded to the new appearance:
new ButtonCell(); // Always uses the most recent Apperance.

Using the Resources convenience constructor will use the old Appearance.
new ButtonCell(myResources); // Uses DefaultAppearance.  May be deprecated when new appearances are added.

We would add a new convenience constructor for the new Appearance:
public ButtonCell(DefaultAppearance2013.Resources resources);

There is no way around the fact that DefaultAppearance.Resources are tied to DefaultAppearance and won't carry over to the new DefaultAppearance2013.

That wouldn't have to be the case though would it? 

Couldn't we stick the Resource in the Appearance, then if the new DefaultAppearance2013 for Button doesn't need to add new css definitions, there then is no need to add an additional constructor or change up the style definitions. Should something happen where DefaultAppearance2013 needs additional classes you still have the option of creating a new ctor for that new theme, you've just given yourself some more options down the road. The main drawback here is that the Appearance.Resources may have css classes that are unused in descendant appearances. If that burden seems too high, then there still is nothing stopping you from implementing the multiple ctor solution.
Thats actually a big problem.  DefaultAppearance.Resources may specify a background gradient image, but in the future we can use CSS to specify a background gradient.  So, we end up with an unused image and users are confused about why the gradient doesn't apply.  Worse, if we want to add a style name or resources to the interface, thats a breaking change.

Still, if DefaultAppearance2013 is a minor change that uses all of the same style names, we could subclass Appearance and use the same Resources.  It would depend on how much of a change we make to the DOM structure.

Philippe Beaudoin

unread,
Feb 28, 2011, 4:10:28 PM2/28/11
to google-web-tool...@googlegroups.com, Jeff Larsen, Philippe Beaudoin, Thomas Broyer, Josh Teague, Joel Webber

On Monday, February 28, 2011 12:42:30 PM UTC-8, John LaBanca wrote:
On Mon, Feb 28, 2011 at 3:14 PM, Jeff Larsen <lars...@gmail.com> wrote:


On Mon, Feb 28, 2011 at 1:34 PM, John LaBanca <jlab...@google.com> wrote:
Let me clarify what I had in mind for replacing the default GWT appearance.  In the future, we might add a new DefaultAppearance implementation, but would leave the existing one.  We would probably give it some trendy name, like ModernAppearance or DefaultAppearance2013, leaving DefaultAppearance.  The GWT deferred binding for Appearance would be changed to point to DefaultAppearance2013.

Using the default constructor will result in being automatically upgraded to the new appearance:
new ButtonCell(); // Always uses the most recent Apperance.

Using the Resources convenience constructor will use the old Appearance.
new ButtonCell(myResources); // Uses DefaultAppearance.  May be deprecated when new appearances are added.

We would add a new convenience constructor for the new Appearance:
public ButtonCell(DefaultAppearance2013.Resources resources);

There is no way around the fact that DefaultAppearance.Resources are tied to DefaultAppearance and won't carry over to the new DefaultAppearance2013.

That wouldn't have to be the case though would it? 

Couldn't we stick the Resource in the Appearance, then if the new DefaultAppearance2013 for Button doesn't need to add new css definitions, there then is no need to add an additional constructor or change up the style definitions. Should something happen where DefaultAppearance2013 needs additional classes you still have the option of creating a new ctor for that new theme, you've just given yourself some more options down the road. The main drawback here is that the Appearance.Resources may have css classes that are unused in descendant appearances. If that burden seems too high, then there still is nothing stopping you from implementing the multiple ctor solution.
Thats actually a big problem.  DefaultAppearance.Resources may specify a background gradient image, but in the future we can use CSS to specify a background gradient.  So, we end up with an unused image and users are confused about why the gradient doesn't apply.  Worse, if we want to add a style name or resources to the interface, thats a breaking change.

Still, if DefaultAppearance2013 is a minor change that uses all of the same style names, we could subclass Appearance and use the same Resources.  It would depend on how much of a change we make to the DOM structure.

I agree, I think defining the ClientBundle outside the Appearance implementation is a bit smelly... But, IMHO this smell is still present in the constructor accepting a DefaultAppearance.Resources, and the constructor-explosion problem mentioned by Jeff tends to confirm it. I think we should acknowledge that ClientBundle depends on the Appearance implementation and invite users to inject Appearance rather than the ClientBundle.

John LaBanca

unread,
Mar 11, 2011, 5:12:30 PM3/11/11
to google-web-tool...@googlegroups.com, Philippe Beaudoin, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber
Sorry for the long delay.  I just read through the email chain and tried to encapsulate the common concerns below:

Appearance: abstract class or interface
I still think Appearance should be an abstract class.  tbroyer is correct that adding a method might be a breaking change for some apps, but thats better than guaranteeing a breaking change.  Adding a method to an interface also has the same problem that users might already implement the method and not realize the change, so I don't see any advantage of using an interface.  We can document the fact that we may add methods to Appearance from time to time.

DefaultAppearance.Resources constructor is bad
I agree that these constructors might confuse users because they might not realize that using it precludes their apps from getting the latest version of Appearance.  We can remove them for now.  If users ask for us to add them later, we can always do so.

Hover methods
I agree that using :hover styles is better than catching mouseover/out events and styling manually.  In general, we want to leverage CSS as much as possible to improve performance.


I'm going to start implementing this and see how far I get.

Thanks,
John LaBanca
jlab...@google.com


Antoine DESSAIGNE

unread,
Mar 23, 2011, 7:28:32 AM3/23/11
to google-web-tool...@googlegroups.com, John LaBanca, Philippe Beaudoin, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber
Hi,

I'm fairly new in the GWT development but I'm not sure that the selected development answers the initial need. Let me explain you why (sorry it is rather long). It is also very possible that I'm completely wrong.

The main goal is allowing the developers to easily theme their application. As a developer I know I'm not good at designing a theme, in the end it always looks ugly. What I want is to be able to browse the themes and say "Oh this one looks nice, I'll pick that one!". And then, I just want to download it, add it to my project, add one configuration line that says "now on, use this theme".

A developer might also have a button in his application that is pretty special: clicking it will erase everything in the database. He wants to have a button that doesn't look like the other ones. He wants to be able to define somewhere a new style for a button with a 10px red border (it's probably good to ask for the help of a designer at this point...).

Now, lets turn around and have a look at the designer perspective. He wants to create a theme, he had the nicest ideas. He doesn't really know how to develop simply because it's not his job, but he is really good at creating the CSS and HTML that match his Photoshop mockups. If the pre-existing DOM structure of the widget doesn't match his needs he should be able to simply redefine it.

A designer might also wants to tweak a little bit an existing theme (the default one for instance). Imagine that he wants to update how Tabs are rendered and just that. He should be able to do so.

Therefore I think that:
- Creating a theme should only require to create:
  - a package that contains it
  - a simple class that inherits the base theme (the default one or another one).
  - a .css file with the name of widget to redefine its style
  - a .ui.xml file with the name of the widget in order to change its DOM structure
- Using a theme should only require to point to the right theme class

The advantages are:
- The rendering of a widget and its style are defined in HTML-like and CSS files.
- The themes are simple to create/update/use
- If the default theme changes or new widgets are added then there are no impact on pre-existing themes.

Now pseudo code samples, it doesn't compile, it just illustrates.

First the base Theme class and the Button widget class.
// This is the base theme the class that all theme should extends.
// This annotation indicates that GWT.create should always return the same instance.
@Singleton
public class Theme {
  protected interface UI {
    // The creation is performed based on the @UiTemplate annotation
    public Element render();
    
    // Inject the CSS based on the @CssSource annotation
    injectCss();
  }
  
  @UiTemplate("button.ui.xml");
  @CssSource("button.css")
  public interface ButtonUI extends UI {
    // Automatic wiring due to the @UiField annotation
    public void setLabel(Element element, @UiField("label") SafeHtml label);
  }
  
  private ButtonUI buttonUI = null;
  
  public final ButtonUI getButtonUI() {
    // Lazy loading
    if (buttonUI == null) {
      buttonUI = GWT.create(ButtonUI.class);
      buttonUI.injectCss();
    }
    return buttonUI;
  }
  
  // This defines the base style such as the font name and size.
  // It isn't linked to any widget but rather to the "body" element
  @CssSource("style.css")
  public interface PageStyle extends CssResource {}
}

public class Button extends NewWidget {
  private final Theme theme;
  private SafeHtml label;
  
  public Button() {
    // This should always returns the same instance
    this.theme = GWT.create(Theme.class)
    // Render the button, it creates a new DOM structure
    // The CSS should already be injected
    setElement(this.theme.getButtonUI().render());
  }
  
  public Button(SafeHtml label) {
    this();
    setLabel(label);
  }

  public void setLabel(SafeHtml label) {
    // We save it because we cannot retrieve it from the UI
    this.label = label;
    // Update the DOM with the new label
    this.theme.getButtonUI().setLabel(getElement(), label);
  }
}

The default button.ui.xml looks like that:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <button class='gwt-button'>
    <span class='icon' ui:field='icon'/>
    <span class='label' ui:field='label'/>
  </button>
</ui:UiBinder>

The related button.css looks like that:
.gwt-button { /* Style in here */ }
.gwt-button icon { /* Style in here */ }
.gwt-button label { /* Style in here */ }

Now the new theme class:
public UglyTheme extends Theme {
  // Nothing in here
}

...with its really ugly button.ui.xml:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <table class='gwt-button'>
    <tr>
      <td class='topleft' />
      <td colspan='2' class='top' />
      <td class='topright' />
    </tr>
    <tr>
      <td class='left' />
      <td class='icon' ui:field='icon' />
      <td class='label' ui:field='label' />
      <td class='right' />
    </tr>
    <tr>
      <td class='bottomleft' />
      <td colspan='2' class='bottom' />
      <td class='bottomright' />
    </tr>
  </table>
</ui:UiBinder>

And we want a new custom look because the button can be scary, so we create a new button.css
.gwt-button { /* Style in here */ }
.gwt-button icon { /* Style in here */ }
.gwt-button label { /* Style in here */ }
.gwt-button-scary { border: solid 10px #f00 }

And it is simply used like that:
Button btn = new Button("This one should be scary");
// If the scary style isn't defined, then it doesn't crashes since it's all CSS.
btn.addStyleDependant("scary");


This proposal implies that:
- GWT.create can return singletons
- The .css and .ui.xml files are searched in the directory of the selected theme and if it's not found then it looks in the directory of the parent class and so on until it founds the default one.
- New class generation with automatic @UiField wiring

There are still several open points and right now I have no clue on how to handle them. The major one is: how to handle the events suchs as clicks or focus especially when the underlying DOM element doesn't support them.

I hope sharing this thought will help designing great widgets.

Antoine.

2011/3/11 John LaBanca <jlab...@google.com>

Jens

unread,
Mar 23, 2011, 9:31:35 AM3/23/11
to google-web-tool...@googlegroups.com, John LaBanca, Philippe Beaudoin, Jeff Larsen, Thomas Broyer, Josh Teague, Joel Webber, Antoine DESSAIGNE
With the appearance pattern your proposed Theme class would be the same as a GWT module containing all the deferred bindings for changing all the appearance classes of all widgets. So you would inherit this theme module and you are done. I think you could also overwrite a specific deferred binding in your app module if the compiler always picks the last matching rule. Or am I missing something?

But indeed it would be nice to have a UiBinder template (or a general xml template) which defines the DOM structure of a widget instead of a render method. That way we could pass the widget.ui.xml and its widget.css file to a designer and the developer only has to deal with the appearance class and event handling.
But with a separate xml template I guess it may be difficult for a developer to implement the onBrowserEvent method because he can not assume any given DOM structure for a widget (each theme may have changed it drastically). So as you pointed out, event handling will be the major issue. 

But maybe there is a solution for it? I really like the idea of decoupling the DOM structure and having a separate xml file for it.

dflorey

unread,
Mar 23, 2011, 10:06:02 AM3/23/11
to google-web-tool...@googlegroups.com, Google Web Toolkit Contributors, Josh Teague, Joel Webber, John LaBanca
I am wondering if you are considering to use html5 widgets if available and provide a fallback gwt implementation for browsers that do not (yet) support widgets like 

I think in general GWT has the right tools for using native browser stuff whenever available and providing some js-pendants if they are not supported.

Ray Ryan

unread,
Mar 23, 2011, 12:29:11 PM3/23/11
to google-web-tool...@googlegroups.com, John Labanca
Antoine, I think the Appearance plan is closer to what you want than
you realize.

In particular, you should know that we're working on a change to
UiBinder to allow it to generate SafeHtmlRenderer instances, and then
to allow those instances to manage cell event handling. We'll share a
design proposal soon.

That said, there is still too much boilerplate and hackery required to
make a new one of these things. It does seem like we should be able to
require a lot less mindless configuration than we do. Making
FooWidget.java, Foo.css and Foo.ui.xml just work by existing, perhaps
allowing me to GWT.create(FooWidget.class) with no extra
configuration, seems like something we should make an explicit goal.

> --
> http://groups.google.com/group/Google-Web-Toolkit-Contributors

John LaBanca

unread,
Mar 23, 2011, 2:11:42 PM3/23/11
to google-web-tool...@googlegroups.com, dflorey, Josh Teague, Joel Webber
@dflorey -

We do plan to include some HTML5 widgets using the Appearance pattern.  HTML5 widgets follow a cool pattern where you can inline the fallback into the HTML5 element.  Browsers that do not support the HTML5 element naturally show the fallback, while browsers that do support the HTML5 widgets hide the fallback.

Example:
<progress value="250" max="1000">
  <span id="downloadProgress">25</span>% <!-- Only visible if progress not supported. -->
</progress>

For performance, we will use deferred bindings if we know that the element is or is not supported.  However, its ambiguous for some user agents.  For example, older versions of webkit do not support progress, but newer versions do.

In IE6, IE8 (will never support HTML5 elements):
<span id="downloadProgress">25</span>% <!-- Only visible if progress not supported. -->

In all other browsers (might support the element now or in the future):
<progress value="250" max="1000">
  <span id="downloadProgress">25</span>% <!-- Only visible if progress not supported. -->
</progress>

Thanks,
John LaBanca
jlab...@google.com

Philippe Beaudoin

unread,
Mar 23, 2011, 2:18:57 PM3/23/11
to google-web-tool...@googlegroups.com
Nice. And will you have the shorter:
<progress value="250" max="1000" />
For permutations that are guaranteed to support it?

> --
> http://groups.google.com/group/Google-Web-Toolkit-Contributors

Daniel Florey

unread,
Mar 23, 2011, 2:23:22 PM3/23/11
to John LaBanca, google-web-tool...@googlegroups.com, Josh Teague, Joel Webber
I'm really looking forward to the new widgets. Sounds extremely promising!

2011/3/23 John LaBanca <jlab...@google.com>

John LaBanca

unread,
Mar 23, 2011, 2:25:12 PM3/23/11
to google-web-tool...@googlegroups.com, Philippe Beaudoin
On Wed, Mar 23, 2011 at 2:18 PM, Philippe Beaudoin <philippe...@gmail.com> wrote:
Nice. And will you have the shorter:
 <progress value="250" max="1000" />
For permutations that are guaranteed to support it?
Eventually... maybe.  When we drop support for older versions of webkit and FF, we could switch those to lose the fallback.  However, if the fallback is minimal, it might be better to just include it.  For example, the string "25%" is pretty simple and only takes a couple lines of code, so we might just leave it in there.
<progress value="250" max="1000" />25%</progress>

Reply all
Reply to author
Forward
0 new messages