5

I have created a custom jTabbedPane class which extends BasicTabbedPaneUI and have successfully created my desired jTabbedPane but now the problem is that how can I set Hand cursor for each tab in my custom jTabbedPane?

I tried to set cursor with this

tabbedPane.setUI(new CustomMainMenuTabs());
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));

this sets the cursor for whole of jTabbedPane but I want to set the cursor when mouse hovers over any of tab in it only.

How can I set Hand cursor for tabs in my jTabbedPane?

My Code is

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.plaf.basic.BasicTabbedPaneUI;


public class HAAMS 
{
  //My Custom class for jTabbedPane
  public static class CustomMainMenuTabs extends BasicTabbedPaneUI
  {
    protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
    {
        Graphics2D g2 = (Graphics2D) g;

        Color color;

        if (isSelected) { color = new Color(74, 175, 211); } 
        else if (getRolloverTab() == tabIndex) {  color = new Color(45, 145, 180); } 
        else {color = new Color(68, 67, 67);}

        g2.setPaint(color);
        g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));

        g2.fill(new Rectangle2D.Double(x + 100,y,w,h));
    }
  }

   public static void main(String[] args) 
   {
     JFrame MainScreen = new JFrame("Custom JTabbedPane");
     MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);

     //Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
     JTabbedPane jtpane = new JTabbedPane(2);
     jtpane.setUI(new CustomMainMenuTabs());
     jtpane.add("1st Tabe", new JPanel());
     jtpane.add("2nd Tabe", new JPanel());
     jtpane.add("3rd Tabe", new JPanel());

     MainScreen.getContentPane().add(jtpane);
     MainScreen.setVisible(true);
  }
}

How to set cursor to HAND_CURSOR cursor when mouse hovers over any tab only not jpanel or any other component. It would be great if done without a mouse listener.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Airy
  • 5,484
  • 7
  • 53
  • 78
  • You need to provide mouse support in your UI. Simple add a mouse listener which sets the cursor to hand when the mouse enters the tab header component. – Sergiy Medvynskyy Sep 22 '14 at 15:19
  • @SergiyMedvynskyy what is the method to detect the tab header component of jtabbedpane so that I can add with mouse listener. – Airy Sep 22 '14 at 15:23
  • Why are you posting a bounty, you have been given an answer? The code works fine for me when you add the `MouseMotionListener` and use the `tabForCoordinate(...)` method. Or are you having a problem because my original answer suggested using a MouseListener? I would hope you would know the difference, but since you didn't post your `SSCCE` I'm not sure if that is your problem or not. – camickr Sep 25 '14 at 05:29
  • @camickr I posted all I got. I tried your suggestion as well but that didn't work for me. That's why I asked you if you could give me a code example so I can understand it. And also I was searching if it was possible doing without a mouse listener – Airy Sep 25 '14 at 05:56
  • @AbdulJabbarWebBestow, `I tried your suggestion as well but that didn't work for me.` and you have been asked twice to post the `SSCCE` that shows what you tried. Prove to us that you made an effort to listen to the suggestion. The basic code is simple. If the `tabForCoordinate()` method returns -1 you set the cursor to null, else you set the cursor to the hand cursor. I am away for a couple of days so I can't comment until I get back. I also have an idea for your other question, but since you haven't posted a compilable SSCCE I am not about to take time to get your code to compile. – camickr Sep 25 '14 at 13:48

7 Answers7

3

I see a lot of answers here that are WAY too complicated (custom UIs, extra listeners, Graphics stuff, etc.).

