From 77fc564e7025ad7a3f54e03266abdaae329f4ae6 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Thu, 8 Apr 2021 01:15:29 +0200 Subject: [PATCH] TabbedPane: fixed actions `scrollTabsForwardAction` and `scrollTabsBackwardAction` when used from outside (e.g. in NetBeans) --- .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 63 +++++++++++++++++++ .../flatlaf/testing/FlatContainerTest.java | 37 +++++++++++ .../flatlaf/testing/FlatContainerTest.jfd | 12 +++- 3 files changed, 111 insertions(+), 1 deletion(-) 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 d67579b3..7bec8d33 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 @@ -58,6 +58,8 @@ import java.util.function.BiConsumer; import java.util.function.IntConsumer; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.ButtonModel; import javax.swing.Icon; import javax.swing.JButton; @@ -490,6 +492,20 @@ public class FlatTabbedPaneUI } } + @Override + protected void installKeyboardActions() { + super.installKeyboardActions(); + + // get shared action map, used for all tabbed panes + ActionMap map = SwingUtilities.getUIActionMap( tabPane ); + if( map != null ) { + // this is required for the case that those actions are used from outside + // (e.g. wheel tab scroller in NetBeans) + RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsForwardAction" ); + RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsBackwardAction" ); + } + } + private Handler getHandler() { if( handler == null ) handler = new Handler(); @@ -2959,4 +2975,51 @@ public class FlatTabbedPaneUI scrollBackwardButtonPrefSize = backwardButton.getPreferredSize(); } } + + //---- class RunWithOriginalLayoutManagerDelegateAction ------------------- + + private static class RunWithOriginalLayoutManagerDelegateAction + implements Action + { + private final Action delegate; + + static void install( ActionMap map, String key ) { + Action oldAction = map.get( key ); + if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction ) + return; // not found or already installed + + map.put( key, new RunWithOriginalLayoutManagerDelegateAction( oldAction ) ); + } + + private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) { + this.delegate = delegate; + } + + @Override + public Object getValue( String key ) { + return delegate.getValue( key ); + } + + @Override + public boolean isEnabled() { + return delegate.isEnabled(); + } + + @Override public void putValue( String key, Object value ) {} + @Override public void setEnabled( boolean b ) {} + @Override public void addPropertyChangeListener( PropertyChangeListener listener ) {} + @Override public void removePropertyChangeListener( PropertyChangeListener listener ) {} + + @Override + public void actionPerformed( ActionEvent e ) { + JTabbedPane tabbedPane = (JTabbedPane) e.getSource(); + ComponentUI ui = tabbedPane.getUI(); + if( ui instanceof FlatTabbedPaneUI ) { + ((FlatTabbedPaneUI)ui).runWithOriginalLayoutManager( () -> { + delegate.actionPerformed( e ); + } ); + } else + delegate.actionPerformed( e ); + } + } } 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 962659af..d465a652 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 @@ -18,7 +18,10 @@ package com.formdev.flatlaf.testing; import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.*; +import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; import javax.swing.*; import javax.swing.border.*; import com.formdev.flatlaf.FlatLaf; @@ -418,6 +421,32 @@ public class FlatContainerTest tabbedPane.setMaximumTabWidth( maximumTabWidth ); } + private void customWheelScrollingChanged() { + if( custoMouseWheelScroller != null ) { + for( FlatTabbedPane tabbedPane : allTabbedPanes ) + tabbedPane.removeMouseWheelListener( custoMouseWheelScroller ); + custoMouseWheelScroller = null; + } + + if( customWheelScrollingCheckBox.isSelected() ) { + custoMouseWheelScroller = new MouseWheelListener() { + @Override + public void mouseWheelMoved( MouseWheelEvent e ) { + if( e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL ) { + JTabbedPane tabbedPane = (JTabbedPane) e.getComponent(); + ActionMap actionMap = tabbedPane.getActionMap(); + Action scrollAction = actionMap.get( (e.getWheelRotation() < 0) + ? "scrollTabsBackwardAction" : "scrollTabsForwardAction" ); + if( scrollAction != null && scrollAction.isEnabled() ) + scrollAction.actionPerformed( new ActionEvent( tabbedPane, 0, "" ) ); + } + } + }; + for( FlatTabbedPane tabbedPane : allTabbedPanes ) + tabbedPane.addMouseWheelListener( custoMouseWheelScroller ); + } + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents JPanel panel9 = new JPanel(); @@ -475,6 +504,7 @@ public class FlatContainerTest showTabSeparatorsCheckBox = new JCheckBox(); secondTabWiderCheckBox = new JCheckBox(); hideTabAreaWithOneTabCheckBox = new JCheckBox(); + customWheelScrollingCheckBox = new JCheckBox(); CellConstraints cc = new CellConstraints(); //======== this ======== @@ -773,6 +803,11 @@ public class FlatContainerTest hideTabAreaWithOneTabCheckBox.setText("Hide tab area with one tab"); hideTabAreaWithOneTabCheckBox.addActionListener(e -> hideTabAreaWithOneTabChanged()); tabbedPaneControlPanel.add(hideTabAreaWithOneTabCheckBox, "cell 1 10"); + + //---- customWheelScrollingCheckBox ---- + customWheelScrollingCheckBox.setText("Custom wheel scrolling"); + customWheelScrollingCheckBox.addActionListener(e -> customWheelScrollingChanged()); + tabbedPaneControlPanel.add(customWheelScrollingCheckBox, "cell 2 10"); } panel9.add(tabbedPaneControlPanel, cc.xywh(1, 11, 3, 1)); } @@ -818,9 +853,11 @@ public class FlatContainerTest private JCheckBox showTabSeparatorsCheckBox; private JCheckBox secondTabWiderCheckBox; private JCheckBox hideTabAreaWithOneTabCheckBox; + private JCheckBox customWheelScrollingCheckBox; // JFormDesigner - End of variables declaration //GEN-END:variables private FlatTabbedPane[] allTabbedPanes; + private MouseWheelListener custoMouseWheelScroller; //---- enum TabPlacement -------------------------------------------------- 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 62d181c7..2fe1d8b9 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 @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -502,6 +502,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 10" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "customWheelScrollingCheckBox" + "text": "Custom wheel scrolling" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customWheelScrollingChanged", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 10" + } ) }, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) { "gridY": 11 "gridWidth": 3