Compare commits

..

52 Commits
2.2 ... 2.4

Author SHA1 Message Date
Karl Tauber
f842530537 release 2.4 2022-07-13 23:43:02 +02:00
Karl Tauber
63077bbb19 Merge PR #565: Window title bar usability improvements (Windows 10/11 only) 2022-07-13 23:28:38 +02:00
Karl Tauber
4dad337377 Window decorations: fixed app icon hit test bounds if icon is shown beside title 2022-07-13 23:11:32 +02:00
Karl Tauber
10a965d765 Window decorations: option to show window icon beside window title, if menu bar is embedded or title is centered 2022-07-13 17:58:25 +02:00
Karl Tauber
3e9c9c9066 execute FlatLaf.initialize() and uninitialize() only for current laf
For NetBeans GUI builder, which invokes `FlatLaf.initialize()` multiple times for preview, but never invokes `FlatLaf.uninitialize()`.

(https://github.com/apache/netbeans/issues/4231)
2022-07-12 12:13:19 +02:00
Karl Tauber
8b5a738e65 Menus: avoid that SubMenuUsabilityHelper can be installed multiple times, which can freeze the application caused pushing multiple event queues and popping wrong event queue first
(e.g. NetBeans Form Editor invokes `FlatLaf.initialize()` but not `uninitialize()`)

(PR #490; https://github.com/apache/netbeans/issues/4231)
2022-07-12 10:33:53 +02:00
Karl Tauber
2c041dce3a Window decorations: add small resize area at top of embedded menu bar only if frame is resizable 2022-07-11 17:47:04 +02:00
Karl Tauber
ef151c68f4 Window decorations:
- improved title bar usability by using larger gaps and minimum sizes
- added minimum gap between embedded menu bar and window title
- fixed oscillating title while resizing window width
- fixed lost right-to-left component orientation in title bar when switching Laf
2022-07-11 17:28:30 +02:00
Karl Tauber
52feaac92a Window decorations: no longer reduce height of window title bar if it has an embedded menu bar and is maximized 2022-07-10 14:03:45 +02:00
Karl Tauber
cddbb3d7d4 Window decorations: make sure that a horizontal glue in embedded menu bar has a minimum width and is always visible 2022-07-10 13:57:37 +02:00
Karl Tauber
42764550e6 Window decorations: improved window title bar layout for small window widths:
- width of iconify/maximize/close buttons is reduced to give more space to embedded menu bar and title
- window title now has a minimum width to always allow moving window
2022-07-09 19:54:29 +02:00
Karl Tauber
6ee737b314 Window decorations: small area at top of embedded menu bar to resize window 2022-07-09 10:30:33 +02:00
Karl Tauber
f460ef7685 UI defaults dumps updated for commits b82ee2ef61 and 93e0496fd2 2022-07-09 10:19:54 +02:00
Karl Tauber
9977bcb468 Window decorations: do not center window title if embedded menu bar is empty or has no menus at left side, but some components at right side (issue #558) 2022-07-09 00:04:51 +02:00
Karl Tauber
7437d984c7 Theme Editor: accept @ as identifier character, which includes it in selection when double clicking e.g. on @background 2022-07-08 17:53:49 +02:00
Karl Tauber
5cd0b2403c Theme Editor: find/replace bar improvements:
- always use editor selection to search if `Ctrl+F` is pressed
- keep find/replace bar open if switching to another editor
- mark matches when switching to another editor
2022-07-08 17:52:08 +02:00
Karl Tauber
a372da22f3 Extras: FlatInspector:
- support embedding into SWT
- added "MigLayout visual padding" to tooltip

fixed typo in MigLayoutVisualPadding.java
2022-07-04 11:09:06 +02:00
Karl Tauber
8b10d3ba5a Native window decorations: fixed missing top window border in dark themes if window drop shadows are disabled in system settings (issue #554) 2022-07-02 23:26:34 +02:00
Karl Tauber
a8b15c6a12 MenuItem: fixed sometimes wrapped HTML text on HiDPI screens on Windows 2022-07-02 22:38:37 +02:00
Karl Tauber
23bac7e5fd Native window decorations: do not use window decorations if system property sun.java2d.opengl is true on Windows 10 (issue #540) 2022-07-02 00:29:29 +02:00
Karl Tauber
b82ee2ef61 Typography: no longer use Consolas or Courier New as monospaced font on Windows because they have bad vertically placement 2022-07-02 00:25:07 +02:00
Karl Tauber
b7761f4b71 HiDPIUtils: support rotated graphics (issue #557) 2022-07-01 15:34:04 +02:00
Karl Tauber
f9a4f9771c Testing: FlatPaintingStringTest:
- added "Fonts" combobox to test various fonts
- reworked/fixed text painting/sizing to get correct results
2022-06-07 11:03:34 +02:00
Karl Tauber
d2acb2c98a HiDPIUtils: reimplemented HiDPIUtils.scale() to make it easier to read and more understandable
(no longer re-using dx1 and dy2 variables for different kind of values)
2022-06-05 23:43:13 +02:00
Karl Tauber
d60bd5df14 FlatEmptyBorder: fixed possible NPE if passed component is null 2022-06-05 00:46:43 +02:00
Karl Tauber
73b6ca3762 ComboBox: fixed vertical alignment of text in popup list with text in combo box in IntelliJ/Darcula themes 2022-06-04 20:15:31 +02:00
Karl Tauber
6c18431a30 TableHeader: fixed exception when changing table structure (e.g. removing column) from a table header popup menu action (issue #532) 2022-05-31 18:56:06 +02:00
Karl Tauber
a49d20249f Gradle: do not set Multi-Release: true in META-INF/MANIFEST.MF if not needed 2022-05-31 15:35:56 +02:00
Karl Tauber
ad384acd57 updated sigtest for FlatLaf 2.3
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-28 18:43:37 +02:00
Karl Tauber
69851b7f3a release 2.3 2022-05-28 17:53:54 +02:00
Karl Tauber
92b53bf0df Merge PR #522: File chooser shortcuts panel 2022-05-28 17:46:12 +02:00
Karl Tauber
93e0496fd2 ToggleButton: button style "tab": added missing foreground colors for hover, focused and selected states (issue #535) 2022-05-28 15:09:04 +02:00
Karl Tauber
5151951f46 Button and ToggleButton: added missing foreground colors for hover, pressed, focused and selected states (issue #535) 2022-05-28 14:54:31 +02:00
Karl Tauber
58dbccec2d Table: optionally paint alternating rows below table if table is smaller than scroll pane (issue #504) 2022-05-25 11:18:22 +02:00
Karl Tauber
90de14d013 Native library: refactored loading of Windows native library from FlatWindowsNativeWindowBorder to FlatNativeLibrary to make it easier to add native libraries for other platforms (for issues #204 and #482) 2022-05-17 20:24:28 +02:00
Karl Tauber
5f961618bf Demo and Theme Editor: updated macOS related comments 2022-05-13 13:54:10 +02:00
Karl Tauber
37c375e2fa Theme Editor:
- support "themes" sub-directory
- added "generate Java class" checkbox to "New" dialog
2022-05-10 11:01:45 +02:00
Karl Tauber
1758c175ed FlatLafUIKeys.txt: added some missing UI defaults 2022-05-09 23:30:34 +02:00
Karl Tauber
96f2a02cfa UIDefaultsLoader: added over() color function to convert a translucent color into a solid color based on any background color 2022-05-09 23:28:40 +02:00
Karl Tauber
96d4bda6c8 Demo: hide accent color buttons (instead of disabling them) if not supported by selected theme 2022-05-09 22:51:38 +02:00
Karl Tauber
02cf6050a1 updates for PR #530:
- added @since tags
- changed `FlatToggleButton.setTabUnderlinePlacement()` implementation so that is behaves similar to `FlatTabbedPane.setTabIconPlacement()`
2022-05-09 22:28:34 +02:00
Karl Tauber
38cf32a2e9 Merge PR #530: ToggleButton: made underline placement configurable 2022-05-09 22:08:42 +02:00
Karl Tauber
2ae7589d14 Merge PR #525: Create Bundle_es.properties 2022-05-09 21:28:47 +02:00
Julien Fischer
bcb2e1f0a1 ToggleButton: made underline placement configurable
Supported values: TOP, LEFT, BOTTOM, or RIGHT
2022-05-06 12:07:50 +02:00
Karl Tauber
14932d3f07 Theme Editor: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-05 13:20:23 +02:00
Karl Tauber
c3b9dc397d Demo: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-01 11:45:36 +02:00
Karl Tauber
58b653f55d updated sigtest for FlatLaf 2.2
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-01 11:44:51 +02:00
Jesús Marín
1dcdc42dde Create Bundle_es.properties
Flatlaf Spanish translation
2022-04-29 14:51:11 +01:00
Karl Tauber
58a0a16985 IntelliJ Themes: fixed TitledBorder text color in "Monokai Pro" theme (issue #524) 2022-04-28 15:16:26 +02:00
Karl Tauber
a117243f14 FileChooser: use large system icons in shortcuts panel also in Java 8 to 16 2022-04-26 22:50:34 +02:00
Karl Tauber
22411060be FileChooser: improve layout for shortcuts panel (give it full height) 2022-04-25 23:49:15 +02:00
Karl Tauber
045263ae58 FileChooser: added (optional) shortcuts panel (issue #100) 2022-04-25 23:06:10 +02:00
62 changed files with 2756 additions and 319 deletions

View File

@@ -1,6 +1,72 @@
FlatLaf Change Log
==================
## 2.4
#### New features and improvements
- Native window decorations (Windows 10/11 only):
- There is now a small area at top of the embedded menu bar to resize the
window.
- Improved window title bar layout for small window widths:
- Width of iconify/maximize/close buttons is reduced (if necessary) to give
more space to embedded menu bar and title.
- Window title now has a minimum width to always allow moving window
(click-and-drag on window title). Instead, embedded menu bar is made
smaller.
- Option to show window icon beside window title, if menu bar is embedded or
title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`.
- No longer reduce height of window title bar if it has an embedded menu bar
and is maximized.
#### Fixed bugs
- ComboBox: Fixed vertical alignment of text in popup list with text in combo
box in IntelliJ/Darcula themes.
- Menus: Fixed application freeze under very special conditions (invoking
`FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has
submenus. See
[NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682)
for details.
- MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows.
- TableHeader: Fixed exception when changing table structure (e.g. removing
column) from a table header popup menu action. (issue #532)
- `HiDPIUtils.paintAtScale1x()` now supports rotated graphics. (issue #557)
- Typography: No longer use `Consolas` or `Courier New` as monospaced font on
Windows because they have bad vertically placement.
- Native window decorations (Windows 10/11 only):
- Do not center window title if embedded menu bar is empty or has no menus at
left side, but some components at right side. (issue #558)
- Do not use window decorations if system property `sun.java2d.opengl` is
`true` on Windows 10. (issue #540)
- Fixed missing top window border in dark themes if window drop shadows are
disabled in system settings. (issue #554; Windows 10 only)
- Right-to-left component orientation of title bar was lost when switching
theme.
## 2.3
#### New features and improvements
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
is empty/hidden. (issue #100)
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
focused and selected states. (issue #535)
- Table: Optionally paint alternating rows below table if table is smaller than
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
- ToggleButton: Made the underline placement of tab-style toggle buttons
configurable. (PR #530; issue #529)
- Added spanish translation. (PR #525)
#### Fixed bugs
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
(issue #524)
## 2.2
#### New features and improvements
@@ -141,7 +207,7 @@ FlatLaf Change Log
- Possibility to hide window title bar icon (for single window set client
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
value `TitlePane.showIcon` to `false`).
- OptionPane: Hide window title bar icon by default. Can be be made visibly by
- OptionPane: Hide window title bar icon by default. Can be made visibly by
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
- No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416)

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
val releaseVersion = "2.2"
val developmentVersion = "2.3-SNAPSHOT"
val releaseVersion = "2.4"
val developmentVersion = "2.5-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion

View File

@@ -61,8 +61,6 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
}
jar {
manifest.attributes( "Multi-Release" to "true" )
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 2.1
#Version 2.3
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -30,6 +30,9 @@ fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY = "JTextFiel
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize"
fld public final static java.lang.String STYLE = "FlatLaf.style"
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
@@ -72,6 +75,7 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement"
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
@@ -662,6 +666,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init()
fld public final static boolean isAARCH64
fld public final static boolean isJava_11_orLater
fld public final static boolean isJava_12_orLater
fld public final static boolean isJava_15_orLater
fld public final static boolean isJava_17_orLater
fld public final static boolean isJava_18_orLater
@@ -670,6 +675,7 @@ fld public final static boolean isJetBrainsJVM
fld public final static boolean isJetBrainsJVM_11_orLater
fld public final static boolean isKDE
fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
fld public final static boolean isMacOS_10_14_Mojave_orLater

View File

@@ -992,7 +992,22 @@ public interface FlatClientProperties
//---- JToggleButton ------------------------------------------------------
/**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* Placement of underline if toggle button type is {@link #BUTTON_TYPE_TAB}
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
* <strong>SupportedValues:</strong>
* {@link SwingConstants#BOTTOM} (default)
* {@link SwingConstants#TOP},
* {@link SwingConstants#LEFT} or
* {@link SwingConstants#RIGHT}
*
* @since 2.3
*/
String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement";
/**
* Thickness of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}

View File

@@ -103,7 +103,7 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper;
private boolean subMenuUsabilityHelperInstalled;
private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters;
@@ -232,6 +232,15 @@ public abstract class FlatLaf
@Override
public void initialize() {
// do not initialize if this is not the current look and feel
// This is only necessary for special Laf usage. E.g. in GUI builders,
// which may use multiple Lafs and may invoke this method directly.
// This avoids that listeners and factories are installed multiple times.
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
// this also avoids that listeners stay registered in the system.
if( UIManager.getLookAndFeel() != this )
return;
if( SystemInfo.isMacOS )
initializeAqua();
@@ -246,8 +255,7 @@ public abstract class FlatLaf
mnemonicHandler.install();
// install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper();
subMenuUsabilityHelper.install();
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) {
@@ -304,6 +312,10 @@ public abstract class FlatLaf
@Override
public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener
if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -329,9 +341,9 @@ public abstract class FlatLaf
}
// uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) {
subMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null;
if( subMenuUsabilityHelperInstalled ) {
SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelperInstalled = false;
}
// restore default link color

View File

@@ -59,6 +59,11 @@ class SubMenuUsabilityHelper
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
// Using a static field to ensure that there is only one instance in the system.
// Multiple instances would freeze the application.
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
private static SubMenuUsabilityHelper instance;
private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter;
private boolean changePending;
@@ -74,13 +79,22 @@ class SubMenuUsabilityHelper
private Rectangle invokerBounds;
void install() {
MenuSelectionManager.defaultManager().addChangeListener( this );
static synchronized boolean install() {
if( instance != null )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
}
void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this );
uninstallEventQueue();
static synchronized void uninstall() {
if( instance == null )
return;
MenuSelectionManager.defaultManager().removeChangeListener( instance );
instance.uninstallEventQueue();
instance = null;
}
@Override

View File

@@ -466,6 +466,10 @@ class UIDefaultsLoader
if( knownValueTypes == null ) {
// create lazy
knownValueTypes = new HashMap<>();
// system colors
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "windowBorder", ValueType.COLOR );
// SplitPane
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
@@ -780,6 +784,7 @@ class UIDefaultsLoader
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
case "shade": return parseColorMix( "#000", params, resolver, reportError );
case "contrast": return parseColorContrast( params, resolver, reportError );
case "over": return parseColorOver( params, resolver, reportError );
}
} finally {
parseColorDepth--;
@@ -847,7 +852,7 @@ class UIDefaultsLoader
int lightness = parsePercentage( params.get( 2 ) );
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
float[] hsl = new float[] { hue, saturation, lightness };
float[] hsl = { hue, saturation, lightness };
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
}
@@ -1041,6 +1046,33 @@ class UIDefaultsLoader
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
}
/**
* Syntax: over(foreground,background)
* - foreground: a foreground color (e.g. #f00) or a color function;
* the alpha of this color is used as weight to mix the two colors
* - background: a background color (e.g. #f00) or a color function
*/
private static Object parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
String foregroundStr = params.get( 0 );
String backgroundStr = params.get( 1 );
// parse foreground color
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
if( foreground == null || foreground.getAlpha() == 255 )
return foreground;
Color foreground2 = new Color( foreground.getRGB() );
// parse background color
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
if( background == null )
return foreground2;
// create new color
float weight = foreground.getAlpha() / 255f;
return new ColorUIResource( ColorFunctions.mix( foreground2, background, weight ) );
}
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError )
{

View File

@@ -78,20 +78,27 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.focusedBackground Color optional
* @uiDefault Button.focusedForeground Color optional
* @uiDefault Button.hoverBackground Color optional
* @uiDefault Button.hoverForeground Color optional
* @uiDefault Button.pressedBackground Color optional
* @uiDefault Button.pressedForeground Color optional
* @uiDefault Button.selectedBackground Color
* @uiDefault Button.selectedForeground Color
* @uiDefault Button.disabledBackground Color optional
* @uiDefault Button.disabledText Color
* @uiDefault Button.disabledSelectedBackground Color
* @uiDefault Button.disabledSelectedForeground Color optional
* @uiDefault Button.default.background Color
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.default.foreground Color
* @uiDefault Button.default.focusedBackground Color optional
* @uiDefault Button.default.focusedForeground Color optional
* @uiDefault Button.default.hoverBackground Color optional
* @uiDefault Button.default.hoverForeground Color optional
* @uiDefault Button.default.pressedBackground Color optional
* @uiDefault Button.default.pressedForeground Color optional
* @uiDefault Button.default.boldText boolean
* @uiDefault Button.paintShadow boolean default is false
* @uiDefault Button.shadowWidth int default is 2
@@ -99,8 +106,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.shadowColor Color optional
* @uiDefault Button.toolbar.spacingInsets Insets
* @uiDefault Button.toolbar.hoverBackground Color
* @uiDefault Button.toolbar.hoverForeground Color optional
* @uiDefault Button.toolbar.pressedBackground Color
* @uiDefault Button.toolbar.pressedForeground Color optional
* @uiDefault Button.toolbar.selectedBackground Color
* @uiDefault Button.toolbar.selectedForeground Color optional
* @uiDefault Button.toolbar.disabledSelectedBackground Color optional
* @uiDefault Button.toolbar.disabledSelectedForeground Color optional
*
* @author Karl Tauber
*/
@@ -117,20 +129,27 @@ public class FlatButtonUI
protected Color startBackground;
protected Color endBackground;
@Styleable protected Color focusedBackground;
/** @since 2.3 */ @Styleable protected Color focusedForeground;
@Styleable protected Color hoverBackground;
/** @since 2.3 */ @Styleable protected Color hoverForeground;
@Styleable protected Color pressedBackground;
/** @since 2.3 */ @Styleable protected Color pressedForeground;
@Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground;
@Styleable protected Color disabledBackground;
@Styleable protected Color disabledText;
@Styleable protected Color disabledSelectedBackground;
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
@Styleable(dot=true) protected Color defaultBackground;
protected Color defaultEndBackground;
@Styleable(dot=true) protected Color defaultForeground;
@Styleable(dot=true) protected Color defaultFocusedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
@Styleable(dot=true) protected Color defaultHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
@Styleable(dot=true) protected Color defaultPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
@Styleable(dot=true) protected boolean defaultBoldText;
@Styleable protected boolean paintShadow;
@@ -139,8 +158,13 @@ public class FlatButtonUI
@Styleable(dot=true) protected Color defaultShadowColor;
@Styleable(dot=true) protected Color toolbarHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
@Styleable(dot=true) protected Color toolbarPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
@Styleable(dot=true) protected Color toolbarSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarSelectedForeground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedForeground;
// only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected String buttonType;
@@ -190,20 +214,27 @@ public class FlatButtonUI
startBackground = UIManager.getColor( prefix + "startBackground" );
endBackground = UIManager.getColor( prefix + "endBackground" );
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" );
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
@@ -212,8 +243,13 @@ public class FlatButtonUI
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
toolbarSelectedForeground = UIManager.getColor( prefix + "toolbar.selectedForeground" );
toolbarDisabledSelectedBackground = UIManager.getColor( prefix + "toolbar.disabledSelectedBackground" );
toolbarDisabledSelectedForeground = UIManager.getColor( prefix + "toolbar.disabledSelectedForeground" );
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
defaultMargin = UIManager.getInsets( prefix + "margin" );
@@ -532,6 +568,8 @@ public class FlatButtonUI
}
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
if(foreground == null)
foreground=Color.red;
FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -545,11 +583,14 @@ public class FlatButtonUI
// selected state
if( ((AbstractButton)c).isSelected() ) {
// in toolbar use same background colors for disabled and enabled because
// in toolbar, if toolbarDisabledSelectedBackground is null,
// use same background colors for disabled and enabled because
// we assume that toolbar icon is shown disabled
return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
toolBarButton
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
: disabledSelectedBackground,
null,
null,
toolBarButton ? toolbarPressedBackground : pressedBackground );
@@ -614,18 +655,48 @@ public class FlatButtonUI
}
protected Color getForeground( JComponent c ) {
if( !c.isEnabled() )
return disabledText;
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
return selectedForeground;
// selected state
if( ((AbstractButton)c).isSelected() ) {
return buttonStateColor( c,
toolBarButton
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
: selectedForeground,
toolBarButton
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
null,
null,
toolBarButton ? toolbarPressedForeground : pressedForeground );
}
// toolbar button
if( toolBarButton ) {
return buttonStateColor( c,
c.getForeground(),
disabledText,
null,
toolbarHoverForeground,
toolbarPressedForeground );
}
boolean def = isDefaultButton( c );
return buttonStateColor( c,
getForegroundBase( c, def ),
disabledText,
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
def ? defaultHoverForeground : hoverForeground,
def ? defaultPressedForeground : pressedForeground );
}
/** @since 2.3 */
protected Color getForegroundBase( JComponent c, boolean def ) {
// use component foreground if explicitly set
Color fg = c.getForeground();
if( isCustomForeground( fg ) )
return fg;
boolean def = isDefaultButton( c );
return def ? defaultForeground : fg;
}

View File

@@ -608,7 +608,7 @@ public class FlatComboBoxUI
boolean shouldValidate = (c instanceof JPanel);
paddingBorder.install( c );
paddingBorder.install( c, 0 );
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
paddingBorder.uninstall();
@@ -682,7 +682,7 @@ public class FlatComboBoxUI
@Override
protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp );
paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall();
return size;
@@ -900,7 +900,7 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
paddingBorder.install( c );
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
return c;
}
@@ -923,6 +923,7 @@ public class FlatComboBoxUI
private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) {
this.padding = padding;
@@ -930,10 +931,12 @@ public class FlatComboBoxUI
// using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done)
synchronized void install( Component c ) {
synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) )
return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder();
if( oldBorder == this )
@@ -987,6 +990,12 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom;
insets.right = padding.right;
}
// if used in popup list, add focus width for exact vertical alignment
// of text in popup list with text in combobox
insets.left += focusWidth;
insets.right += focusWidth;
return insets;
}

View File

@@ -56,7 +56,7 @@ public class FlatEmptyBorder
protected static Insets scaleInsets( Component c, Insets insets,
int top, int left, int bottom, int right )
{
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
boolean leftToRight = left == right || c == null || c.getComponentOrientation().isLeftToRight();
insets.left = scale( leftToRight ? left : right );
insets.top = scale( top );
insets.right = scale( leftToRight ? right : left );

View File

@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.RenderingHints;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault FileChooser.listViewActionLabelText String
* @uiDefault FileChooser.detailsViewActionLabelText String
*
* <!-- FlatFileChooserUI -->
*
* @uiDefault FileChooser.shortcuts.buttonSize Dimension optional; default is 84,64
* @uiDefault FileChooser.shortcuts.iconSize Dimension optional; default is 32,32
* @uiDefault FileChooser.shortcuts.filesFunction Function<File[], File[]>
* @uiDefault FileChooser.shortcuts.displayNameFunction Function<File, String>
* @uiDefault FileChooser.shortcuts.iconFunction Function<File, Icon>
*
* @author Karl Tauber
*/
public class FlatFileChooserUI
extends MetalFileChooserUI
{
private final FlatFileView fileView = new FlatFileView();
private FlatShortcutsPanel shortcutsPanel;
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
super.installComponents( fc );
patchUI( fc );
if( !UIManager.getBoolean( "FileChooser.noPlacesBar" ) ) { // same as in Windows L&F
FlatShortcutsPanel panel = createShortcutsPanel( fc );
if( panel.getComponentCount() > 0 ) {
shortcutsPanel = panel;
fc.add( shortcutsPanel, BorderLayout.LINE_START );
fc.addPropertyChangeListener( shortcutsPanel );
}
}
}
@Override
public void uninstallComponents( JFileChooser fc ) {
super.uninstallComponents( fc );
if( shortcutsPanel != null ) {
fc.removePropertyChangeListener( shortcutsPanel );
shortcutsPanel = null;
}
}
private void patchUI( JFileChooser fc ) {
@@ -192,6 +234,25 @@ public class FlatFileChooserUI
} catch( ArrayIndexOutOfBoundsException ex ) {
// ignore
}
// put north, center and south components into a new panel so that
// the shortcuts panel (at west) gets full height
LayoutManager layout = fc.getLayout();
if( layout instanceof BorderLayout ) {
BorderLayout borderLayout = (BorderLayout) layout;
borderLayout.setHgap( 8 );
Component north = borderLayout.getLayoutComponent( BorderLayout.NORTH );
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
if( north != null && center != null && south != null ) {
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
p.add( north, BorderLayout.NORTH );
p.add( center, BorderLayout.CENTER );
p.add( south, BorderLayout.SOUTH );
fc.add( p, BorderLayout.CENTER );
}
}
}
@Override
@@ -250,9 +311,19 @@ public class FlatFileChooserUI
return p;
}
/** @since 2.3 */
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
return new FlatShortcutsPanel( fc );
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );
Dimension prefSize = super.getPreferredSize( c );
Dimension minSize = getMinimumSize( c );
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
return new Dimension(
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
Math.max( prefSize.height, minSize.height ) );
}
@Override
@@ -316,4 +387,234 @@ public class FlatFileChooserUI
return icon;
}
}
//---- class FlatShortcutsPanel -------------------------------------------
/** @since 2.3 */
public static class FlatShortcutsPanel
extends JToolBar
implements PropertyChangeListener
{
private final JFileChooser fc;
private final Dimension buttonSize;
private final Dimension iconSize;
private final Function<File[], File[]> filesFunction;
private final Function<File, String> displayNameFunction;
private final Function<File, Icon> iconFunction;
protected final File[] files;
protected final JToggleButton[] buttons;
protected final ButtonGroup buttonGroup;
@SuppressWarnings( "unchecked" )
public FlatShortcutsPanel( JFileChooser fc ) {
super( JToolBar.VERTICAL );
this.fc = fc;
setFloatable( false );
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
filesFunction = (Function<File[], File[]>) UIManager.get( "FileChooser.shortcuts.filesFunction" );
displayNameFunction = (Function<File, String>) UIManager.get( "FileChooser.shortcuts.displayNameFunction" );
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
FileSystemView fsv = fc.getFileSystemView();
File[] files = getChooserShortcutPanelFiles( fsv );
if( filesFunction != null )
files = filesFunction.apply( files );
this.files = files;
// create toolbar buttons
buttons = new JToggleButton[files.length];
buttonGroup = new ButtonGroup();
for( int i = 0; i < files.length; i++ ) {
// wrap drive path
if( fsv.isFileSystemRoot( files[i] ) )
files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
File file = files[i];
String name = getDisplayName( fsv, file );
Icon icon = getIcon( fsv, file );
// remove path from name
int lastSepIndex = name.lastIndexOf( File.separatorChar );
if( lastSepIndex >= 0 && lastSepIndex < name.length() - 1 )
name = name.substring( lastSepIndex + 1 );
// scale icon (if necessary)
if( icon instanceof ImageIcon )
icon = new ScaledImageIcon( (ImageIcon) icon, iconSize.width, iconSize.height );
else if( icon != null )
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
// create button
JToggleButton button = createButton( name, icon );
button.addActionListener( e -> {
fc.setCurrentDirectory( file );
} );
add( button );
buttonGroup.add( button );
buttons[i] = button;
}
directoryChanged( fc.getCurrentDirectory() );
}
private Dimension getUIDimension( String key, int defaultWidth, int defaultHeight ) {
Dimension size = UIManager.getDimension( key );
if( size == null )
size = new Dimension( defaultWidth, defaultHeight );
return size;
}
protected JToggleButton createButton( String name, Icon icon ) {
JToggleButton button = new JToggleButton( name, icon );
button.setVerticalTextPosition( SwingConstants.BOTTOM );
button.setHorizontalTextPosition( SwingConstants.CENTER );
button.setAlignmentX( Component.CENTER_ALIGNMENT );
button.setIconTextGap( 0 );
button.setPreferredSize( buttonSize );
button.setMaximumSize( buttonSize );
return button;
}
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
try {
if( SystemInfo.isJava_12_orLater ) {
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
File[] files = (File[]) m.invoke( fsv );
// on macOS and Linux, files consists only of the user home directory
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
files = new File[0];
return files;
} else if( SystemInfo.isWindows ) {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
Method m = cls.getMethod( "get", String.class );
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
}
} catch( IllegalAccessException ex ) {
// do not log because access may be denied via VM option '--illegal-access=deny'
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// fallback
return new File[0];
}
protected String getDisplayName( FileSystemView fsv, File file ) {
if( displayNameFunction != null ) {
String name = displayNameFunction.apply( file );
if( name != null )
return name;
}
return fsv.getSystemDisplayName( file );
}
protected Icon getIcon( FileSystemView fsv, File file ) {
if( iconFunction != null ) {
Icon icon = iconFunction.apply( file );
if( icon != null )
return icon;
}
// Java 17+ supports getting larger system icons
try {
if( SystemInfo.isJava_17_orLater ) {
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
} else if( iconSize.width > 16 || iconSize.height > 16 ) {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
if( cls.isInstance( file ) ) {
Method m = file.getClass().getMethod( "getIcon", boolean.class );
m.setAccessible( true );
Image image = (Image) m.invoke( file, true );
if( image != null )
return new ImageIcon( image );
}
}
} catch( IllegalAccessException ex ) {
// do not log because access may be denied via VM option '--illegal-access=deny'
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// get system icon in default size 16x16
return fsv.getSystemIcon( file );
}
protected void directoryChanged( File file ) {
if( file != null ) {
String absolutePath = file.getAbsolutePath();
for( int i = 0; i < files.length; i++ ) {
// also compare path because otherwise selecting "Documents"
// in "Look in" combobox would not select "Documents" shortcut item
if( files[i].equals( file ) || files[i].getAbsolutePath().equals( absolutePath ) ) {
buttons[i].setSelected( true );
return;
}
}
}
buttonGroup.clearSelection();
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case JFileChooser.DIRECTORY_CHANGED_PROPERTY:
directoryChanged( fc.getCurrentDirectory() );
break;
}
}
}
//---- class ShortcutIcon -------------------------------------------------
private static class ShortcutIcon
implements Icon
{
private final Icon icon;
private final int iconWidth;
private final int iconHeight;
ShortcutIcon( Icon icon, int iconWidth, int iconHeight ) {
this.icon = icon;
this.iconWidth = iconWidth;
this.iconHeight = iconHeight;
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
// set rendering hint for the case that the icon is a bitmap (not used for vector icons)
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
double scale = (double) getIconWidth() / (double) icon.getIconWidth();
g2.translate( x, y );
g2.scale( scale, scale );
icon.paintIcon( c, g2, 0, 0 );
} finally {
g2.dispose();
}
}
@Override
public int getIconWidth() {
return UIScale.scale( iconWidth );
}
@Override
public int getIconHeight() {
return UIScale.scale( iconHeight );
}
}
}

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
@@ -27,6 +29,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -100,6 +105,10 @@ public class FlatMenuBarUI
super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
}
@Override
@@ -221,6 +230,130 @@ public class FlatMenuBarUI
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
}
//---- class FlatMenuBarLayout --------------------------------------------
/**
* @since 2.4
*/
protected static class FlatMenuBarLayout
extends DefaultMenuLayout
{
public FlatMenuBarLayout( Container target ) {
super( target, BoxLayout.LINE_AXIS );
}
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// The only purpose of the code below is to make sure that a horizontal glue,
// which can be used to move window and displays the window title in embedded menu bar,
// is always visible within the menu bar bounds and has a minimum width.
// If this is not the case, the horizontal glue is made larger and
// components that are on the left side of the glue are made smaller.
// get root pane and check whether this menu bar is the root pane menu bar
JRootPane rootPane = SwingUtilities.getRootPane( target );
if( rootPane == null || rootPane.getJMenuBar() != target )
return;
// get title pane and check whether menu bar is embedded
FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane );
if( titlePane == null || !titlePane.isMenuBarEmbedded() )
return;
// check whether there is a horizontal glue (used for window title in embedded menu bar)
// and check minimum width of horizontal glue
Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target );
int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth );
if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) {
// get index of glue component
int glueIndex = -1;
Component[] components = target.getComponents();
for( int i = components.length - 1; i >= 0; i-- ) {
if( components[i] == horizontalGlue ) {
glueIndex = i;
break;
}
}
if( glueIndex < 0 )
return; // should never happen
if( target.getComponentOrientation().isLeftToRight() ) {
// left-to-right
// make horizontal glue wider (minimum title width)
int offset = minTitleWidth - horizontalGlue.getWidth();
horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() );
// check whether glue is fully visible
int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth;
if( minGlueX < horizontalGlue.getX() ) {
// move glue to the left to make it fully visible
offset -= (horizontalGlue.getX() - minGlueX);
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
// shrink and move components that are on the left side of the glue
for( int i = glueIndex - 1; i >= 0; i-- ) {
Component c = components[i];
if( c.getX() > minGlueX ) {
// move component and set width to zero
c.setBounds( minGlueX, c.getY(), 0, c.getHeight() );
} else {
// reduce size of component
c.setSize( minGlueX - c.getX(), c.getHeight() );
break;
}
}
}
// move components that are on the right side of the glue
for( int i = glueIndex + 1; i < components.length; i++ ) {
Component c = components[i];
c.setLocation( c.getX() + offset, c.getY() );
}
} else {
// right-to-left
// make horizontal glue wider (minimum title width)
int offset = minTitleWidth - horizontalGlue.getWidth();
horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(),
minTitleWidth, horizontalGlue.getHeight() );
// check whether glue is fully visible
int minGlueX = target.getInsets().left;
if( minGlueX > horizontalGlue.getX() ) {
// move glue to the right to make it fully visible
offset -= (horizontalGlue.getX() - minGlueX);
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
// shrink and move components that are on the right side of the glue
int x = horizontalGlue.getX() + horizontalGlue.getWidth();
for( int i = glueIndex - 1; i >= 0; i-- ) {
Component c = components[i];
if( c.getX() + c.getWidth() < x ) {
// move component and set width to zero
c.setBounds( x, c.getY(), 0, c.getHeight() );
} else {
// move component and reduce size
c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() );
break;
}
}
}
// move components that are on the left side of the glue
for( int i = glueIndex + 1; i < components.length; i++ ) {
Component c = components[i];
c.setLocation( c.getX() - offset, c.getY() );
}
}
}
}
}
//---- class TakeFocus ----------------------------------------------------
/**

View File

@@ -446,6 +446,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
Rectangle textRect, View htmlView, Color selectionForeground )
{
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
// the width of the HTML view may be initially too small (because component
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
//
// If using a too small width for htmlView.paint(), the view would rearrange
// its children and wrap them to two lines. To avoid this, use view preferred X span
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
//
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
textRect = new Rectangle( textRect );
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2022 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
*
* https://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 java.io.File;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Helper class to load FlatLaf native library (.dll, .so or .dylib),
* if available for current operating system and CPU architecture.
*
* @author Karl Tauber
* @since 2.3
*/
class FlatNativeLibrary
{
private static NativeLibrary nativeLibrary;
/**
* Loads native library (if available) and returns whether loaded successfully.
* Returns {@code false} if no native library is available.
*/
static synchronized boolean isLoaded() {
initialize();
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
}
private static void initialize() {
if( nativeLibrary != null )
return;
String libraryName;
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
// Windows: requires Windows 10 (x86 or x86_64)
libraryName = "flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
// load jawt native library
loadJAWT();
} else
return; // no native library available for current OS or CPU architecture
// load native library
nativeLibrary = createNativeLibrary( libraryName );
}
private static NativeLibrary createNativeLibrary( String libraryName ) {
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
if( libraryPath != null ) {
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
else
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
}
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
}
private static void loadJAWT() {
if( SystemInfo.isJava_9_orLater )
return;
// In Java 8, load jawt.dll (part of JRE) explicitly because it
// is not found when running application with <jdk>/bin/java.exe.
// When using <jdk>/jre/bin/java.exe, it is found.
// jawt.dll is located in <jdk>/jre/bin/.
// Java 9 and later do not have this problem.
try {
System.loadLibrary( "jawt" );
} catch( UnsatisfiedLinkError ex ) {
// log error only if native library jawt.dll not already loaded
String message = ex.getMessage();
if( message == null || !message.contains( "already loaded in another classloader" ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
}

View File

@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatNativeWindowBorder
{
// can use window decorations if:
// - on Windows 10
// - on Windows 10 or later
// - not if system property "sun.java2d.opengl" is true on Windows 10
// - not when running in JetBrains Projector, Webswing or WinPE
// - not disabled via system property
private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater &&
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
!SystemInfo.isProjector &&
!SystemInfo.isWebswing &&
!SystemInfo.isWinPE &&

View File

@@ -130,7 +130,7 @@ public class FlatPopupMenuUI
LayoutManager layout = popupMenu.getLayout();
if( layout == null || layout instanceof UIResource )
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
}
@Override
@@ -230,12 +230,15 @@ public class FlatPopupMenuUI
return screenBounds.height - screenInsets.top - screenInsets.bottom;
}
//---- class FlatMenuLayout -----------------------------------------------
//---- class FlatPopupMenuLayout ------------------------------------------
protected static class FlatMenuLayout
/**
* @since 2.4
*/
protected static class FlatPopupMenuLayout
extends DefaultMenuLayout
{
public FlatMenuLayout( Container target, int axis ) {
public FlatPopupMenuLayout( Container target, int axis ) {
super( target, axis );
}

View File

@@ -364,6 +364,12 @@ public class FlatRootPaneUI
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
}
/** @since 2.4 */
protected static FlatTitlePane getTitlePane( JRootPane rootPane ) {
RootPaneUI ui = rootPane.getUI();
return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null;
}
//---- class FlatRootLayout -----------------------------------------------
protected class FlatRootLayout

View File

@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -195,6 +197,8 @@ public class FlatTableHeaderUI
@Override
public void paint( Graphics g, JComponent c ) {
fixDraggedAndResizingColumns( header );
TableColumnModel columnModel = header.getColumnModel();
if( columnModel.getColumnCount() <= 0 )
return;
@@ -269,6 +273,32 @@ public class FlatTableHeaderUI
return size;
}
static void fixDraggedAndResizingColumns( JTableHeader header ) {
if( header == null )
return;
// Dragged column may be outdated in the case that the table structure
// was changed from a table header popup menu action. In this case
// the paint methods in BasicTableHeaderUI and BasicTableUI would throw exceptions.
TableColumn draggedColumn = header.getDraggedColumn();
if( draggedColumn != null && !isValidColumn( header.getColumnModel(), draggedColumn ) )
header.setDraggedColumn( null );
// also clear outdated resizing column (although this seems not cause exceptions)
TableColumn resizingColumn = header.getResizingColumn();
if( resizingColumn != null && !isValidColumn( header.getColumnModel(), resizingColumn ) )
header.setResizingColumn( null );
}
private static boolean isValidColumn( TableColumnModel cm, TableColumn column ) {
int count = cm.getColumnCount();
for( int i = 0; i < count; i++ ) {
if( cm.getColumn( i ) == column )
return true;
}
return false;
}
//---- class FlatTableCellHeaderRenderer ----------------------------------
/**

View File

@@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.paintOutsideAlternateRows boolean
*
* <!-- FlatTableCellBorder -->
*
@@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTableUI
extends BasicTableUI
implements StyleableUI
implements StyleableUI, FlatViewportUI.ViewportPainter
{
protected boolean showHorizontalLines;
protected boolean showVerticalLines;
@@ -313,6 +314,8 @@ public class FlatTableUI
@Override
public void paint( Graphics g, JComponent c ) {
FlatTableHeaderUI.fixDraggedAndResizingColumns( table.getTableHeader() );
boolean horizontalLines = table.getShowHorizontalLines();
boolean verticalLines = table.getShowVerticalLines();
if( horizontalLines || verticalLines ) {
@@ -421,4 +424,38 @@ public class FlatTableUI
? (viewport != rowHeader)
: (viewport == rowHeader || rowHeader == null);
}
/** @since 2.3 */
@Override
public void paintViewport( Graphics g, JComponent c, JViewport viewport ) {
int viewportWidth = viewport.getWidth();
int viewportHeight = viewport.getHeight();
// fill viewport background in same color as table background
if( viewport.isOpaque() ) {
g.setColor( table.getBackground() );
g.fillRect( 0, 0, viewportWidth, viewportHeight );
}
// paint alternating empty rows
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
Color alternateColor;
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
g.setColor( alternateColor );
int rowCount = table.getRowCount();
// paint alternating empty rows below the table
int tableHeight = table.getHeight();
if( tableHeight < viewportHeight ) {
int tableWidth = table.getWidth();
int rowHeight = table.getRowHeight();
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
if( row % 2 != 0 )
g.fillRect( 0, y, tableWidth, rowHeight );
}
}
}
}
}

