Window decorations: support toolbox-style "small" window title bar (issue #659)

This commit is contained in:
Karl Tauber
2023-07-30 15:26:44 +02:00
parent dfe4404a17
commit 8e3c8ba6c5
22 changed files with 627 additions and 57 deletions

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.IllegalComponentStateException;
import java.awt.Window;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
@@ -124,6 +126,7 @@ public interface FlatClientProperties
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
/**
@@ -266,6 +269,7 @@ public interface FlatClientProperties
*/
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
//---- Popup --------------------------------------------------------------
/**
@@ -305,6 +309,7 @@ public interface FlatClientProperties
*/
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
//---- JProgressBar -------------------------------------------------------
/**
@@ -323,6 +328,7 @@ public interface FlatClientProperties
*/
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
//---- JRootPane ----------------------------------------------------------
/**
@@ -472,6 +478,37 @@ public interface FlatClientProperties
*/
String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight";
/**
* Specifies the style of the window title bar.
* Besides the default title bar style, you can use a Utility-style title bar,
* which is smaller than the default title bar.
* <p>
* On Windows 10/11, this requires FlatLaf window decorations.
* On macOS, Java supports this out of the box.
* <p>
* Note that this client property must be set before the window becomes displayable.
* Otherwise an {@link IllegalComponentStateException} is thrown.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #WINDOW_STYLE_SMALL}
*
* @since 3.2
*/
String WINDOW_STYLE = "Window.style";
/**
* The window has Utility-style title bar, which is smaller than default title bar.
* <p>
* This is the same as using {@link Window#setType}( {@link Window.Type#UTILITY} ).
*
* @see #WINDOW_STYLE
* @since 3.2
*/
String WINDOW_STYLE_SMALL = "small";
//---- JScrollBar / JScrollPane -------------------------------------------
/**
@@ -490,6 +527,7 @@ public interface FlatClientProperties
*/
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JSplitPane ---------------------------------------------------------
/**
@@ -524,6 +562,7 @@ public interface FlatClientProperties
*/
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
//---- JTabbedPane --------------------------------------------------------
/**
@@ -919,6 +958,7 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
//---- JTextField ---------------------------------------------------------
/**
@@ -1088,6 +1128,7 @@ public interface FlatClientProperties
*/
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
//---- JToggleButton ------------------------------------------------------
/**
@@ -1129,6 +1170,7 @@ public interface FlatClientProperties
*/
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
//---- JTree --------------------------------------------------------------
/**
@@ -1148,6 +1190,7 @@ public interface FlatClientProperties
*/
String TREE_PAINT_SELECTION = "JTree.paintSelection";
//---- helper methods -----------------------------------------------------
/**

View File

@@ -27,8 +27,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -548,7 +552,7 @@ class UIDefaultsLoader
case INTEGERORFLOAT:return parseIntegerOrFloat( value );
case FLOAT: return parseFloat( value );
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
case ICON: return parseInstance( value, addonClassLoaders );
case ICON: return parseInstance( value, resolver, addonClassLoaders );
case INSETS: return parseInsets( value );
case DIMENSION: return parseDimension( value );
case COLOR: return parseColorOrFunction( value, resolver );
@@ -557,7 +561,7 @@ class UIDefaultsLoader
case SCALEDFLOAT: return parseScaledFloat( value );
case SCALEDINSETS: return parseScaledInsets( value );
case SCALEDDIMENSION:return parseScaledDimension( value );
case INSTANCE: return parseInstance( value, addonClassLoaders );
case INSTANCE: return parseInstance( value, resolver, addonClassLoaders );
case CLASS: return parseClass( value, addonClassLoaders );
case GRAYFILTER: return parseGrayFilter( value );
case UNKNOWN:
@@ -623,7 +627,7 @@ class UIDefaultsLoader
throws IllegalArgumentException
{
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
@@ -638,13 +642,29 @@ class UIDefaultsLoader
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value, addonClassLoaders );
return parseInstance( value, resolver, addonClassLoaders );
}
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
private static Object parseInstance( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
return (LazyValue) t -> {
try {
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
if( value.indexOf( ',' ) >= 0 ) {
// Syntax: className,param1,param2,...
List<String> parts = splitFunctionParams( value, ',' );
String className = parts.get( 0 );
Class<?> cls = findClass( className, addonClassLoaders );
Constructor<?>[] constructors = cls.getDeclaredConstructors();
Object result = invokeConstructorOrStaticMethod( constructors, parts, resolver );
if( result != null )
return result;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + className
+ "': no constructor found for parameters '"
+ value.substring( value.indexOf( ',' + 1 ) ) + "'.", null );
return null;
} else
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
return null;
@@ -1466,6 +1486,86 @@ class UIDefaultsLoader
return strs;
}
private static Object invokeConstructorOrStaticMethod( Executable[] constructorsOrMethods,
List<String> parts, Function<String, String> resolver )
throws Exception
{
// order constructors/methods by parameter types:
// - String parameters to the end
// - int before float parameters
constructorsOrMethods = constructorsOrMethods.clone();
Arrays.sort( constructorsOrMethods, (c1, c2) -> {
Class<?>[] ptypes1 = c1.getParameterTypes();
Class<?>[] ptypes2 = c2.getParameterTypes();
if( ptypes1.length != ptypes2.length )
return ptypes1.length - ptypes2.length;
for( int i = 0; i < ptypes1.length; i++ ) {
Class<?> pt1 = ptypes1[i];
Class<?> pt2 = ptypes2[i];
if( pt1 == pt2 )
continue;
// order methods with String parameters to the end
if( pt1 == String.class )
return 2;
if( pt2 == String.class )
return -2;
// order int before float
if( pt1 == int.class )
return -1;
if( pt2 == int.class )
return 1;
}
return 0;
} );
// search for best constructor/method for given parameter values
for( Executable cm : constructorsOrMethods ) {
if( cm.getParameterCount() != parts.size() - 1 )
continue;
Object[] params = parseMethodParams( cm.getParameterTypes(), parts, resolver );
if( params == null )
continue;
// invoke constructor or static method
if( cm instanceof Constructor )
return ((Constructor<?>)cm).newInstance( params );
else
return ((Method)cm).invoke( null, params );
}
return null;
}
private static Object[] parseMethodParams( Class<?>[] paramTypes, List<String> parts, Function<String, String> resolver ) {
Object[] params = new Object[paramTypes.length];
try {
for( int i = 0; i < params.length; i++ ) {
Class<?> paramType = paramTypes[i];
String paramValue = parts.get( i + 1 );
if( paramType == String.class )
params[i] = paramValue;
else if( paramType == boolean.class )
params[i] = parseBoolean( paramValue );
else if( paramType == int.class )
params[i] = parseInteger( paramValue );
else if( paramType == float.class )
params[i] = parseFloat( paramValue );
else if( paramType == Color.class )
params[i] = parseColorOrFunction( resolver.apply( paramValue ), resolver );
else
return null; // unsupported parameter type
}
} catch( IllegalArgumentException ex ) {
return null; // failed to parse parameter for expected parameter type
}
return params;
}
/**
* For use in LazyValue to get value for given key from UIManager and report error
* if not found. If key is prefixed by '?', then no error is reported.

View File

@@ -21,7 +21,6 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
@@ -30,6 +29,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* Base class for window icons.
*
* @uiDefault TitlePane.buttonSize Dimension
* @uiDefault TitlePane.buttonSymbolHeight int
* @uiDefault TitlePane.buttonHoverBackground Color
* @uiDefault TitlePane.buttonPressedBackground Color
*
@@ -38,17 +38,22 @@ import com.formdev.flatlaf.util.HiDPIUtils;
public abstract class FlatWindowAbstractIcon
extends FlatAbstractIcon
{
private final int symbolHeight;
private final Color hoverBackground;
private final Color pressedBackground;
public FlatWindowAbstractIcon() {
this( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
/** @since 3.2 */
protected FlatWindowAbstractIcon( String windowStyle ) {
this( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedBackground", windowStyle ) );
}
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
/** @since 3.2 */
protected FlatWindowAbstractIcon( Dimension size, int symbolHeight, Color hoverBackground, Color pressedBackground ) {
super( size.width, size.height, null );
this.symbolHeight = symbolHeight;
this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
}
@@ -80,4 +85,9 @@ public abstract class FlatWindowAbstractIcon
protected Color getForeground( Component c ) {
return c.getForeground();
}
/** @since 3.2 */
protected int getSymbolHeight() {
return symbolHeight;
}
}

View File

@@ -21,8 +21,8 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -38,18 +38,27 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatWindowCloseIcon
extends FlatWindowAbstractIcon
{
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
private final Color hoverForeground;
private final Color pressedForeground;
public FlatWindowCloseIcon() {
super( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.closeHoverBackground" ),
UIManager.getColor( "TitlePane.closePressedBackground" ) );
this( null );
}
/** @since 3.2 */
public FlatWindowCloseIcon( String windowStyle ) {
super( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.closePressedBackground", windowStyle ) );
hoverForeground = FlatUIUtils.getSubUIColor( "TitlePane.closeHoverForeground", windowStyle );
pressedForeground = FlatUIUtils.getSubUIColor( "TitlePane.closePressedForeground", windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int iwh = (int) (getSymbolHeight() * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int ix2 = ix + iwh - 1;

View File

@@ -27,11 +27,17 @@ public class FlatWindowIconifyIcon
extends FlatWindowAbstractIcon
{
public FlatWindowIconifyIcon() {
this( null );
}
/** @since 3.2 */
public FlatWindowIconifyIcon( String windowStyle ) {
super( windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iw = (int) (10 * scaleFactor);
int iw = (int) (getSymbolHeight() * scaleFactor);
int ih = (int) scaleFactor;
int ix = x + ((width - iw) / 2);
int iy = y + ((height - ih) / 2);

View File

@@ -29,11 +29,17 @@ public class FlatWindowMaximizeIcon
extends FlatWindowAbstractIcon
{
public FlatWindowMaximizeIcon() {
this( null );
}
/** @since 3.2 */
public FlatWindowMaximizeIcon( String windowStyle ) {
super( windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int iwh = (int) (getSymbolHeight() * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;

View File

@@ -32,18 +32,24 @@ public class FlatWindowRestoreIcon
extends FlatWindowAbstractIcon
{
public FlatWindowRestoreIcon() {
this( null );
}
/** @since 3.2 */
public FlatWindowRestoreIcon( String windowStyle ) {
super( windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int iwh = (int) (getSymbolHeight() * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
int arcOuter = (int) (arc + (1.5 * scaleFactor));
int rwh = (int) (8 * scaleFactor);
int rwh = (int) ((getSymbolHeight() - 2) * scaleFactor);
int ro2 = iwh - rwh;
// upper-right rectangle

View File

@@ -23,6 +23,7 @@ import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.IllegalComponentStateException;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
@@ -366,6 +367,11 @@ public class FlatRootPaneUI
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
rootPane.revalidate();
break;
case FlatClientProperties.WINDOW_STYLE:
if( rootPane.isDisplayable() )
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
break;
}
}

View File

@@ -97,6 +97,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarTitleMinimumGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
@@ -110,29 +111,30 @@ public class FlatTitlePane
{
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
/** @since 2.5 */ protected final Font titleFont = UIManager.getFont( "TitlePane.font" );
protected final Color activeBackground = UIManager.getColor( "TitlePane.background" );
protected final Color inactiveBackground = UIManager.getColor( "TitlePane.inactiveBackground" );
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
/** @since 2.5 */ protected final Font titleFont;
protected final Color activeBackground;
protected final Color inactiveBackground;
protected final Color activeForeground;
protected final Color inactiveForeground;
protected final Color embeddedForeground;
protected final Color borderColor;
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
/** @since 2.5 */ protected final boolean showIconInDialogs = FlatUIUtils.getUIBoolean( "TitlePane.showIconInDialogs", true );
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
/** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 60 );
/** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 30 );
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
/** @since 2.4 */ protected final boolean showIconBesideTitle = UIManager.getBoolean( "TitlePane.showIconBesideTitle" );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 );
/** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 );
/** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 );
/** @since 2 */ protected final boolean showIcon;
/** @since 2.5 */ protected final boolean showIconInDialogs;
/** @since 2 */ protected final int noIconLeftGap;
protected final Dimension iconSize;
/** @since 2.4 */ protected final int titleMinimumWidth;
/** @since 2.4 */ protected final int buttonMinimumWidth;
protected final int buttonMaximizedHeight;
protected final boolean centerTitle;
protected final boolean centerTitleIfMenuBarEmbedded;
/** @since 2.4 */ protected final boolean showIconBesideTitle;
protected final int menuBarTitleGap;
/** @since 2.4 */ protected final int menuBarTitleMinimumGap;
/** @since 2.4 */ protected final int menuBarResizeHeight;
protected final JRootPane rootPane;
protected final String windowStyle;
protected JPanel leftPanel;
protected JLabel iconLabel;
@@ -151,6 +153,34 @@ public class FlatTitlePane
public FlatTitlePane( JRootPane rootPane ) {
this.rootPane = rootPane;
Window w = SwingUtilities.getWindowAncestor( rootPane );
String defaultWindowStyle = (w != null && w.getType() == Window.Type.UTILITY) ? WINDOW_STYLE_SMALL : null;
windowStyle = clientProperty( rootPane, WINDOW_STYLE, defaultWindowStyle, String.class );
titleFont = FlatUIUtils.getSubUIFont( "TitlePane.font", windowStyle );
activeBackground = FlatUIUtils.getSubUIColor( "TitlePane.background", windowStyle );
inactiveBackground = FlatUIUtils.getSubUIColor( "TitlePane.inactiveBackground", windowStyle );
activeForeground = FlatUIUtils.getSubUIColor( "TitlePane.foreground", windowStyle );
inactiveForeground = FlatUIUtils.getSubUIColor( "TitlePane.inactiveForeground", windowStyle );
embeddedForeground = FlatUIUtils.getSubUIColor( "TitlePane.embeddedForeground", windowStyle );
// not using windowStyle here because TitlePane.borderColor is also used in FlatRootPaneUI
borderColor = UIManager.getColor( "TitlePane.borderColor" );
showIcon = FlatUIUtils.getSubUIBoolean( "TitlePane.showIcon", windowStyle, true );
showIconInDialogs = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconInDialogs", windowStyle, true );
noIconLeftGap = FlatUIUtils.getSubUIInt( "TitlePane.noIconLeftGap", windowStyle, 8 );
iconSize = FlatUIUtils.getSubUIDimension( "TitlePane.iconSize", windowStyle );
titleMinimumWidth = FlatUIUtils.getSubUIInt( "TitlePane.titleMinimumWidth", windowStyle, 60 );
buttonMinimumWidth = FlatUIUtils.getSubUIInt( "TitlePane.buttonMinimumWidth", windowStyle, 30 );
buttonMaximizedHeight = FlatUIUtils.getSubUIInt( "TitlePane.buttonMaximizedHeight", windowStyle, 0 );
centerTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.centerTitle", windowStyle, false );
centerTitleIfMenuBarEmbedded = FlatUIUtils.getSubUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", windowStyle, true );
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
menuBarResizeHeight = FlatUIUtils.getSubUIInt( "TitlePane.menuBarResizeHeight", windowStyle, 4 );
handler = createHandler();
setBorder( createTitlePaneBorder() );
@@ -183,8 +213,8 @@ public class FlatTitlePane
setUI( new FlatTitleLabelUI() );
}
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
iconLabel.setBorder( new FlatEmptyBorder( FlatUIUtils.getSubUIInsets( "TitlePane.iconMargins", windowStyle ) ) );
titleLabel.setBorder( new FlatEmptyBorder( FlatUIUtils.getSubUIInsets( "TitlePane.titleMargins", windowStyle ) ) );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -311,7 +341,7 @@ public class FlatTitlePane
}
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
JButton button = new JButton( UIManager.getIcon( iconKey ) ) {
JButton button = new JButton( FlatUIUtils.getSubUIIcon( iconKey, windowStyle ) ) {
@Override
public Dimension getMinimumSize() {
// allow the button to shrink if space is rare

View File

@@ -39,10 +39,12 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JToolBar;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.RootPaneContainer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -156,6 +158,13 @@ public class FlatToolBarUI
}
}
@Override
protected RootPaneContainer createFloatingWindow( JToolBar toolbar ) {
RootPaneContainer floatingWindow = super.createFloatingWindow( toolbar );
floatingWindow.getRootPane().putClientProperty( FlatClientProperties.WINDOW_STYLE, FlatClientProperties.WINDOW_STYLE_SMALL );
return floatingWindow;
}
@Override
protected ContainerListener createToolBarContListener() {
return new ToolBarContListener() {

View File

@@ -46,6 +46,7 @@ import java.util.IdentityHashMap;
import java.util.WeakHashMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
@@ -166,6 +167,88 @@ public class FlatUIUtils
return defaultValue;
}
/** @since 3.2 */
public static Color getSubUIColor( String key, String subKey ) {
if( subKey != null ) {
Color value = UIManager.getColor( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getColor( key );
}
/** @since 3.2 */
public static boolean getSubUIBoolean( String key, String subKey, boolean defaultValue ) {
if( subKey != null ) {
Object value = UIManager.get( buildSubKey( key, subKey ) );
if( value instanceof Boolean )
return (Boolean) value;
}
return getUIBoolean( key, defaultValue );
}
/** @since 3.2 */
public static int getSubUIInt( String key, String subKey, int defaultValue ) {
if( subKey != null ) {
Object value = UIManager.get( buildSubKey( key, subKey ) );
if( value instanceof Integer )
return (Integer) value;
}
return getUIInt( key, defaultValue );
}
/** @since 3.2 */
public static Insets getSubUIInsets( String key, String subKey ) {
if( subKey != null ) {
Insets value = UIManager.getInsets( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getInsets( key );
}
/** @since 3.2 */
public static Dimension getSubUIDimension( String key, String subKey ) {
if( subKey != null ) {
Dimension value = UIManager.getDimension( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getDimension( key );
}
/** @since 3.2 */
public static Icon getSubUIIcon( String key, String subKey ) {
if( subKey != null ) {
Icon value = UIManager.getIcon( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getIcon( key );
}
/** @since 3.2 */
public static Font getSubUIFont( String key, String subKey ) {
if( subKey != null ) {
Font value = UIManager.getFont( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getFont( key );
}
/**
* Inserts {@code subKey} at last dot in {@code key}.
* <p>
* E.g. {@code buildSubKey( "TitlePane.font", "small" )} returns {@code "TitlePane.small.font"}.
*/
private static String buildSubKey( String key, String subKey ) {
int dot = key.lastIndexOf( '.' );
return (dot >= 0)
? key.substring( 0, dot ) + '.' + subKey + '.' + key.substring( dot + 1 )
: key;
}
/** @since 1.1.2 */
public static boolean getBoolean( JComponent c, String systemPropertyKey,
String clientPropertyKey, String uiKey, boolean defaultValue )

View File

@@ -808,6 +808,7 @@ TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22
TitlePane.buttonSymbolHeight = 10
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.showIconBesideTitle = false
@@ -829,6 +830,16 @@ TitlePane.closePressedBackground = fade($TitlePane.closeHoverBackground,90%)
TitlePane.closeHoverForeground = #fff
TitlePane.closePressedForeground = #fff
# window style "small"
TitlePane.small.font = -1
TitlePane.small.showIcon = false
TitlePane.small.buttonSize = 30,20
TitlePane.small.buttonSymbolHeight = 8
TitlePane.small.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon, small
TitlePane.small.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon, small
TitlePane.small.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon, small
TitlePane.small.restoreIcon = com.formdev.flatlaf.icons.FlatWindowRestoreIcon, small
#---- ToggleButton ----

View File

@@ -21,6 +21,7 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.util.Objects;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.UIDefaults.ActiveValue;
@@ -153,4 +154,102 @@ public class TestUIDefaultsLoader
new Font( name, style, size ),
((ActiveValue)UIDefaultsLoader.parseValue( "dummyFont", actualStyle, null )).createValue( null ) );
}
@Test
void parseInstance() {
String className = TestInstance.class.getName();
assertEquals( new TestInstance(), ((LazyValue)UIDefaultsLoader.parseValue( "dummyIcon", className, null )).createValue( null ) );
assertInstanceEquals( new TestInstance(), null );
assertInstanceEquals( new TestInstance( "some string" ), "some string" );
assertInstanceEquals( new TestInstance( false ), "false" );
assertInstanceEquals( new TestInstance( true ), "true" );
assertInstanceEquals( new TestInstance( 123 ), "123" );
assertInstanceEquals( new TestInstance( 123.456f ), "123.456" );
assertInstanceEquals( new TestInstance( Color.red ), "#f00" );
assertInstanceEquals( new TestInstance( "some string", true ), "some string, true" );
assertInstanceEquals( new TestInstance( "some string", true, 123 ), "some string, true, 123" );
assertInstanceEquals( new TestInstance( "some string", 123, true ), "some string, 123, true" );
assertInstanceEquals( new TestInstance( "some string", 123.456f, true ), "some string, 123.456, true" );
assertInstanceEquals( new TestInstance( 123, "some string" ), "123, some string" );
}
private void assertInstanceEquals( TestInstance expected, String params ) {
String value = TestInstance.class.getName() + (params != null ? "," + params : "");
assertEquals( expected, ((LazyValue)UIDefaultsLoader.parseValue( "dummyIcon", value, null )).createValue( null ) );
}
//---- class TestInstance -------------------------------------------------
public static class TestInstance
{
private String s;
private boolean b;
private int i;
private float f;
private Color color;
public TestInstance() {
}
public TestInstance( String s ) {
this.s = s;
}
public TestInstance( boolean b ) {
this.b = b;
}
public TestInstance( int i ) {
this.i = i;
}
public TestInstance( float f ) {
this.f = f;
}
public TestInstance( Color color ) {
this.color = color;
}
public TestInstance( String s, boolean b ) {
this.s = s;
this.b = b;
}
public TestInstance( String s, boolean b, int i ) {
this.s = s;
this.b = b;
this.i = i;
}
public TestInstance( String s, int i, boolean b ) {
this.s = s;
this.b = b;
this.i = i;
}
public TestInstance( String s, float f, boolean b ) {
this.s = s;
this.b = b;
this.f = f;
}
protected TestInstance( int i, String s ) {
this.s = s;
this.i = i;
}
@Override
public boolean equals( Object obj ) {
if( !(obj instanceof TestInstance) )
return false;
TestInstance inst = (TestInstance) obj;
return Objects.equals( s, inst.s ) &&
b == inst.b &&
i == inst.i &&
f == inst.f &&
Objects.equals( color, inst.color );
}
}
}