TabbedPane: support closable tabs (issue #40)

This commit is contained in:
Karl Tauber
2020-10-20 09:37:28 +02:00
parent 8ccda81d9a
commit 700bb9b567
15 changed files with 583 additions and 37 deletions

View File

@@ -16,6 +16,7 @@ FlatLaf Change Log
- TabbedPane: Support adding custom components to left and right sides of tabs - TabbedPane: Support adding custom components to left and right sides of tabs
area. (set client property `JTabbedPane.leadingComponent` or area. (set client property `JTabbedPane.leadingComponent` or
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (issue #40) `JTabbedPane.trailingComponent` to a `java.awt.Component`) (issue #40)
- TabbedPane: Support closable tabs. (issue #40)
- Support painting separator line between window title and content (use UI value - Support painting separator line between window title and content (use UI value
`TitlePane.borderColor`). (issue #184) `TitlePane.borderColor`). (issue #184)

View File

@@ -25,6 +25,8 @@ import javax.swing.JComponent;
*/ */
public interface FlatClientProperties public interface FlatClientProperties
{ {
//---- JButton ------------------------------------------------------------
/** /**
* Specifies type of a button. * Specifies type of a button.
* <p> * <p>
@@ -104,6 +106,8 @@ public interface FlatClientProperties
*/ */
String SQUARE_SIZE = "JButton.squareSize"; String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
/** /**
* Specifies minimum width of a component. * Specifies minimum width of a component.
* <p> * <p>
@@ -158,6 +162,8 @@ public interface FlatClientProperties
*/ */
String COMPONENT_ROUND_RECT = "JComponent.roundRect"; String COMPONENT_ROUND_RECT = "JComponent.roundRect";
//---- Popup --------------------------------------------------------------
/** /**
* Specifies whether a drop shadow is painted if the component is shown in a popup * Specifies whether a drop shadow is painted if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup. * or if the component is the owner of another component that is shown in a popup.
@@ -167,6 +173,8 @@ public interface FlatClientProperties
*/ */
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"; String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
//---- JProgressBar -------------------------------------------------------
/** /**
* Specifies whether the progress bar has always the larger height even if no string is painted. * Specifies whether the progress bar has always the larger height even if no string is painted.
* <p> * <p>
@@ -183,6 +191,8 @@ public interface FlatClientProperties
*/ */
String PROGRESS_BAR_SQUARE = "JProgressBar.square"; String PROGRESS_BAR_SQUARE = "JProgressBar.square";
//---- JRootPane ----------------------------------------------------------
/** /**
* Specifies whether the menu bar is embedded into the title pane if custom * Specifies whether the menu bar is embedded into the title pane if custom
* window decorations are enabled. Default is {@code true}. * window decorations are enabled. Default is {@code true}.
@@ -192,6 +202,8 @@ public interface FlatClientProperties
*/ */
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"; String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
//---- JScrollBar ---------------------------------------------------------
/** /**
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown. * Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
* <p> * <p>
@@ -208,6 +220,8 @@ public interface FlatClientProperties
*/ */
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling"; String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JTabbedPane --------------------------------------------------------
/** /**
* Specifies whether separators are shown between tabs. * Specifies whether separators are shown between tabs.
* <p> * <p>
@@ -240,6 +254,60 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"; String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
/**
* Specifies whether tabs are closable.
* If set to {@code true} on a tabbed pane component, all tabs in that tabbed pane are closable.
* To make individual tabs closable, set it to {@code true} on a tab content component.
* <p>
* Note that you have to specify a callback (see {@link #TABBED_PANE_TAB_CLOSABLE})
* that is invoked when the user clicks a tab close button.
* The callback is responsible for closing the tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @see #TABBED_PANE_TAB_CLOSE_CALLBACK
*/
String TABBED_PANE_TAB_CLOSABLE = "JTabbedPane.tabClosable";
/**
* Specifies the callback that is invoked when a tab close button is clicked.
* The callback is responsible for closing the tab.
* <p>
* Either use a {@link java.util.function.IntConsumer} that received the tab index as parameter:
* <pre>{@code
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
* (IntConsumer) tabIndex -> {
* // close tab here
* } );
* }</pre>
* Or use a {@link java.util.function.BiConsumer}&lt;javax.swing.JTabbedPane, Integer&gt;
* that received the tabbed pane and the tab index as parameters:
* <pre>{@code
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
* // close tab here
* } );
* }</pre>
* If you need to check whether a modifier key (e.g. Alt or Shift) was pressed
* while the user clicked the tab close button, use {@link java.awt.EventQueue#getCurrentEvent}
* to get current event, check whether it is a {@link java.awt.event.MouseEvent}
* and invoke its methods. E.g.
* <pre>{@code
* AWTEvent e = EventQueue.getCurrentEvent();
* boolean shift = (e instanceof MouseEvent) ? ((MouseEvent)e).isShiftDown() : false;
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.util.function.IntConsumer}
* or {@link java.util.function.BiConsumer}&lt;javax.swing.JTabbedPane, Integer&gt;
*
* @see #TABBED_PANE_TAB_CLOSABLE
*/
String TABBED_PANE_TAB_CLOSE_CALLBACK = "JTabbedPane.tabCloseCallback";
/** /**
* Specifies how to navigate to hidden tabs. * Specifies how to navigate to hidden tabs.
* <p> * <p>
@@ -280,6 +348,8 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent"; String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
//---- JTextField ---------------------------------------------------------
/** /**
* 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>
@@ -322,6 +392,8 @@ public interface FlatClientProperties
*/ */
String PLACEHOLDER_TEXT = "JTextField.placeholderText"; String PLACEHOLDER_TEXT = "JTextField.placeholderText";
//---- JToggleButton ------------------------------------------------------
/** /**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}. * Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p> * <p>
@@ -346,6 +418,8 @@ public interface FlatClientProperties
*/ */
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"; String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
//---- helper methods -----------------------------------------------------
/** /**
* Checks whether a client property of a component has the given value. * Checks whether a client property of a component has the given value.
*/ */

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "close" icon for closable tabs in {@link javax.swing.JTabbedPane}.
*
* @uiDefault TabbedPane.closeSize Dimension
* @uiDefault TabbedPane.closeArc int
* @uiDefault TabbedPane.closeCrossPlainSize float
* @uiDefault TabbedPane.closeCrossFilledSize float
* @uiDefault TabbedPane.closeCrossLineWidth float
* @uiDefault TabbedPane.closeBackground Color
* @uiDefault TabbedPane.closeForeground Color
* @uiDefault TabbedPane.closeHoverBackground Color
* @uiDefault TabbedPane.closeHoverForeground Color
* @uiDefault TabbedPane.closePressedBackground Color
* @uiDefault TabbedPane.closePressedForeground Color
*
* @author Karl Tauber
*/
public class FlatTabbedPaneCloseIcon
extends FlatAbstractIcon
{
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
public FlatTabbedPaneCloseIcon() {
super( 16, 16, null );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
// paint background
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
if( bg != null ) {
g.setColor( bg );
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
size.width, size.height, arc, arc );
}
// set cross color
g.setColor( FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground ) );
float mx = width / 2;
float my = height / 2;
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
// paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
g.draw( path );
}
}

View File

@@ -38,11 +38,14 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
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;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelEvent;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
@@ -51,6 +54,10 @@ import java.beans.PropertyChangeListener;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
@@ -119,6 +126,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.hasFullBorder boolean * @uiDefault TabbedPane.hasFullBorder boolean
* @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons * @uiDefault TabbedPane.hiddenTabsNavigation String moreTabsButton (default) or arrowButtons
* @uiDefault ScrollPane.smoothScrolling boolean * @uiDefault ScrollPane.smoothScrolling boolean
* @uiDefault TabbedPane.closeIcon Icon
* *
* @uiDefault TabbedPane.moreTabsButtonToolTipText String * @uiDefault TabbedPane.moreTabsButtonToolTipText String
* *
@@ -152,18 +160,22 @@ public class FlatTabbedPaneUI
protected boolean hasFullBorder; protected boolean hasFullBorder;
private String hiddenTabsNavigationStr; private String hiddenTabsNavigationStr;
protected Icon closeIcon;
protected String moreTabsButtonToolTipText; protected String moreTabsButtonToolTipText;
protected JViewport tabViewport; protected JViewport tabViewport;
protected FlatWheelTabScroller wheelTabScroller; protected FlatWheelTabScroller wheelTabScroller;
private JButton tabCloseButton;
private JButton moreTabsButton; private JButton moreTabsButton;
private Container leadingComponent; private Container leadingComponent;
private Container trailingComponent; private Container trailingComponent;
private Handler handler; private Handler handler;
private boolean blockRollover; private boolean blockRollover;
private boolean rolloverTabClose;
private boolean pressedTabClose;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatTabbedPaneUI(); return new FlatTabbedPaneUI();
@@ -210,6 +222,7 @@ public class FlatTabbedPaneUI
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" ); tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" ); hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" ); hiddenTabsNavigationStr = UIManager.getString( "TabbedPane.hiddenTabsNavigation" );
closeIcon = UIManager.getIcon( "TabbedPane.closeIcon" );
Locale l = tabPane.getLocale(); Locale l = tabPane.getLocale();
moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l ); moreTabsButtonToolTipText = UIManager.getString( "TabbedPane.moreTabsButtonToolTipText", l );
@@ -255,6 +268,8 @@ public class FlatTabbedPaneUI
tabSeparatorColor = null; tabSeparatorColor = null;
contentAreaColor = null; contentAreaColor = null;
closeIcon = null;
MigLayoutVisualPadding.uninstall( tabPane ); MigLayoutVisualPadding.uninstall( tabPane );
} }
@@ -262,6 +277,11 @@ public class FlatTabbedPaneUI
protected void installComponents() { protected void installComponents() {
super.installComponents(); super.installComponents();
// create tab close button
tabCloseButton = new TabCloseButton();
tabCloseButton.setVisible( false );
tabPane.add( tabCloseButton );
// find scrollable tab viewport // find scrollable tab viewport
tabViewport = null; tabViewport = null;
if( isScrollTabLayout() ) { if( isScrollTabLayout() ) {
@@ -288,6 +308,11 @@ public class FlatTabbedPaneUI
super.uninstallComponents(); super.uninstallComponents();
if( tabCloseButton != null ) {
tabPane.remove( tabCloseButton );
tabCloseButton = null;
}
tabViewport = null; tabViewport = null;
} }
@@ -354,9 +379,7 @@ public class FlatTabbedPaneUI
protected void installListeners() { protected void installListeners() {
super.installListeners(); super.installListeners();
tabPane.addMouseListener( getHandler() ); getHandler().installListeners();
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
@@ -373,9 +396,7 @@ public class FlatTabbedPaneUI
super.uninstallListeners(); super.uninstallListeners();
if( handler != null ) { if( handler != null ) {
tabPane.removeMouseListener( handler ); handler.uninstallListeners();
tabPane.removeMouseMotionListener( handler );
tabPane.removeComponentListener( handler );
handler = null; handler = null;
} }
@@ -399,6 +420,13 @@ public class FlatTabbedPaneUI
return new FlatWheelTabScroller(); return new FlatWheelTabScroller();
} }
@Override
protected MouseListener createMouseListener() {
Handler handler = getHandler();
handler.mouseDelegate = super.createMouseListener();
return handler;
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
Handler handler = getHandler(); Handler handler = getHandler();
@@ -454,6 +482,30 @@ public class FlatTabbedPaneUI
repaintTab( index ); repaintTab( index );
} }
protected boolean isRolloverTabClose() {
return rolloverTabClose;
}
protected void setRolloverTabClose( boolean rollover ) {
if( rolloverTabClose == rollover )
return;
rolloverTabClose = rollover;
repaintTab( getRolloverTab() );
}
protected boolean isPressedTabClose() {
return pressedTabClose;
}
protected void setPressedTabClose( boolean pressed ) {
if( pressedTabClose == pressed )
return;
pressedTabClose = pressed;
repaintTab( getRolloverTab() );
}
private void repaintTab( int tabIndex ) { private void repaintTab( int tabIndex ) {
if( tabIndex < 0 || tabIndex >= tabPane.getTabCount() ) if( tabIndex < 0 || tabIndex >= tabPane.getTabCount() )
return; return;
@@ -465,7 +517,10 @@ public class FlatTabbedPaneUI
@Override @Override
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) { protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */; int tabWidth = super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
if( isTabClosable( tabIndex ) )
tabWidth += closeIcon.getIconWidth();
return tabWidth;
} }
@Override @Override
@@ -528,6 +583,10 @@ public class FlatTabbedPaneUI
@Override @Override
protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) { protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isTabClosable( tabIndex ) ) {
int shift = closeIcon.getIconWidth() / 2;
return isLeftToRight() ? -shift : shift;
}
return 0; return 0;
} }
@@ -619,6 +678,10 @@ public class FlatTabbedPaneUI
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected ) int x, int y, int w, int h, boolean isSelected )
{ {
// paint tab close button
if( isTabClosable( tabIndex ) )
paintTabCloseButton( g, tabIndex, x, y, w, h );
// paint tab separators // paint tab separators
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) && if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
!isLastInRun( tabIndex ) ) !isLastInRun( tabIndex ) )
@@ -628,6 +691,18 @@ public class FlatTabbedPaneUI
paintTabSelection( g, tabPlacement, x, y, w, h ); paintTabSelection( g, tabPlacement, x, y, w, h );
} }
protected void paintTabCloseButton( Graphics g, int tabIndex, int x, int y, int w, int h ) {
// update state of tab close button
boolean rollover = (tabIndex == getRolloverTab());
ButtonModel bm = tabCloseButton.getModel();
bm.setRollover( rollover && isRolloverTabClose() );
bm.setPressed( rollover && isPressedTabClose() );
// paint tab close icon
Rectangle tabCloseRect = getTabCloseBounds( x, y, w, h, calcRect );
closeIcon.paintIcon( tabCloseButton, g, tabCloseRect.x, tabCloseRect.y );
}
protected void paintTabSeparator( Graphics g, int tabPlacement, int x, int y, int w, int h ) { protected void paintTabSeparator( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
float sepWidth = UIScale.scale( 1f ); float sepWidth = UIScale.scale( 1f );
float offset = tabSeparatorsFullHeight ? 0 : UIScale.scale( 5f ); float offset = tabSeparatorsFullHeight ? 0 : UIScale.scale( 5f );
@@ -636,7 +711,7 @@ public class FlatTabbedPaneUI
if( tabPlacement == LEFT || tabPlacement == RIGHT ) { if( tabPlacement == LEFT || tabPlacement == RIGHT ) {
// paint tab separator at bottom side // paint tab separator at bottom side
((Graphics2D)g).fill( new Rectangle2D.Float( x + offset, y + h - sepWidth, w - (offset * 2), sepWidth ) ); ((Graphics2D)g).fill( new Rectangle2D.Float( x + offset, y + h - sepWidth, w - (offset * 2), sepWidth ) );
} else if( tabPane.getComponentOrientation().isLeftToRight() ) { } else if( isLeftToRight() ) {
// paint tab separator at right side // paint tab separator at right side
((Graphics2D)g).fill( new Rectangle2D.Float( x + w - sepWidth, y + offset, sepWidth, h - (offset * 2) ) ); ((Graphics2D)g).fill( new Rectangle2D.Float( x + w - sepWidth, y + offset, sepWidth, h - (offset * 2) ) );
} else { } else {
@@ -819,6 +894,55 @@ public class FlatTabbedPaneUI
return super.getTabBounds( tabIndex, dest ); return super.getTabBounds( tabIndex, dest );
} }
protected Rectangle getTabCloseBounds( int x, int y, int w, int h, Rectangle dest ) {
dest.width = closeIcon.getIconWidth();
dest.height = closeIcon.getIconHeight();
int xoffset = (h - dest.width) / 2;
int yoffset = (h - dest.height) / 2;
dest.x = isLeftToRight() ? (x + w - xoffset - dest.width) : (x + xoffset);
dest.y = y + yoffset;
return dest;
}
protected Rectangle getTabCloseHitArea( int tabIndex ) {
Rectangle tabRect = getTabBounds( tabPane, tabIndex );
Rectangle tabCloseRect = getTabCloseBounds( tabRect.x, tabRect.y, tabRect.width, tabRect.height, calcRect );
return new Rectangle( tabCloseRect.x, tabRect.y, tabCloseRect.width, tabRect.height );
}
protected boolean isTabClosable( int tabIndex ) {
Object value = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSABLE );
return (value instanceof Boolean) ? (boolean) value : false;
}
@SuppressWarnings( { "unchecked" } )
protected void closeTab( int tabIndex ) {
Object callback = getTabClientProperty( tabIndex, TABBED_PANE_TAB_CLOSE_CALLBACK );
if( callback instanceof IntConsumer )
((IntConsumer)callback).accept( tabIndex );
else if( callback instanceof BiConsumer )
((BiConsumer<JTabbedPane, Integer>)callback).accept( tabPane, tabIndex );
else {
throw new RuntimeException( "Missing tab close callback. "
+ "Set client property 'JTabbedPane.tabCloseCallback' "
+ "to a 'java.util.function.IntConsumer' "
+ "or 'java.util.function.BiConsumer<JTabbedPane, Integer>'" );
}
}
protected Object getTabClientProperty( int tabIndex, String key ) {
if( tabIndex < 0 )
return null;
Component c = tabPane.getComponentAt( tabIndex );
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( key );
if( value != null )
return value;
}
return tabPane.getClientProperty( key );
}
protected void ensureCurrentLayout() { protected void ensureCurrentLayout() {
// since super.ensureCurrentLayout() is private, // since super.ensureCurrentLayout() is private,
// use super.getTabRunCount() as workaround // use super.getTabRunCount() as workaround
@@ -834,6 +958,10 @@ public class FlatTabbedPaneUI
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
} }
private boolean isLeftToRight() {
return tabPane.getComponentOrientation().isLeftToRight();
}
protected boolean isHorizontalTabPlacement() { protected boolean isHorizontalTabPlacement() {
int tabPlacement = tabPane.getTabPlacement(); int tabPlacement = tabPane.getTabPlacement();
return tabPlacement == TOP || tabPlacement == BOTTOM; return tabPlacement == TOP || tabPlacement == BOTTOM;
@@ -914,6 +1042,16 @@ public class FlatTabbedPaneUI
} }
} }
//---- class TabCloseButton -----------------------------------------------
private class TabCloseButton
extends JButton
implements UIResource
{
private TabCloseButton() {
}
}
//---- class ContainerUIResource ------------------------------------------ //---- class ContainerUIResource ------------------------------------------
private class ContainerUIResource private class ContainerUIResource
@@ -1011,9 +1149,8 @@ public class FlatTabbedPaneUI
int buttonWidth = getWidth(); int buttonWidth = getWidth();
int buttonHeight = getHeight(); int buttonHeight = getHeight();
Dimension popupSize = popupMenu.getPreferredSize(); Dimension popupSize = popupMenu.getPreferredSize();
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight();
int x = leftToRight ? buttonWidth - popupSize.width : 0; int x = isLeftToRight() ? buttonWidth - popupSize.width : 0;
int y = buttonHeight - popupSize.height; int y = buttonHeight - popupSize.height;
switch( tabPane.getTabPlacement() ) { switch( tabPane.getTabPlacement() ) {
default: default:
@@ -1185,8 +1322,7 @@ 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 ) {
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); x += isLeftToRight() ? amount : -amount;
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 += amount; y += amount;
@@ -1360,18 +1496,69 @@ public class FlatTabbedPaneUI
//---- class Handler ------------------------------------------------------ //---- class Handler ------------------------------------------------------
private class Handler private class Handler
extends MouseAdapter implements MouseListener, MouseMotionListener, PropertyChangeListener,
implements PropertyChangeListener, ChangeListener, ComponentListener ChangeListener, ComponentListener, ContainerListener
{ {
MouseListener mouseDelegate;
PropertyChangeListener propertyChangeDelegate; PropertyChangeListener propertyChangeDelegate;
ChangeListener changeDelegate; ChangeListener changeDelegate;
private int pressedTabIndex = -1;
void installListeners() {
tabPane.addMouseMotionListener( this );
tabPane.addComponentListener( this );
tabPane.addContainerListener( this );
for( Component c : tabPane.getComponents() ) {
if( !(c instanceof UIResource) )
c.addPropertyChangeListener( TABBED_PANE_TAB_CLOSABLE, this );
}
}
void uninstallListeners() {
tabPane.removeMouseMotionListener( this );
tabPane.removeComponentListener( this );
tabPane.removeContainerListener( this );
for( Component c : tabPane.getComponents() ) {
if( !(c instanceof UIResource) )
c.removePropertyChangeListener( TABBED_PANE_TAB_CLOSABLE, this );
}
}
//---- interface MouseListener ---- //---- interface MouseListener ----
@Override
public void mouseClicked( MouseEvent e ) {
mouseDelegate.mouseClicked( e );
}
@Override
public void mousePressed( MouseEvent e ) {
updateRollover( e, true );
if( !isPressedTabClose() )
mouseDelegate.mousePressed( e );
}
@Override
public void mouseReleased( MouseEvent e ) {
if( isPressedTabClose() ) {
updateRollover( e, false );
if( pressedTabIndex >= 0 && pressedTabIndex == getRolloverTab() )
closeTab( pressedTabIndex );
} else
mouseDelegate.mouseReleased( e );
pressedTabIndex = -1;
updateRollover( e, false );
}
@Override @Override
public void mouseEntered( MouseEvent e ) { public void mouseEntered( MouseEvent e ) {
// this is necessary for "more tabs" button // this is necessary for "more tabs" button
setRolloverTab( e.getX(), e.getY() ); updateRollover( e, false );
} }
@Override @Override
@@ -1379,15 +1566,37 @@ public class FlatTabbedPaneUI
// this event occurs also if mouse is moved to a custom tab component // this event occurs also if mouse is moved to a custom tab component
// that handles mouse events (e.g. a close button) // that handles mouse events (e.g. a close button)
// --> make sure that the tab stays highlighted // --> make sure that the tab stays highlighted
setRolloverTab( e.getX(), e.getY() ); updateRollover( e, false );
} }
//---- interface MouseMotionListener ---- //---- interface MouseMotionListener ----
@Override
public void mouseDragged( MouseEvent e ) {
updateRollover( e, false );
}
@Override @Override
public void mouseMoved( MouseEvent e ) { public void mouseMoved( MouseEvent e ) {
// this is necessary for "more tabs" button updateRollover( e, false );
setRolloverTab( e.getX(), e.getY() ); }
private void updateRollover( MouseEvent e, boolean pressed ) {
int x = e.getX();
int y = e.getY();
int tabIndex = tabForCoordinate( tabPane, x, y );
setRolloverTab( tabIndex );
// check whether mouse hit tab close area
boolean hitClose = isTabClosable( tabIndex )
? getTabCloseHitArea( tabIndex ).contains( x, y )
: false;
if( pressed )
pressedTabIndex = hitClose ? tabIndex : -1;
setRolloverTabClose( hitClose );
setPressedTabClose( hitClose && tabIndex == pressedTabIndex );
} }
//---- interface PropertyChangeListener ---- //---- interface PropertyChangeListener ----
@@ -1395,19 +1604,21 @@ public class FlatTabbedPaneUI
@Override @Override
public void propertyChange( PropertyChangeEvent e ) { public void propertyChange( PropertyChangeEvent e ) {
// invoke delegate listener // invoke delegate listener
switch( e.getPropertyName() ) { if( e.getSource() instanceof JTabbedPane ) {
case "tabPlacement": switch( e.getPropertyName() ) {
case "opaque": case "tabPlacement":
case "background": case "opaque":
case "indexForTabComponent": case "background":
runWithOriginalLayoutManager( () -> { case "indexForTabComponent":
propertyChangeDelegate.propertyChange( e ); runWithOriginalLayoutManager( () -> {
} ); propertyChangeDelegate.propertyChange( e );
break; } );
break;
default: default:
propertyChangeDelegate.propertyChange( e ); propertyChangeDelegate.propertyChange( e );
break; break;
}
} }
// handle event // handle event
@@ -1426,6 +1637,7 @@ public class FlatTabbedPaneUI
case TABBED_PANE_HAS_FULL_BORDER: case TABBED_PANE_HAS_FULL_BORDER:
case TABBED_PANE_TAB_HEIGHT: case TABBED_PANE_TAB_HEIGHT:
case TABBED_PANE_HIDDEN_TABS_NAVIGATION: case TABBED_PANE_HIDDEN_TABS_NAVIGATION:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate(); tabPane.revalidate();
tabPane.repaint(); tabPane.repaint();
break; break;
@@ -1470,6 +1682,22 @@ public class FlatTabbedPaneUI
@Override public void componentMoved( ComponentEvent e ) {} @Override public void componentMoved( ComponentEvent e ) {}
@Override public void componentShown( ComponentEvent e ) {} @Override public void componentShown( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {} @Override public void componentHidden( ComponentEvent e ) {}
//---- interface ContainerListener ----
@Override
public void componentAdded( ContainerEvent e ) {
Component c = e.getChild();
if( !(c instanceof UIResource) )
c.addPropertyChangeListener( TABBED_PANE_TAB_CLOSABLE, this );
}
@Override
public void componentRemoved( ContainerEvent e ) {
Component c = e.getChild();
if( !(c instanceof UIResource) )
c.removePropertyChangeListener( TABBED_PANE_TAB_CLOSABLE, this );
}
} }
//---- class FlatTabbedPaneLayout ----------------------------------------- //---- class FlatTabbedPaneLayout -----------------------------------------
@@ -1485,7 +1713,7 @@ public class FlatTabbedPaneUI
Insets insets = tabPane.getInsets(); Insets insets = tabPane.getInsets();
int tabPlacement = tabPane.getTabPlacement(); int tabPlacement = tabPane.getTabPlacement();
Insets tabAreaInsets = getTabAreaInsets( tabPlacement ); Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); boolean leftToRight = isLeftToRight();
// layout leading and trailing components in tab area // layout leading and trailing components in tab area
if( tabPlacement == TOP || tabPlacement == BOTTOM ) { if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
@@ -1556,7 +1784,7 @@ public class FlatTabbedPaneUI
* Layout manager used for scroll tab layout policy. * Layout manager used for scroll tab layout policy.
* <p> * <p>
* Although this class delegates all methods to the original layout manager * Although this class delegates all methods to the original layout manager
* {@link BasicTabbedPaneUI.TabbedPaneScrollLayout}, which extends * {@code BasicTabbedPaneUI.TabbedPaneScrollLayout}, which extends
* {@link BasicTabbedPaneUI.TabbedPaneLayout}, it is necessary that this class * {@link BasicTabbedPaneUI.TabbedPaneLayout}, it is necessary that this class
* also extends {@link TabbedPaneLayout} to avoid a {@code ClassCastException} * also extends {@link TabbedPaneLayout} to avoid a {@code ClassCastException}
* in {@link BasicTabbedPaneUI}.ensureCurrentLayout(). * in {@link BasicTabbedPaneUI}.ensureCurrentLayout().
@@ -1624,7 +1852,7 @@ public class FlatTabbedPaneUI
Dimension moreButtonSize = useMoreButton ? moreTabsButton.getPreferredSize() : null; Dimension moreButtonSize = useMoreButton ? moreTabsButton.getPreferredSize() : null;
Dimension backwardButtonSize = useMoreButton ? null : backwardButton.getPreferredSize(); Dimension backwardButtonSize = useMoreButton ? null : backwardButton.getPreferredSize();
Dimension forwardButtonSize = useMoreButton ? null : forwardButton.getPreferredSize(); Dimension forwardButtonSize = useMoreButton ? null : forwardButton.getPreferredSize();
boolean leftToRight = tabPane.getComponentOrientation().isLeftToRight(); boolean leftToRight = isLeftToRight();
boolean buttonsVisible = false; boolean buttonsVisible = false;
// TabbedPaneScrollLayout adds tabAreaInsets to tab coordinates, // TabbedPaneScrollLayout adds tabAreaInsets to tab coordinates,

View File

@@ -258,6 +258,13 @@ TabbedPane.hoverColor=#2e3133
TabbedPane.focusColor=#3d4b5c TabbedPane.focusColor=#3d4b5c
TabbedPane.contentAreaColor=#646464 TabbedPane.contentAreaColor=#646464
TabbedPane.closeBackground=null
TabbedPane.closeForeground=@disabledText
TabbedPane.closeHoverBackground=lighten($TabbedPane.hoverColor,10%)
TabbedPane.closeHoverForeground=@foreground
TabbedPane.closePressedBackground=lighten($TabbedPane.hoverColor,15%)
TabbedPane.closePressedForeground=$TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----

View File

@@ -552,6 +552,13 @@ TabbedPane.shadow=@background
TabbedPane.contentBorderInsets=null TabbedPane.contentBorderInsets=null
TabbedPane.hiddenTabsNavigation=moreTabsButton TabbedPane.hiddenTabsNavigation=moreTabsButton
TabbedPane.closeIcon=com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon
TabbedPane.closeSize=16,16
TabbedPane.closeArc=4
TabbedPane.closeCrossPlainSize={float}7.5
TabbedPane.closeCrossFilledSize=$TabbedPane.closeCrossPlainSize
TabbedPane.closeCrossLineWidth={float}1
#---- Table ---- #---- Table ----

View File

@@ -270,6 +270,13 @@ TabbedPane.hoverColor=#d9d9d9
TabbedPane.focusColor=#dae4ed TabbedPane.focusColor=#dae4ed
TabbedPane.contentAreaColor=#bfbfbf TabbedPane.contentAreaColor=#bfbfbf
TabbedPane.closeBackground=null
TabbedPane.closeForeground=@disabledText
TabbedPane.closeHoverBackground=darken($TabbedPane.hoverColor,10%)
TabbedPane.closeHoverForeground=@foreground
TabbedPane.closePressedBackground=darken($TabbedPane.hoverColor,15%)
TabbedPane.closePressedForeground=$TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----

View File

@@ -95,6 +95,19 @@ public class TriStateCheckBox
repaint(); repaint();
} }
public Boolean getValue() {
switch( state ) {
default:
case INDETERMINATE: return null;
case SELECTED: return true;
case UNSELECTED: return false;
}
}
public void setValue( Boolean value ) {
setState( value == null ? State.INDETERMINATE : (value ? State.SELECTED : State.UNSELECTED) );
}
public boolean isThirdStateEnabled() { public boolean isThirdStateEnabled() {
return thirdStateEnabled; return thirdStateEnabled;
} }

