Compare commits

...

13 Commits

Author SHA1 Message Date
Karl Tauber
84e95764fa macOS: hide popups when window is resized (issue #1082) 2026-01-30 19:01:26 +01:00
Karl Tauber
1a80a65411 Merge PR #1087: Fix exception when SVG icon name has no file extension 2026-01-30 13:17:56 +01:00
Karl Tauber
bf2227e1b8 Dialog: some client properties (e.g. JRootPane.titleBarShowTitle) did not work before the dialog was made visible (issue #1081) 2026-01-30 12:58:17 +01:00
poce1don
7fc26fe77e Fix exception when SVG icon name has no file extension 2026-01-24 20:44:50 -03:00
Karl Tauber
465254c0da FlatDesktop: avoid unnecessary logging if desktop is not supported (e.g. on NixOS with Plasma/KDE desktop) 2026-01-14 13:21:52 +01:00
Karl Tauber
aaca7cace1 README.md: added notes regarding uber/fat JARs and obfuscation/minimizing/shrinking tools (replaces PR #845) 2026-01-14 13:09:40 +01:00
Karl Tauber
c995b4cbdf Merge PR #1074: Recalculate geometry after styling in FlatSliderUI 2026-01-13 20:03:54 +01:00
Karl Tauber
b251ff76e8 Merge PR #1080: Fix NPE in FlatUIDefaultsInspector when color is null 2026-01-13 19:31:56 +01:00
Karl Tauber
ea2a985481 ToolBar: do not change floatable if it was changed from application code (issue #1075) 2026-01-13 12:35:49 +01:00
poce1don
3ca7ebbfee Fix NPE in FlatUIDefaultsInspector when color is null 2026-01-13 00:44:13 -03:00
daWoife
7e6dc269c5 Recalculate geometry after styling in FlatSliderUI 2025-12-31 11:28:41 +01:00
Karl Tauber
7680c3a817 ComboBox: added UI property ComboBox.buttonFocusedEditableBackground (issue #1068) 2025-12-30 17:36:06 +01:00
Karl Tauber
0e3cb95791 Popup: fixed scrolling popup painting issue on Windows 10 when a glass pane is visible and frame is maximized (issue #1071) 2025-12-30 13:36:09 +01:00
21 changed files with 194 additions and 116 deletions

View File

@@ -1,6 +1,27 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.7.1-SNAPSHOT
- ComboBox: Added UI property `ComboBox.buttonFocusedEditableBackground`. (issue
#1068)
- Dialog: Some client properties (e.g. `JRootPane.titleBarShowTitle`) did not
work before the dialog was made visible. (issue #1081)
- Popup: Fixed scrolling popup painting issue on Windows 10 when a glass pane is
visible and frame is maximized. (issue #1071)
- Slider: Styling `thumbSize` or `focusWidth` did not update slider size/layout.
(PR #1074)
- ToolBar: Grip disappeared when switching between Look and Feels. (issue #1075)
- macOS: Popups (menus and combobox lists) were not always hidden when window is
resized. (issue #1082)
- Extras:
- UI defaults inspector: Fixed NPE if color of `FlatLineBorder` is null. Also
use `FlatLineBorder` line color as cell background color in "Value" column.
(PR #1080)
- `FlatDesktop`: Avoid unnecessary logging if desktop is not supported (e.g.
on NixOS with Plasma/KDE desktop).
## 3.7 ## 3.7
#### New features and improvements #### New features and improvements

View File

@@ -76,10 +76,16 @@ Otherwise, download `flatlaf-<version>.jar` here:
[![Maven Central](https://img.shields.io/maven-central/v/com.formdev/flatlaf?style=flat-square)](https://central.sonatype.com/artifact/com.formdev/flatlaf) [![Maven Central](https://img.shields.io/maven-central/v/com.formdev/flatlaf?style=flat-square)](https://central.sonatype.com/artifact/com.formdev/flatlaf)
See also - See
[Native Libraries distribution](https://www.formdev.com/flatlaf/native-libraries/) [Native Libraries distribution](https://www.formdev.com/flatlaf/native-libraries/)
for instructions on how to redistribute FlatLaf native libraries with your for instructions on how to redistribute FlatLaf native libraries with your
application. application.
- If repackaging FlatLaf (and other) JARs into a single fat/uber JAR:
- add `Multi-Release: true` to `META-INF/MANIFEST.MF`
- keep `META-INF/versions/` and `META-INF/services/` directories
- merge content of equally named files in `META-INF/services/`
- If using obfuscation/minimizing/shrinking tools (e.g. **ProGuard** or
**Shadow**), exclude package `com.formdev.flatlaf` and all sub-packages.
### Snapshots ### Snapshots

View File

@@ -310,8 +310,8 @@ public abstract class FlatLaf
// install submenu usability helper // install submenu usability helper
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install(); subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
// install Linux popup menu canceler // install Linux/macOS popup menu canceler
if( SystemInfo.isLinux ) if( SystemInfo.isLinux || SystemInfo.isMacOS )
linuxPopupMenuCanceler = new LinuxPopupMenuCanceler(); linuxPopupMenuCanceler = new LinuxPopupMenuCanceler();
// listen to desktop property changes to update UI if system font or scaling changes // listen to desktop property changes to update UI if system font or scaling changes

View File

@@ -30,7 +30,7 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
/** /**
* Cancels (hides) popup menus on Linux. * Cancels (hides) popup menus on Linux and macOS.
* <p> * <p>
* On Linux, popups are not hidden under following conditions, which results in * On Linux, popups are not hidden under following conditions, which results in
* misplaced popups: * misplaced popups:
@@ -41,7 +41,13 @@ import javax.swing.event.ChangeListener;
* <li>window deactivated (e.g. activated other application) * <li>window deactivated (e.g. activated other application)
* </ul> * </ul>
* *
* On Windows and macOS, popups are automatically hidden. * On macOS, popups are usually automatically hidden, but not always.
* When resizing a window, then it depends where clicking to start resizing (and on the Java version).
* E.g. with Java 25, clicking at bottom-right corner inside of the window does not hide the popups.
* But clicking on same corner outside of the window, hides the popup.
*
* <p>
* On Windows, popups are automatically hidden.
* <p> * <p>
* The implementation is similar to what's done in * The implementation is similar to what's done in
* {@code javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber}, * {@code javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber},

View File

@@ -113,6 +113,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.buttonBackground Color optional * @uiDefault ComboBox.buttonBackground Color optional
* @uiDefault ComboBox.buttonEditableBackground Color optional * @uiDefault ComboBox.buttonEditableBackground Color optional
* @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground * @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground
* @uiDefault ComboBox.buttonFocusedEditableBackground Color optional; defaults to ComboBox.buttonEditableBackground
* @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth * @uiDefault ComboBox.buttonSeparatorWidth int or float optional; defaults to Component.borderWidth
* @uiDefault ComboBox.buttonSeparatorColor Color optional * @uiDefault ComboBox.buttonSeparatorColor Color optional
* @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional * @uiDefault ComboBox.buttonDisabledSeparatorColor Color optional
@@ -147,6 +148,7 @@ public class FlatComboBoxUI
@Styleable protected Color buttonBackground; @Styleable protected Color buttonBackground;
@Styleable protected Color buttonEditableBackground; @Styleable protected Color buttonEditableBackground;
@Styleable protected Color buttonFocusedBackground; @Styleable protected Color buttonFocusedBackground;
/** @since 3.7.1 */ @Styleable protected Color buttonFocusedEditableBackground;
/** @since 2 */ @Styleable protected float buttonSeparatorWidth; /** @since 2 */ @Styleable protected float buttonSeparatorWidth;
/** @since 2 */ @Styleable protected Color buttonSeparatorColor; /** @since 2 */ @Styleable protected Color buttonSeparatorColor;
/** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor; /** @since 2 */ @Styleable protected Color buttonDisabledSeparatorColor;
@@ -258,6 +260,7 @@ public class FlatComboBoxUI
buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" ); buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" );
buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" ); buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" );
buttonFocusedEditableBackground = UIManager.getColor( "ComboBox.buttonFocusedEditableBackground" );
buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" ); buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" );
buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) ); buttonSeparatorWidth = FlatUIUtils.getUIFloat( "ComboBox.buttonSeparatorWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ) );
buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" ); buttonSeparatorColor = UIManager.getColor( "ComboBox.buttonSeparatorColor" );
@@ -293,6 +296,7 @@ public class FlatComboBoxUI
buttonBackground = null; buttonBackground = null;
buttonEditableBackground = null; buttonEditableBackground = null;
buttonFocusedBackground = null; buttonFocusedBackground = null;
buttonFocusedEditableBackground = null;
buttonSeparatorColor = null; buttonSeparatorColor = null;
buttonDisabledSeparatorColor = null; buttonDisabledSeparatorColor = null;
buttonArrowColor = null; buttonArrowColor = null;
@@ -587,7 +591,7 @@ public class FlatComboBoxUI
// paint arrow button background // paint arrow button background
if( enabled && !isCellRenderer && arrowButton.isVisible() ) { if( enabled && !isCellRenderer && arrowButton.isVisible() ) {
Color buttonColor = paintButton Color buttonColor = paintButton
? buttonEditableBackground ? (buttonFocusedEditableBackground != null && isPermanentFocusOwner( comboBox ) ? buttonFocusedEditableBackground : buttonEditableBackground)
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox ) : (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground) ? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground)
: buttonBackground; : buttonBackground;

View File

@@ -108,10 +108,8 @@ public class FlatPopupFactory
} }
} }
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing ) if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents ); return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, isForceHeavyWeight( owner, contents, x, y ) ), owner, contents );
// macOS and Linux adds drop shadow to heavy weight popups // macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) { if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
@@ -131,12 +129,8 @@ public class FlatPopupFactory
return popup; return popup;
} }
// check whether popup overlaps a heavy weight component
if( !forceHeavyWeight && overlapsHeavyWeightComponent( owner, contents, x, y ) )
forceHeavyWeight = true;
// create drop shadow popup // create drop shadow popup
Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ); Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, isForceHeavyWeight( owner, contents, x, y ) );
GraphicsConfiguration gc = (owner != null) ? owner.getGraphicsConfiguration() : null; GraphicsConfiguration gc = (owner != null) ? owner.getGraphicsConfiguration() : null;
return (gc != null && gc.isTranslucencyCapable()) return (gc != null && gc.isTranslucencyCapable())
? new DropShadowPopup( popupForScreenOfOwner, owner, contents ) ? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
@@ -226,6 +220,11 @@ public class FlatPopupFactory
} }
} }
private static boolean isForceHeavyWeight( Component owner, Component contents, int x, int y ) {
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
return forceHeavyWeight || hasVisibleGlassPane( owner ) || overlapsHeavyWeightComponent( owner, contents, x, y );
}
private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) { private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
Object value = getOption( owner, contents, clientKey, uiKey ); Object value = getOption( owner, contents, clientKey, uiKey );
return (value instanceof Boolean) ? (Boolean) value : false; return (value instanceof Boolean) ? (Boolean) value : false;
@@ -469,6 +468,18 @@ public class FlatPopupFactory
//---- fixes -------------------------------------------------------------- //---- fixes --------------------------------------------------------------
private static boolean hasVisibleGlassPane( Component owner ) {
if( owner == null )
return false;
Window window = SwingUtilities.windowForComponent( owner );
if( !(window instanceof RootPaneContainer) )
return false;
Component glassPane = ((RootPaneContainer)window).getGlassPane();
return (glassPane != null && glassPane.isVisible());
}
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) { private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
if( owner == null ) if( owner == null )
return false; return false;

View File

@@ -227,7 +227,13 @@ public class FlatSliderUI
/** @since 2 */ /** @since 2 */
protected void applyStyle( Object style ) { protected void applyStyle( Object style ) {
Dimension oldThumbSize = thumbSize;
int oldFocusWidth = focusWidth;
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty ); oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
if( !thumbSize.equals( oldThumbSize ) || focusWidth != oldFocusWidth )
calculateGeometry();
} }
/** @since 2 */ /** @since 2 */

