Sunday, March 8, 2009

Validating And The Art Of GlassPane

The GlassPane in LWUIT is inspired by the Swing GlassPane & layered pane with quite a few twists (note: the picture on the right is of the Swing glass pane, I didn't have the energy to make one of my own)... We tried to imagine how Swing developers would have implemented the glass pane knowing what they do now about painters and Swings learning curve. But I'm getting ahead of myself, what is the glass pane?

A typical LWUIT application is essentially composed of 3 layers (this is a gross simplification though), the bg painters are responsible for drawing the background of all components including the main form. The component draws its own content which might overrule the painter and the glass pane paints last...

Essentially the glass pane is a painter that allows us to draw an overlay on top of the LWUIT application. Initially we didn't think we need a glass pane, we used to suggest that people should override the form's paint() method to reach the same result. Feel free to try and guess why this failed before reading the explanation in the next paragraph.


Overriding the paint method of a form worked initially, when you enter a form this behaves just as you would expect. However, when modifying an element within the form only that element gets repainted not the entire form! So if I had a form with a Button and text drawn on top using the Form's paint method it would get erased whenever the button got focus.

Thats good for the forms paint method, calling the forms paint method would be REALLY expensive for every little thing that occurs in LWUIT. However, we do want overlays for some things and we don't need to repaint every component in the screen to get them. The glass pane is called whenever a component gets painted, it only paints within the clipping region of the component hence it won't break the rest of the glass pane.

The painter chain is a tool that allows us to chain several painters together to perform different logistical tasks such as a validation painter coupled with a fade out painter. The sample bellow shows a crude validation panel that allows us to draw error icons next to components while exceeding their physical bounds as is common in many user interfaces

public class ValidationPane implements Painter {
private Vector components = new Vector();
private static Image error;
public ValidationPane(Form parentForm) {
try {
if(error == null) {
error = Image.createImage("/error.png");
}
} catch (IOException ex) {
ex.printStackTrace();
}
PainterChain.installGlassPane(parentForm, this);
}

public void paint(Graphics g, Rectangle rect) {
for(int iter = 0 ; iter < components.size() ; iter++) {
Component c = (Component) components.elementAt(iter);
if(c == null) {
components.removeElementAt(iter);
continue;
}
Object p = c.getClientProperty(VALIDATION_PROP);
int x = c.getAbsoluteX();
int y = c.getAbsoluteY();
x -= error.getWidth() / 2;
y += c.getHeight() - error.getHeight() / 2;
g.drawImage(error, x, y);
}
}

public void addInvalid(Component c) {
components.addElement(c);
}

public void removeInvalid(Component c) {
components.removeElement(c);
}
}

7 comments:

  1. Hi,
    I'm writing in for the 1st time, and I should tell you I'm really delighted by the features/extensibilty provided by LWUIT.

    Now, I have a question.
    I've been trying to put animation on the Glass Pane(using Painter).
    But, the Painter has no "animate " method.

    Moreover, I can't add my code as a component to the form(to use registerAnimated and animate methods), as it'll affect the layout manager.

    And, I can't use my own run() method as animate, coz I can't call repaint while extending the Painter interface.

    I'm just using the Painter's paint method, extending the Painter interface and using setGlassPane method.

    Is there a solution for it ??

    eMJay
    Sr. Software Engineer,
    J2ME

    ReplyDelete
  2. Hi,
    I provided a sample of a fading ScrollBar based on the glass pane as part of my Pimp My LWUIT series (one of the last parts).
    I use animation with glass panes in that case.

    ReplyDelete
  3. Hi Shai,
    I'm really surprised by your fast response. Really appreciate it!! And, Thanx for the info.
    I solved it by extending my class to Animation and registering it with the form.
    Now, there are 2 issues,
    1st there's a graphical glitch, trailing behind the animation on the glass pane. Doesn't the glass pane clear the previous clip region when its new clipping region is changed?
    2nd, when I open the menu, my animation (on the glass pane just disappears). Does opening the menu stops the glass-pane's paint method?
    In my code, when the animation is On, paint(g) is used(triggered by animate) and paint(g, rect) is simply returned. And, when the animation is Off, paint(g, rect) is used.(needless to say, deregisteranimation has already been called for this case, so no need to return paint(g))

    eMJay

    ReplyDelete
  4. Hi,
    you must not manipulate the clipping region for the glass pane and need to draw only within that area. Its somewhat complex to explain but since any component can trigger a repaint complexities will occur if you manipulate clipping.

    I didn't quite understand the second question.

    A menu is simply a dialog which is a separate form technically. So the glass pane won't show since its in a separate form. It will come back when the menu is disposed.

    ReplyDelete
  5. Hi,
    I'm not modifying the clip region, as the paint(g, rect) is called by LWUIT itself. So, he's the boss deciding the Clip region, and I'm not modifying it. But, yes I'm paiting at any (x,y) I want.
    Anyways, what worked for me for the graphical corruption, was calling repaint(x, y, dx, dy) for the form(for the previous clip region) when the glass pane paint/clip region is modified.
    I'm creating the application home-page with the icons animated (using glass-pane)when selected.
    Do you think its not much of an overhead ??
    I'll post the screen/videoshot soon.

    Can you tell me how would the application know that its in the paused state ??(apart from catching the RSK)
    I need to do some task(incl. painting) just before the Menu pop-up is shown and the app is paused. Also, can I custom-paint the Menu and re-define its behaviour?? I couldn't find in the API docs anything about the Menu.
    Also, can I setBgPainter for the Menu (and set it to the current form's glasspane's painter)

    eMJay

    ReplyDelete
  6. I don't quite understand you about the clipping region.

    To customize the menu look at the forum discussions around showMenuDialog.

    For pause look at the MIDlet pause callback.

    ReplyDelete
  7. Hi,

    I have created a Glass pane in my form and drawn a list over the glass pane in its paint (). But list is not being focused. when fire button is clicked its action performed () is not called. How can i make the list, function like normal list placed in form ???

    ReplyDelete