From 5b0c96cd6dc331aea48c01d255972486b99b26d6 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 8 Oct 2020 23:46:43 +0200 Subject: [PATCH] TabbedPane: avoid scrolling selected tab back into visible area (after wheel scrolling) if the mouse is over a custom tab component that handles mouse events (e.g. a close button) --- .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 33 ++++++++------ .../flatlaf/testing/FlatContainerTest.java | 45 +++++++++++++++++++ .../flatlaf/testing/FlatContainerTest.jfd | 10 +++++ 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java index 7740995b..051334cd 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java @@ -744,24 +744,14 @@ public class FlatTabbedPaneUI @Override public void mouseMoved( MouseEvent e ) { - lastMouseX = e.getX(); - lastMouseY = e.getY(); - - boolean wasInViewport = inViewport; - inViewport = isInViewport( e.getX(), e.getY() ); - - if( inViewport != wasInViewport ) { - if( !inViewport ) - viewportExited(); - else if( timer != null ) - timer.stop(); - } + checkViewportExited( e.getX(), e.getY() ); } @Override public void mouseExited( MouseEvent e ) { - inViewport = false; - viewportExited(); + // 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 @@ -778,6 +768,21 @@ public class FlatTabbedPaneUI setRolloverTab( tabForCoordinate( tabPane, 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( timer != null ) + timer.stop(); + } + } + protected void viewportExited() { if( !scrolled ) return; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java index 96a0b0d1..eed8036a 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java @@ -22,6 +22,8 @@ import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPA import java.awt.*; import javax.swing.*; import javax.swing.border.*; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.icons.FlatInternalFrameCloseIcon; import com.formdev.flatlaf.util.ScaledImageIcon; import com.jgoodies.forms.layout.*; import net.miginfocom.swing.*; @@ -47,6 +49,9 @@ public class FlatContainerTest addInitialTabs( tabbedPane1, tabbedPane2, tabbedPane3, tabbedPane4 ); initialTabCount = tabbedPane1.getTabCount(); + + tabScrollCheckBox.setSelected( true ); + tabScrollChanged(); } private void tabScrollChanged() { @@ -177,6 +182,39 @@ public class FlatContainerTest 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() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents JPanel panel9 = new JPanel(); @@ -204,6 +242,7 @@ public class FlatContainerTest tabIconsCheckBox = new JCheckBox(); tabIconSizeSpinner = new JSpinner(); customBorderCheckBox = new JCheckBox(); + customTabsCheckBox = new JCheckBox(); hasFullBorderCheckBox = new JCheckBox(); CellConstraints cc = new CellConstraints(); @@ -363,6 +402,11 @@ public class FlatContainerTest 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()); @@ -387,6 +431,7 @@ public class FlatContainerTest private JCheckBox tabIconsCheckBox; private JSpinner tabIconSizeSpinner; private JCheckBox customBorderCheckBox; + private JCheckBox customTabsCheckBox; private JCheckBox hasFullBorderCheckBox; // JFormDesigner - End of variables declaration //GEN-END:variables diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd index aa07cade..ce4551bc 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd @@ -231,6 +231,16 @@ new FormModel { }, 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"