View File

@@ -34,7 +34,6 @@ import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.Action; import javax.swing.Action;
@@ -66,6 +65,7 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon; import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatUIUtils.FlatPropertyWatcher;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -189,29 +189,26 @@ public class FlatTableUI
if( rowHeight > 0 ) if( rowHeight > 0 )
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) ); LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table ); if( !showHorizontalLines ) {
if( watcher != null ) FlatPropertyWatcher.runIfNotChanged( table, "showHorizontalLines", () -> {
watcher.enabled = false; oldShowHorizontalLines = table.getShowHorizontalLines();
table.setShowHorizontalLines( false );
if( !showHorizontalLines && (watcher == null || !watcher.showHorizontalLinesChanged) ) { } );
oldShowHorizontalLines = table.getShowHorizontalLines();
table.setShowHorizontalLines( false );
} }
if( !showVerticalLines && (watcher == null || !watcher.showVerticalLinesChanged) ) { if( !showVerticalLines ) {
oldShowVerticalLines = table.getShowVerticalLines(); FlatPropertyWatcher.runIfNotChanged( table, "showVerticalLines", () -> {
table.setShowVerticalLines( false ); oldShowVerticalLines = table.getShowVerticalLines();
table.setShowVerticalLines( false );
} );
} }
if( intercellSpacing != null && (watcher == null || !watcher.intercellSpacingChanged) ) { if( intercellSpacing != null ) {
oldIntercellSpacing = table.getIntercellSpacing(); FlatPropertyWatcher.runIfNotChanged( table, "rowMargin", () -> {
table.setIntercellSpacing( intercellSpacing ); oldIntercellSpacing = table.getIntercellSpacing();
table.setIntercellSpacing( intercellSpacing );
} );
} }
if( watcher != null )
watcher.enabled = true;
else
table.addPropertyChangeListener( new FlatTablePropertyWatcher() );
// install boolean renderer // install boolean renderer
oldBooleanRenderer = table.getDefaultRenderer( Boolean.class ); oldBooleanRenderer = table.getDefaultRenderer( Boolean.class );
if( oldBooleanRenderer instanceof UIResource ) if( oldBooleanRenderer instanceof UIResource )
@@ -231,25 +228,24 @@ public class FlatTableUI
oldStyleValues = null; oldStyleValues = null;
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
if( watcher != null )
watcher.enabled = false;
// restore old show horizontal/vertical lines (if not modified) // restore old show horizontal/vertical lines (if not modified)
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() && if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() ) {
(watcher == null || !watcher.showHorizontalLinesChanged) ) FlatPropertyWatcher.runIfNotChanged( table, "showHorizontalLines", () -> {
table.setShowHorizontalLines( true ); table.setShowHorizontalLines( true );
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() && } );
(watcher == null || !watcher.showVerticalLinesChanged) ) }
table.setShowVerticalLines( true ); if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() ) {
FlatPropertyWatcher.runIfNotChanged( table, "showVerticalLines", () -> {
table.setShowVerticalLines( true );
} );
}
// restore old intercell spacing (if not modified) // restore old intercell spacing (if not modified)
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) && if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) ) {
(watcher == null || !watcher.intercellSpacingChanged) ) FlatPropertyWatcher.runIfNotChanged( table, "rowMargin", () -> {
table.setIntercellSpacing( oldIntercellSpacing ); table.setIntercellSpacing( oldIntercellSpacing );
} );
if( watcher != null ) }
watcher.enabled = true;
// uninstall boolean renderer // uninstall boolean renderer
if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) { if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) {
@@ -938,48 +934,6 @@ public class FlatTableUI
} }
} }
//---- class FlatTablePropertyWatcher -------------------------------------
/**
* Listener that watches for change of some table properties from application code.
* This information is used in {@link FlatTableUI#installDefaults()} and
* {@link FlatTableUI#uninstallDefaults()} to decide whether FlatLaf modifies those properties.
* If they are modified in application code, FlatLaf no longer changes them.
*
* The listener is added once for each table, but never removed.
* So switching Laf/theme reuses existing listener.
*/
private static class FlatTablePropertyWatcher
implements PropertyChangeListener
{
boolean enabled = true;
boolean showHorizontalLinesChanged;
boolean showVerticalLinesChanged;
boolean intercellSpacingChanged;
static FlatTablePropertyWatcher get( JTable table ) {
for( PropertyChangeListener l : table.getPropertyChangeListeners() ) {
if( l instanceof FlatTablePropertyWatcher )
return (FlatTablePropertyWatcher) l;
}
return null;
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
if( !enabled )
return;
switch( e.getPropertyName() ) {
case "showHorizontalLines": showHorizontalLinesChanged = true; break;
case "showVerticalLines": showVerticalLinesChanged = true; break;
case "rowMargin": intercellSpacingChanged = true; break;
}
}
}
//---- class FlatBooleanRenderer ------------------------------------------ //---- class FlatBooleanRenderer ------------------------------------------
private static class FlatBooleanRenderer private static class FlatBooleanRenderer

