Key event forwarding patch

Hi Beni!
I was building a tree within a CDockable, and noticed that key events would not be forwarded from the dockable to the components added to it. Instead, the dockable adds itself as a listener on those components.

In order to make F5 refresh the tree I had in the dockable when the key was pressed with focus on the dockable I wrote a patch to the KeyboardController class which will enable it forward every key event to the underlying components without entering in an infinite loop (since it also receives events dispatched to them bakc).

The patch is: add the following field to the class:

/** event being processed and dispatched to components of a dockable **/
private KeyEvent eventBeingProcessed = null;

Add the following method:


    /**
     * Redispatching the given event to the dockable components so they can also
     * do something with it.
     */
    public void redispatchEvent(DockElement element, KeyEvent event) {
        Component component = element.asDockable().getComponent();
        if(eventBeingProcessed == null && component != null
                && component instanceof Container) {
            eventBeingProcessed = event;
            for (Component childComponent : ((Container)component).getComponents()) {
                childComponent.dispatchEvent(event);
            }
        }
        eventBeingProcessed = null;
    }

and then to each of the fireKeyPressed, fireKeyReleased and fireKeyTyped method, add the method call within the if( representative != null ){ … } block. For example, this i the new fireKeyPressed:


    protected void fireKeyPressed( KeyEvent event ){
        if( !event.isConsumed() ){
            DockElementRepresentative representative = controller.searchElement( event.getComponent() );
            if( representative != null ){
                DockElement element = representative.getElement();
                
                List<KeyboardListener> list = keyListeners.affected( element );
                loop:for( KeyboardListener listener : list ){
                    if( listener.keyPressed( element, event )){
                        event.consume();
                        break loop;
                    }
                }
                
                **redispatchEvent(element, event);**
            }
            
        }
        
        for( KeyListener listener : globalListeners.toArray( new KeyListener[ globalListeners.size() ] )){
            listener.keyPressed( event );
        }
    }

This way all components added to the dockable’s content pane will also receive the key events that were fired when focus was on the dockable and not on it’s content, making much easier to implement key event handling there.

Hope this helps! Let me know what you think.

I’m working on a bug with the dividers. When I drag them and stop pressing the mouse button but keep the mouse moving fast, the divider won’t change the mouse pointer back to an arrow. It keeps it with the divider mouse pointer until I pass it over again slower.

Thanks,
Eduardo Born

Thanks. Sounds like a reasonable patch. Question: Is it correct that you use the secure-version? The one where the constructor of CControl gets a “secureEnvironment = true”? If I remember correct, in the unsecure version there is no KeyListener added to the Components.

The problem with the divider… I never found out what happens (or what is missing).

I’m not constructing the CControl with the secureEnvironment, no. But even though I get the dockables registered in the components as a key listener.

My patch needs a correction, it is causing the components in the dockable to receive the key events twice when the event was originated in the component and not in the dockable. The dockable is listening for those events and redispatch them again to the source component. I’ll post a final and correct version shortly.

Yeah, the divider thing is giving me a hard time, but I won’t give up on it. Will post the patch soon as well.

Hi Beni!
I fixed the mouse pointed on the divider thing. The problem was in the SplitDockStation class, where mouse events are handled for the divider. The mouseReleased method was checking if mouse was pressed, setting the flag to false and forwarding the event to mouseExit, but mouse exit would also check the flag and then not set the mouse pointer back to null. The fix was not to forward the event, but do what mouseExit does inside mouseReleased. Basically after the change mouseReleased looks like this:

@Override
        public void mouseReleased( MouseEvent e ) {
            if( pressed ){
                pressed = false;
                if( current != null ){
                    current.setDivider( divider );
                    repaint( bounds.x, bounds.y, bounds.width, bounds.height );
                    updateBounds();
                }
**                current = null;
                SplitDockStation.this.setCursor( null );**
            }
        }

The bold lines are the fix, replacing a call that forwarded the events to mouseExit.

Now it works perfectly.

Uh, nice. Thanks.

Sometimes the solution sits in front of your nose and you don’t see it…

[edit: … ah, stop. The mouse could still be over the divider, in this case the cursor should not be changed. But your patch still shows where to continue.]

While cleaning up 1.0.8p3 I read this post again. And as more as I think about it, as more I doubt this is correct: Input-Events travel upwards, from child to parent, if a child does not handle the event. But not the other way around. In this patch, if a DockTitle is focused, a KeyEvent would first travel upwards, then downwards in order to reach your listener. That is certainly no „natural“ way for a KeyEvent to behave.

After dismissing the patch :wink: I started thinking. There is already another solution for your problem, the „CKeyboardListener“ can be added to a CDockable and receives any event that is related to that dockable:

		dockable.addKeyboardListener( new CKeyboardListener() {
			@Override
			public boolean keyTyped( CDockable source, KeyEvent event ){
				System.out.println( "-> " + event );
				return false;
			}
			
			@Override
			public boolean keyReleased( CDockable source, KeyEvent event ){
				System.out.println( "-> " + event );
				return false;
			}
			
			@Override
			public boolean keyPressed( CDockable source, KeyEvent event ){
				System.out.println( "-> " + event );
				return false;
			}
		});```

(And for Core a KeyboardListener added to the KeyboardManager will do the same)