Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    [ListGrid] - Customizing Cell Style - Using custom CSS classes

    Hello everyone!

    I have a question regarding the skinning of specific cells of a ListGrid.

    -- WALL OF TEXT BEGINS --

    What I am trying to do
    I have a ListGrid where one of the columns represents an amount of money.
    I want to customize the way this amount is displayed: for example, when positive, I'd like to have it be in a green color, and, when negative, have it be red and bold.

    Possible answers
    While searching the documentation and the forums, I found two ways to do this:
    - The first way is to override getCellCSSText(), and return additive style attributes
    see this SmartGwt ShowCase sample for an example
    - The second way is to override getBaseStyle(), and replace the standard CSS class name for this cell with a custom class name (for example, "positiveAmountCell" instead of "cell")
    see this SmartGwt ShowCase sample for an example


    Problems
    Our application will be used by many different customers, and each customer wants to have a different style. For example, some might want positive amounts in blue and italic.
    Thus, it is not possible to use the first answer, since it doesn't allow per-customer customization.

    What we want to do is have a specific CSS class on the cells, and let the customer customize their style sheets on their own.

    The problem we encountered with the second answer, is that the we need to copy over all the CSS properties present in the "cell" class and its associated classes (cellDark, cellOver, cellSelected...):

    example from the Showcase's css :
    Code:
    .myHighGridCell,
    .myHighGridCellDark {
        font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; text-overflow:ellipsis;
        color:black;
        border-bottom:1px solid #ffc0c0; border-top:1px solid #ffc0c0;
        background-color:#ffc0c0;
    }
    .myHighGridCellOver,
    .myHighGridCellOverDark {
        font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; text-overflow:ellipsis;
        color:black;
        border-bottom:1px solid #c0c0c0; border-top:1px solid #c0c0c0;
        background-color:#ffe0e0;
    }
    .myHighGridCellSelected,
    .myHighGridCellSelectedDark {
        font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; text-overflow:ellipsis;
        color:black;
        border-bottom:1px solid #ffc0ff; border-top:1px solid #ffc0ff;
        background-color:#ffc0ff;
    }
    .myHighGridCellSelectedOver,
    .myHighGridCellSelectedOverDark {
        font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; text-overflow:ellipsis;
        color:black;
        border-bottom:1px solid #a0a0a0; border-top:1px solid #a0a0a0;
        background-color:#ffe0ff;
    }
    .myHighGridCellDisabled,
    .myHighGridCellDisabledDark {
        font-family:Verdana,Bitstream Vera Sans,sans-serif; font-size:11px; text-overflow:ellipsis;
        color:#808080;
        border-bottom:1px solid #ffc0c0; border-top:1px solid #ffc0c0;
        background-color:#ffc0c0;
    }
    Aside from causing code duplication (and thus problems when upgrading smartclient if these CSS classes are updated) and rendering the CSS harder to maintain / read, it stops us from having one separate CSS file containing all these customer-specific properties while still being able to switch the smartclient skins separately.

    Attempted Answers
    Our goal was to return an *additional* CSS class, which would be added to the base class, and thus beneficing from CSS inheritance.

    Our first idea was to override getBaseStyle(), and to return TWO classes, like so:
    Code:
     @Override
        protected String getBaseStyle(ListGridRecord record, int rowNum, int colNum) {
            String baseStyle = super.getBaseStyle(record, rowNum, colNum);
            if (record != null) {
                ListGridField field = getField(colNum);
                if (field != null && field.getType() == ListGridFieldType.FLOAT) {
                    Float attributeAsDouble = record.getAttributeAsFloat(field.getName());
                    if (attributeAsDouble == null || attributeAsDouble < 0) {
                        return "lowerZero " + baseStyle;
                    } else {
                        return "upperZero " + baseStyle;
                    }
                }
            }
            return baseStyle;
        }
    But it seems returning two classes doesn't work with getBaseStyle().


    Our second idea was to try to do the same at the "getCellStyle()" level, thinking we would be able to return multiple CSS classes at this point.
    We tried to understand how to override the getCellStyle() javascript method, copying what was done for getBaseStyle() (both adding the getCellStyle()" method and modifying the ListGrid's onInit() method), but to no avail: getCellStyle() seems to be delegated to the "ListGridRenderer", and it didn't work.


    Questions
    • Is there a way to override "getCellStyles()" even though it is delegated to the ListGridRenderer?
    • Is there a reason for not wanting to allow CSS inheritance and allowing to return multiple CSS styles from getBaseStyle()? (like in GWT's UIObject's addStyleName() method)? Is it due to browser compatibility problems?
    • Is there a more elegant way to solve this problem?



    @readers: Thanks a lot for reading this whole post (if you didn't and simply jumped to the end, shame on you!)
    @smartclient/smartgwt devs: thanks a lot for this great API you created! (my coworkers and myself are amazed everyday at all the possibilities we discover in smartclient/smartgwt. We often hear "OMG they allow us to do this and that too! Very cool!")
    @people who help: thanks in advance for your help and suggestions!

    Cya,

    -TG

    PS: I am not a native english speaker, and apologize for the eventual grammatical errors

    #2
    Hi TylerGalt,

    Remember that CSS class definitions are additive. Your customers can redefine the same CSS name styles in a separate CSS file and those definitions will replace only the properties that the customer defined.

    To make this concrete, here's an example from one of the skins of how a stateful tab title style is defined:

    Code:
    .tabTitle,
    .tabTitleDown,
    .tabTitleOver,
    .tabTitleDisabled,
    .tabTitleSelected,
    .tabTitleSelectedDown,
    .tabTitleSelectedOver,
    .tabTitleSelectedDisabled {
        color: #333333;
        font-family:Arial,sans-serif; font-size:11px; 
    }
    
    .tabTitleSelected, 
    .tabTitleSelectedOver,
    .tabTitleSelectedDown {
        color: #333333;
        font-weight:bold;
    }
    
    .tabTitleOver,
    .tabTitleDown,
    .tabTitleSelectedOver,
    .tabTitleSelectedDown {
        color: #333333;
    }

    Comment


      #3
      Hi,

      Thanks for your reply.

      Defining CSS in this way would indeed make it easier and allow for less copy-paste, but IMO having the possibility to return multiple class names on "getBaseStyle()" would be even more powerful.

      For example, if instead of returning these on getBaseStyle() for all my positive numbers:
      Code:
      cellPositiveNumber
      cellDarkPositiveNumber
      cellOverPositiveNumber
      cellOverDarkPositiveNumber
      cellSelectedPositiveNumber
      cellSelectedDarkPositiveNumber
      cellSelectedOverPositiveNumber
      [...]
      tallCellSelectedOverDarkPositiveNumber
      tallCellDisabledPositiveNumber
      tallCellDisabledDarkPositiveNumber
      We could return these (please note the spaces which mean these contain multiple CSS classes at the same time instead of one big class:
      Code:
      cell positiveNumber
      cellDark positiveNumber
      cellOver positiveNumber
      cellOverDark positiveNumber
      cellSelected positiveNumber
      cellSelectedDark positiveNumber
      cellSelectedOver positiveNumber
      [...]
      tallCellSelectedOverDark positiveNumber
      tallCellDisabled positiveNumber
      tallCellDisabledDark positiveNumber

      It would simplify the CSS needed a lot.

      Instead of doing this:

      Code:
      /* We have to define again all of the styles define in the skin ("cellOver", "cellOverDark", and so on) adding "PositiveNumber" at the end of them */
      .cellPositiveNumber, .cellDarkPositiveNumber, .cellOverPositiveNumber, .cellOverDarkPositiveNumber, .cellSelectedPositiveNumber, .cellSelectedDarkPositiveNumber, .cellSelectedOverPositiveNumber, .cellSelectedOverDarkPositiveNumber, .cellDisabledPositiveNumber, .cellDisabledDarkPositiveNumber, .tallCellPositiveNumber, .tallCellDarkPositiveNumber, .tallCellOverPositiveNumber, .tallCellOverDarkPositiveNumber, .tallCellSelectedPositiveNumber, .tallCellSelectedDarkPositiveNumber, .tallCellSelectedOverPositiveNumber, .tallCellSelectedOverDarkPositiveNumber, .tallCellDisabledPositiveNumber, .tallCellDisabledDarkPositiveNumber, .groupNode {
      border-right:1px solid #E1E1E1;
      color:#333333;
      font-family:Verdana,sans-serif;
      font-size:11px;
      }
      .tallCellPositiveNumber, .tallCellDarkPositiveNumber, .tallCellOverPositiveNumber, .tallCellOverDarkPositiveNumber, .tallCellSelectedPositiveNumber, .tallCellSelectedDarkPositiveNumber, .tallCellSelectedOverPositiveNumber, .tallCellSelectedOverDarkPositiveNumber, .tallCellDisabledPositiveNumber, .tallCellDisabledDarkPositiveNumber {
      border-bottom:1px solid #E1E1E1;
      }
      .cellDarkPositiveNumber {
      background:#FFFFFF url(./images/ListGrid/row.png) repeat-x scroll left bottom;
      }
      .tallCellDarkPositiveNumber {
      background-color:#F6F7F9;
      }
      .cellOverPositiveNumber, .cellOverDarkPositiveNumber {
      background:#FFFFFF url(./images/ListGrid/row_Over.png) repeat-x scroll left bottom;
      }
      .tallCellOverPositiveNumber, .tallCellOverDarkPositiveNumber {
      background-color:#E5F6FF;
      border-bottom:1px solid #C4DEFF;
      }
      .cellSelectedPositiveNumber, .cellSelectedDarkPositiveNumber {
      background:#FFFFFF url(./images/ListGrid/row_Selected.png) repeat-x scroll left bottom;
      }
      .tallCellSelectedPositiveNumber, .tallCellSelectedDarkPositiveNumber {
      background-color:#D4E8FF;
      border-bottom:1px solid #C4DEFF;
      }
      .cellSelectedOverPositiveNumber, .cellSelectedOverDarkPositiveNumber {
      background:#FFFFFF url(./images/ListGrid/row_Selected_Over.png) repeat-x scroll left bottom;
      }
      .tallCellSelectedOverPositiveNumber, .tallCellSelectedOverDarkPositiveNumber {
      background-color:#A2CBFF;
      border-bottom:1px solid #9BC8FF;
      }
      .cellDisabledPositiveNumber, .cellDisabledDarkPositiveNumber, .tallCellDisabledPositiveNumber, .tallCellDisabledDarkPositiveNumber {
      background-color:#FFFFFF;
      color:#AAAAAA;
      }
      
      /*
       ... Do the same for all the "*NegativeNumber" styles
      */
      
      /* Now, after having redefined all of these styles, we could add our green color to all the "*PositiveNumber" styles, and red color to all the "*NegativeNumber" styles*/
      
      .cellPositiveNumber, .cellDarkPositiveNumber, .cellOverPositiveNumber, .cellOverDarkPositiveNumber, .cellSelectedPositiveNumber, .cellSelectedDarkPositiveNumber, .cellSelectedOverPositiveNumber, .cellSelectedOverDarkPositiveNumber, .cellDisabledPositiveNumber, .cellDisabledDarkPositiveNumber, .tallCellPositiveNumber, .tallCellDarkPositiveNumber, .tallCellOverPositiveNumber, .tallCellOverDarkPositiveNumber, .tallCellSelectedPositiveNumber, .tallCellSelectedDarkPositiveNumber, .tallCellSelectedOverPositiveNumber, .tallCellSelectedOverDarkPositiveNumber, .tallCellDisabledPositiveNumber, .tallCellDisabledDarkPositiveNumber, .groupNode {
      color: green;
      }
      
      .cellNegativeNumber, .cellDarkNegativeNumber, .cellOverNegativeNumber, .cellOverDarkNegativeNumber, .cellSelectedNegativeNumber, .cellSelectedDarkNegativeNumber, .cellSelectedOverNegativeNumber, .cellSelectedOverDarkNegativeNumber, .cellDisabledNegativeNumber, .cellDisabledDarkNegativeNumber, .tallCellNegativeNumber, .tallCellDarkNegativeNumber, .tallCellOverNegativeNumber, .tallCellOverDarkNegativeNumber, .tallCellSelectedNegativeNumber, .tallCellSelectedDarkNegativeNumber, .tallCellSelectedOverNegativeNumber, .tallCellSelectedOverDarkNegativeNumber, .tallCellDisabledNegativeNumber, .tallCellDisabledDarkNegativeNumber, .groupNode {
      color: green;
      }
      (we would have to add all of these styles to all the skins we wish to use (if we want to allow the user to switch the main style)



      We could simply add this short CSS at the end of each skins_style.css or even better in ONE external client-custom CSS style (if we decide the green and red colors would work for all the available styles):

      Code:
      .positiveNumber{
         color: green;
      }
      .negativeNumber{
         color: red;
      }
      /*
      That's all!
      */
      And we would have the same result, avoiding the headaches of copy/pasting all of these styles from the skin, the maintenance problems (when the smartclient styles are updated, we won't need to update all of our "*PositiveNumber" and "*NegativeNumber" styles to match the skin styles).


      This is why I think it would be great to be able to have more than one style returned on getBaseStyle().

      What do you think?


      Cya,

      -TG

      Comment


        #4
        getCellCSSText() is designed for the use case you describe, and works well for it except that your constraint is that customers use CSS only. If you can give your customers a config file that is not CSS that allows them to specify CSS text as String (maybe a .json file), you're all set with a customization story.

        Otherwise, if you insist on CSS in particular, you could interrogate the loaded stylesheet to find out the CSS text of those styles (this is somewhat advanced).

        And yes, browser limitations make it too slow to allow multiple styles, specifically during updates of a large number of cells in IE.

        Comment


          #5
          One further note - although there's more to copy and paste, the stateful CSS styles provide the ability to make styling stateful, which may be key. For example, some base skins switch from dark text on light for normal rows to light text on dark for selected rows. You can usually pick a single shade of green that is legible in both cases, but the best appearance is going to come from two slightly different shades. Not to mention allowing the text to bright on rollover.

          Comment


            #6
            Originally posted by Isomorphic
            getCellCSSText() is designed for the use case you describe, and works well for it except that your constraint is that customers use CSS only. If you can give your customers a config file that is not CSS that allows them to specify CSS text as String (maybe a .json file), you're all set with a customization story.

            Otherwise, if you insist on CSS in particular, you could interrogate the loaded stylesheet to find out the CSS text of those styles (this is somewhat advanced).

            And yes, browser limitations make it too slow to allow multiple styles, specifically during updates of a large number of cells in IE.
            i think it's more a GWT question than a smartgwt one but how one can interrogate the loaded stylesheet ?

            Comment


              #7
              Originally posted by guybedo
              i think it's more a GWT question than a smartgwt one but how one can interrogate the loaded stylesheet ?
              Can you elaborate? What are you trying to do?

              Sanjiv

              Comment

              Working...
              X