how to dynamically skin a widget via css

Hi All,
I am testing vaadin and trying to figure out how to dynamically apply css to a widget. For example, if vaadin is displaying a widget, but the css for it is generated on the fly, how would I apply that?

Reading the themes section I see that if themes exist on the disk they vaadin can use setTheme() to set the look of the widgets.

However in my situation, I want to generate this css dynamically and it is not on disk. I don’t see anything like,

setCSS(“.v_table{ background: green }”);

Thank you.

Hi Doug,

If all you need are dynamic styles like
.v-table {background: green;}
I suggest you use a custom theme and add appropriate style names to the components/widgets that need a different look. E.g.

Table.addStyleName("green");

Then all you need to write in your custom theme is:

@import "../reindeer/styles.css"; /* Inherit the Reindeer theme */

.v-table-green {
    background: green;
}

But if you need more flexible styling, i.e. you need to alter some property values on runtime, then I suggest you take a look at the CssLayout component. It provides a
getCss(Component c)
method that you can use to add truly dynamic CSS to your components inside that layout.

Lastly I’ll add my own opinion: I think adding pure CSS in your Java source code smells just as bad as having inline styles in HTML pages, and it should be avoided. But sometimes it is of course the quickest way to get the job done.

Hope these help. If not, you could try to describe your use case a bit more, so we can think of the best alternatives to achieve it.

And welcome to the forum!

Hi Jouni,
Thank you this is what I was looking for. Let me test it out and see if I run into any issues.
Thanks

Hi Jouni,

Thank you for the suggestions. However here is the exact problem I am trying to solve.

Lets assume for the sake of simplicity I have a vaadin app, that displays 2 text fields, and each user that logs in has an ability to customize the L & F of these 2 text fields, I can do this by maybe allowing users to enter some css and store it off in a database. For simplicity sake lets assume all css is valid and vaadin compatible. User a might choose background red, while user b might choose yellow. Whenever a user logs back in they must see the colors they set in their previous session. The simplest thing to do here is store the css in a database, and read it back and show them their “theme” when they log back in. This means I probably will not have css theme files on disk and theme must be retrieved and applied after login.

In most situations the UI L & F tends to be pretty static or only has limited choices. But in my situation the 2 users can style the text in any way css allows them to style text boxes.

I am trying to understand how I would solve this problem of applying custom css l & f after user login via vaadin. I know there are other ways of solving this problem so I am interested in your thoughts if vaadin has a different way of solving this. I am assuming as you mentioned earlier csslayout might be the answer I am looking for.

Thank you

Doug,

do you want to let your users to tweak colors and font sizes ? Or just select a theme from the list of available ones ?

In case of first - take a look at Jouni’s
Chameleon Theme
- it just do the same thing - allows you to tweak colors and font sizes and then create a new theme. You can create new theme folders at runtime, then setting the application theme to that newly created one. The same technique is used in my generic
theme editor prototype
app.

In case of second - just create the desired number of themes, in your app scan VAADIN/themes folder to build a list of available ones and let user to pick one.

And also, do not forget that you can have substyles to let the same widget to look different in different situations - thise substyles you can set and change at runtime by calling setStyleName(…) on any widget.

Dmitri

If I understood you correctly at all, your need is to easily add arbitrary styles to the document at any given time.

I have this same need with the Chameleon Theme editor, and I built a little helper component/widget that handles this sort of thing called CSSInject. Basically it takes a String that it passes to the client and the client then appends one additional STYLE-element to the documents HEAD with that string as the style declarations.

Would this be enough? I could package the component as an add-on that you could download from the Directory.

Hi Doug,

Disclosure : I haven’t tried this…

There is nothing magic about themes and CSS in Vaadin; by call Application#setTheme(themeName), the HTML document in the browser is modified to include a css link to the webapp e.g.

setTheme(“example-theme”)

As a far as I can see, there is nothing to stop you creating and mapping your own servlet to serve up the CSS - which could then be dynamically generated from the database. I think that this the way I would tackle this problem :