View File

@@ -484,7 +484,7 @@ public class FlatTitlePane
} }
protected void frameStateChanged() { protected void frameStateChanged() {
if( window == null || rootPane.getWindowDecorationStyle() != JRootPane.FRAME ) if( window == null || rootPane.getWindowDecorationStyle() == JRootPane.NONE )
return; return;
updateVisibility(); updateVisibility();

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatUIUtils.FlatPropertyWatcher;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -144,11 +145,13 @@ public class FlatToolBarUI
hoverButtonGroupBackground = UIManager.getColor( "ToolBar.hoverButtonGroupBackground" ); hoverButtonGroupBackground = UIManager.getColor( "ToolBar.hoverButtonGroupBackground" );
// floatable // floatable
oldFloatable = null;
if( !UIManager.getBoolean( "ToolBar.floatable" ) ) { if( !UIManager.getBoolean( "ToolBar.floatable" ) ) {
oldFloatable = toolBar.isFloatable(); FlatPropertyWatcher.runIfNotChanged( toolBar, "floatable", () -> {
toolBar.setFloatable( false ); oldFloatable = toolBar.isFloatable();
} else toolBar.setFloatable( false );
oldFloatable = null; } );
}
} }
@Override @Override
@@ -158,7 +161,9 @@ public class FlatToolBarUI
hoverButtonGroupBackground = null; hoverButtonGroupBackground = null;
if( oldFloatable != null ) { if( oldFloatable != null ) {
toolBar.setFloatable( oldFloatable ); FlatPropertyWatcher.runIfNotChanged( toolBar, "floatable", () -> {
toolBar.setFloatable( oldFloatable );
} );
oldFloatable = null; oldFloatable = null;
} }
} }