Basically, camickr spelled it out for you. Here's a simple demo:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class JTabbedPaneCursorDemo implements Runnable
{
  JTabbedPane tabbedPane;

  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new JTabbedPaneCursorDemo());
  }

  public void run()
  {
    JPanel panelA = new JPanel();
    JPanel panelB = new JPanel();

    tabbedPane = new JTabbedPane();
    tabbedPane.addTab("A", panelA);
    tabbedPane.addTab("B", panelB);

    tabbedPane.addMouseMotionListener(new MouseMotionListener()
    {
      public void mouseDragged(MouseEvent e) {}

      public void mouseMoved(MouseEvent e)
      {
        adjustCursor(e);
      }
    });

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 200);
    frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  private void adjustCursor(MouseEvent e)
  {
    TabbedPaneUI ui = tabbedPane.getUI();
    int index = ui.tabForCoordinate(tabbedPane, e.getX(), e.getY());

    if (index >= 0)
    {
      tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
    }
    else
    {
      tabbedPane.setCursor(null);
    }
  }
}
splungebob
  • 5,357
  • 2
  • 22
  • 45
  • +1, that would basically be my suggestion except I would use `JTabbedPane tabbedPane = (JTabbedPane)e.getSource();` in the listener so you don't need to use an instance variable. – camickr Oct 01 '14 at 22:19
2

I want to set the cursor when mouse moves over any of tab in it.

I would guess you need to add a MouseMotionListener to the tabbed pane. Then when the mouseMoved(...) event is generated you check if the mouse is over a tab.

You should be able to use the tabForCoordinate(...) method of the BasicTabbePaneUI to determine if the mouse is over a tab or not.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Sorry but how can I detect if mouse is on a tab using tabForCoordinate? Can you give a code example please? – Airy Sep 22 '14 at 15:23
  • @AbdulJabbarWebBestow, I don't understand the question? You use the mouse point from the MouseEvent and invoke the method. – camickr Sep 22 '14 at 16:05
  • Camickr you suggested that I should use tabForCoordinate(...) to determine if the mouse is over a tab or not, tabForCoordinate(...) gives the cordinates of the mouse so how can I detect then if mouse is on a tab? Please can you give some code example so I can understand what you mean? – Airy Sep 22 '14 at 16:12
  • 3
    `tabForCoordinate(...) gives the cordinates of the mouse` No it doesn't. It gives the tab. Did you read the API? Post your [SSCCE](http://sscce.org/) that shows how you attempted to use this method if you need more help. – camickr Sep 22 '14 at 18:33
  • I have updated my code as per your suggestion. Hope this fulfills SSCCE – Airy Sep 23 '14 at 15:58
  • @AbdulJabbarWebBestow, the code you posted has nothing to do with your question. Your question is about changing the cursor. It also has nothing to do with my suggestion. I suggested you use a MouseListener and the `tabForCoordinata(...)` method. I did not suggest a custom UI. Not only that the code doesn't even compile. The solution should be couple of lines of code. You invoke the `tabForCoordinate(...)`. When the tab is -1 you set the cursor to null, otherwise set it to the hand cursor. – camickr Sep 23 '14 at 18:35
  • 3
    @AbdulJabbarWebBestow, I have been away for 5 days and you still have not posted a proper SSCCE using the suggestions I have made. I guess you are not interested in an answer. Why do you expect all of us to post code for you when you are not even willing to post code for us. You are the one asking for help so you should make an effort. You get what you give and you have not given us anything. – camickr Sep 29 '14 at 15:28
  • +1. This is quite simple as spelled out by camickr's suggestions. I did post an answer separately that implements this because (1) it only took a couple of minutes (and I was bored), and (2) so that there's a working demo as a reference for future visitors. This should be the accepted answer. – splungebob Oct 01 '14 at 15:30
  • Also, I intentionally posted my solution after the bounty expired so as not to let anyone think I was stealing a bounty. – splungebob Oct 01 '14 at 15:37
0

Steps:

  • Create a MouseMotionListener and add it to your JTabbedPane
  • Inside the listener -> mouseMoved method, chec kif the current position of the mouse is inside the bounds of your tabs
    • If true, then change the cursor to a hand cursor
    • else show the default cursor

1.Method to check if the mouse is within the bounds of the tabs:

private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
        if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
            return i;
        }
    }
    return -1;
}

2.The mouse listener:

    MouseMotionListener listener = new MouseMotionAdapter() {
        public void mouseMoved(MouseEvent e) {
            JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
            if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
                tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
            } else {
                tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
            }
        }
    };

3.To add the listener to the JTabbedPane:

    jtpane.addMouseMotionListener(listener);

Related Documentation:

The final code:

Putting all the peices together, you get the following:

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

