GWT CssResource Customization - gwt

I'm writing a GWT widget using UIBinder and MVP. The widget's default styles are defined in TheWidgetView.ui.xml:
<ui:style type="com.widgetlib.spinner.display.TheWidgetView.MyStyle">
.textbox {
border: 1px solid #red;
}
.important {
font-weight: bold;
}
</ui:style>
The widget's CssResource interface is defined in TheWidgetView.java:
public class TheWidgetView extends HorizontalPanel implements TheWidgetPresenter.Display {
// ... some code
#UiField MyStyle style;
public interface MyStyle extends CssResource {
String textbox();
String important();
}
// ... more code
}
I'd like the consumer of this widget to be able to customize part of the widget's styles and to have this in their MyExample.ui.xml:
<ui:style type="com.consumer.MyExample.MyStyle">
.textbox {
border: 2px solid #black;
}
</ui:style>
And this be their MyExample.java:
public class MyExample extends Composite {
// ... some code
#UiField MyStyle style;
interface MyStyle extends TheWidgetView.MyStyle{
String textbox();
}
// ... more code
}
Is there a way that my widget can have default styles, but that the consumer of the widget can override one of them? When an interface extends TheWidgetView.MyStyle, the of the widget consumer needs to define all the styles listed in that parent interface. I've seen some widget libraries have the widget's constructor take in a ClientBundle as parameter, which I suppose could apply to CssResource. Although, I'm not sure how I'd pass in this style object in a constructor invoked by UIBinder.
Thanks much in advance!

I have playing around with something similar to make my application skinable. I would start by looking at the source code for any of the cell widgets. They seem to take the resources as a constructor, however they also have constructors that use GWT.create(Resources.class) to create the resources if they are not provided. As far as your question about using this template for UIBinder, there are 3 options mentioned here. However you may run into chicken and the egg issues when trying to define the style inside the UIBinder.
The issue you are running into with your other code is that your 2 different implementations of the style because uibinder creates it's own resource bundle which doesn't reference the parent's css. There are 3 solutions:
1) Tear the css out of the ui binder file into it's own file and use ui:with combined with either a provided field or uifactory to inject the style using your own resource bindle where you can compound the sources (i.e. #Source({DEFAULT_CSS, "myCss.css"})).
2) Your other option is to look at the generated code and use the syntax they are using to refernce the css within the uibinder file, however there are 2 issues you will have to overcome: the chicken and the egg problem and the fact that google can change this without telling you and break your stuff. Here is an example of the generated client bundle from one of my files:
#Source("uibinder:com.foo.gwt.client.workspace.select.SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style.css")
SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style style();
3) The last solution is to use deferred binding to replace the default style.
<replace-with class="com.foo.gwt.laf.mpe.resource.CellTableResources">
<when-type-is class="com.google.gwt.user.cellview.client.CellTable.Resources" />
<when-property-is name="laf" value="MPE" />
</replace-with>

Related

Why there are so many way to set CSS for Widgets in GWT or GWTP, I am confused? Can someone clarify this?

Ok, to set a Css for a widget in Gwt or Gwtp, we can do following:
-1. directly from gwt code. Ex: label1.getElement().getStyle().setBackground("blue");
-2. include "ui:style" in UiBinder xml file, but this css only visible to that UiBinder
-3. include "ui:width" in UiBinder xml file, it will visible to all UiBinder
- and there r many way to set the Css directly to the widget in UiBinder.
The one that made me confused is that if i used , ex
<ui:with field='res' type="com.myproj.client.MyResource" />
& if myResource.css has .gwt-TabLayoutPanel then i don't need to use "addStyleNames", ex <g:TabLayputPanel />, it can recognized CSS perfectly.
However, if i add .gwt-ScrollPanel into myResource.css & use <g: ScrollPanel /> then nothing happened.
So I have to create public interface MyCssResource extends CssResource, then add String gwt-ScrollPanel(); to MyCssResource. But Java eclipse do not allow hyphen - in the method name, so I have to change to String gwtScrollPanel();.
Finally, i have to add addStyleNames into <g: ScrollPanel />, ex <g: ScrollPanel addStyleNames="{res.css.gwtScrollPanel}" /> then it will work.
That also means if i want to use .gwt-TabLayoutPanel in MyCssResource, then i need to remove the hyphen - & this will cause inconsistency in my code.
So, can someone explain to me what is going on here? I am confused?
The only think you should be aware about, is that GWT obfuscates class names when you use then in ui-binders. For instance:
<ui:style>
.gwtTabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel addStyleNames="{style.gwtTabLayoutPanel}" />
.gwtTabLayoutPanel will be renamed to something like .AB in the final injected style-sheet.
But most GWT widgets, are styled with un-obfuscated class names, so for using them in ui-binders files, you have to define as external in order to prevent GWT compier to obfuscate the class name:
<ui:style>
#external .gwt-TabLayoutPanel;
.gwt-TabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel />
That's because when you create a TabLayoutPanel, it has a default class called .gwt-TabLayoutPanel. So you don't need to add that class manually in to your TabLayoutPanel.Just create a TabLayoutPanel and you will see the class ".gwt-TabLayoutPanel" is already there.
But ScrollPanel doesn't comes with a default class called .gwt-ScrollPanel. It is just a div. Try creating a ScrollPanel and see. It doesn't have any classes added initially.see the screenshot
If you want to add a class called .gwt-ScrollPanel you will have to do it manually.