View File

@@ -24,6 +24,7 @@ import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
@@ -50,13 +51,13 @@ import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
@@ -83,10 +84,14 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.titleMinimumWidth int
* @uiDefault TitlePane.buttonMinimumWidth int
* @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon
@@ -107,10 +112,15 @@ public class FlatTitlePane
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", 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 );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 );
/** @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 );
protected final JRootPane rootPane;
@@ -142,6 +152,8 @@ public class FlatTitlePane
// necessary for closing window with double-click on icon
iconLabel.addMouseListener( handler );
applyComponentOrientation( rootPane.getComponentOrientation() );
}
protected FlatTitlePaneBorder createTitlePaneBorder() {
@@ -163,7 +175,6 @@ public class FlatTitlePane
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -183,18 +194,52 @@ public class FlatTitlePane
setLayout( new BorderLayout() {
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// make left panel (with embedded menu bar) smaller if horizontal space is rare
// to avoid that embedded menu bar overlaps button bar
// compute available bounds
Insets insets = target.getInsets();
int width = target.getWidth() - insets.left - insets.right;
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) {
int oldWidth = leftPanel.getWidth();
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 );
leftPanel.setSize( newWidth, leftPanel.getHeight() );
if( !getComponentOrientation().isLeftToRight() )
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
int x = insets.left;
int y = insets.top;
int w = target.getWidth() - insets.left - insets.right;
int h = target.getHeight() - insets.top - insets.bottom;
// compute widths
int leftWidth = leftPanel.getPreferredSize().width;
int buttonsWidth = buttonPanel.getPreferredSize().width;
int titleWidth = w - leftWidth - buttonsWidth;
int minTitleWidth = UIScale.scale( titleMinimumWidth );
// increase minimum width if icon is show besides the title
Icon icon = titleLabel.getIcon();
if( icon != null ) {
Insets iconInsets = iconLabel.getInsets();
int iconTextGap = titleLabel.getComponentOrientation().isLeftToRight() ? iconInsets.right : iconInsets.left;
minTitleWidth += icon.getIconWidth() + iconTextGap;
}
// if title is too small, reduce width of buttons
if( titleWidth < minTitleWidth ) {
buttonsWidth = Math.max( buttonsWidth - (minTitleWidth - titleWidth), buttonPanel.getMinimumSize().width );
titleWidth = w - leftWidth - buttonsWidth;
}
// if title is still too small, reduce width of left panel (icon and embedded menu bar)
if( titleWidth < minTitleWidth ) {
int minLeftWidth = iconLabel.isVisible()
? iconLabel.getWidth() - iconLabel.getInsets().right
: UIScale.scale( noIconLeftGap );
leftWidth = Math.max( leftWidth - (minTitleWidth - titleWidth), minLeftWidth );
titleWidth = w - leftWidth - buttonsWidth;
}
if( target.getComponentOrientation().isLeftToRight() ) {
// left-to-right
leftPanel.setBounds( x, y, leftWidth, h );
titleLabel.setBounds( x + leftWidth, y, titleWidth, h );
buttonPanel.setBounds( x + leftWidth + titleWidth, y, buttonsWidth, h );
} else {
// right-to-left
buttonPanel.setBounds( x, y, buttonsWidth, h );
titleLabel.setBounds( x + buttonsWidth, y, titleWidth, h );
leftPanel.setBounds( x + buttonsWidth + titleWidth, y, leftWidth, h );
}
// If menu bar is embedded and contains a horizontal glue component,
@@ -233,10 +278,7 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if( buttonMaximizedHeight > 0 &&
window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
// make title pane height smaller when frame is maximized
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
}
@@ -259,7 +301,13 @@ public class FlatTitlePane
}
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
JButton button = new JButton( UIManager.getIcon( iconKey ) );
JButton button = new JButton( UIManager.getIcon( iconKey ) ) {
@Override
public Dimension getMinimumSize() {
// allow the button to shrink if space is rare
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
}
};
button.setFocusable( false );
button.setContentAreaFilled( false );
button.setBorder( BorderFactory.createEmptyBorder() );
@@ -356,11 +404,12 @@ public class FlatTitlePane
boolean hasIcon = (images != null && !images.isEmpty());
// set icon
iconLabel.setIcon( hasIcon ? new FlatTitlePaneIcon( images, iconSize ) : null );
iconLabel.setIcon( hasIcon && !showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
titleLabel.setIcon( hasIcon && showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
// show/hide icon
iconLabel.setVisible( hasIcon );
leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
iconLabel.setVisible( hasIcon && !showIconBesideTitle );
leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@@ -567,6 +616,11 @@ debug*/
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
}
/** @since 2.4 */
protected boolean isWindowMaximized() {
return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
/**
* Maximizes the window.
*/
@@ -729,7 +783,7 @@ debug*/
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) {
if( !showIconBesideTitle && iconLabel.isVisible() ) {
// compute real icon size (without insets; 1px larger for easier hitting)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets();
@@ -741,9 +795,7 @@ debug*/
// if frame is maximized, increase icon bounds to upper-left corner
// of window to allow closing window via double-click in upper-left corner
if( window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( isWindowMaximized() ) {
iconBounds.height += iconBounds.y;
iconBounds.y = 0;
@@ -758,6 +810,38 @@ debug*/
hitTestSpots.add( iconBounds );
else
appIconBounds = iconBounds;
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
// compute real icon bounds
Insets insets = titleLabel.getInsets();
Rectangle viewR = new Rectangle( insets.left, insets.top,
titleLabel.getWidth() - insets.left - insets.right,
titleLabel.getHeight() - insets.top - insets.bottom );
Rectangle iconR = new Rectangle();
Rectangle textR = new Rectangle();
ui.layoutCL( titleLabel, titleLabel.getFontMetrics( titleLabel.getFont() ),
titleLabel.getText(), titleLabel.getIcon(),
viewR, iconR, textR );
// Windows shows the window system menu only in the upper-left corner
if( iconR.x == 0 ) {
// convert icon location to window coordinates
Point location = SwingUtilities.convertPoint( titleLabel, 0, 0, window );
iconR.x += location.x;
iconR.y += location.y;
// make icon bounds 1px larger for easier hitting
iconR.x -= 1;
iconR.y -= 1;
iconR.width += 2;
iconR.height += 2;
if( hasJBRCustomDecoration() )
hitTestSpots.add( iconR );
else
appIconBounds = iconR;
}
}
Rectangle r = getNativeHitTestSpot( buttonPanel );
@@ -768,6 +852,15 @@ debug*/
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar );
if( r != null ) {
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
// to have a small area above the menu bar to resize the window
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
// limit to 8, because Windows does not use a larger height
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
r.y += resizeHeight;
r.height -= resizeHeight;
}
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
// If menu bar is embedded and contains a horizontal glue component,
@@ -854,7 +947,7 @@ debug*/
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets;
@@ -873,7 +966,7 @@ debug*/
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
}
@@ -881,10 +974,6 @@ debug*/
JMenuBar menuBar = rootPane.getJMenuBar();
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
}
protected boolean isWindowMaximized( Component c ) {
return window instanceof Frame && (((Frame) window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
}
//---- class FlatTitleLabelUI ---------------------------------------------
@@ -898,32 +987,101 @@ debug*/
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
int labelWidth = l.getWidth();
int textWidth = labelWidth - (textX * 2);
int gap = UIScale.scale( menuBarTitleGap );
protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon,
Rectangle viewR, Rectangle iconR, Rectangle textR )
{
JMenuBar menuBar = rootPane.getJMenuBar();
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar );
boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar );
boolean leftToRight = getComponentOrientation().isLeftToRight();
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise, leave it centered within free space (label bounds).
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
textX = centeredTextX;
} else {
// leading aligned
boolean leftToRight = getComponentOrientation().isLeftToRight();
Insets insets = l.getInsets();
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
textX = leadingTextX;
if( hasEmbeddedMenuBar ) {
int minGap = UIScale.scale( menuBarTitleMinimumGap );
// apply minimum leading gap (between embedded menu bar and title)
if( hasEmbeddedLeadingMenus ) {
if( leftToRight )
viewR.x += minGap;
viewR.width -= minGap;
}
// apply minimum trailing gap (between title and right aligned components of embedded menu bar)
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null && menuBar.getComponent( menuBar.getComponentCount() - 1 ) != horizontalGlue ) {
if( !leftToRight )
viewR.x += minGap;
viewR.width -= minGap;
}
}
super.paintEnabledText( l, g, s, textX, textY );
// compute icon width and gap (if icon is show besides the title)
int iconTextGap = 0;
int iconWidthAndGap = 0;
if( icon != null ) {
Insets iconInsets = iconLabel.getInsets();
iconTextGap = leftToRight ? iconInsets.right : iconInsets.left;
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
}
// layout title and icon (if show besides the title)
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
viewR, iconR, textR,
iconTextGap );
// compute text X location
if( !clippedText.equals( text ) ) {
// if text is clipped, align to left (or right)
textR.x = leftToRight
? viewR.x + iconWidthAndGap
: viewR.x + viewR.width - iconWidthAndGap - textR.width;
} else {
int leadingGap = hasEmbeddedLeadingMenus ? UIScale.scale( menuBarTitleGap - menuBarTitleMinimumGap ) : 0;
boolean center = hasEmbeddedLeadingMenus ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise, center within free space (label bounds).
Container parent = label.getParent();
int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap - label.getX() : -1;
textR.x = (centeredTextX >= viewR.x + leadingGap && centeredTextX + textR.width <= viewR.x + viewR.width - leadingGap)
? centeredTextX
: viewR.x + ((viewR.width - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap;
} else {
// leading aligned with leading gap, which is reduced if space is rare
textR.x = leftToRight
? Math.min( viewR.x + leadingGap + iconWidthAndGap, viewR.x + viewR.width - textR.width )
: Math.max( viewR.x + viewR.width - leadingGap - iconWidthAndGap - textR.width, viewR.x );
}
}
// compute icon X location (relative to text X location)
if( icon != null ) {
iconR.x = leftToRight
? textR.x - iconWidthAndGap
: textR.x + textR.width + iconTextGap;
}
return clippedText;
}
private boolean hasLeadingMenus( JMenuBar menuBar ) {
// check whether menu bar is empty
if( menuBar.getComponentCount() == 0 || menuBar.getWidth() == 0 )
return false;
// check whether menu bar has a leading glue component
// (no menus/components at left side)
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
boolean leftToRight = getComponentOrientation().isLeftToRight();
if( (leftToRight && horizontalGlue.getX() == 0) ||
(!leftToRight && horizontalGlue.getX() + horizontalGlue.getWidth() == menuBar.getWidth()) )
return false;
}
return true;
}
}

