Question

I seem to be having a problem with JTextPane. I have extended JTextPane to render a floating image because the JTextPane icon functionality does not suit my purpose. I want the user to be able to click on the image, and have certain events performed. However, when I click on the image, even when I use evt.consume(), the caret and selection are updated in the JTextPane. I would like clicks and mouse events in general that interact with the image to not affect the caret position or selection at all. Relevant code:

public class JTextPaneImg extends JTextPane {

    public JTextPaneImg(){
        super();
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                formMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                formMouseReleased(evt);
            }
        });
    }

    private void formMousePressed(java.awt.event.MouseEvent evt) {                                  
        if (imgBound.contains(evt.getPoint())) {
             evt.consume();
             //Do some stuff in here to interact with the image
             // but the event still undesirably interacts with selection/caret
        }
    }   


    private void formMouseReleased(java.awt.event.MouseEvent evt) {                                  
        if (imgBound.contains(evt.getPoint())) {
             evt.consume();
             //Do some stuff in here to interact with the image
             // but the event still undesirably interacts with selection/caret
        }
    }   
}

I have even called getMouseListeners and verified that my own mouse listener is the last in the array, I read that listeners are called from highest to lowest index, meaning if my listener calls consume, it should be the last to act on the event. Why is my mouse click event still updating the caret then? Is this a problem with the Look and Feel?

Was it helpful?

Solution

I read that listeners are called from highest to lowest index, meaning if my listener calls consume, it should be the last to act on the event

This order is not guaranteed. All that is guaranteed is that all listeners will be notified. I believe it is up to each listener to check if the event is consumed. At least that is my understanding

You might be able to use a Global Event Dispatcher to prevent the event from being dispatched to the component. Just remember that all events go through the dispatcher so the code should be efficient so you don't slow down the queue.

Or you can use the technique presented on Mouse Wheel Controller which removes all the listeners from the components and replaces it with your custom listener. You can then decide when to forward the events to the other listener.

Or maybe your image should be painted on the viewport, not the text pane.

OTHER TIPS

Resolved by creating my own caret. The issue was the DefaultCaret implementation does NOT check e.isConsumed on mouse events, so you must override and add the isconsumed check. My textpane behaves as intended now that I have the following code in the constructor:

    this.setCaret(new DefaultCaret() {

        @Override
        protected void positionCaret(MouseEvent e) {
            if (!e.isConsumed()) super.positionCaret(e);
        }

        @Override
        protected void moveCaret(MouseEvent e) {
            if (!e.isConsumed()) super.moveCaret(e);
        }
    });

This was my fault for not fully understanding the role of the Caret in the event model. But, if you ask me this is somewhat silly. From the documentation I have read, the convention is to have all listeners only act tangibly if !e.isConsumed().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top