Skip to main content

JXLayer 3.0 - Event handling

Posted by alexfromsun on July 27, 2008 at 3:03 PM PDT

One of the biggest advantage of JXLayer 3.0 over the previous version is the ability to catch input events of its subcomponents. I started to think about this feature when I was asked if it was possible to implement the auto-scrolling feature with jxlayer. With JXLayer 2.0 you can easily paint a scrolling indicator on top of the wrapped component, but how to catch a mouse press event to activate the scrolling?

It is quite obvious that adding MouseListeners recursivly to the components hierarchy is the least desirable solution, first of all
it's not as easy, since I'll have to keep track of the hierarchy changes and remove/add listeners in case a componen is added or removed. Moreover, it can cause problems which I described in my very first entry.

I already knew how a container can catch repaintings of child components and needed a way to do the same for their input events.
AWTEventListener could certainly help, but it contradicts the "no change of global setting" requirement which I set for the JXLayer,
more than that you can't use AWTEventListener in a restricted environment like an unsigned applet.

I was thinking of some AWT methods which I can override in JXLayer to listen all events before they are sent to its subcomponents, I tried:

None of them catches events from the subcomponents. Actually, the only method that processes child components' events is dispatchEvent(AWTEvent), but it is final so I couldn't override it.

InputContext

From time to time I returned to this task but it didn't seem to be solvable without using global settings like AWTEventListener or custom EventQueue. It took a lot of time but I didn't give in, I kept returning to the AWT code and finally found the InputContext.dispatchEvent(AWTEvent) .

The javadoc for this method says:

Dispatches an event to the active input method. Called by AWT.

InputMethods are used to support complex text input, which makes it possible to type e.g. complex Chinese characters to the Swing components. Javadoc for the InputMethod also mentions the support phonetic text input for English. Anyway, I overrode Component.getInputContext() to make it return my custom InputContext and discovered that InputContext.dispatchEvent(AWTEvent) catches mouse, keyboard and focus events even for the child components!

It's time to discover how the InputContext processes events.
Let's take a look at Component.getInputContext() method:

    public InputContext getInputContext() {
        Container parent =
this.parent;
        if (parent == null) {
           
return null;
        }
else {
           
return parent.getInputContext();
        }
    }

As you can see this method only calls the implementation from the base class.

Can you guess what class has the actual inputContext? That's right, it is java.awt.Window:

    /**
     * Gets the input context for this window. A window always has an input context,
     * which is shared by subcomponents unless they create and set their own.
     *
@see Component#getInputContext
     *
@since 1.2
     */
    public InputContext getInputContext() {
       
if (inputContext == null) {
           
synchronized (inputContextLock) {
               
if (inputContext == null) {
                   
inputContext = InputContext.getInstance();
                }
            }
        }
       
return inputContext;
    }

JXLayer has its own InputContext is absolutely transparent for the default InputContext implementation. It's every method calls the super implementation and in addition to that, dispatchEvent() method notifies the LayerUI about the events from any of layer's descendants.

Сonclusion

The solution is found, but wait a bit, isn't it an undesirable hack which I described in my previous etries?
This usage of the InputContext is a little bit unusual, indeed.
To make sure that it is safe, I consulted with our internationalization team and they said - "it's ok".
I was very persistent but they couldn't offer me any scenario where this kind of transparent InputContext may harm. After that I realized that despite of the fact that I seem to be the first who uses custom InputContext for such purpose (wow!), I do nothing against its public specification.


Now you know how JXLayer 3.0 catches events from its subcomponents. Don't wait, grab JXLayer and write your own cool LayerUI extension!

See the MouseScrollableUI for details about event processing and Getting started for the custom painting examples.



Will meet on the JXLayer forum



Thanks

alexp

Related Topics >>

Comments

Hey Alex, Thanks for this wonderful tip.. I've been struggling with this exact problem and have been living with the ugly solution (for a graph editor) of making sure all the sub-components do not register themselves as mouse listeners and have their parent container handle all the hit tests and custom handling like e.g. dragging components around. I'm glad you found a solution and am going to try it out! :-)