GWT CssResource and using #media to support both large and small screens

I'm using GWT's ClientBundle and CssResource to pull in my css definitions.
I'd like to set viewports depending upon screen size. Do you know how to achieve this within CssResource?
My failed attempt... for ease of testing I've used background shades instead. But for all devices the background is green.
body {background-color:green;}
#media only screen and (max-width:480px) {
body {background-color:red;}
}
GWT [v2.5] Compiler kicks out a warning:
[WARN] Line x column 12: encountered "screen". Was expecting one of: "{" ","
Just a warning but in this case it's not to be ignored.
GWT [v2.5] doesn't support media queries. If you want to use it you have to do a workaround:
In your Client bundle interface mark your source css as TextResource:
#Source("mycss.css")
TextResource myCss();
Go to your entryPoint class and inject your resource:
StyleInjector.inject(AppBundle.INSTANCE.carouselCss().getText());
By doing this, you lose the interface MyCss that extends from CssResource and allows you to call the css class from the UIBinder, among other things. But, at least, you can use media queries.
Support for CSS2 #media rules (e.g. #media print { /* ... */ }) was added to GWT 2.5.1. However, CSS3 #media rules are not supported yet; that's Issue 8162 - CSSResource and CSS3.
One nice work-around suggested by Bart Guijt is to place the "only screen and (max-width:480px)" CSS into a separate file and dynamically inject the generated CSS text within the #media rule.
If you have:
public interface MyResources extends ClientBundle {
#Source("my.css")
public MyCssResource desktopCss();
#Source("my-mobile.css")
public MyCssResource mobileCss();
}
Then within your EntryPoint you would add:
final MyResources resources = GWT.create(MyResources.class);
StyleInjector.injectAtEnd("#media only screen and (max-width:480px) {" +
resources.mobileCss().getText() +
"}");
This is similar to the TextResource work-around suggested by user1311201, but has the added benefit that you can use obfuscated CSS class names and other CssResource features.
If is a little change you may use as workaround too #eval as follows
#eval BG_COLOR com.google.gwt.user.client.Window.getClientWidth()<480?"red":"green";
body {background-color:BG_COLOR;}
I know is a bit late since the question was first asked, but I had the same problem recently.
My answer is similar to the one suggested by Daniel Trebbien, but with minor changes.
I do all in the the main class of this section, for I have several sections in the same project.
First, initialize the variables:
private final CssResource style;
private final TextResource mobile;
In the resource class, inside the main class:
#Source("main.css")
CssResource style();
#Source("mobile.css")
TextResource mobile();
Outside the resources class, but in the main class:
private void scheduleFinally() {
StyleInjector.injectAtEnd("#media screen and (max-width:900px) {" +
this.resources.mobile().getText() +
"}");
}
Then, I kind of "initialize" both css:
this.style = this.resources.style();
this.style.ensureInjected();
this.mobile = this.resources.mobile();
scheduleFinally();

Custom tags in UiBinder files