After user login, call Application#setTheme(“mygreatapp-” + userName);

Create a servlet filter to capture all requests to VAADIN/themes before it hits the VAADIN servlet, and snaffle any specifically for your “mygreatapp-*” theme, and forward it to your own servlet where you generate the css (probably with an import to get the standard non-dynamic css)

The example code below will almost certainly not work out of the box - I’ve not compiled it, let alone actually tested it. However, it illustrates my intent clearer than stilted prose. Well, IMHO.

Hope that helps,

Cheers,

Charles.


web.xml


  <filter>
    <filter-name>theme-filter</filter-name>
    <filter-class>org.example.ThemeFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>org.example.ThemeFilter</filter-name>
    <url-pattern>/VAADIN/themes/*</url-pattern>
  </filter-mapping>

<servlet>
    <servlet-name>generate-stylesheet</servlet-name>
    <servlet-class>org.example.GenerateStylesheetServlet</servlet-class>
  </servlet>


ThemeFilter : captures requests for myapp-* theme, and redirects them to the GenerateStylesheetServlet


public class ThemeFilter implements Filter {
  private Pattern regex;

  private FilterConfig config;

  public void init(FilterConfig filterConfig) throws ServletException {
    this.config = filterConfig;
    /* Note : This regex probably won't work first time - my regex foo is minimal without lots of try-it-and-fix-it loops */
    regex = Pattern.compile("VAADIN/themes/mygreatapp-(.*)/styles.css");
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    
    String uri = httpRequest.getRequestURI();
    Matcher matcher = regex.matcher(uri);
    
    
    if(matcher.find()){
      /* It's a user-specific CSS request : extraxt the user name, stick it in the request attributes, and forward
         to a named servlet */
      String userName = matcher.group(1);
      ServletContext context = config.getServletContext();

      RequestDispatcher dispatcher = context.getNamedDispatcher("generate-stylesheet");
      request.setAttribute("username", userName);
      dispatcher.forward(request, response);

    } else {
      /* No match : Pass it through to the standard mechanism */
      filterChain.doFilter(request,response);
    }

  }

  public void destroy() {
    //NOP
  }

[b]
GenerateStyleSheetServlet : Actually serves up the customized CSS[b]


/**
 * This servlet generates a CSS style sheet for the given user (from the request attribute "username").
 * Note that I have added an import for "reindeer" theme to inherit most of the CSS from.
 */
public class GenerateStylesheetServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String userName = (String) request.getAttribute("username");

    String additionalCSS = getCSSForUser("username");

    response.setContentType("text/css");
    PrintWriter out = response.getWriter();
    out.println("/* Dynamically generated for user " + userName + " at " + new Date().toString() + " */");
    out.println("@import \"../reindeer/styles.css\"");
    out.println(additionalCSS);
    out.close();

  }

  private String getCSSForUser(String s) {
    // TODO Your DB Stuff Here
    return "";
  }
}
 

Dear Jouni, Charles, & Dimitri,

Thank you all for the solutions. They all will solve the problem I am trying to solve. Jouni really summarized what I am trying to solve in 1 sentence. I wish I was more articulate. :slight_smile:

Jouni your solution seems the simplest. Do you mind putting it up on add-ons? I will give that a shot.

Thank you. So far for my Vaadin tech eval, I have been not only very impressed with the technology but also the quality responses to my inquiries.

Thank you

Hi again, and sorry for the delay on this.

I’m having a hard time finding any time outside my regular daytime chores, so for now, I’m just pointing you to the sources of the CSSInject add-on. You can compile it yourself if you can.

Relevant files:
CSSInject.java
and
VCSSInject.java

I will still make the add-on on, when I have the time. Or, if someone else is willing, they can take the credit and package these two classes as an add-on, I won’t mind :slight_smile:

Hi Jouni,
I took a shot for creating the add-on. Let me know what do you think:

http://vaadin.com/directory#addon/cssinjector