View File

@@ -905,6 +905,17 @@ SplitPaneUI com.formdev.flatlaf.ui.FlatSplitPaneUI
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.background #3c3f41 javax.swing.plaf.ColorUIResource [UI] TabbedPane.background #3c3f41 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeArc 4
TabbedPane.closeCrossFilledSize 7.5
TabbedPane.closeCrossLineWidth 1.0
TabbedPane.closeCrossPlainSize 7.5
TabbedPane.closeForeground #888888 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverBackground #464b4e javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverForeground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon [UI]
TabbedPane.closePressedBackground #52585b javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closePressedForeground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TabbedPane.contentAreaColor #646464 javax.swing.plaf.ColorUIResource [UI] TabbedPane.contentAreaColor #646464 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.contentOpaque true TabbedPane.contentOpaque true
TabbedPane.contentSeparatorHeight 1 TabbedPane.contentSeparatorHeight 1

View File

@@ -910,6 +910,17 @@ SplitPaneUI com.formdev.flatlaf.ui.FlatSplitPaneUI
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.background #f2f2f2 javax.swing.plaf.ColorUIResource [UI] TabbedPane.background #f2f2f2 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeArc 4
TabbedPane.closeCrossFilledSize 7.5
TabbedPane.closeCrossLineWidth 1.0
TabbedPane.closeCrossPlainSize 7.5
TabbedPane.closeForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverBackground #c0c0c0 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverForeground #000000 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon [UI]
TabbedPane.closePressedBackground #b3b3b3 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closePressedForeground #000000 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TabbedPane.contentAreaColor #bfbfbf javax.swing.plaf.ColorUIResource [UI] TabbedPane.contentAreaColor #bfbfbf javax.swing.plaf.ColorUIResource [UI]
TabbedPane.contentOpaque true TabbedPane.contentOpaque true
TabbedPane.contentSeparatorHeight 1 TabbedPane.contentSeparatorHeight 1

