mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
TabbedPane: repeat scrolling as long as arrow buttons are pressed (issue #40)
This commit is contained in:
@@ -7,6 +7,7 @@ FlatLaf Change Log
|
||||
|
||||
- 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)
|
||||
|
||||
|
||||
## 0.43
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.awt.event.InputEvent;
|
||||
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.Rectangle2D;
|
||||
@@ -46,6 +47,7 @@ import javax.swing.JComponent;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
@@ -266,11 +268,7 @@ public class FlatTabbedPaneUI
|
||||
|
||||
@Override
|
||||
protected JButton createScrollButton( int direction ) {
|
||||
// this method is invoked before installDefaults(), so we can not use color fields here
|
||||
return new FlatArrowButton( direction, UIManager.getString( "Component.arrowType" ),
|
||||
UIManager.getColor( "TabbedPane.foreground" ),
|
||||
UIManager.getColor( "TabbedPane.disabledForeground" ), null,
|
||||
UIManager.getColor( "TabbedPane.hoverColor" ) );
|
||||
return new FlatScrollableTabButton( direction );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -571,6 +569,63 @@ public class FlatTabbedPaneUI
|
||||
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
|
||||
}
|
||||
|
||||
//---- class FlatScrollableTabButton --------------------------------------
|
||||
|
||||
protected static 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 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
|
||||
|
||||
@@ -32,6 +32,9 @@ import net.miginfocom.swing.*;
|
||||
public class FlatContainerTest
|
||||
extends FlatTestPanel
|
||||
{
|
||||
private final int initialTabCount;
|
||||
private boolean autoMoreTabs;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
FlatTestFrame frame = FlatTestFrame.create( args, "FlatContainerTest" );
|
||||
@@ -43,6 +46,7 @@ public class FlatContainerTest
|
||||
initComponents();
|
||||
|
||||
addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 );
|
||||
initialTabCount = tabbedPane1.getTabCount();
|
||||
}
|
||||
|
||||
private void tabScrollChanged() {
|
||||
@@ -63,8 +67,6 @@ public class FlatContainerTest
|
||||
}
|
||||
}
|
||||
|
||||
private boolean autoMoreTabs;
|
||||
|
||||
private void showTabSeparatorsChanged() {
|
||||
Boolean showTabSeparators = showTabSeparatorsCheckBox.isSelected() ? true : null;
|
||||
putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators );
|
||||
@@ -88,28 +90,30 @@ public class FlatContainerTest
|
||||
}
|
||||
|
||||
private void moreTabsChanged() {
|
||||
boolean moreTabs = moreTabsCheckBox.isSelected();
|
||||
addRemoveMoreTabs( tabbedPane1, moreTabs );
|
||||
addRemoveMoreTabs( tabbedPane2, moreTabs );
|
||||
addRemoveMoreTabs( tabbedPane3, moreTabs );
|
||||
addRemoveMoreTabs( tabbedPane4, moreTabs );
|
||||
moreTabsSpinnerChanged();
|
||||
|
||||
autoMoreTabs = false;
|
||||
|
||||
moreTabsSpinner.setEnabled( moreTabsCheckBox.isSelected() );
|
||||
}
|
||||
|
||||
private void addRemoveMoreTabs( JTabbedPane tabbedPane, boolean add ) {
|
||||
if( add ) {
|
||||
addTab( tabbedPane, "Tab 4", "tab content 4" );
|
||||
addTab( tabbedPane, "Tab 5", "tab content 5" );
|
||||
addTab( tabbedPane, "Tab 6", "tab content 6" );
|
||||
addTab( tabbedPane, "Tab 7", "tab content 7" );
|
||||
addTab( tabbedPane, "Tab 8", "tab content 8" );
|
||||
} else {
|
||||
int tabCount = tabbedPane.getTabCount();
|
||||
if( tabCount > 4 ) {
|
||||
for( int i = 0; i < 5; i++ )
|
||||
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 );
|
||||
}
|
||||
private void moreTabsSpinnerChanged() {
|
||||
addRemoveMoreTabs( tabbedPane1 );
|
||||
addRemoveMoreTabs( tabbedPane2 );
|
||||
addRemoveMoreTabs( tabbedPane3 );
|
||||
addRemoveMoreTabs( tabbedPane4 );
|
||||
}
|
||||
|
||||
private void addRemoveMoreTabs( JTabbedPane tabbedPane ) {
|
||||
int oldTabCount = tabbedPane.getTabCount();
|
||||
int newTabCount = initialTabCount + (moreTabsCheckBox.isSelected() ? (Integer) moreTabsSpinner.getValue() : 0);
|
||||
|
||||
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 +166,17 @@ public class FlatContainerTest
|
||||
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 initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
JPanel panel9 = new JPanel();
|
||||
@@ -182,12 +197,14 @@ public class FlatContainerTest
|
||||
tabbedPane4 = new JTabbedPane();
|
||||
JPanel panel14 = new JPanel();
|
||||
moreTabsCheckBox = new JCheckBox();
|
||||
moreTabsSpinner = new JSpinner();
|
||||
tabScrollCheckBox = new JCheckBox();
|
||||
showTabSeparatorsCheckBox = new JCheckBox();
|
||||
hideContentSeparatorCheckBox = new JCheckBox();
|
||||
hasFullBorderCheckBox = new JCheckBox();
|
||||
tabIconsCheckBox = new JCheckBox();
|
||||
tabIconSizeSpinner = new JSpinner();
|
||||
customBorderCheckBox = new JCheckBox();
|
||||
hasFullBorderCheckBox = new JCheckBox();
|
||||
CellConstraints cc = new CellConstraints();
|
||||
|
||||
//======== this ========
|
||||
@@ -294,14 +311,13 @@ public class FlatContainerTest
|
||||
"insets 0,hidemode 3",
|
||||
// columns
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[fill]" +
|
||||
"[]" +
|
||||
"[fill]" +
|
||||
"[]" +
|
||||
"[fill]",
|
||||
// rows
|
||||
"[center]"));
|
||||
"[center]" +
|
||||
"[]"));
|
||||
|
||||
//---- moreTabsCheckBox ----
|
||||
moreTabsCheckBox.setText("More tabs");
|
||||
@@ -309,37 +325,48 @@ public class FlatContainerTest
|
||||
moreTabsCheckBox.addActionListener(e -> moreTabsChanged());
|
||||
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.setText("Use scroll layout");
|
||||
tabScrollCheckBox.setMnemonic('S');
|
||||
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.setText("Show tab separators");
|
||||
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
|
||||
panel14.add(showTabSeparatorsCheckBox, "cell 2 0");
|
||||
panel14.add(showTabSeparatorsCheckBox, "cell 3 0");
|
||||
|
||||
//---- hideContentSeparatorCheckBox ----
|
||||
hideContentSeparatorCheckBox.setText("Hide content separator");
|
||||
hideContentSeparatorCheckBox.addActionListener(e -> hideContentSeparatorChanged());
|
||||
panel14.add(hideContentSeparatorCheckBox, "cell 3 0");
|
||||
|
||||
//---- hasFullBorderCheckBox ----
|
||||
hasFullBorderCheckBox.setText("Show content border");
|
||||
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
|
||||
panel14.add(hasFullBorderCheckBox, "cell 4 0,alignx left,growx 0");
|
||||
panel14.add(hideContentSeparatorCheckBox, "cell 4 0");
|
||||
|
||||
//---- tabIconsCheckBox ----
|
||||
tabIconsCheckBox.setText("Tab icons");
|
||||
tabIconsCheckBox.addActionListener(e -> tabIconsChanged());
|
||||
panel14.add(tabIconsCheckBox, "cell 5 0");
|
||||
panel14.add(tabIconsCheckBox, "cell 0 1");
|
||||
|
||||
//---- tabIconSizeSpinner ----
|
||||
tabIconSizeSpinner.setModel(new SpinnerListModel(new String[] {"16", "24", "32", "48", "64"}));
|
||||
tabIconSizeSpinner.setEnabled(false);
|
||||
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");
|
||||
|
||||
//---- 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));
|
||||
}
|
||||
@@ -353,12 +380,14 @@ public class FlatContainerTest
|
||||
private JTabbedPane tabbedPane2;
|
||||
private JTabbedPane tabbedPane4;
|
||||
private JCheckBox moreTabsCheckBox;
|
||||
private JSpinner moreTabsSpinner;
|
||||
private JCheckBox tabScrollCheckBox;
|
||||
private JCheckBox showTabSeparatorsCheckBox;
|
||||
private JCheckBox hideContentSeparatorCheckBox;
|
||||
private JCheckBox hasFullBorderCheckBox;
|
||||
private JCheckBox tabIconsCheckBox;
|
||||
private JSpinner tabIconSizeSpinner;
|
||||
private JCheckBox customBorderCheckBox;
|
||||
private JCheckBox hasFullBorderCheckBox;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
|
||||
//---- class Tab1Panel ----------------------------------------------------
|
||||
|
||||
@@ -131,8 +131,8 @@ new FormModel {
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3"
|
||||
"$columnConstraints": "[][][][fill][][fill][fill]"
|
||||
"$rowConstraints": "[center]"
|
||||
"$columnConstraints": "[][fill][][][fill]"
|
||||
"$rowConstraints": "[center][]"
|
||||
} ) {
|
||||
name: "panel14"
|
||||
"opaque": false
|
||||
@@ -147,6 +147,20 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"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" ) {
|
||||
name: "tabScrollCheckBox"
|
||||
"text": "Use scroll layout"
|
||||
@@ -156,7 +170,7 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabScrollChanged", false ) )
|
||||
}, 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" ) {
|
||||
name: "showTabSeparatorsCheckBox"
|
||||
@@ -166,7 +180,7 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 2 0"
|
||||
"value": "cell 3 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "hideContentSeparatorCheckBox"
|
||||
@@ -176,17 +190,7 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideContentSeparatorChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 3 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"
|
||||
"value": "cell 4 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JCheckBox" ) {
|
||||
name: "tabIconsCheckBox"
|
||||
@@ -196,7 +200,7 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabIconsChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 5 0"
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSpinner" ) {
|
||||
name: "tabIconSizeSpinner"
|
||||
@@ -215,7 +219,27 @@ new FormModel {
|
||||
}
|
||||
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "tabIconsChanged", false ) )
|
||||
}, 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: "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 ) {
|
||||
"gridY": 11
|
||||
|
||||
Reference in New Issue
Block a user