Merge pull request #187 into master

TabbedPane wheel scrolling
This commit is contained in:
Karl Tauber
2020-10-14 22:43:02 +02:00
6 changed files with 623 additions and 102 deletions

View File

@@ -5,6 +5,9 @@ FlatLaf Change Log
#### New features and improvements #### New features and improvements
- TabbedPane: Support scrolling tabs with mouse wheel (if `tabLayoutPolicy` is
`SCROLL_TAB_LAYOUT`). (issue #40)
- TabbedPane: Repeat scrolling as long as arrow buttons are pressed. (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

@@ -92,6 +92,14 @@ public interface FlatSystemProperties
*/ */
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"; String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
/**
* Specifies whether animations are enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String ANIMATION = "flatlaf.animation";
/** /**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens. * Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p> * <p>

View File

@@ -20,17 +20,22 @@ 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.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.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
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.MouseEvent;
import java.awt.event.MouseListener;
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;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
@@ -40,13 +45,18 @@ import java.util.Set;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager; import javax.swing.UIManager;
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;
import javax.swing.text.View; 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.CubicBezierEasing;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -90,6 +100,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 ScrollPane.smoothScrolling boolean
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -117,6 +128,12 @@ public class FlatTabbedPaneUI
protected boolean hasFullBorder; protected boolean hasFullBorder;
protected boolean tabsOverlapBorder; protected boolean tabsOverlapBorder;
protected JViewport tabViewport;
protected FlatWheelTabScroller wheelTabScroller;
private Handler handler;
private boolean blockRollover;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatTabbedPaneUI(); return new FlatTabbedPaneUI();
} }
@@ -188,36 +205,87 @@ public class FlatTabbedPaneUI
} }
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected void installComponents() {
return new BasicTabbedPaneUI.PropertyChangeHandler() { super.installComponents();
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
switch( e.getPropertyName() ) { // find scrollable tab viewport
case TABBED_PANE_SHOW_TAB_SEPARATORS: tabViewport = null;
case TABBED_PANE_SHOW_CONTENT_SEPARATOR: if( isScrollTabLayout() ) {
case TABBED_PANE_HAS_FULL_BORDER: for( Component c : tabPane.getComponents() ) {
case TABBED_PANE_TAB_HEIGHT: if( c instanceof JViewport && c.getClass().getName().equals( "javax.swing.plaf.basic.BasicTabbedPaneUI$ScrollableTabViewport" ) ) {
tabPane.revalidate(); tabViewport = (JViewport) c;
tabPane.repaint(); break;
break;
} }
} }
}; }
}
@Override
protected void installListeners() {
super.installListeners();
tabPane.addMouseListener( getHandler() );
if( tabViewport != null && (wheelTabScroller = createWheelTabScroller()) != null ) {
// ideally we would add the mouse listeners to the viewport, but then the
// mouse listener of the tabbed pane would not receive events while
// the mouse pointer is over the viewport
tabPane.addMouseWheelListener( wheelTabScroller );
tabPane.addMouseMotionListener( wheelTabScroller );
tabPane.addMouseListener( wheelTabScroller );
}
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
if( handler != null ) {
tabPane.removeMouseListener( handler );
handler = null;
}
if( wheelTabScroller != null ) {
wheelTabScroller.uninstall();
tabPane.removeMouseWheelListener( wheelTabScroller );
tabPane.removeMouseMotionListener( wheelTabScroller );
tabPane.removeMouseListener( wheelTabScroller );
wheelTabScroller = null;
}
}
private Handler getHandler() {
if( handler == null )
handler = new Handler();
return handler;
}
protected FlatWheelTabScroller createWheelTabScroller() {
return new FlatWheelTabScroller();
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
Handler handler = getHandler();
handler.propertyChangeDelegate = super.createPropertyChangeListener();
return handler;
} }
@Override @Override
protected JButton createScrollButton( int direction ) { protected JButton createScrollButton( int direction ) {
// this method is invoked before installDefaults(), so we can not use color fields here return new FlatScrollableTabButton( direction );
return new FlatArrowButton( direction, UIManager.getString( "Component.arrowType" ), }
UIManager.getColor( "TabbedPane.foreground" ),
UIManager.getColor( "TabbedPane.disabledForeground" ), null, protected void setRolloverTab( int x, int y ) {
UIManager.getColor( "TabbedPane.hoverColor" ) ); setRolloverTab( tabForCoordinate( tabPane, x, y ) );
} }
@Override @Override
protected void setRolloverTab( int index ) { protected void setRolloverTab( int index ) {
if( blockRollover )
return;
int oldIndex = getRolloverTab(); int oldIndex = getRolloverTab();
super.setRolloverTab( index ); super.setRolloverTab( index );
@@ -489,17 +557,13 @@ public class FlatTabbedPaneUI
// 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)
if( isScrollTabLayout() && selectedIndex >= 0 ) { if( isScrollTabLayout() && selectedIndex >= 0 && tabViewport != null ) {
Component scrollableTabViewport = findComponentByClassName( tabPane, Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
BasicTabbedPaneUI.class.getName() + "$ScrollableTabViewport" );
if( scrollableTabViewport != null ) {
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
Shape oldClip = g.getClip(); Shape oldClip = g.getClip();
g.setClip( scrollableTabViewport.getBounds() ); g.setClip( tabViewport.getBounds() );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height ); paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip ); g.setClip( oldClip );
}
} }
} }
@@ -518,17 +582,332 @@ public class FlatTabbedPaneUI
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
} }
private Component findComponentByClassName( Container c, String className ) { protected boolean isSmoothScrollingEnabled() {
for( Component child : c.getComponents() ) { if( !Animator.useAnimation() )
if( className.equals( child.getClass().getName() ) ) return false;
return child;
if( child instanceof Container ) { // Note: Getting UI value "ScrollPane.smoothScrolling" here to allow
Component c2 = findComponentByClassName( (Container) child, className ); // applications to turn smooth scrolling on or off at any time
if( c2 != null ) // (e.g. in application options dialog).
return c2; return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
}
//---- class FlatScrollableTabButton --------------------------------------
protected class FlatScrollableTabButton
extends FlatArrowButton
implements MouseListener
{
private Timer autoRepeatTimer;
protected FlatScrollableTabButton( int direction ) {
// this method is invoked before installDefaults(), so we can not use color fields here
super( direction, UIManager.getString( "Component.arrowType" ),
UIManager.getColor( "TabbedPane.foreground" ),
UIManager.getColor( "TabbedPane.disabledForeground" ), null,
UIManager.getColor( "TabbedPane.hoverColor" ) );
addMouseListener( this );
}
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if( direction == WEST || direction == EAST )
return new Dimension( size.width, Math.max( size.height, maxTabHeight ) );
else
return new Dimension( Math.max( size.width, maxTabWidth ), size.height );
}
@Override
public void mousePressed( MouseEvent e ) {
if( SwingUtilities.isLeftMouseButton( e ) && isEnabled() ) {
if( autoRepeatTimer == null ) {
// using same delays as in BasicScrollBarUI and BasicSpinnerUI
autoRepeatTimer = new Timer( 60, e2 -> {
if( isEnabled() )
doClick();
} );
autoRepeatTimer.setInitialDelay( 300 );
}
autoRepeatTimer.start();
}
}
@Override
public void mouseReleased( MouseEvent e ) {
if( autoRepeatTimer != null )
autoRepeatTimer.stop();
}
@Override
public void mouseClicked( MouseEvent e ) {
}
@Override
public void mouseEntered( MouseEvent e ) {
if( autoRepeatTimer != null && isPressed() )
autoRepeatTimer.start();
}
@Override
public void mouseExited( MouseEvent e ) {
if( autoRepeatTimer != null )
autoRepeatTimer.stop();
}
}
//---- class FlatWheelTabScroller -----------------------------------------
protected class FlatWheelTabScroller
extends MouseAdapter
{
private int lastMouseX;
private int lastMouseY;
private boolean inViewport;
private boolean scrolled;
private Timer rolloverTimer;
private Timer exitedTimer;
private Animator animator;
private Point startViewPosition;
private Point targetViewPosition;
protected void uninstall() {
if( rolloverTimer != null )
rolloverTimer.stop();
if( exitedTimer != null )
exitedTimer.stop();
if( animator != null )
animator.cancel();
}
@Override
public void mouseWheelMoved( MouseWheelEvent e ) {
// because this listener receives mouse events for the whole tabbed pane,
// we have to check whether the mouse is located over the viewport
if( !isInViewport( e.getX(), e.getY() ) )
return;
lastMouseX = e.getX();
lastMouseY = e.getY();
double preciseWheelRotation = e.getPreciseWheelRotation();
// compute new view position
Point viewPosition = (targetViewPosition != null)
? targetViewPosition
: tabViewport.getViewPosition();
Dimension viewSize = tabViewport.getViewSize();
int x = viewPosition.x;
int y = viewPosition.y;
int tabPlacement = tabPane.getTabPlacement();
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
x += maxTabHeight * preciseWheelRotation;
x = Math.min( Math.max( x, 0 ), viewSize.width - tabViewport.getWidth() );
} else {
y += maxTabHeight * preciseWheelRotation;
y = Math.min( Math.max( y, 0 ), viewSize.height - tabViewport.getHeight() );
}
// check whether view position has changed
Point newViewPosition = new Point( x, y );
if( newViewPosition.equals( viewPosition ) )
return;
// update view position
if( preciseWheelRotation != 0 &&
preciseWheelRotation != e.getWheelRotation() )
{
// do not use animation for precise scrolling (e.g. with trackpad)
// stop running animation (if any)
if( animator != null )
animator.stop();
tabViewport.setViewPosition( newViewPosition );
updateRolloverDelayed();
} else
setViewPositionAnimated( newViewPosition );
scrolled = true;
}
protected void setViewPositionAnimated( Point viewPosition ) {
// check whether position is equal to current position
if( viewPosition.equals( tabViewport.getViewPosition() ) )
return;
// do not use animation if disabled
if( !isSmoothScrollingEnabled() ) {
tabViewport.setViewPosition( viewPosition );
updateRolloverDelayed();
return;
}
// remember start and target view positions
startViewPosition = tabViewport.getViewPosition();
targetViewPosition = viewPosition;
// create animator
if( animator == null ) {
// using same delays as in FlatScrollBarUI
int duration = 200;
int resolution = 10;
animator = new Animator( duration, fraction -> {
if( tabViewport == null || !tabViewport.isShowing() ) {
animator.stop();
return;
}
// update view position
int x = startViewPosition.x + Math.round( (targetViewPosition.x - startViewPosition.x) * fraction );
int y = startViewPosition.y + Math.round( (targetViewPosition.y - startViewPosition.y) * fraction );
tabViewport.setViewPosition( new Point( x, y ) );
}, () -> {
startViewPosition = targetViewPosition = null;
if( tabPane != null )
setRolloverTab( lastMouseX, lastMouseY );
} );
animator.setResolution( resolution );
animator.setInterpolator( new CubicBezierEasing( 0.5f, 0.5f, 0.5f, 1 ) );
}
// restart animator
animator.restart();
}
protected void updateRolloverDelayed() {
blockRollover = true;
// keep rollover on last tab until it would move to another tab, then clear it
int oldIndex = getRolloverTab();
if( oldIndex >= 0 ) {
int index = tabForCoordinate( tabPane, lastMouseX, lastMouseY );
if( index >= 0 && index != oldIndex ) {
// clear if moved to another tab
blockRollover = false;
setRolloverTab( -1 );
blockRollover = true;
}
}
// create timer
if( rolloverTimer == null ) {
rolloverTimer = new Timer( 150, e -> {
blockRollover = false;
// highlight tab at mouse location
if( tabPane != null )
setRolloverTab( lastMouseX, lastMouseY );
} );
rolloverTimer.setRepeats( false );
}
// restart timer
rolloverTimer.restart();
}
@Override
public void mouseMoved( MouseEvent e ) {
checkViewportExited( e.getX(), e.getY() );
}
@Override
public void mouseExited( MouseEvent e ) {
// this event occurs also if mouse is moved to a custom tab component
// that handles mouse events (e.g. a close button)
checkViewportExited( e.getX(), e.getY() );
}
@Override
public void mousePressed( MouseEvent e ) {
// for the case that the tab was only partly visible before the user clicked it
setRolloverTab( e.getX(), e.getY() );
}
protected boolean isInViewport( int x, int y ) {
return (tabViewport != null && tabViewport.getBounds().contains( x, y ) );
}
protected void checkViewportExited( int x, int y ) {
lastMouseX = x;
lastMouseY = y;
boolean wasInViewport = inViewport;
inViewport = isInViewport( x, y );
if( inViewport != wasInViewport ) {
if( !inViewport )
viewportExited();
else if( exitedTimer != null )
exitedTimer.stop();
}
}
protected void viewportExited() {
if( !scrolled )
return;
if( exitedTimer == null ) {
exitedTimer = new Timer( 500, e -> ensureSelectedTabVisible() );
exitedTimer.setRepeats( false );
}
exitedTimer.start();
}
protected void ensureSelectedTabVisible() {
// check whether UI delegate was uninstalled because this method is invoked via timer
if( tabPane == null || tabViewport == null )
return;
if( !scrolled || tabViewport == null )
return;
scrolled = false;
int selectedIndex = tabPane.getSelectedIndex();
if( selectedIndex >= 0 ) {
Rectangle tabBounds = getTabBounds( tabPane, selectedIndex );
tabViewport.scrollRectToVisible( tabBounds );
}
}
}
//---- class Handler ------------------------------------------------------
private class Handler
extends MouseAdapter
implements PropertyChangeListener
{
PropertyChangeListener propertyChangeDelegate;
@Override
public void mouseExited( MouseEvent e ) {
// this event occurs also if mouse is moved to a custom tab component
// that handles mouse events (e.g. a close button)
// --> make sure that the tab stays highlighted
setRolloverTab( e.getX(), e.getY() );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
propertyChangeDelegate.propertyChange( e );
switch( e.getPropertyName() ) {
case TABBED_PANE_SHOW_TAB_SEPARATORS:
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
case TABBED_PANE_HAS_FULL_BORDER:
case TABBED_PANE_TAB_HEIGHT:
tabPane.revalidate();
tabPane.repaint();
break;
} }
} }
return null;
} }
} }

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.util;
import java.util.ArrayList; import java.util.ArrayList;
import javax.swing.Timer; import javax.swing.Timer;
import com.formdev.flatlaf.FlatSystemProperties;
/** /**
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book * Simple animator based on ideas and concepts from "Filthy Rich Clients" book
@@ -39,6 +40,15 @@ public class Animator
private long startTime; private long startTime;
private Timer timer; private Timer timer;
/**
* Checks whether animations are enabled (the default) or disabled via
* system property {@code flatlaf.animation} set to {@code false}.
* This allows disabling all animations at command line with {@code -Dflatlaf.animation=false}.
*/
public static boolean useAnimation() {
return FlatSystemProperties.getBoolean( FlatSystemProperties.ANIMATION, true );
}
/** /**
* Creates an animation that runs duration milliseconds. * Creates an animation that runs duration milliseconds.
* Use {@link #addTarget(TimingTarget)} to receive timing events * Use {@link #addTarget(TimingTarget)} to receive timing events
@@ -174,14 +184,17 @@ public class Animator
timeToStop = false; timeToStop = false;
startTime = System.nanoTime() / 1000000; startTime = System.nanoTime() / 1000000;
timer = new Timer( resolution, e -> { if( timer == null ) {
if( !hasBegun ) { timer = new Timer( resolution, e -> {
begin(); if( !hasBegun ) {
hasBegun = true; begin();
} hasBegun = true;
}
timingEvent( getTimingFraction() ); timingEvent( getTimingFraction() );
} ); } );
} else
timer.setDelay( resolution );
timer.setInitialDelay( 0 ); timer.setInitialDelay( 0 );
timer.start(); timer.start();
} }
@@ -203,10 +216,11 @@ public class Animator
} }
private void stop( boolean cancel ) { private void stop( boolean cancel ) {
if( timer != null ) { if( !running )
return;
if( timer != null )
timer.stop(); timer.stop();
timer = null;
}
if( !cancel ) if( !cancel )
end(); end();
@@ -215,6 +229,15 @@ public class Animator
timeToStop = false; timeToStop = false;
} }
/**
* Restarts the animation.
* Invokes {@link #cancel()} and {@link #start()}.
*/
public void restart() {
cancel();
start();
}
/** /**
* Returns whether this animation is running. * Returns whether this animation is running.
*/ */

View File

@@ -22,6 +22,8 @@ import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPA
import java.awt.*; import java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import com.formdev.flatlaf.FlatLaf;
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.*;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
@@ -32,6 +34,9 @@ import net.miginfocom.swing.*;
public class FlatContainerTest public class FlatContainerTest
extends FlatTestPanel extends FlatTestPanel
{ {
private final int initialTabCount;
private boolean autoMoreTabs;
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" );
@@ -43,6 +48,10 @@ public class FlatContainerTest
initComponents(); initComponents();
addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 ); addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 );
initialTabCount = tabbedPane1.getTabCount();
tabScrollCheckBox.setSelected( true );
tabScrollChanged();
} }
private void tabScrollChanged() { private void tabScrollChanged() {
@@ -63,8 +72,6 @@ public class FlatContainerTest
} }
} }
private boolean autoMoreTabs;
private void showTabSeparatorsChanged() { private void showTabSeparatorsChanged() {
Boolean showTabSeparators = showTabSeparatorsCheckBox.isSelected() ? true : null; Boolean showTabSeparators = showTabSeparatorsCheckBox.isSelected() ? true : null;
putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ); putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators );
@@ -88,28 +95,30 @@ public class FlatContainerTest
} }
private void moreTabsChanged() { private void moreTabsChanged() {
boolean moreTabs = moreTabsCheckBox.isSelected(); moreTabsSpinnerChanged();
addRemoveMoreTabs( tabbedPane1, moreTabs );
addRemoveMoreTabs( tabbedPane2, moreTabs );
addRemoveMoreTabs( tabbedPane3, moreTabs );
addRemoveMoreTabs( tabbedPane4, moreTabs );
autoMoreTabs = false; autoMoreTabs = false;
moreTabsSpinner.setEnabled( moreTabsCheckBox.isSelected() );
} }
private void addRemoveMoreTabs( JTabbedPane tabbedPane, boolean add ) { private void moreTabsSpinnerChanged() {
if( add ) { addRemoveMoreTabs( tabbedPane1 );
addTab( tabbedPane, "Tab 4", "tab content 4" ); addRemoveMoreTabs( tabbedPane2 );
addTab( tabbedPane, "Tab 5", "tab content 5" ); addRemoveMoreTabs( tabbedPane3 );
addTab( tabbedPane, "Tab 6", "tab content 6" ); addRemoveMoreTabs( tabbedPane4 );
addTab( tabbedPane, "Tab 7", "tab content 7" ); }
addTab( tabbedPane, "Tab 8", "tab content 8" );
} else { private void addRemoveMoreTabs( JTabbedPane tabbedPane ) {
int tabCount = tabbedPane.getTabCount(); int oldTabCount = tabbedPane.getTabCount();
if( tabCount > 4 ) { int newTabCount = initialTabCount + (moreTabsCheckBox.isSelected() ? (Integer) moreTabsSpinner.getValue() : 0);
for( int i = 0; i < 5; i++ )
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 ); if( newTabCount > oldTabCount ) {
} for( int i = oldTabCount + 1; i <= newTabCount; i++ )
addTab( tabbedPane, "Tab " + i, "tab content " + i );
} else if( newTabCount < oldTabCount ) {
while( tabbedPane.getTabCount() > 4 )
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 );
} }
} }
@@ -162,6 +171,50 @@ public class FlatContainerTest
tabbedPane.setIconAt( 1, icon ); tabbedPane.setIconAt( 1, icon );
} }
private void customBorderChanged() {
Border border = customBorderCheckBox.isSelected()
? new LineBorder( Color.magenta, 20 )
: null;
tabbedPane1.setBorder( border );
tabbedPane2.setBorder( border );
tabbedPane3.setBorder( border );
tabbedPane4.setBorder( border );
}
private void customTabsChanged() {
customTabsChanged( tabbedPane1 );
customTabsChanged( tabbedPane2 );
customTabsChanged( tabbedPane3 );
customTabsChanged( tabbedPane4 );
}
private void customTabsChanged( JTabbedPane tabbedPane ) {
if( customTabsCheckBox.isSelected() ) {
tabbedPane.setTabComponentAt( 1, new JButton( tabbedPane1.getTitleAt( 1 ) ) );
tabbedPane.setTabComponentAt( 3, createCustomTab( tabbedPane1.getTitleAt( 3 ) ) );
} else {
tabbedPane.setTabComponentAt( 1, null );
tabbedPane.setTabComponentAt( 3, null );
}
}
private Component createCustomTab( String tabTitle ) {
JButton closeButton;
if( UIManager.getLookAndFeel() instanceof FlatLaf ) {
closeButton = new JButton( new FlatInternalFrameCloseIcon() );
closeButton.setContentAreaFilled( false );
closeButton.setBorder( null );
} else
closeButton = new JButton( "x" );
JPanel tab = new JPanel( new BorderLayout( 5, 0 ) );
tab.setOpaque( false );
tab.add( closeButton, BorderLayout.EAST );
tab.add( new JLabel( tabTitle ), BorderLayout.CENTER );
return tab;
}
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();
@@ -182,12 +235,15 @@ public class FlatContainerTest
tabbedPane4 = new JTabbedPane(); tabbedPane4 = new JTabbedPane();
JPanel panel14 = new JPanel(); JPanel panel14 = new JPanel();
moreTabsCheckBox = new JCheckBox(); moreTabsCheckBox = new JCheckBox();
moreTabsSpinner = new JSpinner();
tabScrollCheckBox = new JCheckBox(); tabScrollCheckBox = new JCheckBox();
showTabSeparatorsCheckBox = new JCheckBox(); showTabSeparatorsCheckBox = new JCheckBox();
hideContentSeparatorCheckBox = new JCheckBox(); hideContentSeparatorCheckBox = new JCheckBox();
hasFullBorderCheckBox = new JCheckBox();
tabIconsCheckBox = new JCheckBox(); tabIconsCheckBox = new JCheckBox();
tabIconSizeSpinner = new JSpinner(); tabIconSizeSpinner = new JSpinner();
customBorderCheckBox = new JCheckBox();
customTabsCheckBox = new JCheckBox();
hasFullBorderCheckBox = new JCheckBox();
CellConstraints cc = new CellConstraints(); CellConstraints cc = new CellConstraints();
//======== this ======== //======== this ========
@@ -294,14 +350,13 @@ public class FlatContainerTest
"insets 0,hidemode 3", "insets 0,hidemode 3",
// columns // columns
"[]" + "[]" +
"[]" +
"[]" +
"[fill]" + "[fill]" +
"[]" + "[]" +
"[fill]" + "[]" +
"[fill]", "[fill]",
// rows // rows
"[center]")); "[center]" +
"[]"));
//---- moreTabsCheckBox ---- //---- moreTabsCheckBox ----
moreTabsCheckBox.setText("More tabs"); moreTabsCheckBox.setText("More tabs");
@@ -309,37 +364,53 @@ public class FlatContainerTest
moreTabsCheckBox.addActionListener(e -> moreTabsChanged()); moreTabsCheckBox.addActionListener(e -> moreTabsChanged());
panel14.add(moreTabsCheckBox, "cell 0 0"); panel14.add(moreTabsCheckBox, "cell 0 0");
//---- moreTabsSpinner ----
moreTabsSpinner.setModel(new SpinnerNumberModel(4, 0, null, 1));
moreTabsSpinner.setEnabled(false);
moreTabsSpinner.addChangeListener(e -> moreTabsSpinnerChanged());
panel14.add(moreTabsSpinner, "cell 1 0");
//---- tabScrollCheckBox ---- //---- tabScrollCheckBox ----
tabScrollCheckBox.setText("Use scroll layout"); tabScrollCheckBox.setText("Use scroll layout");
tabScrollCheckBox.setMnemonic('S'); tabScrollCheckBox.setMnemonic('S');
tabScrollCheckBox.addActionListener(e -> tabScrollChanged()); tabScrollCheckBox.addActionListener(e -> tabScrollChanged());
panel14.add(tabScrollCheckBox, "cell 1 0,alignx left,growx 0"); panel14.add(tabScrollCheckBox, "cell 2 0,alignx left,growx 0");
//---- showTabSeparatorsCheckBox ---- //---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators"); showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged()); showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel14.add(showTabSeparatorsCheckBox, "cell 2 0"); panel14.add(showTabSeparatorsCheckBox, "cell 3 0");
//---- hideContentSeparatorCheckBox ---- //---- hideContentSeparatorCheckBox ----
hideContentSeparatorCheckBox.setText("Hide content separator"); hideContentSeparatorCheckBox.setText("Hide content separator");
hideContentSeparatorCheckBox.addActionListener(e -> hideContentSeparatorChanged()); hideContentSeparatorCheckBox.addActionListener(e -> hideContentSeparatorChanged());
panel14.add(hideContentSeparatorCheckBox, "cell 3 0"); panel14.add(hideContentSeparatorCheckBox, "cell 4 0");
//---- hasFullBorderCheckBox ----
hasFullBorderCheckBox.setText("Show content border");
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
panel14.add(hasFullBorderCheckBox, "cell 4 0,alignx left,growx 0");
//---- tabIconsCheckBox ---- //---- tabIconsCheckBox ----
tabIconsCheckBox.setText("Tab icons"); tabIconsCheckBox.setText("Tab icons");
tabIconsCheckBox.addActionListener(e -> tabIconsChanged()); tabIconsCheckBox.addActionListener(e -> tabIconsChanged());
panel14.add(tabIconsCheckBox, "cell 5 0"); panel14.add(tabIconsCheckBox, "cell 0 1");
//---- tabIconSizeSpinner ---- //---- tabIconSizeSpinner ----
tabIconSizeSpinner.setModel(new SpinnerListModel(new String[] {"16", "24", "32", "48", "64"})); tabIconSizeSpinner.setModel(new SpinnerListModel(new String[] {"16", "24", "32", "48", "64"}));
tabIconSizeSpinner.setEnabled(false); tabIconSizeSpinner.setEnabled(false);
tabIconSizeSpinner.addChangeListener(e -> tabIconsChanged()); tabIconSizeSpinner.addChangeListener(e -> tabIconsChanged());
panel14.add(tabIconSizeSpinner, "cell 6 0"); panel14.add(tabIconSizeSpinner, "cell 1 1");
//---- customBorderCheckBox ----
customBorderCheckBox.setText("Custom border");
customBorderCheckBox.addActionListener(e -> customBorderChanged());
panel14.add(customBorderCheckBox, "cell 2 1");
//---- customTabsCheckBox ----
customTabsCheckBox.setText("Custom tabs");
customTabsCheckBox.addActionListener(e -> customTabsChanged());
panel14.add(customTabsCheckBox, "cell 3 1");
//---- hasFullBorderCheckBox ----
hasFullBorderCheckBox.setText("Show content border");
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
panel14.add(hasFullBorderCheckBox, "cell 4 1,alignx left,growx 0");
} }
panel9.add(panel14, cc.xywh(1, 11, 3, 1)); panel9.add(panel14, cc.xywh(1, 11, 3, 1));
} }
@@ -353,12 +424,15 @@ public class FlatContainerTest
private JTabbedPane tabbedPane2; private JTabbedPane tabbedPane2;
private JTabbedPane tabbedPane4; private JTabbedPane tabbedPane4;
private JCheckBox moreTabsCheckBox; private JCheckBox moreTabsCheckBox;
private JSpinner moreTabsSpinner;
private JCheckBox tabScrollCheckBox; private JCheckBox tabScrollCheckBox;
private JCheckBox showTabSeparatorsCheckBox; private JCheckBox showTabSeparatorsCheckBox;
private JCheckBox hideContentSeparatorCheckBox; private JCheckBox hideContentSeparatorCheckBox;
private JCheckBox hasFullBorderCheckBox;
private JCheckBox tabIconsCheckBox; private JCheckBox tabIconsCheckBox;
private JSpinner tabIconSizeSpinner; private JSpinner tabIconSizeSpinner;
private JCheckBox customBorderCheckBox;
private JCheckBox customTabsCheckBox;
private JCheckBox hasFullBorderCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
//---- class Tab1Panel ---------------------------------------------------- //---- class Tab1Panel ----------------------------------------------------