View File

@@ -22,10 +22,7 @@ import java.awt.Component;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
@@ -51,15 +48,29 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ToggleButton.iconTextGap int
* @uiDefault ToggleButton.startBackground Color optional; if set, a gradient paint is used and ToggleButton.background is ignored
* @uiDefault ToggleButton.endBackground Color optional; if set, a gradient paint is used
* @uiDefault ToggleButton.pressedBackground Color
* @uiDefault ToggleButton.focusedBackground Color optional
* @uiDefault ToggleButton.focusedForeground Color optional
* @uiDefault ToggleButton.hoverBackground Color optional
* @uiDefault ToggleButton.hoverForeground Color optional
* @uiDefault ToggleButton.pressedBackground Color optional
* @uiDefault ToggleButton.pressedForeground Color optional
* @uiDefault ToggleButton.selectedBackground Color
* @uiDefault ToggleButton.selectedForeground Color
* @uiDefault ToggleButton.disabledBackground Color optional
* @uiDefault ToggleButton.disabledText Color
* @uiDefault ToggleButton.disabledSelectedBackground Color
* @uiDefault ToggleButton.disabledSelectedForeground Color optional
* @uiDefault Button.paintShadow boolean default is false
* @uiDefault Button.shadowWidth int default is 2
* @uiDefault Button.shadowColor Color optional
* @uiDefault ToggleButton.toolbar.hoverBackground Color
* @uiDefault ToggleButton.toolbar.hoverForeground Color optional
* @uiDefault ToggleButton.toolbar.pressedBackground Color
* @uiDefault ToggleButton.toolbar.pressedForeground Color optional
* @uiDefault ToggleButton.toolbar.selectedBackground Color
* @uiDefault ToggleButton.toolbar.selectedForeground Color optional
* @uiDefault ToggleButton.toolbar.disabledSelectedBackground Color optional
* @uiDefault ToggleButton.toolbar.disabledSelectedForeground Color optional
*
* <!-- FlatToggleButtonUI -->
*
@@ -67,8 +78,11 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ToggleButton.tab.underlineColor Color
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color
* @uiDefault ToggleButton.tab.selectedBackground Color optional
* @uiDefault ToggleButton.tab.selectedForeground Color optional
* @uiDefault ToggleButton.tab.hoverBackground Color
* @uiDefault ToggleButton.tab.hoverForeground Color optional
* @uiDefault ToggleButton.tab.focusBackground Color
* @uiDefault ToggleButton.tab.focusForeground Color optional
*
*
* @author Karl Tauber
@@ -80,8 +94,11 @@ public class FlatToggleButtonUI
@Styleable(dot=true) protected Color tabUnderlineColor;
@Styleable(dot=true) protected Color tabDisabledUnderlineColor;
@Styleable(dot=true) protected Color tabSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabSelectedForeground;
@Styleable(dot=true) protected Color tabHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabHoverForeground;
@Styleable(dot=true) protected Color tabFocusBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabFocusForeground;
private boolean defaults_initialized = false;
@@ -114,8 +131,11 @@ public class FlatToggleButtonUI
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
tabSelectedBackground = UIManager.getColor( "ToggleButton.tab.selectedBackground" );
tabSelectedForeground = UIManager.getColor( "ToggleButton.tab.selectedForeground" );
tabHoverBackground = UIManager.getColor( "ToggleButton.tab.hoverBackground" );
tabHoverForeground = UIManager.getColor( "ToggleButton.tab.hoverForeground" );
tabFocusBackground = UIManager.getColor( "ToggleButton.tab.focusBackground" );
tabFocusForeground = UIManager.getColor( "ToggleButton.tab.focusForeground" );
defaults_initialized = true;
}
@@ -142,6 +162,7 @@ public class FlatToggleButtonUI
b.repaint();
break;
case TAB_BUTTON_UNDERLINE_PLACEMENT:
case TAB_BUTTON_UNDERLINE_HEIGHT:
case TAB_BUTTON_UNDERLINE_COLOR:
case TAB_BUTTON_SELECTED_BACKGROUND:
@@ -196,13 +217,42 @@ public class FlatToggleButtonUI
// paint underline if selected
if( selected ) {
int underlineHeight = UIScale.scale( clientPropertyInt( c, TAB_BUTTON_UNDERLINE_HEIGHT, tabUnderlineHeight ) );
int underlineThickness = UIScale.scale( clientPropertyInt( c, TAB_BUTTON_UNDERLINE_HEIGHT, tabUnderlineHeight ) );
g.setColor( c.isEnabled()
? clientPropertyColor( c, TAB_BUTTON_UNDERLINE_COLOR, tabUnderlineColor )
: tabDisabledUnderlineColor );
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
int placement = clientPropertyInt( c, TAB_BUTTON_UNDERLINE_PLACEMENT, SwingConstants.BOTTOM );
switch (placement) {
case SwingConstants.TOP:
g.fillRect( 0, 0, width, underlineThickness );
break;
case SwingConstants.LEFT:
g.fillRect( 0, 0, underlineThickness, height );
break;
case SwingConstants.RIGHT:
g.fillRect( width - underlineThickness, 0, underlineThickness, height );
break;
case SwingConstants.BOTTOM:
default:
g.fillRect( 0, height - underlineThickness, width, underlineThickness );
}
}
} else
super.paintBackground( g, c );
}
@Override
protected Color getForeground( JComponent c ) {
if( isTabButton( c ) ) {
if( !c.isEnabled() )
return disabledText;
if( tabSelectedForeground != null && ((AbstractButton)c).isSelected() )
return tabSelectedForeground;
return buttonStateColor( c, c.getForeground(), disabledText,
tabFocusForeground, tabHoverForeground, null );
} else
return super.getForeground( c );
}
}

