diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java index 630c49b8..e53a1776 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatButtonUI.java @@ -114,6 +114,15 @@ public class FlatButtonUI toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" ); helpButtonIcon = UIManager.getIcon( "HelpButton.icon" ); + + MigLayoutVisualPadding.install( b, focusWidth ); + } + + @Override + protected void uninstallDefaults( AbstractButton b ) { + super.uninstallDefaults( b ); + + MigLayoutVisualPadding.uninstall( b ); } static boolean isContentAreaFilled( Component c ) { diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java index 951a3127..3f4e4ddc 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCheckBoxUI.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import javax.swing.AbstractButton; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.metal.MetalCheckBoxUI; @@ -39,6 +40,20 @@ public class FlatCheckBoxUI return instance; } + @Override + public void installDefaults( AbstractButton b ) { + super.installDefaults( b ); + + MigLayoutVisualPadding.install( b, null ); + } + + @Override + protected void uninstallDefaults( AbstractButton b ) { + super.uninstallDefaults( b ); + + MigLayoutVisualPadding.uninstall( b ); + } + @Override protected void paintFocus( Graphics g, Rectangle t, Dimension d ) { // focus border painted in icon diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java index 17c07ce3..50e14a72 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java @@ -106,6 +106,8 @@ public class FlatComboBoxUI // scale padding = UIScale.scale( padding ); + + MigLayoutVisualPadding.install( comboBox, focusWidth ); } @Override @@ -123,6 +125,8 @@ public class FlatComboBoxUI buttonArrowColor = null; buttonDisabledArrowColor = null; buttonHoverArrowColor = null; + + MigLayoutVisualPadding.uninstall( comboBox ); } @Override diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java index fe5937be..a7d8c675 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java @@ -62,6 +62,15 @@ public class FlatPasswordFieldUI focusWidth = UIManager.getInt( "Component.focusWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" ); + + MigLayoutVisualPadding.install( getComponent(), focusWidth ); + } + + @Override + protected void uninstallDefaults() { + super.uninstallDefaults(); + + MigLayoutVisualPadding.uninstall( getComponent() ); } @Override diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java index a3eebc57..c239e1be 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRadioButtonUI.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import javax.swing.AbstractButton; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.metal.MetalRadioButtonUI; @@ -39,6 +40,20 @@ public class FlatRadioButtonUI return instance; } + @Override + public void installDefaults( AbstractButton b ) { + super.installDefaults( b ); + + MigLayoutVisualPadding.install( b, null ); + } + + @Override + protected void uninstallDefaults( AbstractButton b ) { + super.uninstallDefaults( b ); + + MigLayoutVisualPadding.uninstall( b ); + } + @Override protected void paintFocus( Graphics g, Rectangle t, Dimension d ) { // focus border painted in icon diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java index 6a55d867..439a6521 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java @@ -30,6 +30,7 @@ import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.ScrollPaneLayout; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicScrollPaneUI; @@ -54,6 +55,8 @@ public class FlatScrollPaneUI if( scrollpane.getLayout() instanceof UIResource ) scrollpane.setLayout( new FlatScrollPaneLayout() ); + + MigLayoutVisualPadding.install( scrollpane, UIManager.getInt( "Component.focusWidth" ) ); } @Override @@ -61,6 +64,8 @@ public class FlatScrollPaneUI if( scrollpane.getLayout() instanceof FlatScrollPaneLayout ) scrollpane.setLayout( new ScrollPaneLayout.UIResource() ); + MigLayoutVisualPadding.uninstall( scrollpane ); + super.uninstallUI( c ); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java index f6fe7404..597c83b6 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java @@ -101,6 +101,8 @@ public class FlatSpinnerUI // scale padding = scale( padding ); + + MigLayoutVisualPadding.install( spinner, focusWidth ); } @Override @@ -116,6 +118,8 @@ public class FlatSpinnerUI buttonDisabledArrowColor = null; buttonHoverArrowColor = null; padding = null; + + MigLayoutVisualPadding.uninstall( spinner ); } @Override 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 8f575691..6f06c13c 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 @@ -107,6 +107,8 @@ public class FlatTabbedPaneUI tabHeight = scale( tabHeight ); tabSelectionHeight = scale( tabSelectionHeight ); contentSeparatorHeight = scale( contentSeparatorHeight ); + + MigLayoutVisualPadding.install( tabPane, null ); } @Override @@ -120,6 +122,8 @@ public class FlatTabbedPaneUI hoverColor = null; focusColor = null; contentAreaColor = null; + + MigLayoutVisualPadding.uninstall( tabPane ); } @Override diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java index 85633c27..3ba0fc85 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java @@ -59,6 +59,15 @@ public class FlatTextFieldUI focusWidth = UIManager.getInt( "Component.focusWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" ); + + MigLayoutVisualPadding.install( getComponent(), focusWidth ); + } + + @Override + protected void uninstallDefaults() { + super.uninstallDefaults(); + + MigLayoutVisualPadding.uninstall( getComponent() ); } @Override diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java new file mode 100644 index 00000000..3d6c1d47 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/MigLayoutVisualPadding.java @@ -0,0 +1,162 @@ +/* + * Copyright 2019 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 + * + * http://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.ui; + +import static com.formdev.flatlaf.util.UIScale.scale2; +import java.awt.Insets; +import java.beans.PropertyChangeListener; +import java.util.function.Function; +import javax.swing.JComponent; + +/** + * Support for MigLayout visual paddings. + * + * Visual paddings are used by MigLayout to ignore the usually invisible space + * around some components (e.g. buttons) that is used to paint a focus border. + * + * @author Karl Tauber + */ +public class MigLayoutVisualPadding +{ + /** + * Key of visual padding client property. + * Value must be either an integer array of size 4, or java.awt.Insets. + * + * Same as net.miginfocom.layout.PlatformDefaults.VISUAL_PADDING_PROPERTY, + * but we don't want to depend on miglayout library. + */ + public static String VISUAL_PADDING_PROPERTY = "visualPadding"; + + private static final FlatMigInsets ZERO = new FlatMigInsets( 0, 0, 0, 0 ); + private static final boolean migLayoutAvailable; + + static { + // check whether MigLayout is available + boolean available = false; + try { + Class.forName( "net.miginfocom.swing.MigLayout" ); + available = true; + } catch( ClassNotFoundException ex ) { + // ignore + } + migLayoutAvailable = available; + } + + /** + * Sets the client property to the given insets. + */ + public static void install( JComponent c, Insets insets ) { + if( !migLayoutAvailable ) + return; + + setVisualPadding( c, insets ); + } + + /** + * Convenience method that checks whether component border is a FlatBorder. + */ + public static void install( JComponent c, int focusWidth ) { + if( !migLayoutAvailable ) + return; + + install( c, c2 -> { + return (c2.getBorder() instanceof FlatBorder) + ? new Insets( focusWidth, focusWidth, focusWidth, focusWidth ) + : null; + }, "border" ); + } + + /** + * Invokes the given function to retrieve the actual visual paddings and sets + * the client property. Also adds property change listener to component and + * re-invokes the function if one of the given properties have changed. + */ + public static void install( JComponent c, Function getPaddingFunction, String... propertyNames ) { + if( !migLayoutAvailable ) + return; + + // set client property + setVisualPadding( c, getPaddingFunction.apply( c ) ); + + // add listener + c.addPropertyChangeListener( (FlatMigListener) e -> { + String propertyName = e.getPropertyName(); + for( String name : propertyNames ) { + if( name == propertyName ) { + setVisualPadding( c, getPaddingFunction.apply( c ) ); + break; + } + } + } ); + } + + private static void setVisualPadding( JComponent c, Insets visualPadding ) { + Object oldPadding = c.getClientProperty( VISUAL_PADDING_PROPERTY ); + if( oldPadding == null || oldPadding instanceof FlatMigInsets ) { + FlatMigInsets flatVisualPadding = (visualPadding != null) + ? new FlatMigInsets( scale2( visualPadding.top ), scale2( visualPadding.left ), + scale2( visualPadding.bottom ), scale2( visualPadding.right ) ) + : ZERO; + + c.putClientProperty( VISUAL_PADDING_PROPERTY, flatVisualPadding ); + } + } + + /** + * Removes listeners and restores client property. + */ + public static void uninstall( JComponent c ) { + if( !migLayoutAvailable ) + return; + + // remove listener + for( PropertyChangeListener l : c.getPropertyChangeListeners() ) { + if( l instanceof FlatMigListener ) { + c.removePropertyChangeListener( l ); + break; + } + } + + // remove client property + if( c.getClientProperty( VISUAL_PADDING_PROPERTY ) instanceof FlatMigInsets ) + c.putClientProperty( VISUAL_PADDING_PROPERTY, null ); + } + + //---- class FlatMigInsets ------------------------------------------------ + + /** + * Marker class to identify our visual paddings and leaf paddings, + * which were set from outside, untouched. + */ + private static class FlatMigInsets + extends Insets + { + FlatMigInsets( int top, int left, int bottom, int right ) { + super( top, left, bottom, right ); + } + } + + //---- class FlatMigListener ---------------------------------------------- + + /** + * Marker interface needed for listener removal. + */ + private interface FlatMigListener + extends PropertyChangeListener + { + } +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java index ed0b4499..8090a5ce 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java @@ -188,6 +188,13 @@ public class UIScale return (scaleFactor == 1) ? value : Math.round( value * scaleFactor ); } + /** + * Similar as scale(int) but always "rounds down". + */ + public static int scale2( int value ) { + return (scaleFactor == 1) ? value : (int) (value * scaleFactor); + } + public static float unscale( float value ) { return (scaleFactor == 1f) ? value : (value / scaleFactor); }