View File

@@ -131,8 +131,8 @@ 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][fill]" "$columnConstraints": "[][fill][][][fill]"
"$rowConstraints": "[center]" "$rowConstraints": "[center][]"
} ) { } ) {
name: "panel14" name: "panel14"
"opaque": false "opaque": false
@@ -147,6 +147,20 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0" "value": "cell 0 0"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "moreTabsSpinner"
"model": new javax.swing.SpinnerNumberModel {
minimum: 0
value: 4
}
"enabled": false
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "moreTabsSpinnerChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabScrollCheckBox" name: "tabScrollCheckBox"
"text": "Use scroll layout" "text": "Use scroll layout"
@@ -156,7 +170,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabScrollChanged", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabScrollChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx left,growx 0" "value": "cell 2 0,alignx left,growx 0"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox" name: "showTabSeparatorsCheckBox"
@@ -166,7 +180,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0" "value": "cell 3 0"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hideContentSeparatorCheckBox" name: "hideContentSeparatorCheckBox"
@@ -176,17 +190,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideContentSeparatorChanged", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideContentSeparatorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0" "value": "cell 4 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hasFullBorderCheckBox"
"text": "Show content border"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hasFullBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0,alignx left,growx 0"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabIconsCheckBox" name: "tabIconsCheckBox"
@@ -196,7 +200,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabIconsChanged", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabIconsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 0" "value": "cell 0 1"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) { add( new FormComponent( "javax.swing.JSpinner" ) {
name: "tabIconSizeSpinner" name: "tabIconSizeSpinner"
@@ -215,7 +219,37 @@ new FormModel {
} }
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "tabIconsChanged", false ) ) addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "tabIconsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 6 0" "value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "customBorderCheckBox"
"text": "Custom border"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "customTabsCheckBox"
"text": "Custom tabs"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customTabsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hasFullBorderCheckBox"
"text": "Show content border"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hasFullBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 1,alignx left,growx 0"
} ) } )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) { }, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 11 "gridY": 11