View File

@@ -18,8 +18,8 @@ package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Graphics;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicViewportUI;
@@ -43,15 +43,28 @@ public class FlatViewportUI
}
@Override
public void update( Graphics g, JComponent c ) {
Component view = ((JViewport)c).getView();
if( c.isOpaque() && view instanceof JTable ) {
// paint viewport background in same color as table background
g.setColor( view.getBackground() );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
public void paint( Graphics g, JComponent c ) {
super.paint( g, c );
paint( g, c );
} else
super.update( g, c );
Component view = ((JViewport)c).getView();
if( view instanceof JComponent ) {
try {
Method m = view.getClass().getMethod( "getUI" );
Object ui = m.invoke( view );
if( ui instanceof ViewportPainter )
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
} catch( Exception ex ) {
// ignore
}
}
}
//---- interface ViewportPainter ------------------------------------------
/**
* @since 2.3
*/
public interface ViewportPainter {
void paintViewport( Graphics g, JComponent c, JViewport viewport );
}
}

View File

@@ -27,7 +27,6 @@ import java.awt.Window;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -38,9 +37,7 @@ import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo;
//
@@ -84,7 +81,6 @@ class FlatWindowsNativeWindowBorder
private Color colorizationColor;
private int colorizationColorBalance;
private static NativeLibrary nativeLibrary;
private static FlatWindowsNativeWindowBorder instance;
static FlatNativeWindowBorder.Provider getInstance() {
@@ -96,31 +92,8 @@ class FlatWindowsNativeWindowBorder
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
return null;
// load native library
if( nativeLibrary == null ) {
if( !SystemInfo.isJava_9_orLater ) {
// In Java 8, load jawt.dll (part of JRE) explicitly because it
// is not found when running application with <jdk>/bin/java.exe.
// When using <jdk>/jre/bin/java.exe, it is found.
// jawt.dll is located in <jdk>/jre/bin/.
// Java 9 and later does not have this problem.
try {
System.loadLibrary( "jawt" );
} catch( UnsatisfiedLinkError ex ) {
// log error only if native library jawt.dll not already loaded
String message = ex.getMessage();
if( message == null || !message.contains( "already loaded in another classloader" ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
nativeLibrary = createNativeLibrary();
}
// check whether native library was successfully loaded
if( !nativeLibrary.isLoaded() )
if( !FlatNativeLibrary.isLoaded() )
return null;
// create new instance
@@ -129,23 +102,6 @@ class FlatWindowsNativeWindowBorder
return instance;
}
private static NativeLibrary createNativeLibrary() {
String libraryName = "flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
if( libraryPath != null ) {
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
else
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
}
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
}
private FlatWindowsNativeWindowBorder() {
}

View File

@@ -195,11 +195,13 @@ public class JBRCustomDecorations
{
private static JBRWindowTopBorder instance;
private final Color defaultActiveBorder = new Color( 0x707070 );
private final Color activeLightColor = new Color( 0x707070 );
private final Color activeDarkColor = new Color( 0x2D2E2F );
private final Color inactiveLightColor = new Color( 0xaaaaaa );
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor = defaultActiveBorder;
private Color activeColor;
static JBRWindowTopBorder getInstance() {
if( instance == null )
@@ -250,7 +252,7 @@ public class JBRCustomDecorations
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return defaultActiveBorder;
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
@@ -285,15 +287,11 @@ public class JBRCustomDecorations
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
// paint top border
// - in light themes
// - in dark themes only for active windows if colorization affects borders
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
if( !paintTopBorder )
return;
g.setColor( active ? activeColor : inactiveLightColor );
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}

View File

@@ -143,8 +143,8 @@ public class MigLayoutVisualPadding
//---- class FlatMigInsets ------------------------------------------------
/**
* Marker class to identify our visual paddings and leaf paddings,
* which were set from outside, untouched.
* Marker class to identify our visual paddings and leave paddings
* set from outside untouched.
*/
private static class FlatMigInsets
extends Insets

View File

@@ -48,30 +48,55 @@ public class HiDPIUtils
*/
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
// save original transform
AffineTransform transform = g.getTransform();
AffineTransform t = g.getTransform();
// get scale X/Y and shear X/Y
double scaleX = t.getScaleX();
double scaleY = t.getScaleY();
double shearX = t.getShearX();
double shearY = t.getShearY();
// check whether rotated
// (also check for negative scale X/Y because shear X/Y are zero for 180 degrees rotation)
boolean rotated = (shearX != 0 || shearY != 0 || scaleX <= 0 || scaleY <= 0);
if( rotated ) {
// resulting scale X/Y values are always positive
scaleX = Math.hypot( scaleX, shearX );
scaleY = Math.hypot( scaleY, shearY );
} else {
// make scale X/Y positive
scaleX = Math.abs( scaleX );
scaleY = Math.abs( scaleY );
}
// check whether scaled
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
if( scaleX == 1 && scaleY == 1 ) {
painter.paint( g, x, y, width, height, 1 );
return;
}
// scale rectangle
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
Rectangle2D.Double scaledRect = scale( scaleX, scaleY, t, x, y, width, height );
try {
// unscale to factor 1.0 and move origin (to whole numbers)
g.setTransform( new AffineTransform( 1, 0, 0, 1,
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );
// unscale to factor 1.0, keep rotation and move origin (to whole numbers)
AffineTransform t1x;
if( rotated ) {
t1x = new AffineTransform( t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(),
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
t1x.scale( 1. / scaleX, 1. / scaleY );
} else
t1x = new AffineTransform( 1, 0, 0, 1, Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
g.setTransform( t1x );
int swidth = (int) scaledRect.width;
int sheight = (int) scaledRect.height;
// paint
painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() );
painter.paint( g, 0, 0, swidth, sheight, scaleX );
} finally {
// restore original transform
g.setTransform( transform );
g.setTransform( t );
}
}
@@ -80,20 +105,16 @@ public class HiDPIUtils
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*/
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) {
double dx1 = transform.getScaleX();
double dy2 = transform.getScaleY();
double px = x * dx1 + transform.getTranslateX();
double py = y * dy2 + transform.getTranslateY();
dx1 *= width;
dy2 *= height;
private static Rectangle2D.Double scale( double scaleX, double scaleY, AffineTransform t, int x, int y, int width, int height ) {
double px = (x * scaleX) + t.getTranslateX();
double py = (y * scaleY) + t.getTranslateY();
double newx = normalize( px );
double newy = normalize( py );
dx1 = normalize( px + dx1 ) - newx;
dy2 = normalize( py + dy2 ) - newy;
double newX = normalize( px );
double newY = normalize( py );
double newWidth = normalize( px + (width * scaleX) ) - newX;
double newHeight = normalize( py + (height * scaleY) ) - newY;
return new Rectangle2D.Double( newx, newy, dx1, dy2 );
return new Rectangle2D.Double( newX, newY, newWidth, newHeight );
}
private static double normalize( double value ) {

View File

@@ -50,6 +50,7 @@ public class SystemInfo
public static final long javaVersion;
public static final boolean isJava_9_orLater;
public static final boolean isJava_11_orLater;
/** @since 2.3 */ public static final boolean isJava_12_orLater;
public static final boolean isJava_15_orLater;
/** @since 2 */ public static final boolean isJava_17_orLater;
/** @since 2 */ public static final boolean isJava_18_orLater;
@@ -66,6 +67,9 @@ public class SystemInfo
/** @since 1.1.2 */ public static final boolean isWebswing;
/** @since 1.1.1 */ public static final boolean isWinPE;
// features
/** @since 2.3 */ public static final boolean isMacFullWindowContentSupported;
static {
// platforms
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
@@ -92,6 +96,7 @@ public class SystemInfo
javaVersion = scanVersion( System.getProperty( "java.version" ) );
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 ));
isJava_12_orLater = (javaVersion >= toVersion( 12, 0, 0, 0 ));
isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 ));
isJava_17_orLater = (javaVersion >= toVersion( 17, 0, 0, 0 ));
isJava_18_orLater = (javaVersion >= toVersion( 18, 0, 0, 0 ));
@@ -108,6 +113,12 @@ public class SystemInfo
isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" );
isWebswing = (System.getProperty( "webswing.rootDir" ) != null);
isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) );
// features
// available since Java 12; backported to Java 11.0.8 and 8u292
isMacFullWindowContentSupported =
javaVersion >= toVersion( 11, 0, 8, 0 ) ||
(javaVersion >= toVersion( 1, 8, 0, 292 ) && !isJava_9_orLater);
}
public static long scanVersion( String version ) {

View File

@@ -64,7 +64,7 @@ light.font = +0
semibold.font = +0
# monospaced
[win]monospaced.font = Consolas, "Courier New", Monospaced
[win]monospaced.font = Monospaced
[mac]monospaced.font = Menlo, Monospaced
[linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced
monospaced.font = Monospaced
@@ -789,11 +789,16 @@ TitlePane.noIconLeftGap = 8
TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0
TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.menuBarTitleGap = 20
TitlePane.showIconBesideTitle = false
TitlePane.menuBarTitleGap = 40
TitlePane.menuBarTitleMinimumGap = 12
TitlePane.menuBarResizeHeight = 4
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon
@@ -829,6 +834,7 @@ ToggleButton.tab.underlineHeight = 2
ToggleButton.tab.underlineColor = $TabbedPane.underlineColor
ToggleButton.tab.disabledUnderlineColor = $TabbedPane.disabledUnderlineColor
ToggleButton.tab.selectedBackground = $?TabbedPane.selectedBackground
ToggleButton.tab.selectedForeground = $?TabbedPane.selectedForeground
ToggleButton.tab.hoverBackground = $TabbedPane.hoverColor
ToggleButton.tab.focusBackground = $TabbedPane.focusColor

View File

@@ -201,6 +201,8 @@ ToggleButton.endBackground = $ToggleButton.background
[Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monokai_Pro]TitledBorder.titleColor = @foreground
[Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10

View File

@@ -0,0 +1,65 @@
#
# 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
#
# https://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.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Buscar &en:
FileChooser.saveInLabelText = Guardar en:
FileChooser.fileNameLabel.textAndMnemonic = &Nombre de fichero:
FileChooser.folderNameLabel.textAndMnemonic = &Nombre de carpeta:
FileChooser.filesOfTypeLabel.textAndMnemonic = Ficheros de &Tipo:
# toolbar
FileChooser.upFolderToolTipText = Subir un nivel
FileChooser.upFolderAccessibleName = Subir
FileChooser.homeFolderToolTipText = Inicio
FileChooser.homeFolderAccessibleName = Inicio
FileChooser.newFolderToolTipText = Crear nueva carpeta
FileChooser.newFolderAccessibleName = Nueva carpeta
FileChooser.listViewButtonToolTipText = Lista
FileChooser.listViewButtonAccessibleName = Lista
FileChooser.detailsViewButtonToolTipText = Detalles
FileChooser.detailsViewButtonAccessibleName = Detalles
# details table header
FileChooser.fileNameHeaderText = Nombre
FileChooser.fileSizeHeaderText = Tama\u00F1o
FileChooser.fileTypeHeaderText = Tipo
FileChooser.fileDateHeaderText = Modificado
FileChooser.fileAttrHeaderText = Atributos
# popup menu
FileChooser.viewMenuLabelText = Ver
FileChooser.refreshActionLabelText = Refrescar
FileChooser.newFolderActionLabelText = Nueva carpeta
FileChooser.listViewActionLabelText = Lista
FileChooser.detailsViewActionLabelText = Detalles
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Contraer Panel Izquierdo
SplitPaneDivider.collapseRightToolTipText = Contraer panel Derecho
SplitPaneDivider.collapseTopToolTipText = Contraer panel Superior
SplitPaneDivider.collapseBottomToolTipText = Contraer Panel Inferior
SplitPaneDivider.expandLeftToolTipText = Expandir Panel Izquierdo
SplitPaneDivider.expandRightToolTipText = Expandir Panel Derecho
SplitPaneDivider.expandTopToolTipText = Expandir Panel Superior
SplitPaneDivider.expandBottomToolTipText = Expandir Panel Inferior
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText = Mostrar Pesta\u00F1as Ocultas

View File

@@ -96,19 +96,26 @@ public class TestFlatStyleableInfo
"minimumWidth", int.class,
"focusedBackground", Color.class,
"focusedForeground", Color.class,
"hoverBackground", Color.class,
"hoverForeground", Color.class,
"pressedBackground", Color.class,
"pressedForeground", Color.class,
"selectedBackground", Color.class,
"selectedForeground", Color.class,
"disabledBackground", Color.class,
"disabledText", Color.class,
"disabledSelectedBackground", Color.class,
"disabledSelectedForeground", Color.class,
"default.background", Color.class,
"default.foreground", Color.class,
"default.focusedBackground", Color.class,
"default.focusedForeground", Color.class,
"default.hoverBackground", Color.class,
"default.hoverForeground", Color.class,
"default.pressedBackground", Color.class,
"default.pressedForeground", Color.class,
"default.boldText", boolean.class,
"paintShadow", boolean.class,
@@ -118,8 +125,13 @@ public class TestFlatStyleableInfo
"toolbar.spacingInsets", Insets.class,
"toolbar.hoverBackground", Color.class,
"toolbar.hoverForeground", Color.class,
"toolbar.pressedBackground", Color.class,
"toolbar.pressedForeground", Color.class,
"toolbar.selectedBackground", Color.class,
"toolbar.selectedForeground", Color.class,
"toolbar.disabledSelectedBackground", Color.class,
"toolbar.disabledSelectedForeground", Color.class,
"buttonType", String.class,
"squareSize", boolean.class,
@@ -863,8 +875,11 @@ public class TestFlatStyleableInfo
"tab.underlineColor", Color.class,
"tab.disabledUnderlineColor", Color.class,
"tab.selectedBackground", Color.class,
"tab.selectedForeground", Color.class,
"tab.hoverBackground", Color.class,
"tab.focusBackground", Color.class
"tab.hoverForeground", Color.class,
"tab.focusBackground", Color.class,
"tab.focusForeground", Color.class
);
// FlatToggleButtonUI extends FlatButtonUI

View File

@@ -221,19 +221,26 @@ public class TestFlatStyling
ui.applyStyle( b, "minimumWidth: 100" );
ui.applyStyle( b, "focusedBackground: #fff" );
ui.applyStyle( b, "focusedForeground: #fff" );
ui.applyStyle( b, "hoverBackground: #fff" );
ui.applyStyle( b, "hoverForeground: #fff" );
ui.applyStyle( b, "pressedBackground: #fff" );
ui.applyStyle( b, "pressedForeground: #fff" );
ui.applyStyle( b, "selectedBackground: #fff" );
ui.applyStyle( b, "selectedForeground: #fff" );
ui.applyStyle( b, "disabledBackground: #fff" );
ui.applyStyle( b, "disabledText: #fff" );
ui.applyStyle( b, "disabledSelectedBackground: #fff" );
ui.applyStyle( b, "disabledSelectedForeground: #fff" );
ui.applyStyle( b, "default.background: #fff" );
ui.applyStyle( b, "default.foreground: #fff" );
ui.applyStyle( b, "default.focusedBackground: #fff" );
ui.applyStyle( b, "default.focusedForeground: #fff" );
ui.applyStyle( b, "default.hoverBackground: #fff" );
ui.applyStyle( b, "default.hoverForeground: #fff" );
ui.applyStyle( b, "default.pressedBackground: #fff" );
ui.applyStyle( b, "default.pressedForeground: #fff" );
ui.applyStyle( b, "default.boldText: true" );
ui.applyStyle( b, "paintShadow: true" );
@@ -243,8 +250,13 @@ public class TestFlatStyling
ui.applyStyle( b, "toolbar.spacingInsets: 1,2,3,4" );
ui.applyStyle( b, "toolbar.hoverBackground: #fff" );
ui.applyStyle( b, "toolbar.hoverForeground: #fff" );
ui.applyStyle( b, "toolbar.pressedBackground: #fff" );
ui.applyStyle( b, "toolbar.pressedForeground: #fff" );
ui.applyStyle( b, "toolbar.selectedBackground: #fff" );
ui.applyStyle( b, "toolbar.selectedForeground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedBackground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedForeground: #fff" );
ui.applyStyle( b, "buttonType: help" );
ui.applyStyle( b, "squareSize: true" );
@@ -1073,8 +1085,11 @@ public class TestFlatStyling
ui.applyStyle( b, "tab.underlineColor: #fff" );
ui.applyStyle( b, "tab.disabledUnderlineColor: #fff" );
ui.applyStyle( b, "tab.selectedBackground: #fff" );
ui.applyStyle( b, "tab.selectedForeground: #fff" );
ui.applyStyle( b, "tab.hoverBackground: #fff" );
ui.applyStyle( b, "tab.hoverForeground: #fff" );
ui.applyStyle( b, "tab.focusBackground: #fff" );
ui.applyStyle( b, "tab.focusForeground: #fff" );
}
@Test

View File

@@ -38,8 +38,8 @@ import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.components.FlatButton;
@@ -82,13 +82,33 @@ class DemoFrame
if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() )
tabbedPane.setSelectedIndex( tabIndex );
// hide some menu items on macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// hide menu items that are in macOS application menu
exitMenuItem.setVisible( false );
aboutMenuItem.setVisible( false );
// do not use HTML text on macOS
// do not use HTML text in menu items because this is not supported in macOS screen menu
htmlMenuItem.setText( "some text" );
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
}
// integrate into macOS screen menu
@@ -322,8 +342,8 @@ class DemoFrame
// add font families
fontMenu.addSeparator();
ArrayList<String> families = new ArrayList<>( Arrays.asList(
"Arial", "Cantarell", "Comic Sans MS", "Courier New", "DejaVu Sans",
"Dialog", "Liberation Sans", "Monospaced", "Noto Sans", "Roboto",
"Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans",
"Dialog", "Liberation Sans", "Noto Sans", "Roboto",
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
if( !families.contains( currentFamily ) )
families.add( currentFamily );
@@ -433,9 +453,9 @@ class DemoFrame
lafClass == FlatIntelliJLaf.class ||
lafClass == FlatDarculaLaf.class;
accentColorLabel.setEnabled( isAccentColorSupported );
accentColorLabel.setVisible( isAccentColorSupported );
for( int i = 0; i < accentColorButtons.length; i++ )
accentColorButtons[i].setEnabled( isAccentColorSupported );
accentColorButtons[i].setVisible( isAccentColorSupported );
}
private void initComponents() {

View File

@@ -36,7 +36,7 @@ public class FlatLafDemo
static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) );
public static void main( String[] args ) {
// macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// enable screen menu bar
// (moves menu bar from JFrame window to top of screen)
@@ -51,6 +51,7 @@ public class FlatLafDemo
// - "system": use current macOS appearance (light or dark)
// - "NSAppearanceNameAqua": use light appearance
// - "NSAppearanceNameDarkAqua": use dark appearance
// (needs to be set on main thread; setting it on AWT thread does not work)
System.setProperty( "apple.awt.application.appearance", "system" );
}