thanks
dheeraj

This add-on looks interesting. And actually, I have a real use case for this…

I want to change the background image on a layout (with predefined pixel size). The images come from database and can be served as ApplicationResources, but I need a way to apply it at the client-side. Dynamically changing the CSS sounds a good way to do that.

@Dheeraj
Hi I wanted to try your addon, but when I got to that link I get:
“Sorry, requested add-on could not be found.”
I searched the directory for css and I still cant find it.

I managed to make it into a usable addon.
For the moment I just pushed it here:
https://code.launchpad.net/~amanica/%2Bjunk/CSSInject/

If there is any demand, I’ll look into getting it in the addon directory.

Hi everybody, im new in Vaadin and i really need to use this add-on or classes but i dont have the knowledgo (nor the time to learn it for now) to compile it. Could someone create a jar with the compiled add-on and upload it to somewhere?

Thanks to all, Vaadin is great!

Hi, and welcome!

I actually packaged this as an add-on some time ago, and you can find the
CSSInject add-on
from the Directory.

Thank you very much Jouni. I got it but i still get the following error


Widgetset does not contain implementation for org.vaadin.cssinject.CSSInject. Check its @ClientWidget mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions. Unrendered UIDL:
	
org.vaadin.cssinject.CSSInject(NO CLIENT IMPLEMENTATION FOUND)

I think it is related to the “If the add-on contains a client-side implementation, you must compile a widget set for your application.” comment on installing an add-on. I dont use eclipse nor an easy tool to compile my proyect (its a custom ant).

There is some way to avoid the widget compilation when use this add-on or i must include it? It would be nice to have a “ready to go” add-on.

Thanks again. Regards!

Hi,

As you’ve correctly deduced, the error messages means you need to do a widgetset compile.

I’m afraid
every
add-on with a client-side implementation (and that includes CSSInject) requires that you compile the widgetset. Completely unavoidable, I’m afraid, and I can’t imagine that will change in the short-to-medium term[1]

If you search the forums, there are examples of ant scripts that do just that (compile all the addons)

Sorry to be the bearer of bad news,

Cheers,

Charles.

[1]
All of Vaadin’s client-side components are built on top of GWT. GWT requires compilation. At least with Vaadin you only have to do a GWT compile every time you add a new add-on : GWT Compiles are long and heavy compared to java compiles.

Finally it figured out how to compile it, went fine, compilation say me that it compiled the CCSInject but i still get the “org.vaadin.cssinject.CSSInject(NO CLIENT IMPLEMENTATION FOUND)” error.

I also added the parameter to web.xml … what i’m missing? :frowning:



widgetset-init:
generate-widgetset:
Updating ....widgetset.MyAppWidgetSet...
Remember to define the widgetset in web.xml as follows.

            <init-param>
                <param-name>widgetset</param-name>
                <param-value>....widgetset.MyAppWidgetSet</param-value>
            </init-param>
        
31/01/2011 14:13:29 com.vaadin.terminal.gwt.widgetsetutils.ClassPathExplorer getAvailableWidgetSets
INFO: Widgetsets found from classpath:
        com.vaadin.terminal.gwt.DefaultWidgetSet in jar:file:/.../lib/vaadin-6.4.8.jar!/
        ....widgetset.MyAppWidgetSet in file:/.../src
        org.vaadin.cssinject.Cssinject_addonWidgetset in jar:file:/.../lib/cssinject-0.9.jar!/