View File

@@ -898,6 +898,17 @@ SplitPaneUI com.formdev.flatlaf.ui.FlatSplitPaneUI
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.background #ccffcc javax.swing.plaf.ColorUIResource [UI] TabbedPane.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeArc 999
TabbedPane.closeCrossFilledSize 6.5
TabbedPane.closeCrossLineWidth 2.0
TabbedPane.closeCrossPlainSize 12.0
TabbedPane.closeForeground #ff0000 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverBackground #aa0000 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon [UI]
TabbedPane.closePressedBackground #00ff00 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closePressedForeground #000000 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.closeSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TabbedPane.contentAreaColor #bbbbbb javax.swing.plaf.ColorUIResource [UI] TabbedPane.contentAreaColor #bbbbbb javax.swing.plaf.ColorUIResource [UI]
TabbedPane.contentOpaque true TabbedPane.contentOpaque true
TabbedPane.contentSeparatorHeight 1 TabbedPane.contentSeparatorHeight 1

View File

@@ -18,9 +18,12 @@ package com.formdev.flatlaf.testing;
import static com.formdev.flatlaf.FlatClientProperties.*; import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.*; import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.function.BiConsumer;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.TriStateCheckBox;
import com.formdev.flatlaf.icons.FlatInternalFrameCloseIcon; import com.formdev.flatlaf.icons.FlatInternalFrameCloseIcon;
import com.formdev.flatlaf.util.ScaledImageIcon; import com.formdev.flatlaf.util.ScaledImageIcon;
import com.jgoodies.forms.layout.*; import com.jgoodies.forms.layout.*;
@@ -49,6 +52,9 @@ public class FlatContainerTest
addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 ); addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 );
initialTabCount = tabbedPane1.getTabCount(); initialTabCount = tabbedPane1.getTabCount();
tabsClosableCheckBox.setSelected( true );
tabsClosableChanged();
tabScrollCheckBox.setSelected( true ); tabScrollCheckBox.setSelected( true );
tabScrollChanged(); tabScrollChanged();
} }
@@ -236,10 +242,7 @@ public class FlatContainerTest
case "arrowButtons": value = TABBED_PANE_HIDDEN_TABS_NAVIGATION_ARROW_BUTTONS; break; case "arrowButtons": value = TABBED_PANE_HIDDEN_TABS_NAVIGATION_ARROW_BUTTONS; break;
} }
tabbedPane1.putClientProperty( TABBED_PANE_HIDDEN_TABS_NAVIGATION, value ); putTabbedPanesClientProperty( 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() { private void tabBackForegroundChanged() {
@@ -270,6 +273,32 @@ public class FlatContainerTest
} }
} }
private void tabsClosableChanged() {
boolean closable = tabsClosableCheckBox.isSelected();
putTabbedPanesClientProperty( TABBED_PANE_TAB_CLOSABLE, closable ? true : null );
if( closable ) {
putTabbedPanesClientProperty( TABBED_PANE_TAB_CLOSE_CALLBACK,
(BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
AWTEvent e = EventQueue.getCurrentEvent();
int modifiers = (e instanceof MouseEvent) ? ((MouseEvent)e).getModifiers() : 0;
JOptionPane.showMessageDialog( this, "Closed tab '" + tabbedPane.getTitleAt( tabIndex ) + "'."
+ "\n\n(modifiers: " + MouseEvent.getMouseModifiersText( modifiers ) + ")",
"Tab Closed", JOptionPane.PLAIN_MESSAGE );
} );
}
}
private void secondTabClosableChanged() {
Boolean value = secondTabClosableCheckBox.getValue();
JTabbedPane[] tabbedPanes = new JTabbedPane[] { tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 };
for( JTabbedPane tabbedPane : tabbedPanes ) {
Component c = tabbedPane.getComponentAt( 1 );
((JComponent)c).putClientProperty( TABBED_PANE_TAB_CLOSABLE, value );
}
}
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();
@@ -306,6 +335,8 @@ public class FlatContainerTest
tabBackForegroundCheckBox = new JCheckBox(); tabBackForegroundCheckBox = new JCheckBox();
leadingComponentCheckBox = new JCheckBox(); leadingComponentCheckBox = new JCheckBox();
trailingComponentCheckBox = new JCheckBox(); trailingComponentCheckBox = new JCheckBox();
tabsClosableCheckBox = new JCheckBox();
secondTabClosableCheckBox = new TriStateCheckBox();
CellConstraints cc = new CellConstraints(); CellConstraints cc = new CellConstraints();
//======== this ======== //======== this ========
@@ -518,6 +549,16 @@ public class FlatContainerTest
trailingComponentCheckBox.setText("Trailing"); trailingComponentCheckBox.setText("Trailing");
trailingComponentCheckBox.addActionListener(e -> trailingComponentChanged()); trailingComponentCheckBox.addActionListener(e -> trailingComponentChanged());
panel14.add(trailingComponentCheckBox, "cell 1 3"); panel14.add(trailingComponentCheckBox, "cell 1 3");
//---- tabsClosableCheckBox ----
tabsClosableCheckBox.setText("Tabs closable");
tabsClosableCheckBox.addActionListener(e -> tabsClosableChanged());
panel14.add(tabsClosableCheckBox, "cell 2 3");
//---- secondTabClosableCheckBox ----
secondTabClosableCheckBox.setText("Second Tab closable");
secondTabClosableCheckBox.addActionListener(e -> secondTabClosableChanged());
panel14.add(secondTabClosableCheckBox, "cell 3 3");
} }
panel9.add(panel14, cc.xywh(1, 11, 3, 1)); panel9.add(panel14, cc.xywh(1, 11, 3, 1));
} }
@@ -545,6 +586,8 @@ public class FlatContainerTest
private JCheckBox tabBackForegroundCheckBox; private JCheckBox tabBackForegroundCheckBox;
private JCheckBox leadingComponentCheckBox; private JCheckBox leadingComponentCheckBox;
private JCheckBox trailingComponentCheckBox; private JCheckBox trailingComponentCheckBox;
private JCheckBox tabsClosableCheckBox;
private TriStateCheckBox secondTabClosableCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
//---- class Tab1Panel ---------------------------------------------------- //---- class Tab1Panel ----------------------------------------------------