View File

@@ -65,6 +65,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.MigLayoutVisualPadding;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -113,6 +114,7 @@ public class FlatInspector
private int inspectParentLevel;
private boolean wasModifierKeyPressed;
private boolean showClassHierarchy;
private long lastWhen;
private JComponent highlightFigure;
private Popup popup;
@@ -131,8 +133,22 @@ public class FlatInspector
(((KeyEvent)e).getModifiersEx() & KEY_MODIFIERS_MASK) == (keyStroke.getModifiers() & KEY_MODIFIERS_MASK) )
{
Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if( activeWindow instanceof RootPaneContainer ) {
JRootPane rootPane = ((RootPaneContainer)activeWindow).getRootPane();
RootPaneContainer rootPaneContainer = null;
if( activeWindow instanceof RootPaneContainer )
rootPaneContainer = (RootPaneContainer) activeWindow;
else {
// search for root pain container in children
// (e.g. for Swing embedded into SWT)
for( Component child : activeWindow.getComponents() ) {
if( child instanceof RootPaneContainer ) {
rootPaneContainer = (RootPaneContainer) child;
break;
}
}
}
if( rootPaneContainer != null ) {
JRootPane rootPane = rootPaneContainer.getRootPane();
FlatInspector inspector = (FlatInspector) rootPane.getClientProperty( FlatInspector.class );
if( inspector == null ) {
inspector = new FlatInspector( rootPane );
@@ -172,6 +188,11 @@ public class FlatInspector
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT )
wasModifierKeyPressed = true;
} else if( id == KeyEvent.KEY_RELEASED && wasModifierKeyPressed ) {
// ignore duplicate events (for Swing embedded into SWT)
if( (keyEvent.getWhen() - lastWhen) <= 5 )
return;
lastWhen = keyEvent.getWhen();
if( keyCode == KeyEvent.VK_CONTROL ) {
inspectParentLevel++;
int parentLevel = inspect( lastX, lastY );
@@ -464,6 +485,15 @@ public class FlatInspector
if( margin != null )
appendRow( buf, "Margin", toString( margin ) );
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( MigLayoutVisualPadding.VISUAL_PADDING_PROPERTY );
Insets visualPadding = (value instanceof int[])
? new Insets( ((int[])value)[0], ((int[])value)[1], ((int[])value)[2], ((int[])value)[3] )
: (value instanceof Insets ? (Insets) value : null);
if( visualPadding != null )
appendRow( buf, "Mig visual padding", toString( visualPadding ) );
}
Dimension prefSize = c.getPreferredSize();
Dimension minSize = c.getMinimumSize();
Dimension maxSize = c.getMaximumSize();

View File

@@ -18,7 +18,7 @@ package com.formdev.flatlaf.extras.components;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color;
import javax.swing.JToggleButton;
import javax.swing.*;
import com.formdev.flatlaf.extras.components.FlatButton.ButtonType;
/**
@@ -116,16 +116,37 @@ public class FlatToggleButton
putClientProperty( OUTLINE, outline );
}
/**
* Returns placement of underline if toggle button type is {@link ButtonType#tab}.
* If underline placement is not specified, returns {@link #BOTTOM} as the default
* value.
*
* @since 2.3
*/
public int getTabUnderlinePlacement() {
return getClientPropertyInt( TAB_BUTTON_UNDERLINE_PLACEMENT, BOTTOM );
}
/**
* Returns height of underline if toggle button type is {@link ButtonType#tab}.
* Specifies placement of underline if toggle button type is {@link ButtonType#tab}.
*
* @param placement One of the following constants defined in SwingConstants:
* {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, or {@link #RIGHT}.
* @since 2.3
*/
public void setTabUnderlinePlacement( int placement ) {
putClientProperty( TAB_BUTTON_UNDERLINE_PLACEMENT, (placement >= 0) ? placement : null );
}
/**
* Returns thickness of underline if toggle button type is {@link ButtonType#tab}.
*/
public int getTabUnderlineHeight() {
return getClientPropertyInt( TAB_BUTTON_UNDERLINE_HEIGHT, "ToggleButton.tab.underlineHeight" );
}
/**
* Specifies height of underline if toggle button type is {@link ButtonType#tab}.
* Specifies thickness of underline if toggle button type is {@link ButtonType#tab}.
*/
public void setTabUnderlineHeight( int tabUnderlineHeight ) {
putClientProperty( TAB_BUTTON_UNDERLINE_HEIGHT, (tabUnderlineHeight >= 0) ? tabUnderlineHeight : null );

View File

@@ -129,7 +129,7 @@
#---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
- monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]

View File

@@ -1214,6 +1214,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #303234 HSL 210 4 20 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse)
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonPressedBackground #484c4f HSL 206 5 30 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
@@ -1233,11 +1234,15 @@ TitlePane.inactiveBackground #303234 HSL 210 4 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1545,7 +1550,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----

View File

@@ -129,7 +129,7 @@
#---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
- monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]

View File

@@ -1219,6 +1219,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse)
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonPressedBackground #ebebeb HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] darken(8% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
@@ -1238,11 +1239,15 @@ TitlePane.inactiveBackground #ffffff HSL 0 0 100 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1550,7 +1555,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----

View File

@@ -73,43 +73,57 @@ Button.default.endBorderColor #0000ff HSL 240 100 50 javax.swing.plaf.Colo
Button.default.focusColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.focusedBackground #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.focusedBorderColor #537699 HSL 210 30 46 javax.swing.plaf.ColorUIResource [UI]
Button.default.focusedForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.foreground #880000 HSL 0 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.default.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.defaultButtonFollowsFocus false
Button.disabledBackground #e0e0e0 HSL 0 0 88 javax.swing.plaf.ColorUIResource [UI]
Button.disabledBorderColor #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBackground #112233 HSL 210 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedForeground #ffcccc HSL 0 100 90 javax.swing.plaf.ColorUIResource [UI]
Button.disabledText #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.endBackground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Button.endBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedBackground #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedBorderColor #466d94 HSL 210 36 43 javax.swing.plaf.ColorUIResource [UI]
Button.focusedForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.font [active] $defaultFont [UI]
Button.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.iconTextGap 4
Button.innerFocusWidth 1
Button.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI]
Button.margin 2,14,2,14 javax.swing.plaf.InsetsUIResource [UI]
Button.minimumWidth 72
Button.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.rollover true
Button.selectedBackground #ffbbbb HSL 0 100 87 javax.swing.plaf.ColorUIResource [UI]
Button.selectedForeground #332211 HSL 30 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI]
Button.startBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.startBorderColor #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.textIconGap 4
Button.textShiftOffset 0
Button.toolbar.disabledSelectedBackground #cccccc HSL 0 0 80 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.disabledSelectedForeground #886666 HSL 0 14 47 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.hoverBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.hoverForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
Button.toolbar.pressedBackground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.pressedForeground #666666 HSL 0 0 40 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.selectedBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.selectedForeground #880000 HSL 0 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.spacingInsets 1,2,1,2 javax.swing.plaf.InsetsUIResource [UI]
ButtonUI com.formdev.flatlaf.ui.FlatButtonUI
@@ -1235,6 +1249,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
TitlePane.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
@@ -1252,11 +1267,15 @@ TitlePane.inactiveBackground #008800 HSL 120 100 27 javax.swing.plaf.Colo
TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1280,31 +1299,43 @@ ToggleButton.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.F
ToggleButton.darkShadow #696969 HSL 0 0 41 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.disabledBackground #e0e0e0 HSL 0 0 88 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.disabledSelectedBackground #44dd44 HSL 120 69 57 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.disabledSelectedForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.disabledText #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.focusedBackground #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.focusedForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.font [active] $defaultFont [UI]
ToggleButton.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.iconTextGap 4
ToggleButton.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.margin 2,14,2,14 javax.swing.plaf.InsetsUIResource [UI]
ToggleButton.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.rollover true
ToggleButton.selectedBackground #44ff44 HSL 120 100 63 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.selectedForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.disabledUnderlineColor #7a7a7a HSL 0 0 48 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.focusBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.focusForeground #008800 HSL 120 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.hoverBackground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedBackground #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedBackground #008800 HSL 120 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.underlineColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.underlineHeight 2
ToggleButton.textIconGap 4
ToggleButton.textShiftOffset 0
ToggleButton.toolbar.disabledSelectedBackground #cccccc HSL 0 0 80 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.disabledSelectedForeground #886666 HSL 0 14 47 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.hoverBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.hoverForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.pressedBackground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.pressedForeground #666666 HSL 0 0 40 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.selectedBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.toolbar.selectedForeground #880000 HSL 0 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButtonUI com.formdev.flatlaf.ui.FlatToggleButtonUI
@@ -1563,7 +1594,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----

View File