View File

@@ -44,6 +44,8 @@ import java.awt.geom.Path2D;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -1413,4 +1415,47 @@ debug*/
return delegate.isBorderOpaque(); return delegate.isBorderOpaque();
} }
} }
//---- class FlatPropertyWatcher ------------------------------------------
/**
* Listener that watches for change of a property from application code.
* This information can be used to decide whether FlatLaf modifies the property.
* If it is modified in application code, FlatLaf no longer changes it.
* <p>
* The listener is added once for a property, but never removed.
* So switching Laf/theme reuses existing listener.
*/
static class FlatPropertyWatcher
implements PropertyChangeListener
{
private boolean changed;
static void runIfNotChanged( JComponent c, String propName, Runnable runnable ) {
FlatPropertyWatcher watcher = getOrInstall( c, propName );
if( watcher.changed )
return;
runnable.run();
watcher.changed = false;
}
private static FlatPropertyWatcher getOrInstall( JComponent c, String propName ) {
for( PropertyChangeListener l : c.getPropertyChangeListeners( propName ) ) {
if( l instanceof FlatPropertyWatcher )
return (FlatPropertyWatcher) l;
}
FlatPropertyWatcher watcher = new FlatPropertyWatcher();
c.addPropertyChangeListener( propName, watcher );
return watcher;
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
changed = true;
}
}
} }