public class HAAMS {
    // My Custom class for jTabbedPane
    public static class CustomMainMenuTabs extends BasicTabbedPaneUI {
        protected void paintTabBackground(Graphics g, int tabPlacement,
                int tabIndex, int x, int y, int w, int h, boolean isSelected) {
            Graphics2D g2 = (Graphics2D) g;
            Color color;
            if (isSelected) {
                color = new Color(74, 175, 211);
            } else if (getRolloverTab() == tabIndex) {
                color = new Color(45, 145, 180);
            } else {
                color = new Color(68, 67, 67);
            }
            g2.setPaint(color);
            g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));
            g2.fill(new Rectangle2D.Double(x + 100, y, w, h));
        }
    }

    public static void main(String[] args) {
        JFrame MainScreen = new JFrame("Custom JTabbedPane");
        MainScreen.setExtendedState(MainScreen.getExtendedState()
                | JFrame.MAXIMIZED_BOTH);
        JTabbedPane jtpane = new JTabbedPane(2);
        jtpane.setUI(new CustomMainMenuTabs());
        MouseMotionListener listener = new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent e) {
                JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
                if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
                    tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
                } else {
                    tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
                }
            }
        };
        jtpane.add("1st Tabe", new JPanel());
        jtpane.add("2nd Tabe", new JPanel());
        jtpane.add("3rd Tabe", new JPanel());
        jtpane.addMouseMotionListener(listener);
        MainScreen.getContentPane().add(jtpane);
        MainScreen.setVisible(true);
    }

    private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
        for (int i = 0; i < tabbedPane.getTabCount(); i++) {
            if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
                return i;
            }
        }
        return -1;
    }
}
Infinite Recursion
  • 6,511
  • 28
  • 39
  • 51
  • I already this your suggested code but in this way the problem is cursor remains to HAND_CURSOR even if has moved from tab head to its panel. – Airy Sep 25 '14 at 12:23
  • You can try it as move your mouse on a tab and click it. then you can see that when you move your mouse inside tab's panel, it remain HAND_CURSOR. And that's what I asked in my last 2 lines in my question. – Airy Sep 25 '14 at 12:25
  • I want my cursor to change to HAND_CURSOR only if it is inside a tab head not it's panel or anyother component. – Airy Sep 25 '14 at 12:26
0

You can use:

public void setTabComponentAt(int index,
                     Component component)

And then you do

component.addMouseListener(yourListener)
  • As said to Infinite Recursion, this way it sets the cursor on whole jtabbedpane not only tab heads. – Airy Sep 26 '14 at 11:51
  • No, because you set a new component only for the tab. And you set the listener on the new component a label for example. – Alexander Campos Sep 26 '14 at 12:49
  • This won't work as expected because MouseEvents source will be the component which is the tab's renderer and there won't be mouse events transferred to the tabbed pane when mouse is over the tab. Consequently tabbed pane won't switch between tabs when mouse is clicked just because of this mouse listener overriding the default behavior. – dic19 Sep 26 '14 at 13:16
  • Well if that happens you can emulate the tab clicking in the label with the MouseListener (we assume that the component we put is a label) so when can achieve tab behavior. – Alexander Campos Sep 26 '14 at 13:58
  • No it isn't a label it is pure tab head with tab pane. – Airy Sep 27 '14 at 09:21
0

I have changed main menthod according to your need that Hand cursor will be visible only on tab header . check if it solve your problem

Working Code