@@ -16,7 +16,12 @@
package com.formdev.flatlaf.testing;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
import javax.swing.*;
import com.formdev.flatlaf.icons.FlatFileChooserHomeFolderIcon;
import net.miginfocom.swing.*;
/**
@@ -28,6 +33,24 @@ public class FlatChooserTest
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatChooserTest" );
UIManager.put( "FileChooser.shortcuts.filesFunction", (Function<File[], File[]>) files -> {
ArrayList<File> list = new ArrayList<>( Arrays.asList( files ) );
list.add( 0, new File( System.getProperty( "user.home" ) ) );
return list.toArray( new File[list.size()] );
} );
UIManager.put( "FileChooser.shortcuts.displayNameFunction", (Function<File, String>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return "Home";
return null;
} );
UIManager.put( "FileChooser.shortcuts.iconFunction", (Function<File, Icon>) file -> {
if( file.getAbsolutePath().equals( System.getProperty( "user.home" ) ) )
return new FlatFileChooserHomeFolderIcon();
return null;
} );
frame.showFrame( FlatChooserTest::new );
} );
}

View File

@@ -106,6 +106,20 @@ public class FlatComponents2Test
table1.setModel( tableModel );
xTable1.setModel( tableModel );
// table header popup menu
JMenuItem addMenuItem = new JMenuItem( "Add column" );
addMenuItem.addActionListener( e -> {
tableModel.setColumnCount( tableModel.getColumnCount() + 1 );
});
JMenuItem removeMenuItem = new JMenuItem( "Remove last column" );
removeMenuItem.addActionListener( e -> {
tableModel.setColumnCount( tableModel.getColumnCount() - 1 );
});
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( addMenuItem );
popupMenu.add( removeMenuItem );
table1.getTableHeader().setComponentPopupMenu( popupMenu );
// table column editors
initTableEditors( table1 );
initTableEditors( xTable1 );
@@ -336,6 +350,16 @@ public class FlatComponents2Test
table.setSurrendersFocusOnKeystroke( focusCellEditorCheckBox.isSelected() );
}
private void alternatingRowsChanged() {
UIManager.put( "Table.alternateRowColor", alternatingRowsCheckBox.isSelected() ? Color.orange : null );
table1ScrollPane.repaint();
}
private void paintOutsideAlternateRowsChanged() {
UIManager.put( "Table.paintOutsideAlternateRows", paintOutsideAlternateRowsCheckBox.isSelected() ? true : null );
table1ScrollPane.repaint();
}
private void treeRendererChanged() {
Object sel = treeRendererComboBox.getSelectedItem();
if( !(sel instanceof String) )
@@ -493,8 +517,10 @@ public class FlatComponents2Test
focusCellEditorCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
columnSelectionCheckBox = new JCheckBox();
alternatingRowsCheckBox = new JCheckBox();
intercellSpacingCheckBox = new JCheckBox();
rowHeaderCheckBox = new JCheckBox();
paintOutsideAlternateRowsCheckBox = new JCheckBox();
redGridColorCheckBox = new JCheckBox();
tableHeaderButtonCheckBox = new JCheckBox();
@@ -875,6 +901,11 @@ public class FlatComponents2Test
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
tableOptionsPanel.add(columnSelectionCheckBox, "cell 1 2");
//---- alternatingRowsCheckBox ----
alternatingRowsCheckBox.setText("alternating rows");
alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged());
tableOptionsPanel.add(alternatingRowsCheckBox, "cell 2 2");
//---- intercellSpacingCheckBox ----
intercellSpacingCheckBox.setText("intercell spacing");
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
@@ -885,6 +916,11 @@ public class FlatComponents2Test
rowHeaderCheckBox.addActionListener(e -> rowHeaderChanged());
tableOptionsPanel.add(rowHeaderCheckBox, "cell 1 3");
//---- paintOutsideAlternateRowsCheckBox ----
paintOutsideAlternateRowsCheckBox.setText("outside alternating rows");
paintOutsideAlternateRowsCheckBox.addActionListener(e -> paintOutsideAlternateRowsChanged());
tableOptionsPanel.add(paintOutsideAlternateRowsCheckBox, "cell 2 3");
//---- redGridColorCheckBox ----
redGridColorCheckBox.setText("red grid color");
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
@@ -927,8 +963,10 @@ public class FlatComponents2Test
private JCheckBox focusCellEditorCheckBox;
private JCheckBox showVerticalLinesCheckBox;
private JCheckBox columnSelectionCheckBox;
private JCheckBox alternatingRowsCheckBox;
private JCheckBox intercellSpacingCheckBox;
private JCheckBox rowHeaderCheckBox;
private JCheckBox paintOutsideAlternateRowsCheckBox;
private JCheckBox redGridColorCheckBox;
private JCheckBox tableHeaderButtonCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables
@@ -1150,6 +1188,7 @@ public class FlatComponents2Test
{ "item 12", null, "December", null, null, null },
};
private int columnCount = columnNames.length;
private int rowCount = rows.length;
private final Map<Integer, Object[]> moreRowsMap = new HashMap<>();
@@ -1157,6 +1196,16 @@ public class FlatComponents2Test
setRowCount( rowCount );
}
void setColumnCount( int columnCount ) {
if( columnCount > columnNames.length )
columnCount = columnNames.length;
this.columnCount = columnCount;
// fire event
fireTableStructureChanged();
}
void setRowCount( int rowCount ) {
int oldRowCount = this.rowCount;
this.rowCount = rowCount;
@@ -1175,7 +1224,7 @@ public class FlatComponents2Test
@Override
public int getColumnCount() {
return columnNames.length;
return columnCount;
}
@Override

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -485,6 +485,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "alternatingRowsCheckBox"
"text": "alternating rows"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alternatingRowsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "intercellSpacingCheckBox"
"text": "intercell spacing"
@@ -505,6 +515,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "paintOutsideAlternateRowsCheckBox"
"text": "outside alternating rows"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "paintOutsideAlternateRowsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "redGridColorCheckBox"
"text": "red grid color"

View File

@@ -181,6 +181,10 @@ public class FlatMenusTest
JMenuItem menuItem45 = new JMenuItem();
JMenuItem menuItem46 = new JMenuItem();
JMenuItem menuItem47 = new JMenuItem();
JMenu menu13 = new JMenu();
JMenuItem menuItem48 = new JMenuItem();
JMenuItem menuItem49 = new JMenuItem();
JMenuItem menuItem50 = new JMenuItem();
menuBar2 = new JMenuBar();
JMenu menu8 = new JMenu();
FlatMenusTest.LargerMenuItem menuItem13 = new FlatMenusTest.LargerMenuItem();
@@ -442,6 +446,24 @@ public class FlatMenusTest
menu12.add(menuItem47);
}
menuBar1.add(menu12);
//======== menu13 ========
{
menu13.setText("HTML");
//---- menuItem48 ----
menuItem48.setText("<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem48);
//---- menuItem49 ----
menuItem49.setText("<html>some longer <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem49);
//---- menuItem50 ----
menuItem50.setText("<html>another <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem50);
}
menuBar1.add(menu13);
}
add(menuBar1, "cell 1 0 2 1,growx");

View File

@@ -194,6 +194,22 @@ new FormModel {
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test64.png" )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu13"
"text": "HTML"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem48"
"text": "<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem49"
"text": "<html>some longer <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem50"
"text": "<html>another <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0 2 1,growx"
} )

View File

@@ -0,0 +1,376 @@
/*
* Copyright 2022 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
*
* https://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.testing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatPaintingHiDPITest
extends JPanel
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatPaintingHiDPITest" );
frame.showFrame( FlatPaintingHiDPITest::new );
} );
}
FlatPaintingHiDPITest() {
initComponents();
sliderChanged();
}
private void sliderChanged() {
painter.translateX = translateXSlider.getValue();
painter.translateY = translateYSlider.getValue();
painter.scaleX = scaleXSlider.getValue();
painter.scaleY = scaleYSlider.getValue();
painter.rotate = rotateSlider.getValue();
painter.repaint();
AffineTransform t = new AffineTransform();
t.translate( painter.translateX, painter.translateY );
t.scale( painter.scaleX / 100., painter.scaleY / 100. );
t.rotate( Math.toRadians( painter.rotate ) );
tScaleXLabel.setText( Double.toString( t.getScaleX() ) );
tScaleYLabel.setText( Double.toString( t.getScaleY() ) );
tShearXLabel.setText( Double.toString( t.getShearX() ) );
tShearYLabel.setText( Double.toString( t.getShearY() ) );
tTranslateXLabel.setText( Double.toString( t.getTranslateX() ) );
tTranslateYLabel.setText( Double.toString( t.getTranslateY() ) );
double scaleX = Math.hypot( t.getScaleX(), t.getShearX() );
double scaleY = Math.hypot( t.getScaleY(), t.getShearY() );
if( t.getScaleX() < 0 )
scaleX = -scaleX;
if( t.getScaleY() < 0 )
scaleY = -scaleY;
double rotation = Math.atan2( t.getShearY(), t.getScaleY() );
double rotationDegrees = Math.toDegrees( rotation );
cScaleXLabel.setText( Double.toString( scaleX ) );
cScaleYLabel.setText( Double.toString( scaleY ) );
cRotationDegreesLabel.setText( Double.toString( rotationDegrees ) );
}
private void reset() {
translateXSlider.setValue( 100 );
translateYSlider.setValue( 100 );
scaleXSlider.setValue( 100 );
scaleYSlider.setValue( 100 );
rotateSlider.setValue( 0 );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel translateXLabel = new JLabel();
translateXSlider = new JSlider();
JLabel translateYLabel = new JLabel();
translateYSlider = new JSlider();
JLabel scaleXLabel = new JLabel();
scaleXSlider = new JSlider();
JLabel scaleYLabel = new JLabel();
scaleYSlider = new JSlider();
JLabel rotateLabel = new JLabel();
rotateSlider = new JSlider();
JPanel panel1 = new JPanel();
JLabel tLabel = new JLabel();
JLabel xLabel = new JLabel();
JLabel yLabel = new JLabel();
JLabel tScaleLabel = new JLabel();
tScaleXLabel = new JLabel();
tScaleYLabel = new JLabel();
JLabel tShearLabel = new JLabel();
tShearXLabel = new JLabel();
tShearYLabel = new JLabel();
JLabel tTranslateLabel = new JLabel();
tTranslateXLabel = new JLabel();
tTranslateYLabel = new JLabel();
JLabel cLabel = new JLabel();
JLabel cScaleLabel = new JLabel();
cScaleXLabel = new JLabel();
cScaleYLabel = new JLabel();
JLabel cRotationLabel = new JLabel();
cRotationDegreesLabel = new JLabel();
JButton resetButton = new JButton();
painter = new FlatPaintingHiDPITest.HiDPI1xPainter();
//======== this ========
setBorder(null);
setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[400,fill]para" +
"[grow,fill]",
// rows
"[fill]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[grow]"));
//---- translateXLabel ----
translateXLabel.setText("TranslateX:");
add(translateXLabel, "cell 0 0");
//---- translateXSlider ----
translateXSlider.setMaximum(500);
translateXSlider.setPaintLabels(true);
translateXSlider.setPaintTicks(true);
translateXSlider.setMajorTickSpacing(100);
translateXSlider.setMinorTickSpacing(25);
translateXSlider.setValue(100);
translateXSlider.addChangeListener(e -> sliderChanged());
add(translateXSlider, "cell 1 0");
//---- translateYLabel ----
translateYLabel.setText("TranslateY:");
add(translateYLabel, "cell 0 1");
//---- translateYSlider ----
translateYSlider.setMaximum(500);
translateYSlider.setPaintLabels(true);
translateYSlider.setPaintTicks(true);
translateYSlider.setMajorTickSpacing(100);
translateYSlider.setMinorTickSpacing(25);
translateYSlider.setValue(100);
translateYSlider.addChangeListener(e -> sliderChanged());
add(translateYSlider, "cell 1 1");
//---- scaleXLabel ----
scaleXLabel.setText("ScaleX:");
add(scaleXLabel, "cell 0 2");
//---- scaleXSlider ----
scaleXSlider.setMaximum(300);
scaleXSlider.setValue(100);
scaleXSlider.setPaintTicks(true);
scaleXSlider.setPaintLabels(true);
scaleXSlider.setMajorTickSpacing(50);
scaleXSlider.setSnapToTicks(true);
scaleXSlider.setMinorTickSpacing(10);
scaleXSlider.setMinimum(-100);
scaleXSlider.addChangeListener(e -> sliderChanged());
add(scaleXSlider, "cell 1 2");
//---- scaleYLabel ----
scaleYLabel.setText("ScaleY:");
add(scaleYLabel, "cell 0 3");
//---- scaleYSlider ----
scaleYSlider.setMaximum(300);
scaleYSlider.setValue(100);
scaleYSlider.setPaintTicks(true);
scaleYSlider.setPaintLabels(true);
scaleYSlider.setMajorTickSpacing(50);
scaleYSlider.setSnapToTicks(true);
scaleYSlider.setMinorTickSpacing(10);
scaleYSlider.setMinimum(-100);
scaleYSlider.addChangeListener(e -> sliderChanged());
add(scaleYSlider, "cell 1 3");
//---- rotateLabel ----
rotateLabel.setText("Rotate:");
add(rotateLabel, "cell 0 4");
//---- rotateSlider ----
rotateSlider.setMaximum(360);
rotateSlider.setMinimum(-360);
rotateSlider.setValue(0);
rotateSlider.setMajorTickSpacing(90);
rotateSlider.setPaintLabels(true);
rotateSlider.setPaintTicks(true);
rotateSlider.addChangeListener(e -> sliderChanged());
add(rotateSlider, "cell 1 4");
//======== panel1 ========
{
panel1.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[50,fill]" +
"[50,fill]",
// rows
"[]" +
"[]0" +
"[]0" +
"[]para" +
"[]" +
"[]0" +
"[]"));
//---- tLabel ----
tLabel.setText("AffineTransform:");
panel1.add(tLabel, "cell 0 0");
//---- xLabel ----
xLabel.setText("X");
panel1.add(xLabel, "cell 1 0,alignx center,growx 0");
//---- yLabel ----
yLabel.setText("Y");
panel1.add(yLabel, "cell 2 0,alignx center,growx 0");
//---- tScaleLabel ----
tScaleLabel.setText("Scale:");
panel1.add(tScaleLabel, "cell 0 1,gapx indent");
//---- tScaleXLabel ----
tScaleXLabel.setText("text");
panel1.add(tScaleXLabel, "cell 1 1");
//---- tScaleYLabel ----
tScaleYLabel.setText("text");
panel1.add(tScaleYLabel, "cell 2 1");
//---- tShearLabel ----
tShearLabel.setText("Shear:");
panel1.add(tShearLabel, "cell 0 2,gapx indent");
//---- tShearXLabel ----
tShearXLabel.setText("text");
panel1.add(tShearXLabel, "cell 1 2");
//---- tShearYLabel ----
tShearYLabel.setText("text");
panel1.add(tShearYLabel, "cell 2 2");
//---- tTranslateLabel ----
tTranslateLabel.setText("Translate:");
panel1.add(tTranslateLabel, "cell 0 3,gapx indent");
//---- tTranslateXLabel ----
tTranslateXLabel.setText("text");
panel1.add(tTranslateXLabel, "cell 1 3");
//---- tTranslateYLabel ----
tTranslateYLabel.setText("text");
panel1.add(tTranslateYLabel, "cell 2 3");
//---- cLabel ----
cLabel.setText("Computed:");
panel1.add(cLabel, "cell 0 4");
//---- cScaleLabel ----
cScaleLabel.setText("Scale:");
panel1.add(cScaleLabel, "cell 0 5,gapx indent");
//---- cScaleXLabel ----
cScaleXLabel.setText("text");
panel1.add(cScaleXLabel, "cell 1 5");
//---- cScaleYLabel ----
cScaleYLabel.setText("text");
panel1.add(cScaleYLabel, "cell 2 5");
//---- cRotationLabel ----
cRotationLabel.setText("Rotation:");
panel1.add(cRotationLabel, "cell 0 6,gapx indent");
//---- cRotationDegreesLabel ----
cRotationDegreesLabel.setText("text");
panel1.add(cRotationDegreesLabel, "cell 1 6");
}
add(panel1, "cell 2 0 1 4");
//---- resetButton ----
resetButton.setText("Reset");
resetButton.addActionListener(e -> reset());
add(resetButton, "cell 2 4,align left bottom,grow 0 0");
add(painter, "cell 0 5 3 1,grow,width 600,height 400");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JSlider translateXSlider;
private JSlider translateYSlider;
private JSlider scaleXSlider;
private JSlider scaleYSlider;
private JSlider rotateSlider;
private JLabel tScaleXLabel;
private JLabel tScaleYLabel;
private JLabel tShearXLabel;
private JLabel tShearYLabel;
private JLabel tTranslateXLabel;
private JLabel tTranslateYLabel;
private JLabel cScaleXLabel;
private JLabel cScaleYLabel;
private JLabel cRotationDegreesLabel;
private FlatPaintingHiDPITest.HiDPI1xPainter painter;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class HiDPI1xPainter -----------------------------------------------
public static class HiDPI1xPainter
extends JComponent
{
int translateX;
int translateY;
int scaleX;
int scaleY;
int rotate;
public HiDPI1xPainter() {
}
@Override
protected void paintComponent( Graphics g ) {
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( Color.blue );
g2.drawRect( 0, 0, width - 1, height - 1 );
g2.setColor( Color.cyan );
g2.drawLine( 0, translateY, width, translateY );
g2.drawLine( translateX, 0, translateX, height );
g2.translate( translateX, translateY );
g2.scale( scaleX / 100., scaleY / 100. );
g2.rotate( Math.toRadians( rotate ) );
g2.setColor( Color.red );
g2.fillRect( 0, 0, 100, 50 );
g2.setColor( Color.green );
HiDPIUtils.paintAtScale1x( g2, 0, 0, 100, 50,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
g2d.fillRect( x2 + 10, y2 + 10, width2 - 20, height2 - 20 );
} );
} finally {
g2.dispose();
}
}
}
}

View File

@@ -0,0 +1,289 @@
JFDML JFormDesigner: "8.0.0.0.122" Java: "17.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][400,fill]para[grow,fill]"
"$rowConstraints": "[fill][][][][][grow]"
} ) {
name: "this"
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
add( new FormComponent( "javax.swing.JLabel" ) {
name: "translateXLabel"
"text": "TranslateX:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "translateXSlider"
"maximum": 500
"paintLabels": true
"paintTicks": true
"majorTickSpacing": 100
"minorTickSpacing": 25
"value": 100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "translateYLabel"
"text": "TranslateY:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "translateYSlider"
"maximum": 500
"paintLabels": true
"paintTicks": true
"majorTickSpacing": 100
"minorTickSpacing": 25
"value": 100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleXLabel"
"text": "ScaleX:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "scaleXSlider"
"maximum": 300
"value": 100
"paintTicks": true
"paintLabels": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleYLabel"
"text": "ScaleY:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "scaleYSlider"
"maximum": 300
"value": 100
"paintTicks": true
"paintLabels": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "rotateLabel"
"text": "Rotate:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "rotateSlider"
"maximum": 360
"minimum": -360
"value": 0
"majorTickSpacing": 90
"paintLabels": true
"paintTicks": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][50,fill][50,fill]"
"$rowConstraints": "[][]0[]0[]para[][]0[]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tLabel"
"text": "AffineTransform:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "xLabel"
"text": "X"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "yLabel"
"text": "Y"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleLabel"
"text": "Scale:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearLabel"
"text": "Shear:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateLabel"
"text": "Translate:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cLabel"
"text": "Computed:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleLabel"
"text": "Scale:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cRotationLabel"
"text": "Rotation:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cRotationDegreesLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0 1 4"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "resetButton"
"text": "Reset"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "reset", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4,align left bottom,grow 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatPaintingHiDPITest$HiDPI1xPainter" ) {
name: "painter"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5 3 1,grow,width 600,height 400"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 685, 680 )
} )
}
}

View File

@@ -22,12 +22,14 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
@@ -57,18 +59,60 @@ public class FlatPaintingStringTest
FlatPaintingStringTest() {
initComponents();
String[] availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames().clone();
Arrays.sort( availableFontFamilyNames );
Font currentFont = UIManager.getFont( "Label.font" );
String currentFamily = currentFont.getFamily();
// initialize font families combobox
String[] families = {
// regular
"Arial", "Cantarell", "DejaVu Sans",
"Dialog", "Helvetica Neue", "Inter", "Liberation Sans", "Noto Sans", "Open Sans", "Roboto",
"SansSerif", "Segoe UI", "Tahoma", "Ubuntu", "Verdana", ".SF NS Text",
// light, semibold
"Segoe UI Light", "Segoe UI Semibold",
"HelveticaNeue-Thin", "HelveticaNeue-Medium",
"Lato Light", "Ubuntu Light", "Cantarell Light",
"Lato Semibold", "Ubuntu Medium", "Montserrat SemiBold",
// monospaced
"Monospaced", "Consolas", "Courier New", "Menlo", "Liberation Mono", "Ubuntu Mono",
};
Arrays.sort( families, String.CASE_INSENSITIVE_ORDER );
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
model.addElement( currentFamily );
for( String family : families ) {
if( !family.equals( currentFamily ) && Arrays.binarySearch( availableFontFamilyNames, family ) >= 0 )
model.addElement( family );
}
fontField.setModel( model );
fontField.setSelectedItem( currentFamily );
updateFontMetricsLabel();
add( new JLabel() );
add( new JLabel( "none" ) );
add( new JLabel( "flatlaf" ) );
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel() );
if( SystemInfo.isJava_9_orLater ) {
add( new JLabel( "0.125" ) );
add( new JLabel( "0.25" ) );
add( new JLabel( "0.5" ) );
add( new JLabel( "0.625" ) );
add( new JLabel( "0.75" ) );
add( new JLabel( "0.875" ) );
add( new JLabel( "1" ) );
add( new JLabel( "1.25" ) );
add( new JLabel() );
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel( "ty" ) );
} else {
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel( "0.625*scale" ) );
add( new JLabel( "0.75*scale" ) );
add( new JLabel( "0.875*scale" ) );
@@ -80,16 +124,14 @@ public class FlatPaintingStringTest
? HiDPIUtils.computeTextYCorrection( g )
: (scaleFactor > 1 ? -(0.625f * scaleFactor) : 0);
};
YCorrectionFunction oneQSysScale = (g, scaleFactor) -> -(0.25f * scaleFactor);
YCorrectionFunction halfSysScale = (g, scaleFactor) -> -(0.5f * scaleFactor);
YCorrectionFunction fiveEightsQSysScale = (g, scaleFactor) -> -(0.625f * scaleFactor);
YCorrectionFunction threeQSysScale = (g, scaleFactor) -> -(0.75f * scaleFactor);
YCorrectionFunction sevenEightsSysScale = (g, scaleFactor) -> -(0.875f * scaleFactor);
YCorrectionFunction oneQ = (g, scaleFactor) -> -0.25f;
YCorrectionFunction half = (g, scaleFactor) -> -0.5f;
YCorrectionFunction fiveEights = (g, scaleFactor) -> -0.625f;
YCorrectionFunction threeQ = (g, scaleFactor) -> -0.75f;
YCorrectionFunction sevenEights = (g, scaleFactor) -> -0.875f;
YCorrectionFunction ty = (g, scaleFactor) -> {
// Based on translateY, which is the scaled Y coordinate translation of the graphics context.
// When painting whole window, translateY is from top of window, and this works fine.
// But when repainting only parts of the window, then translateY starts somewhere
// else and the text if (re-)painted at the wrong Y location.
double y = g.getTransform().getTranslateY();
return (float) -(y - (int) y);
};
float[] scaleFactors = new float[] { 1f, 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 3f, 3.5f, 4f };
@@ -98,34 +140,72 @@ public class FlatPaintingStringTest
add( scaleFactor, none );
add( scaleFactor, flatlaf );
add( scaleFactor, oneQSysScale );
add( scaleFactor, halfSysScale );
add( new JLabel( " " ) );
if( SystemInfo.isJava_9_orLater ) {
add( scaleFactor, oneQ );
add( scaleFactor, half );
add( scaleFactor, fiveEights );
add( scaleFactor, threeQ );
add( scaleFactor, sevenEights );
add( scaleFactor, (g, sf) -> -0.125f );
add( scaleFactor, (g, sf) -> -0.25f );
add( scaleFactor, (g, sf) -> -0.5f );
add( scaleFactor, (g, sf) -> -0.625f );
add( scaleFactor, (g, sf) -> -0.75f );
add( scaleFactor, (g, sf) -> -0.875f );
add( scaleFactor, (g, sf) -> -1f );
add( scaleFactor, (g, sf) -> -1.25f );
add( new JLabel( " " ) );
add( scaleFactor, (g, sf) -> -(0.25f * sf) );
add( scaleFactor, (g, sf) -> -(0.5f * sf) );
add( scaleFactor, ty );
} else {
add( scaleFactor, fiveEightsQSysScale );
add( scaleFactor, threeQSysScale );
add( scaleFactor, sevenEightsSysScale );
add( scaleFactor, (g, sf) -> -(0.25f * sf) );
add( scaleFactor, (g, sf) -> -(0.5f * sf) );
add( scaleFactor, (g, sf) -> -(0.625f * sf) );
add( scaleFactor, (g, sf) -> -(0.75f * sf) );
add( scaleFactor, (g, sf) -> -(0.875f * sf) );
}
add( new JLabel( String.valueOf( scaleFactor ) ) );
}
}
private void add( float scaleFactor, YCorrectionFunction correctionFunction ) {
if( SystemInfo.isJava_9_orLater ) {
add( new Painter( scaleFactor, correctionFunction, 0 ), "split 4, gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.25f ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.5f ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.75f ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 1 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 2 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 3 ), "gapx 0 0" );
} else
add( new Painter( scaleFactor, correctionFunction, 0 ) );
}
private void fontChanged() {
String family = (String) fontField.getSelectedItem();
Font font = UIManager.getFont( "defaultFont" );
if( font.getFamily().equals( family ) )
return;
Font newFont = StyleContext.getDefaultStyleContext().getFont( family, font.getStyle(), font.getSize() );
UIManager.put( "defaultFont", newFont );
updateFontMetricsLabel();
FlatLaf.updateUILater();
}
private void updateFontMetricsLabel() {
Font font = UIManager.getFont( "defaultFont" );
FontMetrics fm = getFontMetrics( font );
fontMetricsLabel.setText( String.format( "%s %d height %d ascent %d descent %d max ascent %d max descent %d leading %d",
font.getFamily(), font.getSize(),
fm.getHeight(), fm.getAscent(), fm.getDescent(),
fm.getMaxAscent(), fm.getMaxDescent(), fm.getLeading()
) );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JPanel panel1 = new JPanel();
JLabel fontLabel = new JLabel();
fontField = new JComboBox<>();
fontMetricsLabel = new JLabel();
//======== this ========
setBorder(null);
@@ -134,11 +214,40 @@ public class FlatPaintingStringTest
// columns
"[fill]",
// rows
"[top]unrel" +
"[top]"));
//======== panel1 ========
{
panel1.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]" +
"[fill]",
// rows
"[]"));
//---- fontLabel ----
fontLabel.setText("Font:");
panel1.add(fontLabel, "cell 0 0");
//---- fontField ----
fontField.setMaximumRowCount(20);
fontField.addActionListener(e -> fontChanged());
panel1.add(fontField, "cell 1 0");
//---- fontMetricsLabel ----
fontMetricsLabel.setText("text");
panel1.add(fontMetricsLabel, "cell 2 0");
}
add(panel1, "north");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JComboBox<String> fontField;
private JLabel fontMetricsLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables
private interface YCorrectionFunction {
@@ -152,17 +261,29 @@ public class FlatPaintingStringTest
{
private final float scaleFactor;
private final YCorrectionFunction correctionFunction;
private final float yOffset;
private final int yOffset;
public Painter( float scaleFactor, YCorrectionFunction correctionFunction, float yOffset ) {
public Painter( float scaleFactor, YCorrectionFunction correctionFunction, int yOffset ) {
super( "E" );
this.scaleFactor = scaleFactor;
this.correctionFunction = correctionFunction;
this.yOffset = yOffset;
setBorder( new EmptyBorder( 2, 0, 2, 0 ) );
updateFont();
}
@Override
public void updateUI() {
super.updateUI();
updateFont();
}
private void updateFont() {
if( scaleFactor == 0 )
return; // invoked from super constructor
if( !SystemInfo.isJava_9_orLater ) {
Font font = getFont();
Font font = UIManager.getFont( "defaultFont" );
setFont( font.deriveFont( (float) Math.round( font.getSize() * scaleFactor ) ) );
}
}
@@ -170,35 +291,36 @@ public class FlatPaintingStringTest
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
Insets insets = getInsets();
int leftRight = insets.left + insets.right;
return new Dimension(
scale( size.width -leftRight ) + leftRight,
scale( size.height ) );
if( SystemInfo.isJava_9_orLater ) {
// compute component size using JRE scaling
//
// The y offset is used to simulate different vertical component positions,
// which may result in different component heights.
// E.g. scaling following bounds by 150% results in different heights:
// 0,0, 10,15 --> [x=0,y=0,width=15,height=22]
// 0,1, 10,15 --> [x=0,y=1,width=15,height=23]
Rectangle r = scaleTo1x( scaleFactor, 0, yOffset, size.width, size.height );
return new Dimension( r.width, r.height );
} else
return size;
}
@Override
protected void paintComponent( Graphics g ) {
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
// simulate component y position at a fraction
if( scaleFactor > 1 && SystemInfo.isJava_9_orLater )
g2.translate( 0, yOffset );
int width = getWidth();
int height = getHeight();
Insets insets = getInsets();
FontMetrics fm = getFontMetrics( getFont() );
// paint lines at 1x
HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height,
(g2d, x2, y2, width2, height2, scaleFactor2) -> {
// g.setColor( Color.blue );
// g.drawLine( 0, 0, width2, 0 );
// g.drawLine( 0, height2 - 1, width2, height2 - 1 );
g.setColor( Color.blue );
g.drawLine( 0, 0, width2, 0 );
g.drawLine( 0, height2 - 1, width2, height2 - 1 );
int baseline = (int) Math.round( (insets.top + fm.getAscent()) * scaleFactor2
int baseline = (int) Math.round( fm.getAscent() * scaleFactor2
* (SystemInfo.isJava_9_orLater ? scaleFactor : 1f) ) - 1;
int topline = height2 - baseline - 1;
@@ -207,8 +329,15 @@ public class FlatPaintingStringTest
g.drawLine( 0, topline, width2, topline );
} );
// move x before scaling to have same left inset at all scale factors
g.translate( insets.left, 0 );
// simulate different vertical component positions
if( yOffset > 0 && SystemInfo.isJava_9_orLater ) {
double ty = yOffset * scaleFactor;
ty -= (int) ty;
if( ty == 0 )
return; // no need to paint
g2.translate( 0, ty );
}
// scale
if( SystemInfo.isJava_9_orLater )
@@ -232,21 +361,36 @@ public class FlatPaintingStringTest
// draw string
g.setColor( getForeground() );
int y = insets.top + fm.getAscent();
int y = fm.getAscent();
JavaCompatibility.drawStringUnderlineCharAt( this, cg, "E", -1, 0, y );
// set tooltip text
if( getToolTipText() == null ) {
AffineTransform t = g2.getTransform();
double textY = t.getTranslateY() + (y * t.getScaleY());
setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) );
}
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
AffineTransform t = g2.getTransform();
double textY = t.getTranslateY() + (y * t.getScaleY());
setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) );
}
private int scale( int value ) {
return SystemInfo.isJava_9_orLater ? Math.round( value * scaleFactor ) : value;
/**
* Scales a rectangle in the same way as the JRE does in
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*
* This is a copy of HiDPIUtils.scale()
*/
public static Rectangle scaleTo1x( double scaleFactor, int x, int y, int width, int height ) {
double px = (x * scaleFactor);
double py = (y * scaleFactor);
double newX = normalize( px );
double newY = normalize( py );
double newWidth = normalize( px + (width * scaleFactor) ) - newX;
double newHeight = normalize( py + (height * scaleFactor) ) - newY;
return new Rectangle( (int) Math.floor( newX ), (int) Math.floor( newY ), (int) newWidth, (int) newHeight );
}
private static double normalize( double value ) {
return Math.floor( value + 0.25 ) + 0.25;
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,10 +9,45 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[top]"
"$rowConstraints": "[top]unrel[top]"
} ) {
name: "this"
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontLabel"
"text": "Font:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "fontField"
"maximumRowCount": 20
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "String"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fontChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontMetricsLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "north"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 450, 300 )