View File

@@ -199,8 +199,9 @@ public class TestFlatStyleableInfo
"disabledForeground", Color.class, "disabledForeground", Color.class,
"buttonBackground", Color.class, "buttonBackground", Color.class,
"buttonFocusedBackground", Color.class,
"buttonEditableBackground", Color.class, "buttonEditableBackground", Color.class,
"buttonFocusedBackground", Color.class,
"buttonFocusedEditableBackground", Color.class,
"buttonSeparatorWidth", float.class, "buttonSeparatorWidth", float.class,
"buttonSeparatorColor", Color.class, "buttonSeparatorColor", Color.class,
"buttonDisabledSeparatorColor", Color.class, "buttonDisabledSeparatorColor", Color.class,

View File

@@ -388,8 +388,9 @@ public class TestFlatStyleableValue
testColor( c, ui, "disabledForeground" ); testColor( c, ui, "disabledForeground" );
testColor( c, ui, "buttonBackground" ); testColor( c, ui, "buttonBackground" );
testColor( c, ui, "buttonFocusedBackground" );
testColor( c, ui, "buttonEditableBackground" ); testColor( c, ui, "buttonEditableBackground" );
testColor( c, ui, "buttonFocusedBackground" );
testColor( c, ui, "buttonFocusedEditableBackground" );
testFloat( c, ui, "buttonSeparatorWidth" ); testFloat( c, ui, "buttonSeparatorWidth" );
testColor( c, ui, "buttonSeparatorColor" ); testColor( c, ui, "buttonSeparatorColor" );
testColor( c, ui, "buttonDisabledSeparatorColor" ); testColor( c, ui, "buttonDisabledSeparatorColor" );