View File

@@ -327,6 +327,26 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3" "value": "cell 1 3"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabsClosableCheckBox"
"text": "Tabs closable"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabsClosableChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "com.formdev.flatlaf.extras.TriStateCheckBox" ) {
name: "secondTabClosableCheckBox"
"text": "Second Tab closable"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "secondTabClosableChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 3"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) { }, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 11 "gridY": 11
"gridWidth": 3 "gridWidth": 3

View File

@@ -284,6 +284,18 @@ TabbedPane.focusColor=#ddd
TabbedPane.tabSeparatorColor=#00f TabbedPane.tabSeparatorColor=#00f
TabbedPane.contentAreaColor=#bbb TabbedPane.contentAreaColor=#bbb
TabbedPane.closeSize=16,16
TabbedPane.closeArc=999
TabbedPane.closeCrossPlainSize={float}12
TabbedPane.closeCrossFilledSize={float}6.5
TabbedPane.closeCrossLineWidth={float}2
#TabbedPane.closeBackground=#faa
TabbedPane.closeForeground=#f00
TabbedPane.closeHoverBackground=#a00
TabbedPane.closeHoverForeground=#fff
TabbedPane.closePressedBackground=#0f0
TabbedPane.closePressedForeground=#000
#---- Table ---- #---- Table ----

View File

@@ -634,6 +634,17 @@ SplitPaneDivider.oneTouchHoverArrowColor
SplitPaneUI SplitPaneUI
TabbedPane.ancestorInputMap TabbedPane.ancestorInputMap
TabbedPane.background TabbedPane.background
TabbedPane.closeArc
TabbedPane.closeCrossFilledSize
TabbedPane.closeCrossLineWidth
TabbedPane.closeCrossPlainSize
TabbedPane.closeForeground
TabbedPane.closeHoverBackground
TabbedPane.closeHoverForeground
TabbedPane.closeIcon
TabbedPane.closePressedBackground
TabbedPane.closePressedForeground
TabbedPane.closeSize
TabbedPane.contentAreaColor TabbedPane.contentAreaColor
TabbedPane.contentOpaque TabbedPane.contentOpaque
TabbedPane.contentSeparatorHeight TabbedPane.contentSeparatorHeight