View File

@@ -241,6 +241,23 @@ public class FlatWindowDecorationsTest
if( c instanceof Box.Filler )
return;
}
/*debug
menuBar.add( new Box.Filler( new Dimension(), new Dimension(),
new Dimension( Short.MAX_VALUE, Short.MAX_VALUE ) )
{
@Override
protected void paintComponent( Graphics g ) {
int w = getWidth();
int h = getHeight();
g.setColor( Color.blue );
g.drawRect( 0, 0, w - 1, h - 1 );
g.drawLine( 0, 0, w, h );
g.drawLine( 0, h, w, 0 );
}
} );
debug*/
menuBar.add( Box.createGlue() );
menuBar.revalidate();
}

View File

@@ -78,9 +78,15 @@ controlDkShadow = #696969
Button.startBackground = #fff
Button.endBackground = #bbb
Button.focusedBackground = #0ff
Button.focusedForeground = #00f
Button.hoverBackground = #ff0
Button.hoverForeground = #00f
Button.pressedBackground = #FFC800
Button.pressedForeground = #0080ff
Button.selectedBackground = #fbb
Button.selectedForeground = #321
Button.disabledSelectedBackground = #123
Button.disabledSelectedForeground = #fcc
Button.borderColor = #0f0
Button.startBorderColor = #00f
@@ -95,8 +101,11 @@ Button.default.startBackground = #ddd
Button.default.endBackground = #888
Button.default.foreground = #800
Button.default.focusedBackground = #0ff
Button.default.focusedForeground = #00f
Button.default.hoverBackground = #ff0
Button.default.hoverForeground = #00f
Button.default.pressedBackground = #FFC800
Button.default.pressedForeground = #0080ff
Button.default.startBorderColor = #f00
Button.default.endBorderColor = #00f
Button.default.hoverBorderColor = #f00
@@ -104,8 +113,13 @@ Button.default.focusedBorderColor = #537699
Button.default.focusColor = #f00
Button.toolbar.hoverBackground = #fff
Button.toolbar.hoverForeground = #000
Button.toolbar.pressedBackground = #eee
Button.toolbar.pressedForeground = #666
Button.toolbar.selectedBackground = #ddd
Button.toolbar.selectedForeground = #800
Button.toolbar.disabledSelectedBackground = #ccc
Button.toolbar.disabledSelectedForeground = #866
#---- CheckBox ----
@@ -422,12 +436,28 @@ ToggleButton.background = #ddf
ToggleButton.selectedBackground = #4f4
ToggleButton.selectedForeground = #000
ToggleButton.disabledSelectedBackground = #4d4
ToggleButton.disabledSelectedForeground = #fff
ToggleButton.focusedBackground = #0ff
ToggleButton.focusedForeground = #00f
ToggleButton.hoverBackground = #ff0
ToggleButton.hoverForeground = #00f
ToggleButton.pressedBackground = #FFC800
ToggleButton.pressedForeground = #0080ff
ToggleButton.toolbar.hoverBackground = #fff
ToggleButton.toolbar.hoverForeground = #000
ToggleButton.toolbar.pressedBackground = #eee
ToggleButton.toolbar.pressedForeground = #666
ToggleButton.toolbar.selectedBackground = #ddd
ToggleButton.toolbar.selectedForeground = #800
ToggleButton.toolbar.disabledSelectedBackground = #ccc
ToggleButton.toolbar.disabledSelectedForeground = #866
ToggleButton.tab.selectedBackground = #080
ToggleButton.tab.selectedForeground = #fff
ToggleButton.tab.hoverForeground = #00f
ToggleButton.tab.focusForeground = #080
#---- ToolBar ----

View File

