Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TabbedPane closable tabs #193

Merged
merged 5 commits into from
Oct 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ FlatLaf Change Log
- TabbedPane: Support adding custom components to left and right sides of tabs
area. (set client property `JTabbedPane.leadingComponent` or
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (issue #40)
- TabbedPane: Support closable tabs. (issues #31 and #40)
- Support painting separator line between window title and content (use UI value
`TitlePane.borderColor`). (issue #184)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
*/
public interface FlatClientProperties
{
//---- JButton ------------------------------------------------------------

/**
* Specifies type of a button.
* <p>
Expand Down Expand Up @@ -104,6 +106,8 @@ public interface FlatClientProperties
*/
String SQUARE_SIZE = "JButton.squareSize";

//---- JComponent ---------------------------------------------------------

/**
* Specifies minimum width of a component.
* <p>
Expand Down Expand Up @@ -158,6 +162,8 @@ public interface FlatClientProperties
*/
String COMPONENT_ROUND_RECT = "JComponent.roundRect";

//---- 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.
Expand All @@ -167,6 +173,8 @@ public interface FlatClientProperties
*/
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";

//---- JProgressBar -------------------------------------------------------

/**
* Specifies whether the progress bar has always the larger height even if no string is painted.
* <p>
Expand All @@ -183,6 +191,8 @@ public interface FlatClientProperties
*/
String PROGRESS_BAR_SQUARE = "JProgressBar.square";

//---- JRootPane ----------------------------------------------------------

/**
* Specifies whether the menu bar is embedded into the title pane if custom
* window decorations are enabled. Default is {@code true}.
Expand All @@ -192,6 +202,8 @@ public interface FlatClientProperties
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";

//---- JScrollBar ---------------------------------------------------------

/**
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
* <p>
Expand All @@ -208,6 +220,8 @@ public interface FlatClientProperties
*/
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";

//---- JTabbedPane --------------------------------------------------------

/**
* Specifies whether separators are shown between tabs.
* <p>
Expand Down Expand Up @@ -240,6 +254,78 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";

/**
* Specifies the insets of a 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.awt.Insets}
*/
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";

/**
* 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 tooltip text used for tab close buttons.
* <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.String}
*/
String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JTabbedPane.tabCloseToolTipText";

/**
* 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.
* <p>
Expand Down Expand Up @@ -280,6 +366,8 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";

//---- JTextField ---------------------------------------------------------

/**
* Specifies whether all text is selected when the text component gains focus.
* <p>
Expand Down Expand Up @@ -322,6 +410,8 @@ public interface FlatClientProperties
*/
String PLACEHOLDER_TEXT = "JTextField.placeholderText";

//---- JToggleButton ------------------------------------------------------

/**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p>
Expand All @@ -346,6 +436,8 @@ public interface FlatClientProperties
*/
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";

//---- helper methods -----------------------------------------------------

/**
* Checks whether a client property of a component has the given value.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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 );
}
}
Loading