View File

@@ -43,7 +43,8 @@ public class FlatDesktop
public static boolean isSupported( Action action ) { public static boolean isSupported( Action action ) {
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
try { try {
return Desktop.getDesktop().isSupported( Enum.valueOf( Desktop.Action.class, action.name() ) ); return Desktop.isDesktopSupported() &&
Desktop.getDesktop().isSupported( Enum.valueOf( Desktop.Action.class, action.name() ) );
} catch( Exception ex ) { } catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex ); LoggingFacade.INSTANCE.logSevere( null, ex );
return false; return false;

View File

@@ -524,7 +524,9 @@ public class FlatSVGIcon
private URL getIconURL( String name, boolean dark ) { private URL getIconURL( String name, boolean dark ) {
if( dark ) { if( dark ) {
int dotIndex = name.lastIndexOf( '.' ); int dotIndex = name.lastIndexOf( '.' );
name = name.substring( 0, dotIndex ) + "_dark" + name.substring( dotIndex ); name = (dotIndex > 0)
? name.substring( 0, dotIndex ) + "_dark" + name.substring( dotIndex )
: name + "_dark";
} }
ClassLoader cl = (classLoader != null) ? classLoader : FlatSVGIcon.class.getClassLoader(); ClassLoader cl = (classLoader != null) ? classLoader : FlatSVGIcon.class.getClassLoader();

View File

@@ -777,6 +777,9 @@ public class FlatUIDefaultsInspector
@SuppressWarnings( "FormatString" ) // Error Prone @SuppressWarnings( "FormatString" ) // Error Prone
private static String color2hex( Color color ) { private static String color2hex( Color color ) {
if( color == null )
return "";
int rgb = color.getRGB(); int rgb = color.getRGB();
boolean hasAlpha = color.getAlpha() != 255; boolean hasAlpha = color.getAlpha() != 255;
@@ -1018,28 +1021,36 @@ public class FlatUIDefaultsInspector
item = (Item) value; item = (Item) value;
init( table, item.key, isSelected, row ); init( table, item.key, isSelected, row );
// reset background, foreground and icon // get color of value
if( !(item.value instanceof Color) ) { valueColor = null;
if( item.value instanceof Color )
valueColor = (item.info instanceof Color[]) ? ((Color[])item.info)[0] : (Color) item.value;
else if( item.value instanceof FlatLineBorder )
valueColor = ((FlatLineBorder)item.value).getLineColor();
// reset background and foreground
if( valueColor == null ) {
setBackground( null ); setBackground( null );
setForeground( null ); setForeground( null );
} }
if( !(item.value instanceof Icon) )
setIcon( null );
// value to string // value to string
value = item.getValueAsString(); value = item.getValueAsString();
super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column ); super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
if( item.value instanceof Color ) { // set foreground, if value has color
Color color = (item.info instanceof Color[]) ? ((Color[])item.info)[0] : (Color) item.value; if( valueColor != null ) {
boolean isDark = new HSLColor( color ).getLuminance() < 70 && color.getAlpha() >= 128; boolean isDark = new HSLColor( valueColor ).getLuminance() < 70 && valueColor.getAlpha() >= 128;
valueColor = color;
setForeground( isDark ? Color.white : Color.black ); setForeground( isDark ? Color.white : Color.black );
} else if( item.value instanceof Icon ) { }
// set icon
if( item.value instanceof Icon ) {
Icon icon = (Icon) item.value; Icon icon = (Icon) item.value;
setIcon( new SafeIcon( icon ) ); setIcon( new SafeIcon( icon ) );
} } else
setIcon( null );
// set tooltip // set tooltip
String toolTipText = (item.value instanceof Object[]) String toolTipText = (item.value instanceof Object[])
@@ -1056,7 +1067,7 @@ public class FlatUIDefaultsInspector
@Override @Override
protected void paintComponent( Graphics g ) { protected void paintComponent( Graphics g ) {
if( item.value instanceof Color ) { if( valueColor != null ) {
int width = getWidth(); int width = getWidth();
int height = getHeight(); int height = getHeight();
Color background = valueColor; Color background = valueColor;

View File

@@ -184,6 +184,7 @@ buttonDisabledArrowColor java.awt.Color
buttonDisabledSeparatorColor java.awt.Color buttonDisabledSeparatorColor java.awt.Color
buttonEditableBackground java.awt.Color buttonEditableBackground java.awt.Color
buttonFocusedBackground java.awt.Color buttonFocusedBackground java.awt.Color
buttonFocusedEditableBackground java.awt.Color
buttonHoverArrowColor java.awt.Color buttonHoverArrowColor java.awt.Color
buttonPressedArrowColor java.awt.Color buttonPressedArrowColor java.awt.Color
buttonSeparatorColor java.awt.Color buttonSeparatorColor java.awt.Color

View File

@@ -235,6 +235,7 @@ ComboBox.buttonDisabledArrowColor #ababab HSL 0 0 67 javax.swing.plaf.C
ComboBox.buttonDisabledSeparatorColor #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonDisabledSeparatorColor #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonEditableBackground #cccccc HSL 0 0 80 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonEditableBackground #cccccc HSL 0 0 80 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonFocusedBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonFocusedBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonFocusedEditableBackground #ffff44 HSL 60 100 63 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonHighlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonHighlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonHoverArrowColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonHoverArrowColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
ComboBox.buttonPressedArrowColor #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI] ComboBox.buttonPressedArrowColor #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]

View File

@@ -182,6 +182,7 @@ ComboBox.buttonEditableBackground = #ccc
ComboBox.focusedBackground = #ff8 ComboBox.focusedBackground = #ff8
ComboBox.buttonFocusedBackground = #ff0 ComboBox.buttonFocusedBackground = #ff0
ComboBox.buttonFocusedEditableBackground = #ff4
ComboBox.popupBackground = #ffc ComboBox.popupBackground = #ffc

View File

@@ -226,6 +226,7 @@ ComboBox.buttonDisabledArrowColor
ComboBox.buttonDisabledSeparatorColor ComboBox.buttonDisabledSeparatorColor
ComboBox.buttonEditableBackground ComboBox.buttonEditableBackground
ComboBox.buttonFocusedBackground ComboBox.buttonFocusedBackground
ComboBox.buttonFocusedEditableBackground
ComboBox.buttonHighlight ComboBox.buttonHighlight
ComboBox.buttonHoverArrowColor ComboBox.buttonHoverArrowColor
ComboBox.buttonPressedArrowColor ComboBox.buttonPressedArrowColor

View File

@@ -15,7 +15,7 @@
# #
flatlaf.releaseVersion = 3.7 flatlaf.releaseVersion = 3.7
flatlaf.developmentVersion = 3.8-SNAPSHOT flatlaf.developmentVersion = 3.7.1-SNAPSHOT
org.gradle.configuration-cache = true org.gradle.configuration-cache = true
# org.gradle.warning.mode = all # org.gradle.warning.mode = all