Tuesday, October 12, 2010

Metawidget: Adjusting the Style and Layout of Fields

I was recently asked:

"The one thing I'm still missing is information about how to adjust the style and layout of the fields. In addition to field ordering, I think this is the one major concern people always have. Early on, they will likely just throw up the fields on the page however they fall, but over time, they will want to tweak it. What level of control do they have here? Explaining this customization closes a very important gap in people's minds"

There are 3 parts to the answer:

#1: Metawidget doesn't try to 'own' your entire UI
Metawidget, as its name suggests, is just a widget. It's not trying to be your whole UI. It just focuses on creating native subcomponents for slotting into existing UIs. And it's very lightweight, so there's nothing stopping you combining 4 or 5 or more Metawidgets on the same screen:

Here you can see a simple UI with its Metawidgets outlined with red squares. There are 5 Metawidgets used here. You can scatter them around in an arbitrary fashion, embed them inside other parts of your UI (like dialog boxes and status bars), and surround them however you want (like images next to them, or JTables under them).

This approach alone gives you a huge amount of flexibility in how you design the look of your UI, and ensures you're not locked in to any particular appearance and your app doesn't 'feel' like it is using UI generation.

#2: Metawidget doesn't hide your existing UI toolkit
Most UI frameworks have extensive support for tweaking the look of your UI. For example Swing has Look & Feels; JSF has CSS and skinnable component libraries; Android has XML style files.

Metawidget doesn't hide any of these. It just creates the native components for you, and you can style them like you always do. It helps a little where it can, for example by putting CSS style classes where you want them and generating sensible ids for table rows. Here's some actual output from the JSF Metawidget:

<table id="form:j_id_jsp_628835842_3" class="table-form">
   <tfoot>
      <tr>
         <td colspan="3" class="buttons">
            <input id="form:contactSearchSearch" name="form:contactSearchSearch" type="submit" value="Search" />
            <input id="form:contactSearchAddPersonal" name="form:contactSearchAddPersonal" type="submit" value="Add Personal Contact" />
            <input id="form:contactSearchAddBusiness" name="form:contactSearchAddBusiness" type="submit" value="Add Business Contact" />
         </td>
      </tr>
   </tfoot>
   <tbody>
      <tr id="table-contactSearchCurrentFirstname-row">
         <th id="table-contactSearchCurrentFirstname-label-cell" class="table-label-column">Firstname:</th>
         <td id="table-contactSearchCurrentFirstname-cell" class="table-component-column"><input id="form:contactSearchCurrentFirstname" name="form:contactSearchCurrentFirstname" type="text" value="" /></td>
         <td class="table-required-column"><div></div></td>
      </tr><tr id="table-contactSearchCurrentSurname-row">
         <th id="table-contactSearchCurrentSurname-label-cell" class="table-label-column">Surname:</th>
         <td id="table-contactSearchCurrentSurname-cell" class="table-component-column"><input id="form:contactSearchCurrentSurname" name="form:contactSearchCurrentSurname" type="text" value="" /></td>
         <td class="table-required-column"><div></div></td>
      </tr><tr id="table-contactSearchCurrentType-row">
         <th id="table-contactSearchCurrentType-label-cell" class="table-label-column">Type:</th>
         <td id="table-contactSearchCurrentType-cell" class="table-component-column">
            <select id="form:contactSearchCurrentType" name="form:contactSearchCurrentType" size="1">
               <option value="" selected="selected"></option>
               <option value="PERSONAL">Personal</option>
               <option value="BUSINESS">Business</option>
            </select>
         </td>
         <td class="table-required-column"><div></div></td>
      </tr>
   </tbody>
</table>

Not very shocking, and that's kind of the point. This is familiar territory. The same approach your UI toolkit already uses. Oh, and it's fully configurable of course.

#3: Metawidget has pluggable layouts
The one part of your UI Metawidget does control is how the subcomponents inside each Metawidget are laid out. But this is completely pluggable. Metawidget comes with pre-built layouts for, say, arranging the widgets in a table (with one column for the label and another for the widget) or arranging the widgets horizontally all in a row (handy for button bars).

It also comes with a bunch of 'layout decorators', so you can decorate one layout with another. For example, you can decorate a HtmlTableLayout with a RichFaces TabPanelLayoutDecorator to put parts of your UI inside tabs:

And finally Metawidget makes it straightforward to plug in your own, custom Layouts as needed. See this section of the Reference Documentation.

2 comments:

liuk said...

Hi Richard,
good post. It really lets do understand what is Metawidget.
But I'd add some features:
1) components size should be settable with annotations
2) a "dummy" components to fill some column in layout should be provided always through annotation.

with this two features it's possible create classical application (with database too) using only pojo objects.

thank you for your aid
Luca

Richard said...

Luca,

Thanks for your comment! For:

1) I'd recommend using a custom annotation and Inspector (see here) and a custom WidgetProcessor (see here).

I'm unclear what you mean by 2) though. Could you possibly send me a sample application with your implementations of 1) and 2) to support@metawidget.org? Then I can see about incorporating your ideas into the next release.

Thanks,

Richard.