5

I have a problem that when I add a mouse listener to a component that is used as a tab, I can't switch tabs.

This demonstrates the problem:

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

public class JTabBug {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JTabbedPane jTabbedPane = new JTabbedPane();
                jTabbedPane.addTab("Red", new JLabel("Roses"));
                jTabbedPane.addTab("Blue", new JLabel("Skies"));
                jTabbedPane.addTab("Green", new JLabel("Grass"));

                for (int i = 0; i < jTabbedPane.getTabCount(); i++) {
                    JLabel tabComponent = new JLabel(jTabbedPane.getTitleAt(i));

                    tabComponent.addMouseMotionListener(new MouseMotionAdapter() {
                        @Override
                        public void mouseDragged(MouseEvent e) {
                            System.out.println("dragging");
                        }
                    });
                    jTabbedPane.setTabComponentAt(i, tabComponent);
                }

                JFrame jFrame = new JFrame("Testing");
                jFrame.add(jTabbedPane);
                jFrame.setSize(400, 500);
                jFrame.setVisible(true);
                jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}

dragging is printed out as expected but you can't change tabs.

stacker
  • 68,052
  • 28
  • 140
  • 210
Boomah
  • 1,172
  • 13
  • 24

5 Answers5

1

Override the contains method of your tab component (a JLabel in your case) to return false.

        public boolean contains(int x, int y)
        {
            return false;
        }
Avrom
  • 4,967
  • 2
  • 28
  • 35
0

This seems to work: Notice that I'm getting the JLabel that was added, not creating a new one to add again.

import javax.swing.*;

import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

public class JTabBug {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JTabbedPane jTabbedPane = new JTabbedPane();
                jTabbedPane.addTab("Red", new JLabel("Roses"));
                jTabbedPane.addTab("Blue", new JLabel("Skies"));
                jTabbedPane.addTab("Green", new JLabel("Grass"));

                for (int i = 0; i < jTabbedPane.getTabCount(); i++) {
                  JLabel tabComponent = (JLabel)jTabbedPane.getComponent(i);

                    tabComponent.addMouseMotionListener(new MouseMotionAdapter() {
                        @Override
                        public void mouseDragged(MouseEvent e) {
                            System.out.println("dragging");
                        }
                    });
                }

                JFrame jFrame = new JFrame("Testing");
                jFrame.add(jTabbedPane);
                jFrame.setSize(400, 500);
                jFrame.setVisible(true);
                jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
Merky
  • 494
  • 2
  • 4
  • 1
    This adds the mouse motion listener to the label that was added (roses, skies, grass), and not to the tab itself, which is what the OP is trying to do. – Ben Taitelbaum Dec 03 '10 at 18:05
  • I have some code that does that somewhere... let me see if I can find it. – Merky Dec 03 '10 at 18:08
  • The code I have also has a bug in it just as you called out! I did notice that there is a small region where running your code, it does switch. Click on the far right lower side of a tab, it switches it for me. Not sure what the deal is what that... – Merky Dec 03 '10 at 18:27
  • See if this might fix your problem:http://stackoverflow.com/questions/962282/jtabbedpane-attach-mouse-listener-to-the-part-of-the-tab-select-ui-that-doesnt That might or might not be what you are looking for but it might give some useful info you can use... – Merky Dec 03 '10 at 18:33
0

I don't think this is a bug, as it's doing what I would expect. You're creating a new component for the tab (a JLabel), attaching a motion listener to it, and then setting that as the tab. You're not adding a mouse click listener to the label that would cause the tabs to change, so I wouldn't expect this to be there. The original tab component handles this mouse click event, so if you can get access to that component, try copying it if you can (or just get access to that component and add the mouse motion adapter). If this isn't possible, just handle the click event yourself.

Ben Taitelbaum
  • 7,343
  • 3
  • 25
  • 45
  • But if you create a new component (a JLabel) and don't attach a motion listener then the click events do change the tab. – jzd Dec 03 '10 at 18:18
  • yeah, you're right, that's definitely undocumented behavior (at least in the api). I'm guessing they decided that if you don't have any mouse listeners then you want the default behavior, and otherwise you're trying to override the default behavior so they should let it be. If I'm not mistaken, getting access to the tab component itself was added in 1.6, so this kink hasn't worked itself out fully yet. – Ben Taitelbaum Dec 03 '10 at 18:31
  • 1
    Ah right. For some reason I was stuck in thinking that the tab component itself was handling the click event. In this case, however, the label is handling the mouse event for mouse motion, not for clicking -- is it just "if a component handles any mouse event, don't pass any mouse events up the hierarchy"? With this in mind, the OP might be better off just adding a motion listener to the tabbed pane itself, and getting the selected tab component inside that listener. – Ben Taitelbaum Dec 03 '10 at 19:08
0

It looks like mouse events don't change the selection of the tab if the new tab component has another listener. Not sure why this is because the new label tab component works without the mouse motion listener. If you add another mouse listener to change the selection:

                final int index = i;
                tabComponent.addMouseListener(new MouseAdapter() {                      
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        jTabbedPane.setSelectedIndex(index);
                    }             

                });

You get the desired result, but it seems like this would be a strange workaround.

jzd
  • 23,473
  • 9
  • 54
  • 76
  • this will not work because JTabbedPane changes the indexes internally so that you always have a contiguous valid interval of indexes starting from 0 – Bax Nov 09 '12 at 12:29
  • you should use setSelectedComponent() instead – Bax Nov 09 '12 at 12:35
0

My solution is a bit more elaborated then jzd's, I didn't know that it could be done so cleanly. I like your solution it thought me something new. Thanks, jzd for that.



public class JTabBug
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                final JTabbedPane jTabbedPane = new JTabbedPane();
                jTabbedPane.addTab("Red", new JLabel("Roses"));
                jTabbedPane.addTab("Blue", new JLabel("Skies"));
                jTabbedPane.addTab("Green", new JLabel("Grass"));

                for(int i = 0; i < jTabbedPane.getTabCount(); i++)
                {
                    final JLabel tabComponent = new JLabel(jTabbedPane.getTitleAt(i));

                    tabComponent.addMouseMotionListener(new MouseMotionAdapter()
                    {
                        @Override
                        public void mouseDragged(MouseEvent e)
                        {
                            System.out.println("tabComponent dragging");
                        }
                    });
                    jTabbedPane.setTabPlacement(JTabbedPane.LEFT);

                    tabComponent.addMouseListener(new MouseAdapter()
                    {
                        @Override
                        public void mousePressed(MouseEvent e)
                        {
                            int x =  tabComponent.getLocationOnScreen().x - jTabbedPane.getLocationOnScreen().x;
                            int y =  tabComponent.getLocationOnScreen().y - jTabbedPane.getLocationOnScreen().y;
                            MouseEvent me = new MouseEvent(
                                    (JLabel)e.getSource(),
                                    e.getID(), e.getWhen(), e.getModifiers(), 
                                    x, y,
                                    e.getLocationOnScreen(). x, e.getLocationOnScreen().y, 
                                    e.getClickCount(), e.isPopupTrigger(),
                                    e.getButton());                             
                            jTabbedPane.getMouseListeners()[0].mousePressed(me);
                            System.out.println("tabComponent mousePressed e="+e);
                        }
                    });
                    jTabbedPane.setTabComponentAt(i, tabComponent);
                }
                JFrame jFrame = new JFrame("Testing");
                jFrame.add(jTabbedPane);
                jFrame.setSize(400, 500);
                jFrame.setVisible(true);
                jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}

Enjoy, Boro

Boro
  • 7,913
  • 4
  • 43
  • 85