compile-widgetset:
Compiling ....widgetset.MyAppWidgetSet into webapps/pm/VAADIN/widgetsets directory...
Compiling module ....widgetset.MyAppWidgetSet
   Scanning for additional dependencies: jar:file:/.../lib/vaadin-6.4.8.jar!/com/vaadin/terminal/gwt/client/WidgetSet.java
      Computing all possible rebind results for 'com.vaadin.terminal.gwt.client.WidgetMap'
         Rebinding com.vaadin.terminal.gwt.client.WidgetMap
            Invoking generator com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator
               Detecting Vaadin components in classpath to generate WidgetMapImpl.java ...
               Widget set will contain implementations for following components: 
                       com.vaadin.ui.AbsoluteLayout
                       com.vaadin.ui.Accordion
                       com.vaadin.ui.Button
                       com.vaadin.ui.CheckBox
                       com.vaadin.ui.ComboBox
                       com.vaadin.ui.CssLayout
                       com.vaadin.ui.CustomComponent
                       com.vaadin.ui.CustomLayout
                       com.vaadin.ui.DateField
                       com.vaadin.ui.DragAndDropWrapper
                       com.vaadin.ui.Embedded
                       com.vaadin.ui.Form
                       com.vaadin.ui.FormLayout
                       com.vaadin.ui.GridLayout
                       com.vaadin.ui.HorizontalLayout
                       com.vaadin.ui.Label
                       com.vaadin.ui.Link
                       com.vaadin.ui.ListSelect
                       com.vaadin.ui.MenuBar
                       com.vaadin.ui.NativeButton
                       com.vaadin.ui.NativeSelect
                       com.vaadin.ui.OptionGroup
                       com.vaadin.ui.OrderedLayout
                       com.vaadin.ui.Panel
                       com.vaadin.ui.PopupView
                       com.vaadin.ui.ProgressIndicator
                       com.vaadin.ui.RichTextArea
                       com.vaadin.ui.Select
                       com.vaadin.ui.Slider
                       com.vaadin.ui.SplitPanel
                       com.vaadin.ui.TabSheet
                       com.vaadin.ui.Table
                       com.vaadin.ui.TextField
                       com.vaadin.ui.Tree
                       com.vaadin.ui.TwinColSelect
                       com.vaadin.ui.Upload
                       com.vaadin.ui.UriFragmentUtility
                       com.vaadin.ui.VerticalLayout
                       com.vaadin.ui.Window
                       org.vaadin.cssinject.CSSInject
               Done. (0seconds)
   Scanning for additional dependencies: jar:file:/.../lib/vaadin-6.4.8.jar!/com/vaadin/terminal/gwt/client/ui/dd/VAcceptCriteria.java
      Computing all possible rebind results for 'com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory'
         Rebinding com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory
            Invoking generator com.vaadin.terminal.gwt.widgetsetutils.AcceptCriteriaFactoryGenerator
               Detecting available criteria ...
               creating mapping for com.vaadin.event.dd.acceptcriteria.Or
               creating mapping for com.vaadin.event.dd.acceptcriteria.ServerSideCriterion
               creating mapping for com.vaadin.event.dd.acceptcriteria.SourceIsTarget
               creating mapping for com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor
               creating mapping for com.vaadin.event.dd.acceptcriteria.AcceptAll
               creating mapping for com.vaadin.ui.Table.TableDropCriterion
               creating mapping for com.vaadin.event.dd.acceptcriteria.Not
               creating mapping for com.vaadin.event.dd.acceptcriteria.SourceIs
               creating mapping for com.vaadin.ui.AbstractSelect.TargetItemIs
               creating mapping for com.vaadin.event.dd.acceptcriteria.And
               creating mapping for com.vaadin.ui.Tree.TreeDropCriterion
               creating mapping for com.vaadin.ui.Tree.TargetInSubtree
               creating mapping for com.vaadin.ui.AbstractSelect.AcceptItem
               creating mapping for com.vaadin.event.dd.acceptcriteria.TargetDetailIs
               Done. (0seconds)
   Compiling 5 permutations
      Compiling permutation 0...
      Compiling permutation 1...
      Compiling permutation 2...
      Compiling permutation 3...
      Compiling permutation 4...
   Compile of permutations succeeded
Linking into .../VAADIN/widgetsets/....widgetset.MyAppWidgetSet
   Link succeeded
   Compilation succeeded -- 149,320s

Thanks!

Ok, i got it working. I was setting the init-param in the wrong place.