public static void main(String[] args)
{
    JFrame MainScreen = new JFrame("Custom JTabbedPane");
    MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);

    MouseListener listener = new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            JTabbedPane jp=(JTabbedPane)(e.getComponent().getParent().getParent());
            jp.setSelectedIndex(jp.indexAtLocation(e.getComponent().getX(),e.getComponent().getY()));
       }

        @Override
        public void mouseEntered(MouseEvent e) {
            e.getComponent().setCursor(new Cursor((Cursor.HAND_CURSOR)));
        }


    };

    JLabel jlabel1=new JLabel("1st Tabe");
    jlabel1.addMouseListener(listener);
    JLabel jlabel2=new JLabel("2nd Tabe");
   jlabel2.addMouseListener(listener);
    JLabel jlabel3=new JLabel("3rd Tabe");
   jlabel3.addMouseListener(listener);

    //Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
    JTabbedPane jtpane = new JTabbedPane(2);

   jtpane.setUI(new CustomMainMenuTabs());
    jtpane.add("1st Tabe", new JPanel());
    jtpane.setTabComponentAt( 0, jlabel1);
    jtpane.add("2nd  Tabe", new JPanel());
    jtpane.setTabComponentAt(1, jlabel2);
    jtpane.add("3rd  Tabe", new JPanel());
    jtpane.setTabComponentAt( 2, jlabel3);




    MainScreen.getContentPane().add(jtpane);
    MainScreen.setVisible(true);
}
VaibJ
  • 84
  • 5
  • Nop that didn't help too – Airy Sep 27 '14 at 09:22
  • can you provide details what went wrong .. I will try to improve the code. – VaibJ Sep 27 '14 at 09:27
  • As said, I have created my own custom L&F class for TabbedPane and my jtabbedPane implements that class and my custon L&F class has a long code that works for jTabbedPane so i cannot do all of work for JLabels again. Please I want it to work on my tab header itself without using jlabels because this way I will have to do alot of work on l&F for jLabels as well. – Airy Sep 27 '14 at 09:42
  • I think you if you have control over tab ui , then on mouse motion listener, compare the color of pixel where mouse is moved and since you have color defined for tab header , compare color and change Cursor accordingly. – VaibJ Sep 27 '14 at 11:06
  • Well color comparing woudln't be good solution so I thought of comparing x and y coordinates of tabs but that didn't work as well. – Airy Sep 27 '14 at 13:58
0

Short

Just add this code to your CustomMainMenuTabs:

  public static class CustomMainMenuTabs extends BasicTabbedPaneUI
  {
    protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
    {
    // ...
    }
    private static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();
    private static final Cursor HAND_CURSOR = new Cursor((Cursor.HAND_CURSOR));
    protected void setRolloverTab(int index) {
        tabPane.setCursor((index != -1) ? HAND_CURSOR : DEFAULT_CURSOR);
        super.setRolloverTab(index);
    }
  }

Explanation

Since you're already extending BasicTabbedPaneUI you can simply extend the mechanics for painting the rollover tab, which is already implemented there without the need of using more listeners or calculating coordinates yourself.

The rolling over is a mechanic that has been present in the component since Java 5 and this is a proper extension, just need to override and extend the method. This method is called whenever the mouse moves in the tab component (it affects the tab area but does not affect the children) and and it's kept updated.

I've tried your code snippet with this addition and worked fine.

0

It's actually a lot easier than installing a custom UI delegate.

You can install your own labels as the tab components (the components inside the tab handles), which will have their own cursors. Following is a simple example with 3 tabs, and a different cursor for the body of the tabbed pane and each of the tabs:

import java.awt.*;
import javax.swing.*;

public class TestTabCursor extends JFrame {

    private JTabbedPane contentPane;

    public TestTabCursor() {
        super("Test tab cursor");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(640, 480);
        setLocation(100, 100);
        createContentPane();        
        setCursors();
    }

    private void createContentPane() {
        contentPane = new JTabbedPane();

        addTab(contentPane);
        addTab(contentPane);
        addTab(contentPane);

        setContentPane(contentPane);
    }

    private void addTab(JTabbedPane tabbedPane) {
        int index = tabbedPane.getTabCount() + 1;

        JLabel label = new JLabel("Panel #" + index);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setFont(label.getFont().deriveFont(72f));

        JPanel panel = new JPanel(new BorderLayout());
        panel.setBackground(Color.white);
        panel.add(label, BorderLayout.CENTER);

        JLabel title = new JLabel("Tab " + index);

        tabbedPane.add(panel);
        tabbedPane.setTabComponentAt(index - 1, title);
    }

    private void setCursors() {
        contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

        contentPane.getTabComponentAt(0).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        contentPane.getTabComponentAt(1).setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        contentPane.getTabComponentAt(2).setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));     
    }

    public static void main(String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new TestTabCursor();
                frame.setVisible(true);
            }
        });
    }
}
ethanfar
  • 3,733
  • 24
  • 43