mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-09 16:25:10 +03:00
Merge pull request #190 into master
Tabbedpane "Show Hidden Tabs" button
This commit is contained in:
@@ -5,6 +5,11 @@ FlatLaf Change Log
|
|||||||
|
|
||||||
#### New features and improvements
|
#### New features and improvements
|
||||||
|
|
||||||
|
- TabbedPane: Replaced forward/backward scrolling arrow buttons with "Show
|
||||||
|
Hidden Tabs" button. If pressed, it shows a popup menu that contains (partly)
|
||||||
|
hidden tabs and selecting one activates that tab. If you prefer
|
||||||
|
forward/backward buttons, use `UIManager.put(
|
||||||
|
"TabbedPane.hiddenTabsNavigation", "arrowButtons" )`. (issue #40)
|
||||||
- TabbedPane: Support scrolling tabs with mouse wheel (if `tabLayoutPolicy` is
|
- TabbedPane: Support scrolling tabs with mouse wheel (if `tabLayoutPolicy` is
|
||||||
`SCROLL_TAB_LAYOUT`). (issue #40)
|
`SCROLL_TAB_LAYOUT`). (issue #40)
|
||||||
- TabbedPane: Repeat scrolling as long as arrow buttons are pressed. (issue #40)
|
- TabbedPane: Repeat scrolling as long as arrow buttons are pressed. (issue #40)
|
||||||
|
|||||||
@@ -240,6 +240,30 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies how to navigate to hidden tabs.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}
|
||||||
|
* <strong>Allowed Values</strong> {@link #TABBED_PANE_HIDDEN_TABS_NAVIGATION_MORE_TABS_BUTTON}
|
||||||
|
* or {@link #TABBED_PANE_HIDDEN_TABS_NAVIGATION_ARROW_BUTTONS}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_HIDDEN_TABS_NAVIGATION = "JTabbedPane.hiddenTabsNavigation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use "more tabs" button for navigation to hidden tabs.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_HIDDEN_TABS_NAVIGATION
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_HIDDEN_TABS_NAVIGATION_MORE_TABS_BUTTON = "moreTabsButton";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use forward/backward buttons for navigation to hidden tabs.
|
||||||
|
*
|
||||||
|
* @see #TABBED_PANE_HIDDEN_TABS_NAVIGATION
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_HIDDEN_TABS_NAVIGATION_ARROW_BUTTONS = "arrowButtons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether all text is selected when the text component gains focus.
|
* Specifies whether all text is selected when the text component gains focus.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -20,16 +20,23 @@ import static com.formdev.flatlaf.util.UIScale.scale;
|
|||||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
@@ -41,15 +48,22 @@ import java.awt.geom.Rectangle2D;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
@@ -57,6 +71,7 @@ import javax.swing.text.View;
|
|||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.util.Animator;
|
import com.formdev.flatlaf.util.Animator;
|
||||||
import com.formdev.flatlaf.util.CubicBezierEasing;
|
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||||
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,6 +115,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.showTabSeparators boolean
|
* @uiDefault TabbedPane.showTabSeparators boolean
|
||||||
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
||||||
* @uiDefault TabbedPane.hasFullBorder boolean
|
* @uiDefault TabbedPane.hasFullBorder boolean
|
||||||
|
* @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons
|
||||||
* @uiDefault ScrollPane.smoothScrolling boolean
|
* @uiDefault ScrollPane.smoothScrolling boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -107,6 +123,10 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatTabbedPaneUI
|
public class FlatTabbedPaneUI
|
||||||
extends BasicTabbedPaneUI
|
extends BasicTabbedPaneUI
|
||||||
{
|
{
|
||||||
|
// hidden tabs navigation types
|
||||||
|
protected static final int MORE_TABS_BUTTON = 0;
|
||||||
|
protected static final int ARROW_BUTTONS = 1;
|
||||||
|
|
||||||
private static Set<KeyStroke> focusForwardTraversalKeys;
|
private static Set<KeyStroke> focusForwardTraversalKeys;
|
||||||
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
||||||
|
|
||||||
@@ -126,11 +146,16 @@ public class FlatTabbedPaneUI
|
|||||||
protected boolean showTabSeparators;
|
protected boolean showTabSeparators;
|
||||||
protected boolean tabSeparatorsFullHeight;
|
protected boolean tabSeparatorsFullHeight;
|
||||||
protected boolean hasFullBorder;
|
protected boolean hasFullBorder;
|
||||||
protected boolean tabsOverlapBorder;
|
|
||||||
|
protected int hiddenTabsNavigation = MORE_TABS_BUTTON;
|
||||||
|
|
||||||
|
protected String moreTabsButtonToolTipText;
|
||||||
|
|
||||||
protected JViewport tabViewport;
|
protected JViewport tabViewport;
|
||||||
protected FlatWheelTabScroller wheelTabScroller;
|
protected FlatWheelTabScroller wheelTabScroller;
|
||||||
|
|
||||||
|
private JButton moreTabsButton;
|
||||||
|
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private boolean blockRollover;
|
private boolean blockRollover;
|
||||||
|
|
||||||
@@ -140,7 +165,27 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
if( UIManager.getBoolean( "TabbedPane.tabsOverlapBorder" ) ) {
|
||||||
|
// Force BasicTabbedPaneUI.tabsOverlapBorder to false,
|
||||||
|
// which is necessary for "more tabs" button to work correctly.
|
||||||
|
//
|
||||||
|
// If it would be true, class TabbedPaneScrollLayout would invoke TabbedPaneLayout.padSelectedTab(),
|
||||||
|
// which would modify rectangle of selected tab in a wrong way (for wrap tab layout policy).
|
||||||
|
// This would cause tab painting issues when scrolled and
|
||||||
|
// missing "more tabs" button if last tab is selected.
|
||||||
|
//
|
||||||
|
// All methods of BasicTabbedPaneUI that use tabsOverlapBorder (except
|
||||||
|
// the one method mentioned above) are overridden.
|
||||||
|
//
|
||||||
|
// This is normally not invoked because the default value for
|
||||||
|
// TabbedPane.tabsOverlapBorder is false in all FlatLaf themes.
|
||||||
|
// Anyway, 3rd party themes may have changed it.
|
||||||
|
// So make sure that it works anyway to avoid issues.
|
||||||
|
Object oldValue = UIManager.put( "TabbedPane.tabsOverlapBorder", false );
|
||||||
|
super.installDefaults();
|
||||||
|
UIManager.put( "TabbedPane.tabsOverlapBorder", oldValue );
|
||||||
|
} else
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
disabledForeground = UIManager.getColor( "TabbedPane.disabledForeground" );
|
disabledForeground = UIManager.getColor( "TabbedPane.disabledForeground" );
|
||||||
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
||||||
@@ -158,7 +203,9 @@ public class FlatTabbedPaneUI
|
|||||||
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
||||||
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||||
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
||||||
tabsOverlapBorder = UIManager.getBoolean( "TabbedPane.tabsOverlapBorder" );
|
|
||||||
|
Locale l = tabPane.getLocale();
|
||||||
|
moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l );
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
textIconGap = scale( textIconGap );
|
textIconGap = scale( textIconGap );
|
||||||
@@ -218,6 +265,56 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
installHiddenTabsNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallComponents() {
|
||||||
|
// uninstall hidden tabs navigation before invoking super.uninstallComponents() for
|
||||||
|
// correct uninstallation of BasicTabbedPaneUI tab scroller support
|
||||||
|
uninstallHiddenTabsNavigation();
|
||||||
|
|
||||||
|
super.uninstallComponents();
|
||||||
|
|
||||||
|
tabViewport = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installHiddenTabsNavigation() {
|
||||||
|
// initialize here because used in installHiddenTabsNavigation() before installDefaults() was invoked
|
||||||
|
String hiddenTabsNavigationStr = (String) tabPane.getClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION );
|
||||||
|
if( hiddenTabsNavigationStr == null )
|
||||||
|
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
|
||||||
|
hiddenTabsNavigation = parseHiddenTabsNavigation( hiddenTabsNavigationStr );
|
||||||
|
|
||||||
|
if( hiddenTabsNavigation != MORE_TABS_BUTTON ||
|
||||||
|
!isScrollTabLayout() ||
|
||||||
|
tabViewport == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// At this point, BasicTabbedPaneUI already has installed
|
||||||
|
// TabbedPaneScrollLayout (in super.createLayoutManager()) and
|
||||||
|
// ScrollableTabSupport, ScrollableTabViewport, ScrollableTabPanel, etc
|
||||||
|
// (in super.installComponents()).
|
||||||
|
|
||||||
|
// install own layout manager that delegates to original layout manager
|
||||||
|
tabPane.setLayout( createScrollLayoutManager( (TabbedPaneLayout) tabPane.getLayout() ) );
|
||||||
|
|
||||||
|
// create and add "more tabs" button
|
||||||
|
moreTabsButton = createMoreTabsButton();
|
||||||
|
tabPane.add( moreTabsButton );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void uninstallHiddenTabsNavigation() {
|
||||||
|
// restore layout manager before invoking super.uninstallComponents() for
|
||||||
|
// correct uninstallation of BasicTabbedPaneUI tab scroller support
|
||||||
|
if( tabPane.getLayout() instanceof FlatTabbedPaneScrollLayout )
|
||||||
|
tabPane.setLayout( ((FlatTabbedPaneScrollLayout)tabPane.getLayout()).delegate );
|
||||||
|
|
||||||
|
if( moreTabsButton != null ) {
|
||||||
|
tabPane.remove( moreTabsButton );
|
||||||
|
moreTabsButton = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -225,6 +322,8 @@ public class FlatTabbedPaneUI
|
|||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
tabPane.addMouseListener( getHandler() );
|
tabPane.addMouseListener( getHandler() );
|
||||||
|
tabPane.addMouseMotionListener( getHandler() );
|
||||||
|
tabPane.addComponentListener( getHandler() );
|
||||||
|
|
||||||
if( tabViewport != null && (wheelTabScroller = createWheelTabScroller()) != null ) {
|
if( tabViewport != null && (wheelTabScroller = createWheelTabScroller()) != null ) {
|
||||||
// ideally we would add the mouse listeners to the viewport, but then the
|
// ideally we would add the mouse listeners to the viewport, but then the
|
||||||
@@ -242,6 +341,8 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
if( handler != null ) {
|
if( handler != null ) {
|
||||||
tabPane.removeMouseListener( handler );
|
tabPane.removeMouseListener( handler );
|
||||||
|
tabPane.removeMouseMotionListener( handler );
|
||||||
|
tabPane.removeComponentListener( handler );
|
||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +373,21 @@ public class FlatTabbedPaneUI
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChangeListener createChangeListener() {
|
||||||
|
Handler handler = getHandler();
|
||||||
|
handler.changeDelegate = super.createChangeListener();
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LayoutManager createScrollLayoutManager( TabbedPaneLayout delegate ) {
|
||||||
|
return new FlatTabbedPaneScrollLayout( delegate );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JButton createMoreTabsButton() {
|
||||||
|
return new FlatMoreTabsButton();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JButton createScrollButton( int direction ) {
|
protected JButton createScrollButton( int direction ) {
|
||||||
return new FlatScrollableTabButton( direction );
|
return new FlatScrollableTabButton( direction );
|
||||||
@@ -317,6 +433,21 @@ public class FlatTabbedPaneUI
|
|||||||
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
|
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Insets getTabAreaInsets( int tabPlacement ) {
|
||||||
|
Insets currentTabAreaInsets = super.getTabAreaInsets( tabPlacement );
|
||||||
|
Insets insets = (Insets) currentTabAreaInsets.clone();
|
||||||
|
|
||||||
|
// This is a "trick" to get rid of the cropped edge:
|
||||||
|
// super.getTabAreaInsets() returns private field BasicTabbedPaneUI.currentTabAreaInsets,
|
||||||
|
// which is also used to translate the origin of the cropped edge in
|
||||||
|
// BasicTabbedPaneUI.CroppedEdge.paintComponent().
|
||||||
|
// Giving it large values clips painting of the cropped edge and makes it invisible.
|
||||||
|
currentTabAreaInsets.top = currentTabAreaInsets.left = -10000;
|
||||||
|
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content border insets are used to create a separator between tabs and content.
|
* The content border insets are used to create a separator between tabs and content.
|
||||||
* If client property JTabbedPane.hasFullBorder is true, then the content border insets
|
* If client property JTabbedPane.hasFullBorder is true, then the content border insets
|
||||||
@@ -353,6 +484,19 @@ public class FlatTabbedPaneUI
|
|||||||
super.update( g, c );
|
super.update( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
ensureCurrentLayout();
|
||||||
|
|
||||||
|
int tabPlacement = tabPane.getTabPlacement();
|
||||||
|
int selectedIndex = tabPane.getSelectedIndex();
|
||||||
|
|
||||||
|
paintContentBorder( g, tabPlacement, selectedIndex );
|
||||||
|
|
||||||
|
if( !isScrollTabLayout() )
|
||||||
|
paintTabArea( g, tabPlacement, selectedIndex );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintText( Graphics g, int tabPlacement, Font font, FontMetrics metrics,
|
protected void paintText( Graphics g, int tabPlacement, Font font, FontMetrics metrics,
|
||||||
int tabIndex, String title, Rectangle textRect, boolean isSelected )
|
int tabIndex, String title, Rectangle textRect, boolean isSelected )
|
||||||
@@ -366,6 +510,21 @@ public class FlatTabbedPaneUI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clip title if "more tabs" button is used
|
||||||
|
// (normally this is done by invoker, but fails in this case)
|
||||||
|
if( hiddenTabsNavigation == MORE_TABS_BUTTON &&
|
||||||
|
tabViewport != null &&
|
||||||
|
(tabPlacement == TOP || tabPlacement == BOTTOM) )
|
||||||
|
{
|
||||||
|
Rectangle viewRect = tabViewport.getViewRect();
|
||||||
|
viewRect.width -= 4; // subtract width of cropped edge
|
||||||
|
if( !viewRect.contains( textRect ) ) {
|
||||||
|
Rectangle r = viewRect.intersection( textRect );
|
||||||
|
if( r.x > viewRect.x )
|
||||||
|
title = JavaCompatibility.getClippedString( null, metrics, title, r.width );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// plain text
|
// plain text
|
||||||
Color color;
|
Color color;
|
||||||
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
|
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
|
||||||
@@ -493,13 +652,17 @@ public class FlatTabbedPaneUI
|
|||||||
/**
|
/**
|
||||||
* Actually does nearly the same as super.paintContentBorder() but
|
* Actually does nearly the same as super.paintContentBorder() but
|
||||||
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
||||||
|
* - tabsOverlapBorder is always true
|
||||||
|
* - paint full border (if enabled)
|
||||||
* - not invoking paintContentBorder*Edge() methods
|
* - not invoking paintContentBorder*Edge() methods
|
||||||
* - repaint selection
|
* - repaint selection
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
|
||||||
if( tabPane.getTabCount() <= 0 )
|
if( tabPane.getTabCount() <= 0 ||
|
||||||
return;
|
contentSeparatorHeight == 0 ||
|
||||||
|
!clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
|
return;
|
||||||
|
|
||||||
Insets insets = tabPane.getInsets();
|
Insets insets = tabPane.getInsets();
|
||||||
Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
|
Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
|
||||||
@@ -514,46 +677,40 @@ public class FlatTabbedPaneUI
|
|||||||
case TOP:
|
case TOP:
|
||||||
default:
|
default:
|
||||||
y += calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
|
y += calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
|
||||||
if( tabsOverlapBorder )
|
y -= tabAreaInsets.bottom;
|
||||||
y -= tabAreaInsets.bottom;
|
|
||||||
h -= (y - insets.top);
|
h -= (y - insets.top);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
h -= calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
|
h -= calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
|
||||||
if( tabsOverlapBorder )
|
h += tabAreaInsets.top;
|
||||||
h += tabAreaInsets.top;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LEFT:
|
case LEFT:
|
||||||
x += calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
|
x += calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
|
||||||
if( tabsOverlapBorder )
|
x -= tabAreaInsets.right;
|
||||||
x -= tabAreaInsets.right;
|
|
||||||
w -= (x - insets.left);
|
w -= (x - insets.left);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
w -= calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
|
w -= calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
|
||||||
if( tabsOverlapBorder )
|
w += tabAreaInsets.left;
|
||||||
w += tabAreaInsets.left;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( contentSeparatorHeight != 0 && clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) ) {
|
// compute insets for separator or full border
|
||||||
// compute insets for separator or full border
|
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
||||||
boolean hasFullBorder = clientPropertyBoolean( tabPane, TABBED_PANE_HAS_FULL_BORDER, this.hasFullBorder );
|
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
|
||||||
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
|
Insets ci = new Insets( 0, 0, 0, 0 );
|
||||||
Insets ci = new Insets( 0, 0, 0, 0 );
|
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
||||||
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
|
|
||||||
|
|
||||||
// paint content separator or full border
|
// paint content separator or full border
|
||||||
g.setColor( contentAreaColor );
|
g.setColor( contentAreaColor );
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( new Rectangle2D.Float( x, y, w, h ), false );
|
path.append( new Rectangle2D.Float( x, y, w, h ), false );
|
||||||
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
|
||||||
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
|
||||||
((Graphics2D)g).fill( path );
|
((Graphics2D)g).fill( path );
|
||||||
}
|
|
||||||
|
|
||||||
// repaint selection in scroll-tab-layout because it may be painted before
|
// repaint selection in scroll-tab-layout because it may be painted before
|
||||||
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
|
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
|
||||||
@@ -573,6 +730,43 @@ public class FlatTabbedPaneUI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int tabForCoordinate( JTabbedPane pane, int x, int y ) {
|
||||||
|
if( moreTabsButton != null ) {
|
||||||
|
// convert x,y from JTabbedPane coordinate space to ScrollableTabPanel coordinate space
|
||||||
|
Point viewPosition = tabViewport.getViewPosition();
|
||||||
|
x = x - tabViewport.getX() + viewPosition.x;
|
||||||
|
y = y - tabViewport.getY() + viewPosition.y;
|
||||||
|
|
||||||
|
// check whether point is within viewport
|
||||||
|
if( !tabViewport.getViewRect().contains( x, y ) )
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.tabForCoordinate( pane, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rectangle getTabBounds( int tabIndex, Rectangle dest ) {
|
||||||
|
if( moreTabsButton != null ) {
|
||||||
|
// copy tab bounds to dest
|
||||||
|
dest.setBounds( rects[tabIndex] );
|
||||||
|
|
||||||
|
// convert tab bounds to coordinate space of JTabbedPane
|
||||||
|
Point viewPosition = tabViewport.getViewPosition();
|
||||||
|
dest.x = dest.x + tabViewport.getX() - viewPosition.x;
|
||||||
|
dest.y = dest.y + tabViewport.getY() - viewPosition.y;
|
||||||
|
return dest;
|
||||||
|
} else
|
||||||
|
return super.getTabBounds( tabIndex, dest );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ensureCurrentLayout() {
|
||||||
|
// since super.ensureCurrentLayout() is private,
|
||||||
|
// use super.getTabRunCount() as workaround
|
||||||
|
super.getTabRunCount( tabPane );
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isLastInRun( int tabIndex ) {
|
private boolean isLastInRun( int tabIndex ) {
|
||||||
int run = getRunForTab( tabPane.getTabCount(), tabIndex );
|
int run = getRunForTab( tabPane.getTabCount(), tabIndex );
|
||||||
return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex;
|
return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex;
|
||||||
@@ -592,6 +786,179 @@ public class FlatTabbedPaneUI
|
|||||||
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static int parseHiddenTabsNavigation( String str ) {
|
||||||
|
if( str == null )
|
||||||
|
return MORE_TABS_BUTTON;
|
||||||
|
|
||||||
|
switch( str ) {
|
||||||
|
default:
|
||||||
|
case "moreTabsButton": return MORE_TABS_BUTTON;
|
||||||
|
case "arrowButtons": return ARROW_BUTTONS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runWithOriginalLayoutManager( Runnable runnable ) {
|
||||||
|
LayoutManager layout = tabPane.getLayout();
|
||||||
|
if( layout instanceof FlatTabbedPaneScrollLayout ) {
|
||||||
|
// temporary change layout manager because the runnable may use
|
||||||
|
// BasicTabbedPaneUI.scrollableTabLayoutEnabled()
|
||||||
|
tabPane.setLayout( ((FlatTabbedPaneScrollLayout)layout).delegate );
|
||||||
|
runnable.run();
|
||||||
|
tabPane.setLayout( layout );
|
||||||
|
} else
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ensureSelectedTabIsVisible() {
|
||||||
|
if( tabPane == null || tabViewport == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ensureCurrentLayout();
|
||||||
|
|
||||||
|
int selectedIndex = tabPane.getSelectedIndex();
|
||||||
|
if( selectedIndex < 0 || selectedIndex >= rects.length )
|
||||||
|
return;
|
||||||
|
|
||||||
|
((JComponent)tabViewport.getView()).scrollRectToVisible( (Rectangle) rects[selectedIndex].clone() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatMoreTabsButton -------------------------------------------
|
||||||
|
|
||||||
|
protected class FlatMoreTabsButton
|
||||||
|
extends FlatArrowButton
|
||||||
|
implements ActionListener, PopupMenuListener
|
||||||
|
{
|
||||||
|
private boolean popupVisible;
|
||||||
|
|
||||||
|
public FlatMoreTabsButton() {
|
||||||
|
// this method is invoked before installDefaults(), so we can not use color fields here
|
||||||
|
super( SOUTH, UIManager.getString( "Component.arrowType" ),
|
||||||
|
UIManager.getColor( "TabbedPane.foreground" ),
|
||||||
|
UIManager.getColor( "TabbedPane.disabledForeground" ), null,
|
||||||
|
UIManager.getColor( "TabbedPane.hoverColor" ) );
|
||||||
|
|
||||||
|
updateDirection();
|
||||||
|
setToolTipText( moreTabsButtonToolTipText );
|
||||||
|
addActionListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateDirection() {
|
||||||
|
int direction;
|
||||||
|
switch( tabPane.getTabPlacement() ) {
|
||||||
|
default:
|
||||||
|
case TOP: direction = SOUTH; break;
|
||||||
|
case BOTTOM: direction = NORTH; break;
|
||||||
|
case LEFT: direction = EAST; break;
|
||||||
|
case RIGHT: direction = WEST; break;
|
||||||
|
|
||||||
|
}
|
||||||
|
setDirection( direction );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
// paint arrow button near separator line
|
||||||
|
if( direction == EAST || direction == WEST ) {
|
||||||
|
int xoffset = (getWidth() / 2) - getHeight();
|
||||||
|
setXOffset( (direction == EAST) ? xoffset : -xoffset );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paint( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isHover() {
|
||||||
|
return super.isHover() || popupVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
if( tabViewport == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// detect (partly) hidden tabs and build popup menu
|
||||||
|
JPopupMenu popupMenu = new JPopupMenu();
|
||||||
|
popupMenu.addPopupMenuListener( this );
|
||||||
|
Rectangle viewRect = tabViewport.getViewRect();
|
||||||
|
int lastIndex = -1;
|
||||||
|
for( int i = 0; i < rects.length; i++ ) {
|
||||||
|
if( !viewRect.contains( rects[i] ) ) {
|
||||||
|
// add separator between leading and trailing tabs
|
||||||
|
if( lastIndex >= 0 && lastIndex + 1 != i )
|
||||||
|
popupMenu.addSeparator();
|
||||||
|
lastIndex = i;
|
||||||
|
|
||||||
|
// create menu item for tab
|
||||||
|
popupMenu.add( createMenuItem( i ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute popup menu location
|
||||||
|
int buttonWidth = getWidth();
|
||||||
|
int buttonHeight = getHeight();
|
||||||
|
Dimension popupSize = popupMenu.getPreferredSize();
|
||||||
|
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight();
|
||||||
|
|
||||||
|
int x = leftToRight ? buttonWidth - popupSize.width : 0;
|
||||||
|
int y = buttonHeight - popupSize.height;
|
||||||
|
switch( tabPane.getTabPlacement() ) {
|
||||||
|
default:
|
||||||
|
case TOP: y = buttonHeight; break;
|
||||||
|
case BOTTOM: y = -popupSize.height; break;
|
||||||
|
case LEFT: x = buttonWidth; break;
|
||||||
|
case RIGHT: x = -popupSize.width; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// show popup menu
|
||||||
|
popupMenu.show( this, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JMenuItem createMenuItem( int index ) {
|
||||||
|
JMenuItem menuItem = new JMenuItem( tabPane.getTitleAt( index ), tabPane.getIconAt( index ) );
|
||||||
|
menuItem.setDisabledIcon( tabPane.getDisabledIconAt( index ) );
|
||||||
|
menuItem.setToolTipText( tabPane.getToolTipTextAt( index ) );
|
||||||
|
|
||||||
|
Color foregroundAt = tabPane.getForegroundAt( index );
|
||||||
|
if( foregroundAt != tabPane.getForeground() )
|
||||||
|
menuItem.setForeground( foregroundAt );
|
||||||
|
|
||||||
|
Color backgroundAt = tabPane.getBackgroundAt( index );
|
||||||
|
if( backgroundAt != tabPane.getBackground() ) {
|
||||||
|
menuItem.setBackground( backgroundAt );
|
||||||
|
menuItem.setOpaque( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !tabPane.isEnabledAt( index ) )
|
||||||
|
menuItem.setEnabled( false );
|
||||||
|
|
||||||
|
menuItem.addActionListener( e -> selectTab( index ) );
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void selectTab( int index ) {
|
||||||
|
tabPane.setSelectedIndex( index );
|
||||||
|
ensureSelectedTabIsVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
|
||||||
|
popupVisible = true;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
||||||
|
popupVisible = false;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuCanceled( PopupMenuEvent e ) {
|
||||||
|
popupVisible = false;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatScrollableTabButton --------------------------------------
|
//---- class FlatScrollableTabButton --------------------------------------
|
||||||
|
|
||||||
protected class FlatScrollableTabButton
|
protected class FlatScrollableTabButton
|
||||||
@@ -612,11 +979,21 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
|
// Use half width/height if "more tabs" button is used, because size of
|
||||||
|
// "more tabs" button is the union of the backward and forward scroll buttons.
|
||||||
|
// With this "trick", viewport gets correct size.
|
||||||
|
boolean halfSize = (hiddenTabsNavigation == MORE_TABS_BUTTON);
|
||||||
|
|
||||||
Dimension size = super.getPreferredSize();
|
Dimension size = super.getPreferredSize();
|
||||||
if( direction == WEST || direction == EAST )
|
if( direction == WEST || direction == EAST ) {
|
||||||
return new Dimension( size.width, Math.max( size.height, maxTabHeight ) );
|
return new Dimension(
|
||||||
else
|
halfSize ? ((size.width / 2) + scale( 4 )) : size.width,
|
||||||
return new Dimension( Math.max( size.width, maxTabWidth ), size.height );
|
Math.max( size.height, maxTabHeight ) );
|
||||||
|
} else {
|
||||||
|
return new Dimension(
|
||||||
|
Math.max( size.width, maxTabWidth ),
|
||||||
|
halfSize ? ((size.height / 2) + scale( 4 )) : size.height );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -695,6 +1072,7 @@ public class FlatTabbedPaneUI
|
|||||||
lastMouseY = e.getY();
|
lastMouseY = e.getY();
|
||||||
|
|
||||||
double preciseWheelRotation = e.getPreciseWheelRotation();
|
double preciseWheelRotation = e.getPreciseWheelRotation();
|
||||||
|
int amount = (int) (maxTabHeight * preciseWheelRotation);
|
||||||
|
|
||||||
// compute new view position
|
// compute new view position
|
||||||
Point viewPosition = (targetViewPosition != null)
|
Point viewPosition = (targetViewPosition != null)
|
||||||
@@ -705,10 +1083,11 @@ public class FlatTabbedPaneUI
|
|||||||
int y = viewPosition.y;
|
int y = viewPosition.y;
|
||||||
int tabPlacement = tabPane.getTabPlacement();
|
int tabPlacement = tabPane.getTabPlacement();
|
||||||
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
|
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
|
||||||
x += maxTabHeight * preciseWheelRotation;
|
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight();
|
||||||
|
x += leftToRight ? amount : -amount;
|
||||||
x = Math.min( Math.max( x, 0 ), viewSize.width - tabViewport.getWidth() );
|
x = Math.min( Math.max( x, 0 ), viewSize.width - tabViewport.getWidth() );
|
||||||
} else {
|
} else {
|
||||||
y += maxTabHeight * preciseWheelRotation;
|
y += amount;
|
||||||
y = Math.min( Math.max( y, 0 ), viewSize.height - tabViewport.getHeight() );
|
y = Math.min( Math.max( y, 0 ), viewSize.height - tabViewport.getHeight() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,11 +1250,8 @@ public class FlatTabbedPaneUI
|
|||||||
return;
|
return;
|
||||||
scrolled = false;
|
scrolled = false;
|
||||||
|
|
||||||
int selectedIndex = tabPane.getSelectedIndex();
|
// scroll selected tab into visible area
|
||||||
if( selectedIndex >= 0 ) {
|
ensureSelectedTabIsVisible();
|
||||||
Rectangle tabBounds = getTabBounds( tabPane, selectedIndex );
|
|
||||||
tabViewport.scrollRectToVisible( tabBounds );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,9 +1259,18 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
private class Handler
|
private class Handler
|
||||||
extends MouseAdapter
|
extends MouseAdapter
|
||||||
implements PropertyChangeListener
|
implements PropertyChangeListener, ChangeListener, ComponentListener
|
||||||
{
|
{
|
||||||
PropertyChangeListener propertyChangeDelegate;
|
PropertyChangeListener propertyChangeDelegate;
|
||||||
|
ChangeListener changeDelegate;
|
||||||
|
|
||||||
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
// this is necessary for "more tabs" button
|
||||||
|
setRolloverTab( e.getX(), e.getY() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseExited( MouseEvent e ) {
|
public void mouseExited( MouseEvent e ) {
|
||||||
@@ -895,11 +1280,41 @@ public class FlatTabbedPaneUI
|
|||||||
setRolloverTab( e.getX(), e.getY() );
|
setRolloverTab( e.getX(), e.getY() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface MouseMotionListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseMoved( MouseEvent e ) {
|
||||||
|
// this is necessary for "more tabs" button
|
||||||
|
setRolloverTab( e.getX(), e.getY() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface PropertyChangeListener ----
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
propertyChangeDelegate.propertyChange( e );
|
// invoke delegate listener
|
||||||
|
|
||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
|
case "tabPlacement":
|
||||||
|
case "opaque":
|
||||||
|
case "background":
|
||||||
|
case "indexForTabComponent":
|
||||||
|
runWithOriginalLayoutManager( () -> {
|
||||||
|
propertyChangeDelegate.propertyChange( e );
|
||||||
|
} );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
propertyChangeDelegate.propertyChange( e );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle event
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "tabPlacement":
|
||||||
|
if( moreTabsButton instanceof FlatMoreTabsButton )
|
||||||
|
((FlatMoreTabsButton)moreTabsButton).updateDirection();
|
||||||
|
break;
|
||||||
|
|
||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
|
||||||
case TABBED_PANE_HAS_FULL_BORDER:
|
case TABBED_PANE_HAS_FULL_BORDER:
|
||||||
@@ -907,7 +1322,162 @@ public class FlatTabbedPaneUI
|
|||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
tabPane.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TABBED_PANE_HIDDEN_TABS_NAVIGATION:
|
||||||
|
uninstallHiddenTabsNavigation();
|
||||||
|
installHiddenTabsNavigation();
|
||||||
|
tabPane.repaint();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- interface ChangeListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
changeDelegate.stateChanged( e );
|
||||||
|
|
||||||
|
// scroll selected tab into visible area
|
||||||
|
if( moreTabsButton != null )
|
||||||
|
ensureSelectedTabIsVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ComponentListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
// make sure that selected tab stays visible when component size changed
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
ensureSelectedTabIsVisible();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void componentMoved( ComponentEvent e ) {}
|
||||||
|
@Override public void componentShown( ComponentEvent e ) {}
|
||||||
|
@Override public void componentHidden( ComponentEvent e ) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatTabbedPaneScrollLayout -----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout manager used if "TabbedPane.hiddenTabsNavigation" is "moreTabsButton".
|
||||||
|
* <p>
|
||||||
|
* Although this class delegates all methods to the original layout manager
|
||||||
|
* {@link BasicTabbedPaneUI.TabbedPaneScrollLayout}, which extends
|
||||||
|
* {@link BasicTabbedPaneUI.TabbedPaneLayout}, it is necessary that this class
|
||||||
|
* also extends {@link TabbedPaneLayout} to avoid a {@code ClassCastException}
|
||||||
|
* in {@link BasicTabbedPaneUI}.ensureCurrentLayout().
|
||||||
|
*/
|
||||||
|
protected class FlatTabbedPaneScrollLayout
|
||||||
|
extends TabbedPaneLayout
|
||||||
|
implements LayoutManager
|
||||||
|
{
|
||||||
|
private final TabbedPaneLayout delegate;
|
||||||
|
|
||||||
|
protected FlatTabbedPaneScrollLayout( TabbedPaneLayout delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void calculateLayoutInfo() {
|
||||||
|
delegate.calculateLayoutInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface LayoutManager ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLayoutComponent( String name, Component comp ) {
|
||||||
|
delegate.addLayoutComponent( name, comp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeLayoutComponent( Component comp ) {
|
||||||
|
delegate.removeLayoutComponent( comp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension preferredLayoutSize( Container parent ) {
|
||||||
|
return delegate.preferredLayoutSize( parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension minimumLayoutSize( Container parent ) {
|
||||||
|
return delegate.minimumLayoutSize( parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container parent ) {
|
||||||
|
// delegate to original layout manager and let it layout tabs and buttons
|
||||||
|
//
|
||||||
|
// runWithOriginalLayoutManager() is necessary for correct locations
|
||||||
|
// of tab components layed out in TabbedPaneLayout.layoutTabComponents()
|
||||||
|
runWithOriginalLayoutManager( () -> {
|
||||||
|
delegate.layoutContainer( parent );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// check whether scroll buttons are visible, which is changed by original
|
||||||
|
// layout manager depending on whether there is enough room for all tabs
|
||||||
|
boolean moreTabsButtonVisible = false;
|
||||||
|
Rectangle buttonsBounds = null;
|
||||||
|
for( Component c : tabPane.getComponents() ) {
|
||||||
|
if( c instanceof FlatScrollableTabButton && c.isVisible() ) {
|
||||||
|
moreTabsButtonVisible = true;
|
||||||
|
|
||||||
|
// compute union bounds of all scroll buttons
|
||||||
|
Rectangle r = c.getBounds();
|
||||||
|
buttonsBounds = (buttonsBounds != null) ? buttonsBounds.union( r ) : r;
|
||||||
|
|
||||||
|
// hide scroll button
|
||||||
|
c.setVisible( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixes for bugs in TabbedPaneScrollLayout
|
||||||
|
if( tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM ) {
|
||||||
|
Insets insets = tabPane.getInsets();
|
||||||
|
if( !tabPane.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// fixes for right-to-left, which is faulty in TabbedPaneScrollLayout.calculateTabRects()
|
||||||
|
|
||||||
|
// if tabbed pane width is smaller than total tabs width,
|
||||||
|
// the x locations are not zero based
|
||||||
|
int xLastTab = rects[rects.length - 1].x;
|
||||||
|
int offset = (xLastTab < 0) ? xLastTab : 0;
|
||||||
|
if( offset != 0 ) {
|
||||||
|
for( int i = 0; i < rects.length; i++ ) {
|
||||||
|
// fix x location in rects
|
||||||
|
rects[i].x -= offset;
|
||||||
|
|
||||||
|
// fix tab component location
|
||||||
|
Component c = tabPane.getTabComponentAt( i );
|
||||||
|
if( c != null )
|
||||||
|
c.setLocation( c.getX() - offset, c.getY() );
|
||||||
|
}
|
||||||
|
|
||||||
|
moreTabsButtonVisible = true;
|
||||||
|
|
||||||
|
Insets tabAreaInsets = getTabAreaInsets( tabPane.getTabPlacement() );
|
||||||
|
Rectangle bounds = tabViewport.getBounds();
|
||||||
|
|
||||||
|
// compute "more tabs" button bounds
|
||||||
|
int buttonWidth = moreTabsButton.getPreferredSize().width + UIScale.scale( 8 );
|
||||||
|
int buttonHeight = bounds.height - tabAreaInsets.top - tabAreaInsets.bottom;
|
||||||
|
buttonsBounds = new Rectangle( bounds.x, bounds.y + bounds.height - buttonHeight, buttonWidth, buttonHeight );
|
||||||
|
|
||||||
|
// make viewport smaller on left side so that there is room for the button
|
||||||
|
tabViewport.setBounds( bounds.x + buttonWidth, bounds.y, bounds.width - buttonWidth, bounds.height );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TabbedPaneScrollLayout.layoutContainer() uses insets.left to
|
||||||
|
// compute button x-location where it should use insets.right
|
||||||
|
if( buttonsBounds != null && insets.left != insets.right )
|
||||||
|
buttonsBounds.x = tabPane.getWidth() - insets.right - buttonsBounds.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// show/hide "more tabs" button and layout it
|
||||||
|
moreTabsButton.setVisible( moreTabsButtonVisible );
|
||||||
|
if( buttonsBounds != null )
|
||||||
|
moreTabsButton.setBounds( buttonsBounds );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.util;
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -27,7 +28,7 @@ import com.formdev.flatlaf.FlatLaf;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Java version compatibility methods.
|
* Provides Java version compatibility methods.
|
||||||
*
|
* <p>
|
||||||
* WARNING: This is private API and may change.
|
* WARNING: This is private API and may change.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -35,10 +36,12 @@ import com.formdev.flatlaf.FlatLaf;
|
|||||||
public class JavaCompatibility
|
public class JavaCompatibility
|
||||||
{
|
{
|
||||||
private static Method drawStringUnderlineCharAtMethod;
|
private static Method drawStringUnderlineCharAtMethod;
|
||||||
|
private static Method getClippedStringMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
||||||
* Graphics g, String text, int underlinedIndex, int x, int y )
|
* Graphics g, String text, int underlinedIndex, int x, int y )
|
||||||
|
* <br>
|
||||||
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c,
|
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c,
|
||||||
* Graphics2D g, String string, int underlinedIndex, float x, float y )
|
* Graphics2D g, String string, int underlinedIndex, float x, float y )
|
||||||
*/
|
*/
|
||||||
@@ -71,4 +74,37 @@ public class JavaCompatibility
|
|||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 8: sun.swing.SwingUtilities2.clipStringIfNecessary( JComponent c,
|
||||||
|
* FontMetrics fm, String string, int availTextWidth )
|
||||||
|
* <br>
|
||||||
|
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.getClippedString( JComponent c,
|
||||||
|
* FontMetrics fm, String string, int availTextWidth )
|
||||||
|
*/
|
||||||
|
public static String getClippedString( JComponent c, FontMetrics fm, String string, int availTextWidth ) {
|
||||||
|
synchronized( JavaCompatibility.class ) {
|
||||||
|
if( getClippedStringMethod == null ) {
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
||||||
|
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
||||||
|
: "sun.swing.SwingUtilities2" );
|
||||||
|
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
|
||||||
|
? "getClippedString"
|
||||||
|
: "clipStringIfNecessary",
|
||||||
|
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
|
||||||
|
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
||||||
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -546,10 +546,11 @@ TabbedPane.tabInsets=4,12,4,12
|
|||||||
TabbedPane.tabAreaInsets=0,0,0,0
|
TabbedPane.tabAreaInsets=0,0,0,0
|
||||||
TabbedPane.selectedTabPadInsets=0,0,0,0
|
TabbedPane.selectedTabPadInsets=0,0,0,0
|
||||||
TabbedPane.tabRunOverlay=0
|
TabbedPane.tabRunOverlay=0
|
||||||
TabbedPane.tabsOverlapBorder=true
|
TabbedPane.tabsOverlapBorder=false
|
||||||
TabbedPane.disabledForeground=@disabledText
|
TabbedPane.disabledForeground=@disabledText
|
||||||
TabbedPane.shadow=@background
|
TabbedPane.shadow=@background
|
||||||
TabbedPane.contentBorderInsets=null
|
TabbedPane.contentBorderInsets=null
|
||||||
|
TabbedPane.hiddenTabsNavigation=moreTabsButton
|
||||||
|
|
||||||
|
|
||||||
#---- Table ----
|
#---- Table ----
|
||||||
|
|||||||
@@ -46,3 +46,8 @@ FileChooser.refreshActionLabelText=Refresh
|
|||||||
FileChooser.newFolderActionLabelText=New Folder
|
FileChooser.newFolderActionLabelText=New Folder
|
||||||
FileChooser.listViewActionLabelText=List
|
FileChooser.listViewActionLabelText=List
|
||||||
FileChooser.detailsViewActionLabelText=Details
|
FileChooser.detailsViewActionLabelText=Details
|
||||||
|
|
||||||
|
|
||||||
|
#---- TabbedPane ----
|
||||||
|
|
||||||
|
TabbedPane.moreTabsButtonToolTipText=Show Hidden Tabs
|
||||||
|
|||||||
@@ -46,3 +46,8 @@ FileChooser.refreshActionLabelText=Aktualisieren
|
|||||||
FileChooser.newFolderActionLabelText=Neuer Ordner
|
FileChooser.newFolderActionLabelText=Neuer Ordner
|
||||||
FileChooser.listViewActionLabelText=Liste
|
FileChooser.listViewActionLabelText=Liste
|
||||||
FileChooser.detailsViewActionLabelText=Details
|
FileChooser.detailsViewActionLabelText=Details
|
||||||
|
|
||||||
|
|
||||||
|
#---- TabbedPane ----
|
||||||
|
|
||||||
|
TabbedPane.moreTabsButtonToolTipText=Verdeckte Tabs anzeigen
|
||||||
|
|||||||
@@ -916,6 +916,7 @@ TabbedPane.focusColor #3d4b5c javax.swing.plaf.ColorUIResource [UI]
|
|||||||
TabbedPane.font [active] $defaultFont [UI]
|
TabbedPane.font [active] $defaultFont [UI]
|
||||||
TabbedPane.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hasFullBorder false
|
TabbedPane.hasFullBorder false
|
||||||
|
TabbedPane.hiddenTabsNavigation moreTabsButton
|
||||||
TabbedPane.highlight #242424 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.highlight #242424 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hoverColor #2e3133 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.hoverColor #2e3133 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.labelShift 1
|
TabbedPane.labelShift 1
|
||||||
@@ -932,7 +933,7 @@ TabbedPane.tabRunOverlay 0
|
|||||||
TabbedPane.tabSelectionHeight 3
|
TabbedPane.tabSelectionHeight 3
|
||||||
TabbedPane.tabSeparatorsFullHeight false
|
TabbedPane.tabSeparatorsFullHeight false
|
||||||
TabbedPane.tabsOpaque true
|
TabbedPane.tabsOpaque true
|
||||||
TabbedPane.tabsOverlapBorder true
|
TabbedPane.tabsOverlapBorder false
|
||||||
TabbedPane.textIconGap 4
|
TabbedPane.textIconGap 4
|
||||||
TabbedPane.underlineColor #4a88c7 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.underlineColor #4a88c7 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
||||||
|
|||||||
@@ -921,6 +921,7 @@ TabbedPane.focusColor #dae4ed javax.swing.plaf.ColorUIResource [UI]
|
|||||||
TabbedPane.font [active] $defaultFont [UI]
|
TabbedPane.font [active] $defaultFont [UI]
|
||||||
TabbedPane.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hasFullBorder false
|
TabbedPane.hasFullBorder false
|
||||||
|
TabbedPane.hiddenTabsNavigation moreTabsButton
|
||||||
TabbedPane.highlight #ffffff javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.highlight #ffffff javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hoverColor #d9d9d9 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.hoverColor #d9d9d9 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.labelShift 1
|
TabbedPane.labelShift 1
|
||||||
@@ -937,7 +938,7 @@ TabbedPane.tabRunOverlay 0
|
|||||||
TabbedPane.tabSelectionHeight 3
|
TabbedPane.tabSelectionHeight 3
|
||||||
TabbedPane.tabSeparatorsFullHeight false
|
TabbedPane.tabSeparatorsFullHeight false
|
||||||
TabbedPane.tabsOpaque true
|
TabbedPane.tabsOpaque true
|
||||||
TabbedPane.tabsOverlapBorder true
|
TabbedPane.tabsOverlapBorder false
|
||||||
TabbedPane.textIconGap 4
|
TabbedPane.textIconGap 4
|
||||||
TabbedPane.underlineColor #4083c9 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.underlineColor #4083c9 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
||||||
|
|||||||
@@ -909,6 +909,7 @@ TabbedPane.focusColor #dddddd javax.swing.plaf.ColorUIResource [UI]
|
|||||||
TabbedPane.font [active] $defaultFont [UI]
|
TabbedPane.font [active] $defaultFont [UI]
|
||||||
TabbedPane.foreground #ff0000 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.foreground #ff0000 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hasFullBorder false
|
TabbedPane.hasFullBorder false
|
||||||
|
TabbedPane.hiddenTabsNavigation moreTabsButton
|
||||||
TabbedPane.highlight #ffffff javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.highlight #ffffff javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.hoverColor #eeeeee javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.hoverColor #eeeeee javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.labelShift 1
|
TabbedPane.labelShift 1
|
||||||
@@ -928,7 +929,7 @@ TabbedPane.tabSelectionHeight 3
|
|||||||
TabbedPane.tabSeparatorColor #0000ff javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.tabSeparatorColor #0000ff javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPane.tabSeparatorsFullHeight false
|
TabbedPane.tabSeparatorsFullHeight false
|
||||||
TabbedPane.tabsOpaque true
|
TabbedPane.tabsOpaque true
|
||||||
TabbedPane.tabsOverlapBorder true
|
TabbedPane.tabsOverlapBorder false
|
||||||
TabbedPane.textIconGap 4
|
TabbedPane.textIconGap 4
|
||||||
TabbedPane.underlineColor #4a88c7 javax.swing.plaf.ColorUIResource [UI]
|
TabbedPane.underlineColor #4a88c7 javax.swing.plaf.ColorUIResource [UI]
|
||||||
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
TabbedPaneUI com.formdev.flatlaf.ui.FlatTabbedPaneUI
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.testing;
|
package com.formdev.flatlaf.testing;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_CONTENT_SEPARATOR;
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.*;
|
import javax.swing.border.*;
|
||||||
@@ -40,6 +38,7 @@ public class FlatContainerTest
|
|||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
SwingUtilities.invokeLater( () -> {
|
SwingUtilities.invokeLater( () -> {
|
||||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatContainerTest" );
|
FlatTestFrame frame = FlatTestFrame.create( args, "FlatContainerTest" );
|
||||||
|
frame.useApplyComponentOrientation = true;
|
||||||
frame.showFrame( FlatContainerTest::new );
|
frame.showFrame( FlatContainerTest::new );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@@ -117,7 +116,7 @@ public class FlatContainerTest
|
|||||||
for( int i = oldTabCount + 1; i <= newTabCount; i++ )
|
for( int i = oldTabCount + 1; i <= newTabCount; i++ )
|
||||||
addTab( tabbedPane, "Tab " + i, "tab content " + i );
|
addTab( tabbedPane, "Tab " + i, "tab content " + i );
|
||||||
} else if( newTabCount < oldTabCount ) {
|
} else if( newTabCount < oldTabCount ) {
|
||||||
while( tabbedPane.getTabCount() > 4 )
|
while( tabbedPane.getTabCount() > newTabCount )
|
||||||
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 );
|
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +172,7 @@ public class FlatContainerTest
|
|||||||
|
|
||||||
private void customBorderChanged() {
|
private void customBorderChanged() {
|
||||||
Border border = customBorderCheckBox.isSelected()
|
Border border = customBorderCheckBox.isSelected()
|
||||||
? new LineBorder( Color.magenta, 20 )
|
? new MatteBorder( 10, 20, 25, 35, Color.green )
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
tabbedPane1.setBorder( border );
|
tabbedPane1.setBorder( border );
|
||||||
@@ -215,6 +214,40 @@ public class FlatContainerTest
|
|||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void tabPlacementChanged() {
|
||||||
|
int tabPlacement = -1;
|
||||||
|
switch( (String) tabPlacementField.getSelectedItem() ) {
|
||||||
|
case "top": tabPlacement = SwingConstants.TOP; break;
|
||||||
|
case "bottom": tabPlacement = SwingConstants.BOTTOM; break;
|
||||||
|
case "left": tabPlacement = SwingConstants.LEFT; break;
|
||||||
|
case "right": tabPlacement = SwingConstants.RIGHT; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabbedPane1.setTabPlacement( (tabPlacement >= 0) ? tabPlacement : SwingConstants.TOP );
|
||||||
|
tabbedPane2.setTabPlacement( (tabPlacement >= 0) ? tabPlacement : SwingConstants.BOTTOM );
|
||||||
|
tabbedPane3.setTabPlacement( (tabPlacement >= 0) ? tabPlacement : SwingConstants.LEFT );
|
||||||
|
tabbedPane4.setTabPlacement( (tabPlacement >= 0) ? tabPlacement : SwingConstants.RIGHT );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hiddenTabsNavigationChanged() {
|
||||||
|
String value = null;
|
||||||
|
switch( (String) hiddenTabsNavigationField.getSelectedItem() ) {
|
||||||
|
case "moreTabsButton": value = TABBED_PANE_HIDDEN_TABS_NAVIGATION_MORE_TABS_BUTTON; break;
|
||||||
|
case "arrowButtons": value = TABBED_PANE_HIDDEN_TABS_NAVIGATION_ARROW_BUTTONS; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabbedPane1.putClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION, value );
|
||||||
|
tabbedPane2.putClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION, value );
|
||||||
|
tabbedPane3.putClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION, value );
|
||||||
|
tabbedPane4.putClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tabBackForegroundChanged() {
|
||||||
|
boolean enabled = tabBackForegroundCheckBox.isSelected();
|
||||||
|
tabbedPane1.setBackgroundAt( 0, enabled ? Color.red : null );
|
||||||
|
tabbedPane1.setForegroundAt( 1, enabled ? Color.red : null );
|
||||||
|
}
|
||||||
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||||
JPanel panel9 = new JPanel();
|
JPanel panel9 = new JPanel();
|
||||||
@@ -244,6 +277,11 @@ public class FlatContainerTest
|
|||||||
customBorderCheckBox = new JCheckBox();
|
customBorderCheckBox = new JCheckBox();
|
||||||
customTabsCheckBox = new JCheckBox();
|
customTabsCheckBox = new JCheckBox();
|
||||||
hasFullBorderCheckBox = new JCheckBox();
|
hasFullBorderCheckBox = new JCheckBox();
|
||||||
|
JLabel tabPlacementLabel = new JLabel();
|
||||||
|
tabPlacementField = new JComboBox<>();
|
||||||
|
JLabel hiddenTabsNavigationLabel = new JLabel();
|
||||||
|
hiddenTabsNavigationField = new JComboBox<>();
|
||||||
|
tabBackForegroundCheckBox = new JCheckBox();
|
||||||
CellConstraints cc = new CellConstraints();
|
CellConstraints cc = new CellConstraints();
|
||||||
|
|
||||||
//======== this ========
|
//======== this ========
|
||||||
@@ -356,6 +394,7 @@ public class FlatContainerTest
|
|||||||
"[fill]",
|
"[fill]",
|
||||||
// rows
|
// rows
|
||||||
"[center]" +
|
"[center]" +
|
||||||
|
"[]" +
|
||||||
"[]"));
|
"[]"));
|
||||||
|
|
||||||
//---- moreTabsCheckBox ----
|
//---- moreTabsCheckBox ----
|
||||||
@@ -411,6 +450,39 @@ public class FlatContainerTest
|
|||||||
hasFullBorderCheckBox.setText("Show content border");
|
hasFullBorderCheckBox.setText("Show content border");
|
||||||
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
|
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
|
||||||
panel14.add(hasFullBorderCheckBox, "cell 4 1,alignx left,growx 0");
|
panel14.add(hasFullBorderCheckBox, "cell 4 1,alignx left,growx 0");
|
||||||
|
|
||||||
|
//---- tabPlacementLabel ----
|
||||||
|
tabPlacementLabel.setText("Tab placement:");
|
||||||
|
panel14.add(tabPlacementLabel, "cell 0 2");
|
||||||
|
|
||||||
|
//---- tabPlacementField ----
|
||||||
|
tabPlacementField.setModel(new DefaultComboBoxModel<>(new String[] {
|
||||||
|
"default",
|
||||||
|
"top",
|
||||||
|
"bottom",
|
||||||
|
"left",
|
||||||
|
"right"
|
||||||
|
}));
|
||||||
|
tabPlacementField.addActionListener(e -> tabPlacementChanged());
|
||||||
|
panel14.add(tabPlacementField, "cell 1 2");
|
||||||
|
|
||||||
|
//---- hiddenTabsNavigationLabel ----
|
||||||
|
hiddenTabsNavigationLabel.setText("Hidden tabs navigation:");
|
||||||
|
panel14.add(hiddenTabsNavigationLabel, "cell 2 2");
|
||||||
|
|
||||||
|
//---- hiddenTabsNavigationField ----
|
||||||
|
hiddenTabsNavigationField.setModel(new DefaultComboBoxModel<>(new String[] {
|
||||||
|
"default",
|
||||||
|
"moreTabsButton",
|
||||||
|
"arrowButtons"
|
||||||
|
}));
|
||||||
|
hiddenTabsNavigationField.addActionListener(e -> hiddenTabsNavigationChanged());
|
||||||
|
panel14.add(hiddenTabsNavigationField, "cell 3 2");
|
||||||
|
|
||||||
|
//---- tabBackForegroundCheckBox ----
|
||||||
|
tabBackForegroundCheckBox.setText("Tab back/foreground");
|
||||||
|
tabBackForegroundCheckBox.addActionListener(e -> tabBackForegroundChanged());
|
||||||
|
panel14.add(tabBackForegroundCheckBox, "cell 4 2");
|
||||||
}
|
}
|
||||||
panel9.add(panel14, cc.xywh(1, 11, 3, 1));
|
panel9.add(panel14, cc.xywh(1, 11, 3, 1));
|
||||||
}
|
}
|
||||||
@@ -433,6 +505,9 @@ public class FlatContainerTest
|
|||||||
private JCheckBox customBorderCheckBox;
|
private JCheckBox customBorderCheckBox;
|
||||||
private JCheckBox customTabsCheckBox;
|
private JCheckBox customTabsCheckBox;
|
||||||
private JCheckBox hasFullBorderCheckBox;
|
private JCheckBox hasFullBorderCheckBox;
|
||||||
|
private JComboBox<String> tabPlacementField;
|
||||||
|
private JComboBox<String> hiddenTabsNavigationField;
|
||||||
|
private JCheckBox tabBackForegroundCheckBox;
|
||||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||||
|
|
||||||
//---- class Tab1Panel ----------------------------------------------------
|
//---- class Tab1Panel ----------------------------------------------------
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ new FormModel {
|
|||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "insets 0,hidemode 3"
|
"$layoutConstraints": "insets 0,hidemode 3"
|
||||||
"$columnConstraints": "[][fill][][][fill]"
|
"$columnConstraints": "[][fill][][][fill]"
|
||||||
"$rowConstraints": "[center][]"
|
"$rowConstraints": "[center][][]"
|
||||||
} ) {
|
} ) {
|
||||||
name: "panel14"
|
name: "panel14"
|
||||||
"opaque": false
|
"opaque": false
|
||||||
@@ -251,6 +251,62 @@ new FormModel {
|
|||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 4 1,alignx left,growx 0"
|
"value": "cell 4 1,alignx left,growx 0"
|
||||||
} )
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "tabPlacementLabel"
|
||||||
|
"text": "Tab placement:"
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 0 2"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JComboBox" ) {
|
||||||
|
name: "tabPlacementField"
|
||||||
|
"model": new javax.swing.DefaultComboBoxModel {
|
||||||
|
selectedItem: "default"
|
||||||
|
addElement( "default" )
|
||||||
|
addElement( "top" )
|
||||||
|
addElement( "bottom" )
|
||||||
|
addElement( "left" )
|
||||||
|
addElement( "right" )
|
||||||
|
}
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
"JavaCodeGenerator.typeParameters": "String"
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 1 2"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||||
|
name: "hiddenTabsNavigationLabel"
|
||||||
|
"text": "Hidden tabs navigation:"
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 2 2"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JComboBox" ) {
|
||||||
|
name: "hiddenTabsNavigationField"
|
||||||
|
"model": new javax.swing.DefaultComboBoxModel {
|
||||||
|
selectedItem: "default"
|
||||||
|
addElement( "default" )
|
||||||
|
addElement( "moreTabsButton" )
|
||||||
|
addElement( "arrowButtons" )
|
||||||
|
}
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
"JavaCodeGenerator.typeParameters": "String"
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hiddenTabsNavigationChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 3 2"
|
||||||
|
} )
|
||||||
|
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||||
|
name: "tabBackForegroundCheckBox"
|
||||||
|
"text": "Tab back/foreground"
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.variableLocal": false
|
||||||
|
}
|
||||||
|
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabBackForegroundChanged", false ) )
|
||||||
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
|
"value": "cell 4 2"
|
||||||
|
} )
|
||||||
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
|
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
|
||||||
"gridY": 11
|
"gridY": 11
|
||||||
"gridWidth": 3
|
"gridWidth": 3
|
||||||
@@ -260,7 +316,7 @@ new FormModel {
|
|||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( null ) {
|
}, new FormLayoutConstraints( null ) {
|
||||||
"location": new java.awt.Point( 0, 0 )
|
"location": new java.awt.Point( 0, 0 )
|
||||||
"size": new java.awt.Dimension( 810, 515 )
|
"size": new java.awt.Dimension( 810, 570 )
|
||||||
} )
|
} )
|
||||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||||
"$layoutConstraints": "hidemode 3,align center center"
|
"$layoutConstraints": "hidemode 3,align center center"
|
||||||
|
|||||||
@@ -646,6 +646,7 @@ TabbedPane.focusInputMap
|
|||||||
TabbedPane.font
|
TabbedPane.font
|
||||||
TabbedPane.foreground
|
TabbedPane.foreground
|
||||||
TabbedPane.hasFullBorder
|
TabbedPane.hasFullBorder
|
||||||
|
TabbedPane.hiddenTabsNavigation
|
||||||
TabbedPane.highlight
|
TabbedPane.highlight
|
||||||
TabbedPane.hoverColor
|
TabbedPane.hoverColor
|
||||||
TabbedPane.labelShift
|
TabbedPane.labelShift
|
||||||
|
|||||||
Reference in New Issue
Block a user