@@ -25,7 +25,6 @@ dependencies {
implementation( "com.miglayout:miglayout-swing:5.3" )
implementation( "com.fifesoft:rsyntaxtextarea:3.1.4" )
implementation( "com.fifesoft:autocomplete:3.1.3" )
implementation( "com.fifesoft:rstaui:3.1.3" )
}
tasks {

View File

@@ -482,6 +482,10 @@ class FlatCompletionProvider
"dark", colorParamDesc,
"light", colorParamDesc,
"threshold", "(optional) 0-100%, default is 43%" );
addFunction( "over",
"foreground", colorParamDesc,
"background", colorParamDesc );
}
private void addFunction( String name, String... paramNamesAndDescs ) {

View File

@@ -16,7 +16,7 @@
package com.formdev.flatlaf.themeeditor;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.List;
@@ -26,7 +26,6 @@ import javax.swing.border.MatteBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import com.formdev.flatlaf.extras.components.*;
import org.fife.rsta.ui.CollapsibleSectionPanel;
import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
@@ -44,9 +43,13 @@ import net.miginfocom.swing.*;
class FlatFindReplaceBar
extends JPanel
{
static final String PROP_CLOSED = "closed";
private final RSyntaxTextArea textArea;
private SearchContext context;
private boolean inSetContext;
private boolean markAllPending;
FlatFindReplaceBar( RSyntaxTextArea textArea ) {
this.textArea = textArea;
@@ -74,6 +77,10 @@ class FlatFindReplaceBar
regexToggleButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/regex.svg" ) );
closeButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/close.svg" ) );
registerKeyboardAction( e -> close(),
KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, false ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
SearchContext context = new SearchContext();
context.setSearchWrap( true );
setSearchContext( context );
@@ -93,29 +100,32 @@ class FlatFindReplaceBar
void setSearchContext( SearchContext context ) {
this.context = context;
findField.setText( context.getSearchFor() );
replaceField.setText( context.getReplaceWith() );
matchCaseToggleButton.setSelected( context.getMatchCase() );
matchWholeWordToggleButton.setSelected( context.getWholeWord() );
regexToggleButton.setSelected( context.isRegularExpression() );
inSetContext = true;
try {
findField.setText( context.getSearchFor() );
replaceField.setText( context.getReplaceWith() );
matchCaseToggleButton.setSelected( context.getMatchCase() );
matchWholeWordToggleButton.setSelected( context.getWholeWord() );
regexToggleButton.setSelected( context.isRegularExpression() );
} finally {
inSetContext = false;
}
}
@Override
public boolean requestFocusInWindow() {
// invoked from CollapsibleSectionPanel
// use selected text in editor for searching
String selectedText = textArea.getSelectedText();
if( !StringUtils.isEmpty( selectedText ) && selectedText.indexOf( '\n' ) < 0 )
findField.setText( selectedText );
else
findField.selectAll();
void activate( boolean findEditorSelection ) {
// use selected text of editor for searching
if( findEditorSelection ) {
String selectedText = textArea.getSelectedText();
if( !StringUtils.isEmpty( selectedText ) && selectedText.indexOf( '\n' ) < 0 )
findField.setText( selectedText );
else
findField.selectAll();
}
// if showing bar, highlight matches in editor
// (not invoking this from addNotify() because this would break the slide-in animation)
markAll();
return findField.requestFocusInWindow();
findField.requestFocusInWindow();
}
@Override
@@ -142,7 +152,20 @@ class FlatFindReplaceBar
}
void markAll() {
findOrMarkAll( false );
if( inSetContext )
return;
// do mark all only once
if( markAllPending )
return;
markAllPending = true;
EventQueue.invokeLater( () -> {
markAllPending = false;
findOrMarkAll( false );
} );
}
private void findOrMarkAll( boolean find ) {
@@ -254,11 +277,8 @@ class FlatFindReplaceBar
}
private void close() {
Container parent = getParent();
if( parent instanceof CollapsibleSectionPanel )
((CollapsibleSectionPanel)parent).hideBottomComponent();
else if( parent != null )
parent.remove( this );
setVisible( false );
firePropertyChange( PROP_CLOSED, false, true );
}
private void initComponents() {

View File

@@ -26,7 +26,7 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatLafThemeEditor
{
public static void main( String[] args ) {
// macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// enable screen menu bar
// (moves menu bar from JFrame window to top of screen)
@@ -41,6 +41,7 @@ public class FlatLafThemeEditor
// - "system": use current macOS appearance (light or dark)
// - "NSAppearanceNameAqua": use light appearance
// - "NSAppearanceNameDarkAqua": use dark appearance
// (needs to be set on main thread; setting it on AWT thread does not work)
System.setProperty( "apple.awt.application.appearance", "system" );
}

View File

@@ -34,7 +34,6 @@ import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.fife.rsta.ui.CollapsibleSectionPanel;
import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
@@ -48,6 +47,7 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextarea.SearchContext;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -62,7 +62,10 @@ class FlatThemeEditorPane
private static final String FLATLAF_STYLE = "text/flatlaf";
private final CollapsibleSectionPanel collapsiblePanel;
private static boolean findReplaceVisible;
private static SearchContext findReplaceContext;
private final JPanel editorPanel;
private final RTextScrollPane scrollPane;
private final FlatSyntaxTextArea textArea;
private final ErrorStrip errorStrip;
@@ -115,11 +118,11 @@ class FlatThemeEditorPane
// create error strip
errorStrip = new ErrorStrip( textArea );
// create collapsible panel
collapsiblePanel = new CollapsibleSectionPanel();
collapsiblePanel.add( scrollPane );
collapsiblePanel.add( errorStrip, BorderLayout.LINE_END );
add( collapsiblePanel, BorderLayout.CENTER );
// create editor panel
editorPanel = new JPanel( new BorderLayout() );
editorPanel.add( scrollPane );
editorPanel.add( errorStrip, BorderLayout.LINE_END );
add( editorPanel, BorderLayout.CENTER );
updateTheme();
}
@@ -166,6 +169,13 @@ class FlatThemeEditorPane
scrollPane.getGutter().setLineNumberFont( font );
}
void selected() {
if( findReplaceVisible )
showFindReplaceBar( false );
else
hideFindReplaceBar();
}
void windowActivated() {
if( preview != null )
preview.repaint();
@@ -261,13 +271,30 @@ class FlatThemeEditorPane
return (window instanceof JFrame) ? ((JFrame)window).getTitle() : null;
}
void showFindReplaceBar() {
void showFindReplaceBar( boolean findEditorSelection ) {
if( findReplaceBar == null ) {
findReplaceBar = new FlatFindReplaceBar( textArea );
collapsiblePanel.addBottomComponent( findReplaceBar );
findReplaceBar.addPropertyChangeListener( FlatFindReplaceBar.PROP_CLOSED, e -> {
findReplaceVisible = false;
textArea.requestFocusInWindow();
} );
editorPanel.add( findReplaceBar, BorderLayout.SOUTH );
editorPanel.revalidate();
}
collapsiblePanel.showBottomComponent( findReplaceBar );
findReplaceVisible = true;
if( findReplaceContext == null )
findReplaceContext = findReplaceBar.getSearchContext();
else
findReplaceBar.setSearchContext( findReplaceContext );
findReplaceBar.setVisible( true );
findReplaceBar.activate( findEditorSelection );
}
void hideFindReplaceBar() {
if( findReplaceBar != null )
findReplaceBar.setVisible( false );
}
void showPreview( boolean show ) {

View File

@@ -173,10 +173,30 @@ class FlatThemeFileEditor
enableDisableActions();
// hide some menu items on macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// hide menu items that are in macOS application menu
exitMenuItem.setVisible( false );
aboutMenuItem.setVisible( false );
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
controlPanel.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
}
// integrate into macOS screen menu
@@ -316,6 +336,7 @@ class FlatThemeFileEditor
SwingUtilities.invokeLater( () -> {
activateEditor();
notifyEditorSelected();
} );
saveState();
enableDisableActions();
@@ -365,7 +386,17 @@ class FlatThemeFileEditor
String n2 = toSortName( f2.getName() );
return n1.compareToIgnoreCase( n2 );
} );
return propertiesFiles;
File themesDir = new File( dir, "themes" );
if( !themesDir.isDirectory() )
return propertiesFiles;
// get files from "themes" sub-directory
File[] themesFiles = getPropertiesFiles( themesDir );
File[] allFiles = new File[propertiesFiles.length + themesFiles.length];
System.arraycopy( propertiesFiles, 0, allFiles, 0, propertiesFiles.length );
System.arraycopy( themesFiles, 0, allFiles, propertiesFiles.length, themesFiles.length );
return allFiles;
}
private String toSortName( String name ) {
@@ -418,11 +449,13 @@ class FlatThemeFileEditor
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
String filename = (themeEditorPane != null) ? themeEditorPane.getFile().getName() : null;
putPrefsString( state, KEY_RECENT_FILE, filename );
notifyEditorSelected();
}
private void enableDisableActions() {
boolean dirOpen = (directoryField.getSelectedItem() != null);
boolean editorOpen = (dirOpen &&tabbedPane.getSelectedIndex() >= 0);
boolean editorOpen = (dirOpen && tabbedPane.getSelectedIndex() >= 0);
// enable/disable buttons
newButton.setEnabled( dirOpen );
@@ -450,12 +483,21 @@ class FlatThemeFileEditor
FlatIntelliJLaf.NAME,
FlatDarculaLaf.NAME,
} );
JCheckBox genJavaClassCheckBox = new JCheckBox( "Generate Java class" );
genJavaClassCheckBox.setMnemonic( 'G' );
File themesDir = new File( dir, "themes" );
JCheckBox useThemesDirCheckBox = themesDir.isDirectory()
? new JCheckBox( "Create in 'themes' directory", true )
: null;
JOptionPane optionPane = new JOptionPane( new Object[] {
new JLabel( "Theme name:" ),
themeNameField,
new JLabel( "Base Theme:" ),
baseThemeField,
genJavaClassCheckBox,
useThemesDirCheckBox,
}, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION ) {
@Override
public void selectInitialValue() {
@@ -477,7 +519,8 @@ class FlatThemeFileEditor
return;
}
File file = new File( dir, themeName + ".properties" );
File dir2 = (useThemesDirCheckBox != null && useThemesDirCheckBox.isSelected()) ? themesDir : dir;
File file = new File( dir2, themeName + ".properties" );
if( file.exists() ) {
JOptionPane.showMessageDialog( this, "Theme '" + themeName + "' already exists.", title, JOptionPane.INFORMATION_MESSAGE );
return;
@@ -486,7 +529,8 @@ class FlatThemeFileEditor
try {
String baseTheme = (String) baseThemeField.getSelectedItem();
createTheme( file, baseTheme );
createThemeClass( dir, themeName, baseTheme );
if( genJavaClassCheckBox.isSelected() )
createThemeClass( dir2, themeName, baseTheme );
openFile( file, true );
} catch( IOException ex ) {
ex.printStackTrace();
@@ -646,6 +690,12 @@ class FlatThemeFileEditor
return result;
}
private void notifyEditorSelected() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null )
themeEditorPane.selected();
}
private void activateEditor() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null )
@@ -668,7 +718,7 @@ class FlatThemeFileEditor
private void find() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null )
themeEditorPane.showFindReplaceBar();
themeEditorPane.showFindReplaceBar( true );
}
private void insertColor() {

View File

@@ -76,6 +76,7 @@ public class FlatThemeTokenMaker
tokenMap.put( "tint", TOKEN_FUNCTION );
tokenMap.put( "shade", TOKEN_FUNCTION );
tokenMap.put( "contrast", TOKEN_FUNCTION );
tokenMap.put( "over", TOKEN_FUNCTION );
// function options
tokenMap.put( "relative", Token.RESERVED_WORD );
@@ -201,6 +202,11 @@ public class FlatThemeTokenMaker
}
}
@Override
public boolean isIdentifierChar( int languageIndex, char ch ) {
return super.isIdentifierChar( languageIndex, ch ) || ch == '@';
}
/*debug
private java.util.HashMap<Integer, String> tokenTypeStrMap;

View File

@@ -23,10 +23,13 @@ Button.default.endBorderColor
Button.default.focusColor
Button.default.focusedBackground
Button.default.focusedBorderColor
Button.default.focusedForeground
Button.default.foreground
Button.default.hoverBackground
Button.default.hoverBorderColor
Button.default.hoverForeground
Button.default.pressedBackground
Button.default.pressedForeground
Button.default.startBackground
Button.default.startBorderColor
Button.defaultButtonFollowsFocus
@@ -34,23 +37,27 @@ Button.disabledBackground
Button.disabledBorderColor
Button.disabledForeground
Button.disabledSelectedBackground
Button.disabledSelectedForeground
Button.disabledText
Button.endBackground
Button.endBorderColor
Button.focusInputMap
Button.focusedBackground
Button.focusedBorderColor
Button.focusedForeground
Button.font
Button.foreground
Button.highlight
Button.hoverBackground
Button.hoverBorderColor
Button.hoverForeground
Button.iconTextGap
Button.innerFocusWidth
Button.light
Button.margin
Button.minimumWidth
Button.pressedBackground
Button.pressedForeground
Button.rollover
Button.selectedBackground
Button.selectedForeground
@@ -59,10 +66,15 @@ Button.startBackground
Button.startBorderColor
Button.textIconGap
Button.textShiftOffset
Button.toolbar.disabledSelectedBackground
Button.toolbar.disabledSelectedForeground
Button.toolbar.hoverBackground
Button.toolbar.hoverForeground
Button.toolbar.margin
Button.toolbar.pressedBackground
Button.toolbar.pressedForeground
Button.toolbar.selectedBackground
Button.toolbar.selectedForeground
Button.toolbar.spacingInsets
ButtonUI
Caret.width
@@ -185,6 +197,7 @@ ComboBox.buttonShadow
ComboBox.buttonStyle
ComboBox.disabledBackground
ComboBox.disabledForeground
ComboBox.editableBackground
ComboBox.editorColumns
ComboBox.focusedBackground
ComboBox.font
@@ -252,6 +265,8 @@ FileChooser.homeFolderIcon
FileChooser.listViewIcon
FileChooser.newFolderIcon
FileChooser.readOnly
FileChooser.shortcuts.buttonSize
FileChooser.shortcuts.iconSize
FileChooser.upFolderIcon
FileChooser.useSystemExtensionHiding
FileChooser.usesSingleFilePane
@@ -714,6 +729,7 @@ Separator.stripeIndent
Separator.stripeWidth
SeparatorUI
Slider.background
Slider.disabledThumbBorderColor
Slider.disabledThumbColor
Slider.disabledTrackColor
Slider.focus
@@ -964,6 +980,7 @@ TitlePane.background
TitlePane.borderColor
TitlePane.buttonHoverBackground
TitlePane.buttonMaximizedHeight
TitlePane.buttonMinimumWidth
TitlePane.buttonPressedBackground
TitlePane.buttonSize
TitlePane.centerTitle
@@ -983,11 +1000,15 @@ TitlePane.inactiveBackground
TitlePane.inactiveForeground
TitlePane.maximizeIcon
TitlePane.menuBarEmbedded
TitlePane.menuBarResizeHeight
TitlePane.menuBarTitleGap
TitlePane.menuBarTitleMinimumGap
TitlePane.noIconLeftGap
TitlePane.restoreIcon
TitlePane.showIcon
TitlePane.showIconBesideTitle
TitlePane.titleMargins
TitlePane.titleMinimumWidth
TitlePane.unifiedBackground
TitlePane.useWindowDecorations
TitledBorder.border
@@ -999,32 +1020,44 @@ ToggleButton.border
ToggleButton.darkShadow
ToggleButton.disabledBackground
ToggleButton.disabledSelectedBackground
ToggleButton.disabledSelectedForeground
ToggleButton.disabledText
ToggleButton.focusInputMap
ToggleButton.focusedBackground
ToggleButton.focusedForeground
ToggleButton.font
ToggleButton.foreground
ToggleButton.highlight
ToggleButton.hoverBackground
ToggleButton.hoverForeground
ToggleButton.iconTextGap
ToggleButton.light
ToggleButton.margin
ToggleButton.pressedBackground
ToggleButton.pressedForeground
ToggleButton.rollover
ToggleButton.selectedBackground
ToggleButton.selectedForeground
ToggleButton.shadow
ToggleButton.tab.disabledUnderlineColor
ToggleButton.tab.focusBackground
ToggleButton.tab.focusForeground
ToggleButton.tab.hoverBackground
ToggleButton.tab.hoverForeground
ToggleButton.tab.selectedBackground
ToggleButton.tab.selectedForeground
ToggleButton.tab.underlineColor
ToggleButton.tab.underlineHeight
ToggleButton.textIconGap
ToggleButton.textShiftOffset
ToggleButton.toolbar.disabledSelectedBackground
ToggleButton.toolbar.disabledSelectedForeground
ToggleButton.toolbar.hoverBackground
ToggleButton.toolbar.hoverForeground
ToggleButton.toolbar.pressedBackground
ToggleButton.toolbar.pressedForeground
ToggleButton.toolbar.selectedBackground
ToggleButton.toolbar.selectedForeground
ToggleButtonUI
ToolBar.ancestorInputMap
ToolBar.arrowKeysOnlyNavigation

View File

@@ -118,6 +118,12 @@ Prop.6.selectionForeground = contrast($Prop.6.selectionBackground,#000,#fff)
Prop.7.selectionBackground = #FF9500
Prop.7.selectionForeground = contrast($Prop.7.selectionBackground,#000,#fff)
Prop.colorFunc60 = over(#fff8,#f00)
Prop.colorFunc61 = over(#fff8,#0f0)
Prop.colorFunc62 = over(#f000,#0f0)
Prop.colorFunc63 = over(#f00,#0f0)
Prop.colorFunc64 = over(#f008,null)
@varStyle1 = #f0f