When using a <g:LayoutPanel> in UiBinder.ui.xml files, you can specify <g:layer> tags. Some other Google-built widgets have special tags like that as well - <g:tab> even has a sub-tag, <g:header>.
How can I specify these for my own widgets?
The new answer to this question, after some GWT improvements, is at https://stackoverflow.com/a/11785903/439317 . Copied below to avoid moderator deletion (maybe?).
You can use #UiChild to declare special functions in your widgets accessible in UiBinders.
for example,
class MyPanel extends AbsolutePanel {
#UiChild
public void addAt(Widget w, String parameter1, String parameter2) {
....
Then, in your uiBinder, you can say
<custom:MyPanel>
<custom:at parameter1="HI" parameter2="Anything you like!">
<g:AnySingleWidget />
</custom:at>
</custom:MyPanel>
See #UiChild at http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/uibinder/client/UiChild.html
What you're looking for is a custom element parser for UiBinder. See this issue. Unfortunately it's not supported yet.
You might be interested in this post for some guidance on how to extend the current parser on your own.

Modify Style from GWT

I executed but only FF and chrome moves the textarea 0px from top and 0px from left but in IE
textarea is in default position.
Here is my code:
public class MyGWT implements EntryPoint {
TextArea ta= TextArea.wrap(DOM.getElementById("t"));
public void onModuleLoad() {
ta.getElement().setAttribute("style", "position:absolute;top:0px;left:0px;");
}
}
is there any bug or how can i change style attribute programmatically from GWT ??
Don't set style via setAttribute. In JavaScript the style attribute is actually an array. Thus depending on how smart the browser is and understands you want to set the style attributes it will work by setting style or won't work.
You should set style attributes individually via getElement().getStyle().setProperty(). Or use the specific methods, like: ta.getElement().getStyle().setPosition(Position.ABSOLUTE) or via the setProperty method: ta.getElement().getStyle().setProperty("position", "absolute"). And the same for the 2 other properties. See the Style class for what specific methods are supported.
What about using style classes? I mean, you can write a style class like this:
.someClass {
position:absolute;
top:0px;
left:0px;
}
And then add the style class to the TextArea
ta.addStyleName("someClass");
It will help you to write a more concise code, without inline styling that could be difficult to maintain.

GWT - How to define a Widget outside layout hierarchy in uibinder xml file

this is my first post. I hope someone could help me.
I'm looking for a way to define a widget in UiBinder XML layout file separately, without being part of the layout hierachy.
Here's a small example:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:Label ui:field="testlabel" text="Hallo" />
<g:HTMLPanel>
...
</g:HTMLPanel>
The compile fails since the ui:UiBinder element expects only one child element.
In Java Code i will access and bind the Label widget as usual:
#UiField Label testlabel;
For example, this could be useful when you define a Grid or FlexTable - i want to define the Labels for the table header within the XML layout file, not programmatically within the code.
Many thanks in advance
Sorry, no can do, UiBinder just doesn't support this sort of syntax. Think about it - where would this Widget go? Should it be added to the DOM tree? It also makes it hard for the compiler to tell if this code should be pruned as unused.
You'll have to wait for the GWT team to create custom tags for Grid (like they did with, for example, DockLayoutPanel). I wouldn't expect such functionality for the FlexTable though, since it's meant by design for dynamic content, meaning adding it programmatically anyway.
Understanding the UiBinder Dev Guide is the key to figuring out how to structure your UiBinder templates cleanly.
So, if all you want to do is create a widget and not have it initially in the DOM, then don't mention it at all in the UiBinder template. Instead, in the Java file that goes with your UiBinder template, create it and add it to a panel that's defined in your template.
For instance, put this in your template:
<g:HTMLPanel ui:field='container'>
...
And put this in your Java file:
#UiField HTMLPanel container;
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
Label testLabel = new Label("hallo");
// Do whatever you want to testLabel
container.add(testLabel);
}
I am not certain if you and have the same unusual motivation to place more than two root widgets under the uibinder tag. Anyway, this is how I do it.
Since uibinder tag allows only one root widget, I place an HTML ui tag as the root widget, and then pile up my multiple pseudo-root widgets within that tag.
In the following example, notice that the actual root widget does not have ui:field name, because we don't intend to use it. As far as we are concerned, the effective root widgets are "tabLayout" and "content".
File Hello.ui.xml:
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTML>
<g:TabLayoutPanel ui:field="tabLayout" ... >
blah ... blah
</g:TabLayoutPanel>
<g:VerticalPanel ui:field="content">
blah .. blah
</g:VerticalPanel>
</g:HTML>
</ui:UiBinder>
Do not extend composite as your java code behind the template.
Create a class imported by your module entrypoint or use the template directly on your module.
I have various reasons why I need more than one root widget. In this example, I attach the "effective root widget" conditionally:
public class Hello
implements EntryPoint{
private static HelloUiBinder uiBinder = GWT.create(HelloUiBinder.class);
interface HelloUiBinder
extends UiBinder<Widget, Hello>{}
#UiField TabLayoutPanel tabLayout;
#UiField VerticalPanel content;
#Override public void onModuleLoad() {
uiBinder.createAndBindUi(this);
if (condition1)
RootPanel.get().add(tabLayout);
else
RootPanel.get().add(content);
blah ... blah
}
}
So, the trick is not to use the actual root widget at all.