4

I have this JDialog with a JTabbedPane:

enter image description here

I just want to add a JLabel in the right-top area of the JTabbedPane so i can fire some events (e.g close the JDialog). And i don't want to make it a JFrame.

PS: I know that is may be duplicated, but none of the responses give me a solution.

imanis_tn
  • 1,150
  • 1
  • 12
  • 33
  • 1
    See also this possible duplicate: [JTabbedPane's tab area add a button, it can display, but it can't click](http://stackoverflow.com/questions/9937927/jtabbedpanes-tab-area-add-a-button-it-can-display-but-it-cant-click). – trashgod May 16 '12 at 15:55

2 Answers2

8

Well, actually that positioning inside JTabbedPane is not supported in Swing. But you can always make some street magic with layouts and layers:

public static void main ( String[] args )
{
    try
    {
        UIManager.setLookAndFeel ( UIManager.getSystemLookAndFeelClassName () );
    }
    catch ( Throwable e )
    {
        e.printStackTrace ();
    }

    JFrame frame = new JFrame ();
    frame.setUndecorated ( true );

    frame.setLayout ( new LayoutManager ()
    {
        private List<Component> special = new ArrayList<Component> ();

        public void addLayoutComponent ( String name, Component comp )
        {
            if ( name != null )
            {
                special.add ( comp );
            }
        }

        public void removeLayoutComponent ( Component comp )
        {
            special.remove ( comp );
        }

        public Dimension preferredLayoutSize ( Container parent )
        {
            Dimension ps = new Dimension ();
            for ( Component component : parent.getComponents () )
            {
                if ( !special.contains ( component ) )
                {
                    Dimension cps = component.getPreferredSize ();
                    ps.width = Math.max ( ps.width, cps.width );
                    ps.height = Math.max ( ps.height, cps.height );
                }
            }
            return ps;
        }

        public Dimension minimumLayoutSize ( Container parent )
        {
            return preferredLayoutSize ( parent );
        }

        public void layoutContainer ( Container parent )
        {
            Insets insets = parent.getInsets ();
            for ( Component component : parent.getComponents () )
            {
                if ( !special.contains ( component ) )
                {
                    component.setBounds ( insets.left, insets.top,
                            parent.getWidth () - insets.left - insets.right,
                            parent.getHeight () - insets.top - insets.bottom );
                }
                else
                {
                    Dimension ps = component.getPreferredSize ();
                    component.setBounds ( parent.getWidth () - insets.right - 2 - ps.width,
                            insets.top + 2, ps.width, ps.height );
                }
            }
        }
    } );

    final JTabbedPane tabbedPane = new JTabbedPane ();
    tabbedPane.addTab ( "Tab1", new JLabel () );
    tabbedPane.addTab ( "Tab2", new JLabel () );
    tabbedPane.addTab ( "Tab3", new JLabel () );
    frame.add ( tabbedPane );

    final JLabel label = new JLabel ( "Close X" );
    label.addMouseListener ( new MouseAdapter ()
    {
        public void mousePressed ( MouseEvent e )
        {
            System.exit ( 0 );
        }
    } );
    frame.add ( label, "special", 0 );

    frame.setSize ( 200, 150 );
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setVisible ( true );
}

This will work anywhere without any problems. Just be aware that label will be always in top right corner and will not position itself (unless you modify the code to support more "features") on other OS LaF's like Mac OS laf where tabs might be in the middle. Also it will be painted over tabs if they will cross with label.

So you will get something like this:

enter image description here

Anyway, you can easily modify applied to JLabel bounds inside the layout to make some side-spacing and such...

Mikle Garin
  • 10,083
  • 37
  • 59
  • nice code, [I'd be use GlassPane](http://stackoverflow.com/a/9734016/714968) rather ...., because in the case that there are bunch of Tabs you can by incident to click to close instead of Tab(s) hidden under JLabel, GlassPane to consume MouseEvent +1 – mKorbel May 16 '12 at 17:07
  • Well, he was going to put something like dialog close label there, so there is no point to pass events through. Plus GlassPane does consume events - its behavior depends only on how you will create it. You can always replace "contains" method with "false" return in any component to make it "transparent" to mouse events - there is no need to create workarounds with GlassPane. GlassPane is actually good to display some global things like tooltips/popups/drag animation and such stuff... – Mikle Garin May 16 '12 at 17:19
3

This can be done by writing your own TabbedPaneUI by extending BasicTabbedPaneUI or MetalTabbedPaneUI. But this would be difficult to write.

I belive that you want to implement something like close button (below will work with JLabel as well)

  • I would try to implement this button on GlassPane and Layers

or

  • (if your JDialog is not resizable) you can setLayout(null) and provide hardcoded values for position and size of the components and add JButton over JTabbedPane

or

  • you can override paintComponents on your content pane and draw your own button (or provided by "fake" JButton). Next - you have to setMouseListener on JTabbedPane and check if mouse click position is in button's bounds. Of course - you will need to implement button visual state (pressed, hover, etc.) or just pass event to faked JButton instance.
Xeon
  • 5,949
  • 5
  • 31
  • 52