Compare commits

..

223 Commits
2.1 ... 3.0

Author SHA1 Message Date
Karl Tauber
b879b393ad release 3.0 2022-12-16 00:19:03 +01:00
Karl Tauber
e4503c2a54 Native window decorations: signed Windows DLLs with FormDev Software GmbH code signing certificate (issue #624) 2022-12-14 17:08:29 +01:00
Karl Tauber
7e2d02b997 added DSC Software AG as Platinum sponsor; added Jeyla Studio 2022-12-14 15:29:03 +01:00
Karl Tauber
d286550572 Merge PR #613: Window decorations: Title bar customizing 2022-12-03 19:33:05 +01:00
Karl Tauber
4e44e25d30 macOS themes: fix horizontal centering of combobox arrows (issue #497; PR #533) 2022-12-03 19:17:10 +01:00
Karl Tauber
9fef2f9d05 SwingX: update fonts in JXHeader, JXMonthView, JXTaskPane and JXTitledPanel when changing default font 2022-12-01 12:51:19 +01:00
Karl Tauber
04f1f5921d Native window decorations: updated DLLs (issue #591)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/3554848392
2022-11-26 19:34:40 +01:00
Karl Tauber
f9ecffb850 flatlaf-natives-windows: fixed memory allocation error handling (issue #591) 2022-11-26 19:14:24 +01:00
Karl Tauber
c9b5274ccf flatlaf-natives-windows: reworked memory allocation error handling 2022-11-26 19:05:24 +01:00
Karl Tauber
d209d47b9e Testing: added FlatPaintingArrowsTest (extracted from FlatPaintingTest and used parts of FlatPaintingIconsTest) 2022-11-26 18:04:29 +01:00
Karl Tauber
21baaf810c CHANGELOG.md: added changelog for merged PRs 2022-11-26 17:11:35 +01:00
Karl Tauber
95b4366270 Merge PR #615: Fonts: lazy loading 2022-11-26 16:24:54 +01:00
Karl Tauber
c3adadfe2f flatlaf-natives-windows: fixed compile and link errors 2022-11-23 21:27:55 +01:00
Karl Tauber
adf7753617 Fonts: fixed gradle build error and javadoc warnings 2022-11-21 12:55:55 +01:00
Karl Tauber
d491847754 Fonts: support lazy font file loading (extends PRs #545 and #614) 2022-11-21 11:51:27 +01:00
Karl Tauber
6afc747790 Merge PR #614: Fonts: Roboto 2022-11-20 14:08:58 +01:00
Karl Tauber
ff46935448 Demo: "Data components" tab: demonstrate rounded selection for JList and JTree 2022-11-20 14:07:02 +01:00
Karl Tauber
78c2f98f1f Fonts: added Roboto 2022-11-19 16:49:26 +01:00
Karl Tauber
91be9aa2fe Fonts: do not publish font snapshots/releases in CI 2022-11-19 13:11:08 +01:00
Karl Tauber
13e5da584f Fonts: do not skip all gradle font tasks when building snapshots and releases because they are used in demo and theme editor 2022-11-19 12:01:14 +01:00
Karl Tauber
1762e0b7a6 Fonts: added font license to maven pom 2022-11-19 11:31:06 +01:00
Karl Tauber
05240abfe0 GitHub Actions: removed on.pull_request.* to avoid duplicate execution or actions in PRs 2022-11-19 11:28:06 +01:00
Karl Tauber
b515e8be04 Fonts: fixed GitHub Actions:
- ci.yml: skip fonts in snapshot and release jobs
- fonts.yml: build on all branches/PRs; publish snapshots
- fixed version for font snapshots
2022-11-19 10:48:07 +01:00
Karl Tauber
24bc7fb0b5 Merge PR #545: Fonts (Inter and JetBrains Mono) 2022-11-18 17:54:00 +01:00
Karl Tauber
0d2e1e6d18 Fonts: HiDPIUtils: improved vertical position correction of text (on Windows) for various fonts 2022-11-18 17:35:59 +01:00
Karl Tauber
f23c523baf GitHub Actions: ci.yml: include font JARs in build artifacts 2022-11-17 23:01:56 +01:00
Karl Tauber
76fee29f5b Demo: install Inter font only when used; removed JetBrains Mono 2022-11-17 23:01:17 +01:00
Karl Tauber
ec77746a43 Fonts: support specifying preferred font family for easy using another font (e.g. Inter) for all components 2022-11-17 23:01:03 +01:00
Karl Tauber
92cd6f8f34 Theme Editor:
- use JetBrains Mono font for editor area
- added Inter font to allow using it in preview (Java 11+)
2022-11-17 23:00:07 +01:00
Karl Tauber
e7d2b5cbb6 Fonts: added Inter and JetBrains Mono 2022-11-17 22:59:31 +01:00
Karl Tauber
4d175da3a0 Window decorations: added debug option to paint title bar rectangles that are used by Windows 10/11 in WM_NCHITTEST to identify special areas 2022-11-16 20:07:11 +01:00
Karl Tauber
5f047ddda9 Window decorations: added client properties to hide title, iconify, maximize/restore and close buttons (issue #608) 2022-11-16 11:08:31 +01:00
Karl Tauber
ccca6fe88e Merge PR #612: macOS themes: make spinner look like macOS stepper 2022-11-16 10:59:10 +01:00
Karl Tauber
a1f18e1ec9 macOS themes: fixed spinner arrow hover/pressed colors (issue #497; PR #533) 2022-11-16 10:51:38 +01:00
Karl Tauber
afdaf7a0a5 Merge PR #609: Tree: hide default closed/opened/leaf icons by default 2022-11-16 10:29:24 +01:00
Karl Tauber
62f0ef19f4 macOS themes: make spinner look like macOS stepper (issue #497; PR #533) 2022-11-15 14:29:47 +01:00
Karl Tauber
b736502c27 Tree: hide default closed/opened/leaf icons by default 2022-11-14 14:59:47 +01:00
Karl Tauber
2be2dae3d6 macOS themes: updated UI defaults dumps (PR #533) 2022-11-14 14:19:49 +01:00
Karl Tauber
aefe104ca4 FlatSVGIcon: no longer use classes from package com.formdev.flatlaf.ui to allow using FlatSVGIcon (and flatlaf-extras.jar) in NetBeans plugin (NetBeans ships with FlatLaf, but does not export that package) 2022-11-14 14:02:27 +01:00
Karl Tauber
3e6bce9cec no longer check for system property apple.awt.graphics.UseQuartz because openjdk seems not support it
not found `apple.awt.graphics.UseQuartz` in:
- https://github.com/openjdk/jdk8u-dev
- https://github.com/openjdk/jdk
2022-11-14 13:48:54 +01:00
Karl Tauber
a6394cac38 minor code cleanup:
- remove redundant semicolon
- create array with curly
2022-11-14 12:25:29 +01:00
Karl Tauber
1e09ddfc93 Merge PR #607: systemColor() function and support changing accent color in macOS themes 2022-11-14 12:05:47 +01:00
Karl Tauber
664f5c98e9 macOS themes: support changing accent and highlight colors (issue #497) 2022-11-02 21:59:45 +01:00
Karl Tauber
c7bfd2ea82 UIDefaultsLoader: added systemColor() color function that can be used to change accent color (preparation for getting accent color from operating system) 2022-11-02 21:59:07 +01:00
Karl Tauber
9ce7ddd088 UIDefaultsLoader: reworked error handling when parsing colors to support null as result (preparation for systemColor() function) 2022-11-02 21:57:57 +01:00
Karl Tauber
cca8d427d2 Merge PR #533: macOS light and dark themes 2022-11-01 12:23:57 +01:00
Karl Tauber
aa9263a2e7 macOS themes: use rounded selection for menus and combo boxes; fixed menus and combo box selection colors (issue #497) 2022-11-01 12:00:11 +01:00
Karl Tauber
5eaebde437 macOS themes: added some ScrollBar UI properties so that themes look the same on Windows or Linux as on macOS (issue #497) 2022-11-01 11:52:36 +01:00
Karl Tauber
7f15f557a5 ComboBox: for style "mac", place popup over combobox (issue #497) 2022-11-01 11:52:36 +01:00
Karl Tauber
b459715cb5 macOS light and dark themes (issue #497) 2022-11-01 11:37:29 +01:00
Karl Tauber
bfede219d0 added DbVisualizer as Platinum sponsor 2022-11-01 10:13:37 +01:00
Karl Tauber
ef21efecf5 Tree:
- Fixed missing tree lines (if enabled) for wide-selected rows. (issue #598)
- Fixed scaling of tree lines and fixed alignment to expand/collapse arrows.
- Removed support for dashed tree lines. `Tree.lineTypeDashed` is now ignored.
2022-11-01 10:12:49 +01:00
Karl Tauber
2bfbc9dc12 Merge PR #577: Rounded outlined icons 2022-10-30 10:39:09 +01:00
Karl Tauber
c3a1b45546 Merge PR #548: ComboBox: support rounded selection 2022-10-30 10:28:48 +01:00
Karl Tauber
b72508f920 Merge PR #547: List: Support rounded selection
# Conflicts:
#	flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java
2022-10-30 10:24:16 +01:00
Karl Tauber
22bb80218d Merge PR #546: Tree: rounded selection 2022-10-30 10:16:07 +01:00
Karl Tauber
873a7e8572 Menu: fixed missing background on hover if top-level JMenu is opaque and selectionInsets or selectionArc are set (PR #536) 2022-10-30 10:10:22 +01:00
Karl Tauber
0c5016fe89 Merge PR #536: Menus: rounded selection
# Conflicts:
#	flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java
#	flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuUI.java
#	flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
#	flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt
2022-10-30 09:55:09 +01:00
Karl Tauber
607b084697 Merge PR #534: ToolBar: hover effect for button groups 2022-10-29 19:51:37 +02:00
Karl Tauber
9d8ffec276 Merge PR #605: FlatInspector: add/remove mouse listener in setEnabled 2022-10-29 14:41:24 +02:00
Max Weber
15f08e9b7c FlatInspector: add/remove mouse listener in setEnabled 2022-10-29 05:16:29 -06:00
Karl Tauber
08aa6b1894 added ej-technologies (creator of JProfiler and install4j) as Platinum sponsor 2022-10-28 22:44:02 +02:00
Karl Tauber
06b02c4f7c README.md: new applications using FlatLaf:
- JProfiler
- JFormDesigner
- Fanurio
- Antares
- Logisim-evolution
- Cinecred
- tinyMediaManager
- Weasis
- Makelangelo Software

(changed "New" to "Hot")
2022-10-28 22:22:10 +02:00
Karl Tauber
b56acd271f added Sponsor button 2022-10-26 18:09:28 +02:00
Karl Tauber
b24e2db59e FileChooser: fixed layout of (optional) accessory component and fixed too large right margin (issue #604; regression since implementing PR #522 in FlatLaf 2.3) 2022-10-21 13:12:52 +02:00
Karl Tauber
f215356629 updated sigtest for FlatLaf 2.6
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-10-18 11:26:38 +02:00
Karl Tauber
069a7c809c release 2.6 2022-10-18 11:11:24 +02:00
Karl Tauber
883b4d735a changed "since 3" to "since 2.6" 2022-10-18 11:08:14 +02:00
Karl Tauber
9f39b269bb CHANGELOG.md: moved PR #595 from "Fixed bugs" to "New features and improvements" 2022-10-17 18:15:19 +02:00
Karl Tauber
36c405c708 fixed NPE in FlatUIUtils.isCellEditor() (issue #601) 2022-10-17 18:09:12 +02:00
Karl Tauber
bc7c68ebe4 MenuBar: fixed NPE in FlatMenuItemRenderer.getTopLevelFont() if menu item does not have a parent (issue #600; regression since implementing #589 in FlatLaf 2.5; commit f6c5db07f2) 2022-10-17 17:08:12 +02:00
Karl Tauber
6c502ad4c5 2.6-SNAPSHOT 2022-10-17 16:31:54 +02:00
Karl Tauber
100aa0b621 Native libraries: load jawt.dll also on Windows when running in Java 9 and later (to be on the safe side) 2022-10-06 10:58:32 +02:00
Karl Tauber
8e42b19934 Native libraries: support loading via System.loadLibrary()
(for pre-extracted native libs in NetBeans)
2022-10-06 10:49:55 +02:00
Karl Tauber
1a456d5d68 ScaledImageIcon: do not throw exceptions if image has invalid size (e.g. not found); instead paint a red rectangle (similar to FlatSVGIcon) 2022-10-01 20:12:32 +02:00
Karl Tauber
e83c26a76a - ScrollBar: show "pressed" feedback on track/thumb only for left mouse button; if absolute positioning is enabled (the default), then also for middle mouse button
- Arrow buttons in ComboBox, Spinner, ScrollBar and TabbedPane: show "pressed" feedback only for left mouse button
2022-09-30 19:55:42 +02:00
Karl Tauber
6e7c2a616b updated CHANGELOG.md for PR #595 and added tab context menu test 2022-09-30 15:33:37 +02:00
Karl Tauber
0699454df8 Merge PR #595: Switch and close tabs on left mouse click only 2022-09-30 15:10:45 +02:00
Karl Tauber
92c110548a ComboBox and Spinner: no longer use preferred height for arrow button width, because preferred height may be zero, which would hide arrow button (see https://github.com/scijava/scijava-ui-swing/issues/77#issuecomment-1261452712)
- arrow button width depends on combobox/spinner height
- default/max button width is height of a raw combobox/spinner (without insets)
- min button width is 3/4 of default button width
2022-09-30 12:30:46 +02:00
Karl Tauber
ca88023560 GitHub Actions: build using Java 19 (use toolchain because Gradle 7.5.1 does not support running on Java 19) 2022-09-28 19:15:59 +02:00
Karl Tauber
12fc2299ec update to Gradle 7.5.1
./gradlew wrapper --gradle-version=7.5.1
2022-09-28 15:44:16 +02:00
Karl Tauber
2089c77b84 updated sigtest for FlatLaf 2.5
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-09-27 16:50:34 +02:00
Karl Tauber
4f5a3e8d8b release 2.5 2022-09-27 16:11:48 +02:00
ShadelessFox
95522846ac Switch and close tabs on left mouse click only 2022-09-25 21:52:44 +03:00
Karl Tauber
614ac956de updated sigtest to 1.7 2022-09-23 12:31:42 +02:00
Karl Tauber
c228362c01 Window decorations: added UI value TitlePane.font to customize window title font (issue #589) 2022-09-23 11:57:38 +02:00
Karl Tauber
f6c5db07f2 MenuBar: top level menus now use MenuBar.font instead of Menu.font (issue #589) 2022-09-23 00:31:14 +02:00
Karl Tauber
78e7839213 Window decorations: added option to show window icon only in frames, but not in dialogs (issue #589) 2022-09-23 00:13:01 +02:00
John Platts
f7be12df67 Add AllocRoutines.h include 2022-09-19 14:23:51 -05:00
John Platts
a1d1e221ae Remove operator new and operator delete overloads from Runtime.cpp
The ```operator new``` and ```operator delete``` overloads in Runtime.cpp are replaced by placement ```operator new``` and ```operator delete``` operators in AllocRoutines.h that take a const FlatLafNoThrowT& placement parameter.

Using ```new (FlatLafNoThrow) FlatWndProc``` instead of ```new FlatWndProc``` also allows for inlining by the C++ compiler.
2022-09-19 14:23:16 -05:00
John Platts
0a4dc54fb9 Update put and ensureCapacity routines 2022-09-19 14:17:37 -05:00
John Platts
b8c7801365 Change ensureCapacity method to return a bool 2022-09-19 14:06:26 -05:00
John Platts
a7099c039f Rename allocation functions 2022-09-19 13:57:25 -05:00
John Platts
a4d2d347e3 Change put method to return a bool 2022-09-19 13:56:28 -05:00
John Platts
829c537fd3 Add checks for allocation failure 2022-09-19 13:55:33 -05:00
John Platts
28437f99cf Update new and delete FlatWndProc.cpp 2022-09-19 13:53:33 -05:00
John Platts
c1402d85e1 Update HWNDMap.h 2022-09-19 13:39:47 -05:00
John Platts
32e071ab89 Update AllocRoutines.h 2022-09-19 13:36:10 -05:00
John Platts
01125e030e Create AllocRoutines.h 2022-09-19 13:26:33 -05:00
John Platts
b43278439a Delete AllocRoutines.h 2022-09-19 13:26:01 -05:00
John Platts
7a445aabd7 Create AllocRoutines.h 2022-09-19 13:25:26 -05:00
Karl Tauber
86a4f306c6 Styling: added convenience methods to invoke StyleableUI interface methods
~~~java
JButton button = new JButton();
int arc = FlatLaf.getStyleableValue( button, "arc" );
Color borderColor = FlatLaf.getStyleableValue( button, "borderColor" );
~~~
2022-09-14 10:51:11 +02:00
Karl Tauber
0e523f1193 SwingX: fixed missing highlighting of "today" in JXMonthView and JXDatePicker 2022-09-12 13:29:53 +02:00
Karl Tauber
9041a16b22 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2022-09-11 18:03:11 +02:00
Karl Tauber
596ff3382d PasswordField: reveal button is now hidden (and turned off) if password field is disabled (issue #501) 2022-09-11 17:05:48 +02:00
Karl Tauber
cbd80252ed Testing: introduced client property to exclude components in FlatTestFrame.updateComponentsRecur() 2022-09-11 14:17:55 +02:00
Karl Tauber
bcd7a7e3dd FlatClientProperties: fixed typo in javadoc 2022-09-11 13:59:54 +02:00
Karl Tauber
9c98f1a553 fixed compiler warnings 2022-09-11 12:40:27 +02:00
Karl Tauber
c3d214aa23 Merge PR #579: Linux window decorations: native move window and system menu 2022-09-11 12:11:06 +02:00
Karl Tauber
d301f6e104 MenuBar: support different menu selection style UI defaults for MenuBar and MenuItem (issue #587) 2022-09-11 12:00:38 +02:00
Karl Tauber
eb9fa585f7 more fixes for AWT components on macOS (issue #583)
- ScrollBar: disable hover because scroll bar does not receive mouse exited event
2022-09-10 18:56:33 +02:00
Karl Tauber
16c6ffb032 more fixes for AWT components on macOS (issue #583)
- use light theme for AWT components if dark FlatLaf theme is active (AWT is always light)
- made AWT peer background compatible with Aqua Laf
2022-09-10 16:53:29 +02:00
Karl Tauber
7858e42e37 fixed AWT components on macOS (issue #583)
- fixed missing focus indicator
- fixed round corners
- fixed java.awt.Button background
- fixed java.awt.Choice background
- fixed java.awt.Checkbox hover
2022-09-05 14:47:17 +02:00
Karl Tauber
30132aa6b0 added system property flatlaf.updateUIOnSystemFontChange to allow disabling automatic UI update when system font changes (issue #580) 2022-08-24 19:32:38 +02:00
Karl Tauber
bf4d4cc2c5 Linux: fixed double-click on title bar to maximize/restore on Ubuntu 22.04 (issue #482) 2022-08-21 19:49:41 +02:00
Karl Tauber
9f0554c883 Linux: added libflatlaf-linux-x86_64.so (issue #482)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/2898994332
2022-08-21 17:35:44 +02:00
Karl Tauber
218ea6ce47 Linux: fixed double-click on title bar to maximize/restore (issue #482) 2022-08-21 17:24:50 +02:00
Karl Tauber
0baae7da8b Linux: load jawt.so explicitly before loading FlatLaf native library to fix UnsatisfiedLinkError: ... libjawt.so: cannot open shared object file ... (issue #482) 2022-08-20 23:49:14 +02:00
Karl Tauber
fb4576fc1b Linux: use X11 window manager events to move window and to show window menu (right-click on window title bar), if custom window decorations are enabled (issue #482) 2022-08-20 21:09:49 +02:00
Karl Tauber
16f3f9e6ff Window decorations: added client property to mark components in embedded menu bar as "caption" (issue #569) 2022-08-20 19:42:38 +02:00
Karl Tauber
fee7cf6265 FlatPopupFactory: use method handles instead of reflection 2022-08-13 11:18:47 +02:00
Karl Tauber
2dd75c4a64 fixed possible exception in FlatUIUtils.resetRenderingHints() (issue #575) 2022-08-12 15:41:55 +02:00
Karl Tauber
d2f46cd0b5 TabbedPane: option to disable tab run rotation in wrap layout (issue #574) 2022-08-12 15:32:39 +02:00
Karl Tauber
10914083e6 JavaCompatibility: use method handles instead of reflection 2022-08-12 11:20:42 +02:00
Karl Tauber
5d167da55e Styling: fixed styling protected JRE fields using @StyleableField annotation (regression in commit ff00a6c0f0) 2022-08-12 11:15:49 +02:00
Karl Tauber
b381e20e57 UIDefaultsLoader: over() color function should always return a ColorUIResource 2022-08-11 23:52:35 +02:00
Karl Tauber
380dae1017 Icons: cache paths for (complex) immutable icons that may be painted often (e.g. Tree icons or FileView icons) 2022-08-11 22:26:48 +02:00
Karl Tauber
fb15cdc546 Icons:
- reduced temporary memory usage by specifying optimal initial capacity to `new Path2D.Float()`
- replaced `path.append( new Line2D.Float(...) )` with `path.moveTo(...); path.lineTo(...);`, which does the same, but does not use temporary objects
2022-08-11 18:09:47 +02:00
Karl Tauber
a525fe29db Icons: changed icons for FileChooser, OptionPane and Tree to rounded outlined style (issue #543) 2022-08-11 17:02:24 +02:00
Karl Tauber
475cc9a9a5 Testing: extended FlatPaintingIconsTest to paint icons as pixels 2022-08-11 13:51:19 +02:00
Karl Tauber
264d6fbd6d Testing: added FlatPaintingIconsTest 2022-08-11 00:28:44 +02:00
Karl Tauber
2826cf379b added arrow icons to FlatLaf Icons.sketch and exported as SVGs 2022-08-11 00:24:22 +02:00
Karl Tauber
d28745df29 added missing @since 1.2 tags to setup() methods 2022-08-05 11:19:52 +02:00
Karl Tauber
94f9e4a1be fixed missing UI value MenuItem.acceleratorDelimiter on macOS (was null, is now an empty string) 2022-08-03 13:16:43 +02:00
Karl Tauber
ec547e1d65 fixed compiler warnings 2022-08-03 11:55:59 +02:00
Karl Tauber
61d4eb649b Styling: fixed failing unit test TestFlatStyleableValue
- caused by non-english locale
- when running on Java 17
2022-08-03 11:21:06 +02:00
Karl Tauber
52ad15e375 Styling: added StyleableUI.getStyleableValue() for tooling (e.g. GUI builder) 2022-07-31 10:57:28 +02:00
Karl Tauber
ff00a6c0f0 Styling: use annotation on UI classes for fields in Basic* classes to apply style properties (to avoid boilerplate code) 2022-07-30 11:03:17 +02:00
Karl Tauber
9b1ebd658d updated sigtest for FlatLaf 2.4
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-07-13 23:45:28 +02:00
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
bf0685cee2 ComboBox: support rounded selection 2022-06-05 00:53:22 +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
8262793751 List: support rounded selection for layout orientations VERTICAL_WRAP and HORIZONTAL_WRAP 2022-06-04 11:16:04 +02:00
Karl Tauber
5ec7848fb0 List: fixed endless loop rounded selection painting 2022-06-03 17:32:55 +02:00
Karl Tauber
450fc123ff List: support rounded selection 2022-06-03 16:12:02 +02:00
Karl Tauber
3802c64be3 Tree: better support for non-wide rounded selection 2022-06-03 09:33:19 +02:00
Karl Tauber
7bf1b26812 Tree: support rounded selection 2022-06-02 12:03:20 +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
f1792e46c6 Menus:
- support different selection colors for top-level JMenus
- fixed styling of underline selection properties for top-level JMenus
2022-05-15 16:39:11 +02:00
Karl Tauber
84e9c36280 Menus: support rounded selection 2022-05-15 14:24:38 +02:00
Karl Tauber
2ef6a2c3c9 ToolBar: add hover effect to button groups 2022-05-14 13:59:47 +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
Karl Tauber
024b6daaf6 release 2.2 2022-04-25 19:36:43 +02:00
Karl Tauber
bd5512c121 SplitPane: allow limiting one-touch expanding to a single side (issue #355) 2022-04-23 17:13:32 +02:00
Karl Tauber
9afce83a02 SplitPane: added missing BasicSplitPaneDivider properties to javadoc 2022-04-23 16:19:04 +02:00
Karl Tauber
07a8bd9486 ComboBox: added missing BasicComboPopup properties to javadoc 2022-04-22 10:55:25 +02:00
Karl Tauber
bcdc0a8fce IntelliJ Themes: added "Monokai Pro" and "Xcode-Dark" themes 2022-04-21 22:03:05 +02:00
Karl Tauber
b295809432 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2022-04-21 22:02:09 +02:00
Karl Tauber
52763ab932 GitHub Actions:
- natives.yml: include core natives in artifacts
- updated versions of used actions
2022-04-21 14:18:27 +02:00
Karl Tauber
99666265c9 gradle:
- use gradle `cpp-library` plugin instead of 3rd party plugin
- build natives only via task `build-natives`
2022-04-21 12:58:16 +02:00
Karl Tauber
af3e280d74 Table: slightly changed grid colors to make grid better recognizable (issue #514) 2022-04-19 23:00:01 +02:00
Karl Tauber
b57e4c0565 TabbedPane: selected tab underline color now changes depending on whether the focus is within the tab content (issue #398) 2022-04-19 22:19:47 +02:00
Karl Tauber
aca9931560 IntelliJ Themes: TabbedPane: use DefaultTabs.underlinedTabBackground and DefaultTabs.underlinedTabForeground from JSON themes for selected tab background/foreground 2022-04-19 16:50:27 +02:00
Karl Tauber
d09e166e4a SplitPane: fixed StackOverflowError caused by layout loop that may occur under special circumstances (issue #513) 2022-04-12 13:47:04 +02:00
Karl Tauber
68a7a60ff2 FileChooser: enabled full row selection for details view to fix alternate row coloring (issue #512) 2022-04-12 13:28:39 +02:00
Karl Tauber
f21261914b gradle: build target flatlaf-natives-windows only on Windows
(to fix build error on macOS)
2022-04-09 18:34:36 +02:00
Karl Tauber
7b11339fdc update to Gradle 7.4.2
./gradlew wrapper --gradle-version=7.4.2
2022-04-09 18:18:45 +02:00
Karl Tauber
081fd43d98 IntelliJ Themes: Component.accentColor UI property now has useful theme specific values (issue #507) 2022-04-07 18:07:09 +02:00
Karl Tauber
ef2eedfc7c Button: fixed icon layout and preferred width of default buttons that use bold font (issue #506) 2022-04-06 23:36:58 +02:00
Karl Tauber
0dba9265be ToolBar: fixed endless loop in focus navigation that may occur under special circumstances (issue #505) 2022-04-06 18:53:45 +02:00
Karl Tauber
301aae9b8e NativeLibrary: use System.mapLibraryName() instead of own implementation 2022-03-19 11:07:46 +01:00
Karl Tauber
c63f4e9662 Window decorations on Linux: limit window resizing/moving to left mouse button (issue #482) 2022-03-18 00:05:15 +01:00
Karl Tauber
47508dc6ac Native window decorations: updated DLLs (issue #502)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/2000978687
2022-03-17 22:48:01 +01:00
Karl Tauber
3a8879608a Native window decorations: fixed wrong window title character encoding used in Windows taskbar (issue #502) 2022-03-17 22:31:18 +01:00
Karl Tauber
b221889549 updated sigtest for FlatLaf 2.1
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-03-17 18:34:44 +01:00
360 changed files with 39135 additions and 6366 deletions

2
.gitattributes vendored
View File

@@ -20,7 +20,9 @@
*.gif binary *.gif binary
*.jar binary *.jar binary
*.lib binary *.lib binary
*.otf binary
*.png binary *.png binary
*.sketch binary *.sketch binary
*.so binary *.so binary
*.ttf binary
*.zip binary *.zip binary

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: https://www.formdev.com/flatlaf/sponsor/

View File

@@ -1,4 +1,5 @@
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: CI name: CI
@@ -8,9 +9,6 @@ on:
- '*' - '*'
tags: tags:
- '[0-9]*' - '[0-9]*'
pull_request:
branches:
- '*'
jobs: jobs:
build: build:
@@ -26,30 +24,35 @@ jobs:
- 8 - 8
- 11 # LTS - 11 # LTS
- 17 # LTS - 17 # LTS
toolchain: [""]
include:
- java: 17
toolchain: 19 # latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
if: matrix.java == '8' if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }} - name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v2 uses: actions/setup-java@v3
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
cache: gradle cache: gradle
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: matrix.java == '11' if: matrix.java == '11'
with: with:
name: FlatLaf-build-artifacts name: FlatLaf-build-artifacts
path: | path: |
flatlaf-*/build/libs flatlaf-*/build/libs
flatlaf-*/flatlaf-*/build/libs
!**/*-javadoc.jar !**/*-javadoc.jar
!**/*-sources.jar !**/*-sources.jar
@@ -63,17 +66,17 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v2 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt # pre-installed on ubuntu-latest distribution: adopt # pre-installed on ubuntu-latest
cache: gradle cache: gradle
- name: Publish snapshot to oss.sonatype.org - name: Publish snapshot to oss.sonatype.org
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true run: ./gradlew publish :flatlaf-theme-editor:build -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true
env: env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
@@ -99,17 +102,17 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v2 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt # pre-installed on ubuntu-latest distribution: adopt # pre-installed on ubuntu-latest
cache: gradle cache: gradle
- name: Release a new stable version to Maven Central - name: Release a new stable version to Maven Central
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease
env: env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

59
.github/workflows/fonts.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Fonts
on:
push:
branches:
- '*'
tags:
- 'fonts/*-[0-9]*'
paths:
- 'flatlaf-fonts/**'
- '.github/workflows/fonts.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
jobs:
Fonts:
strategy:
matrix:
font:
- inter
- jetbrains-mono
- roboto
runs-on: ubuntu-latest
if: |
github.event_name == 'push' &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
cache: gradle
- name: Build with Gradle
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) ) != true
- name: Publish snapshot to oss.sonatype.org
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:publish -Dorg.gradle.internal.publish.checksums.insecure=true
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
if: github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )
- name: Release a new stable version to Maven Central
run: ./gradlew :flatlaf-fonts-${{ matrix.font }}:build :flatlaf-fonts-${{ matrix.font }}:publish -Prelease
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
if: startsWith( github.ref, format( 'refs/tags/fonts/{0}-', matrix.font ) )

View File

@@ -1,4 +1,5 @@
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Native Libraries name: Native Libraries
@@ -9,41 +10,41 @@ on:
tags: tags:
- '[0-9]*' - '[0-9]*'
paths: paths:
- 'flatlaf-natives/flatlaf-natives-windows/**' - 'flatlaf-natives/**'
- '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
pull_request:
branches:
- '*'
paths:
- 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml' - '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties' - 'gradle/wrapper/gradle-wrapper.properties'
jobs: jobs:
Windows: Natives:
runs-on: windows-latest strategy:
matrix:
os:
- windows
- ubuntu
runs-on: ${{ matrix.os }}-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v2 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt distribution: adopt
cache: gradle cache: gradle
- name: Build with Gradle - name: Build with Gradle
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with: # --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied # tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
run: ./gradlew :flatlaf-natives-windows:build --no-daemon run: ./gradlew build-natives --no-daemon
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: FlatLaf-natives-windows-build-artifacts name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
path: | path: |
flatlaf-natives/flatlaf-natives-windows/build flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
flatlaf-natives/flatlaf-natives-*/build

2
.gitignore vendored
View File

@@ -9,5 +9,7 @@ out/
*.iml *.iml
*.ipr *.ipr
*.iws *.iws
*.xcuserstate
*.xcworkspacedata
.vs/ .vs/
.vscode/ .vscode/

View File

@@ -1,6 +1,201 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.0
#### New features and improvements
- **macOS light and dark themes**: The two new themes `FlatMacLightLaf` and
`FlatMacDarkLaf` use macOS colors and look similar to native macOS controls.
(PRs #533, #612 and #607)
- **Fonts**: Packaged some fonts into JARs and provide an easy way to use them
with FlatLaf. (PRs #545, #614 and #615) At the moment there are three fonts:
- **Inter** (https://rsms.me/inter/) - a typeface carefully crafted & designed
for computer screens
- **Roboto** (https://fonts.google.com/specimen/Roboto) - default font on
Android and recommended for Material Design
- **JetBrains Mono** (https://www.jetbrains.com/mono) - a monospaced typeface
- **Rounded selection**: Optionally use rounded selection in:
- Menus (PR #536)
- ComboBox (PR #548)
- List (PR #547)
- Tree (PR #546)
- Tree: Hide default closed/opened/leaf icons by default. Set UI value
`Tree.showDefaultIcons` to `true` to show them.
- ToolBar: Hover effect for button groups. (PR #534)
- Icons: New modern **rounded outlined icons** for `JFileChooser`,
`JOptionPane`, `JPasswordField` and `JTree`. (PR #577)
#### Fixed bugs
- FileChooser: Fixed layout of (optional) accessory component and fixed too
large right margin. (issue #604; regression since implementing PR #522 in
FlatLaf 2.3)
- Tree:
- Fixed missing tree lines (if enabled) for wide-selected rows. (issue #598)
- Fixed scaling of tree lines and fixed alignment to expand/collapse arrows.
- Removed support for dashed tree lines. `Tree.lineTypeDashed` is now ignored.
- SwingX: Fonts in `JXHeader`, `JXMonthView`, `JXTaskPane` and `JXTitledPanel`
were not updated when changing default font.
## 2.6
#### New features and improvements
- If value of system property `flatlaf.nativeLibraryPath` is `system`, then
`System.loadLibrary(String)` is used to load the native library.
- TabbedPane: Switch and close tabs on left mouse click only. (PR #595)
#### Fixed bugs
- ComboBox and Spinner: Fixed missing arrow buttons if preferred height is zero.
Minimum width of arrow buttons is 3/4 of default width.
- MenuBar: Fixed NPE in `FlatMenuItemRenderer.getTopLevelFont()` if menu item
does not have a parent. (issue #600; regression since implementing #589 in
FlatLaf 2.5)
- ScrollBar: Show "pressed" feedback on track/thumb only for left mouse button.
If absolute positioning is enabled (the default), then also for middle mouse
button.
- Arrow buttons in ComboBox, Spinner, ScrollBar and TabbedPane: Show "pressed"
feedback only for left mouse button.
- ScaledImageIcon: Do not throw exceptions if image was has invalid size (e.g.
not found). Instead, paint a red rectangle (similar to `FlatSVGIcon`).
- Fixed NPE in `FlatUIUtils.isCellEditor()`. (issue #601)
## 2.5
#### New features and improvements
- Linux: Use X11 window manager events to move window and to show window menu
(right-click on window title bar), if custom window decorations are enabled.
This gives FlatLaf windows a more "native" feeling. (issue #482)
- MenuBar: Support different menu selection style UI defaults for `MenuBar` and
`MenuItem`. (issue #587)
- MenuBar: Top level menus now use `MenuBar.font` instead of `Menu.font`. (issue
#589)
- PasswordField: Reveal button is now hidden (and turned off) if password field
is disabled. (issue #501)
- TabbedPane: New option to disable tab run rotation in wrap layout. Set UI
value `TabbedPane.rotateTabRuns` to `false`. (issue #574)
- Window decorations:
- Added client property to mark components in embedded menu bar as "caption"
(allow moving window). (issue #569)
- Option to show window icon only in frames, but not in dialogs. Set UI value
`TitlePane.showIconInDialogs` to `false`. (issue #589)
- Added UI value `TitlePane.font` to customize window title font. (issue #589)
- Added system property `flatlaf.updateUIOnSystemFontChange` to allow disabling
automatic UI update when system font changes. (issue #580)
#### Fixed bugs
- Fixed missing UI value `MenuItem.acceleratorDelimiter` on macOS. (was `null`,
is now an empty string)
- Fixed possible exception in `FlatUIUtils.resetRenderingHints()`. (issue #575)
- Fixed AWT components on macOS, which use Swing components internally. (issue
#583)
- SwingX: Fixed missing highlighting of "today" in `JXMonthView` and
`JXDatePicker`.
## 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
- SplitPane: Allow limiting one-touch expanding to a single side (set client
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
- TabbedPane: Selected tab underline color now changes depending on whether the
focus is within the tab content. (issue #398)
- IntelliJ Themes:
- Added "Monokai Pro" and "Xcode-Dark" themes.
- TabbedPane now use different background color for selected tabs in all "Arc"
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
#### Fixed bugs
- Native window decorations (Windows 10/11 only): Fixed wrong window title
character encoding used in Windows taskbar. (issue #502)
- Button: Fixed icon layout and preferred width of default buttons that use bold
font. (issue #506)
- FileChooser: Enabled full row selection for details view to fix alternate row
coloring. (issue #512)
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
under special circumstances. (issue #513)
- Table: Slightly changed grid colors to make grid better recognizable. (issue
#514)
- ToolBar: Fixed endless loop in focus navigation that may occur under special
circumstances. (issue #505)
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
specific values. (issue #507)
## 2.1 ## 2.1
#### New features and improvements #### New features and improvements
@@ -110,7 +305,7 @@ FlatLaf Change Log
- Possibility to hide window title bar icon (for single window set client - Possibility to hide window title bar icon (for single window set client
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
value `TitlePane.showIcon` to `false`). 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) setting UI default `OptionPane.showIcon` to `true`. (issue #416)
- No longer show the Java "duke/cup" icon if no window icon image is set. - No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416) (issue #416)

View File

@@ -25,6 +25,17 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
![IntelliJ Platform Themes](images/intellij_platform_themes.png) ![IntelliJ Platform Themes](images/intellij_platform_themes.png)
Sponsors
--------
<a href="https://www.ej-technologies.com/"><img src="https://www.formdev.com/flatlaf/sponsor/ej-technologies.png" width="200" alt="ej-technologies" title="ej-technologies - Java APM, Java Profiler, Java Installer Builder"></a>
&nbsp; &nbsp; &nbsp; &nbsp;
<a href="https://www.dbvis.com/"><img src="https://www.formdev.com/flatlaf/sponsor/dbvisualizer.svg" width="200" alt="DbVisualizer" title="DbVisualizer - SQL Client and Editor"></a>
&nbsp; &nbsp; &nbsp; &nbsp;
<a href="https://www.dscsag.com/"><img src="https://www.formdev.com/flatlaf/sponsor/DSC.png" height="48" alt="DSC Software AG" title="DSC Software AG - Your Companion for Integrative PLM"></a>
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
Demo Demo
---- ----
@@ -74,6 +85,8 @@ Addons
- [SwingX](flatlaf-swingx) - support for SwingX components - [SwingX](flatlaf-swingx) - support for SwingX components
- [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer - [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer
components components
- [Fonts](flatlaf-fonts) - some font families bundled in easy-to-use and
redistributable JARs
Getting started Getting started
@@ -126,42 +139,64 @@ Buzz
Applications using FlatLaf Applications using FlatLaf
-------------------------- --------------------------
- ![New](images/new.svg) [Ultorg](https://www.ultorg.com/) (**commercial**) - a - ![New](images/new.svg)
[JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)
12 (**commercial**) - the award-winning all-in-one Java profiler
- ![New](images/new.svg) [JFormDesigner](https://www.formdev.com/) 8
(**commercial**) - Java/Swing GUI Designer
- ![New](images/new.svg) [Jeyla Studio](https://www.jeylastudio.com/) - Salon
Software
- ![New](images/new.svg) [Fanurio](https://www.fanuriotimetracking.com/) 3.3.2
(**commercial**) - time tracking and billing for freelancers and teams
- ![New](images/new.svg) [Antares](https://www.antarescircuit.io/) - a free,
powerful platform for designing, simulating and explaining digital circuits
- ![New](images/new.svg)
[Logisim-evolution](https://github.com/logisim-evolution/logisim-evolution)
3.6 - Digital logic design tool and simulator
- ![New](images/new.svg) [Cinecred](https://loadingbyte.com/cinecred/) - create
beautiful film credit sequences
- ![New](images/new.svg) [tinyMediaManager](https://www.tinymediamanager.org/)
v4 (**commercial**) - a media management tool
- ![New](images/new.svg) [Weasis](https://nroduit.github.io/) - medical DICOM
viewer used in healthcare by hospitals, health networks, etc
- ![New](images/new.svg)
[Makelangelo Software](https://github.com/MarginallyClever/Makelangelo-software)
7.3.0 - for plotters, especially the wall-hanging polargraph
- ![Hot](images/hot.svg) [Ultorg](https://www.ultorg.com/) (**commercial**) - a
visual query system for relational databases visual query system for relational databases
- ![New](images/new.svg) [MooInfo](https://github.com/rememberber/MooInfo) - - [MooInfo](https://github.com/rememberber/MooInfo) - visual implementation of
visual implementation of OSHI, to view information about the system and OSHI, to view information about the system and hardware
hardware - [Jailer](https://github.com/Wisser/Jailer) 11.2 - database subsetting and
- ![New](images/new.svg) [Jailer](https://github.com/Wisser/Jailer) 11.2 - relational data browsing tool
database subsetting and relational data browsing tool - ![Hot](images/hot.svg) [Apache NetBeans](https://netbeans.apache.org/) 11.3 -
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML IDE for Java, PHP, HTML and much more
and much more
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5 - [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3 - [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- ![New](images/new.svg) - ![Hot](images/hot.svg)
[install4j](https://www.ej-technologies.com/products/install4j/overview.html) [install4j](https://www.ej-technologies.com/products/install4j/overview.html)
9.0 (**commercial**) - the powerful multi-platform Java installer builder 9.0 (**commercial**) - the powerful multi-platform Java installer builder
- ![New](images/new.svg) [DbVisualizer](https://www.dbvis.com/) 12.0 - ![Hot](images/hot.svg) [DbVisualizer](https://www.dbvis.com/) 12.0
(**commercial**) - the universal database tool for developers, analysts and (**commercial**) - the universal database tool for developers, analysts and
DBAs DBAs
- ![New](images/new.svg) [MagicPlot](https://magicplot.com/) 3.0 - ![Hot](images/hot.svg) [MagicPlot](https://magicplot.com/) 3.0
(**commercial**) - Software for nonlinear fitting, plotting and data analysis (**commercial**) - Software for nonlinear fitting, plotting and data analysis
- ![New](images/new.svg) - ![Hot](images/hot.svg)
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a [Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
(**commercial**) - Thermodynamics and Properties Software (**commercial**) - Thermodynamics and Properties Software
- [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds most widely used web - ![Hot](images/hot.svg) [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds
app scanner most widely used web app scanner
- ![New](images/new.svg) - ![Hot](images/hot.svg)
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro) [Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
2020.11.2 (**commercial**) - the leading software for web security testing 2020.11.2 (**commercial**) - the leading software for web security testing
- ![New](images/new.svg) - [BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
[BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
FlatLaf themes to Burp Suite FlatLaf themes to Burp Suite
- [JOSM](https://josm.openstreetmap.de/) - an extensible editor for - ![Hot](images/hot.svg) [JOSM](https://josm.openstreetmap.de/) - an extensible
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin) editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album JOSM plugin)
websites - ![Hot](images/hot.svg) [jAlbum](https://jalbum.net/) 21 (**commercial**) -
- ![New](images/new.svg) [PDF Studio](https://www.qoppa.com/pdfstudio/) 2021 creates photo album websites
(**commercial**) - create, review and edit PDF documents - [PDF Studio](https://www.qoppa.com/pdfstudio/) 2021 (**commercial**) - create,
review and edit PDF documents
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**) - [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (**commercial**)
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) - - [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
checks your website checks your website
@@ -187,8 +222,8 @@ Applications using FlatLaf
[mendelson AS2](https://mendelson-e-c.com/as2/), [mendelson AS2](https://mendelson-e-c.com/as2/),
[AS4](https://mendelson-e-c.com/as4/) and [AS4](https://mendelson-e-c.com/as4/) and
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**) [OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
- ![New](images/new.svg) [IGMAS+](https://www.gfz-potsdam.de/igmas) - - [IGMAS+](https://www.gfz-potsdam.de/igmas) - Interactive Gravity and Magnetic
Interactive Gravity and Magnetic Application System Application System
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific - [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
computation environment for meteorological community computation environment for meteorological community
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information - [lsfusion platform](https://github.com/lsfusion/platform) 4 - information

View File

@@ -14,10 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
val releaseVersion = "2.1" val releaseVersion = "3.0"
val developmentVersion = "2.2-SNAPSHOT" val developmentVersion = "3.1-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion version = if( rootProject.hasProperty( "release" ) ) releaseVersion else developmentVersion
allprojects { allprojects {
version = rootProject.version version = rootProject.version
@@ -37,6 +37,9 @@ println( "----------------------------------------------------------------------
println( "FlatLaf Version: ${version}" ) println( "FlatLaf Version: ${version}" )
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" ) println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
println( "Java ${System.getProperty( "java.version" )}" ) println( "Java ${System.getProperty( "java.version" )}" )
val toolchainJavaVersion = System.getProperty( "toolchain" )
if( !toolchainJavaVersion.isNullOrEmpty() )
println( "Java toolchain ${toolchainJavaVersion}" )
println() println()

View File

@@ -0,0 +1,46 @@
/*
* 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.
*/
plugins {
`cpp-library`
}
library {
// disable debuggable for release builds to make shared libraries smaller
binaries.configureEach( CppSharedLibrary::class ) {
with( compileTask.get() ) {
if( name.contains( "Release" ) )
isDebuggable = false
}
with( linkTask.get() ) {
if( name.contains( "Release" ) )
debuggable.set( false )
}
}
}
tasks {
withType<CppCompile>().configureEach {
doFirst {
println( "Used Tool Chain:" )
println( " - ${toolChain.get()}" )
println( "Available Tool Chains:" )
toolChains.forEach {
println( " - $it" )
}
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.
*/
open class JniHeadersExtension {
var headers: List<String> = emptyList()
}
val extension = project.extensions.create<JniHeadersExtension>( "flatlafJniHeaders" )
tasks {
register<Copy>( "jni-headers" ) {
// depend on :flatlaf-core:compileJava because it generates the JNI headers
dependsOn( ":flatlaf-core:compileJava" )
from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
into( "src/main/headers" )
include( extension.headers )
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
"eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
)
}
}

View File

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

View File

@@ -80,7 +80,7 @@ publishing {
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" val snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
url = uri( if( java.lang.Boolean.getBoolean( "release" ) ) releasesRepoUrl else snapshotsRepoUrl ) url = uri( if( rootProject.hasProperty( "release" ) ) releasesRepoUrl else snapshotsRepoUrl )
credentials { credentials {
// get from gradle.properties // get from gradle.properties
@@ -108,5 +108,5 @@ signing {
// disable signing of snapshots // disable signing of snapshots
tasks.withType<Sign>().configureEach { tasks.withType<Sign>().configureEach {
onlyIf { java.lang.Boolean.getBoolean( "release" ) } onlyIf { rootProject.hasProperty( "release" ) }
} }

View File

@@ -0,0 +1,26 @@
/*
* 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.
*/
plugins {
java
}
val toolchainJavaVersion = System.getProperty( "toolchain" )
if( !toolchainJavaVersion.isNullOrEmpty() ) {
java.toolchain {
languageVersion.set( JavaLanguageVersion.of( toolchainJavaVersion ) )
}
}

View File

@@ -16,6 +16,7 @@
plugins { plugins {
`java-library` `java-library`
`flatlaf-toolchain`
`flatlaf-module-info` `flatlaf-module-info`
`flatlaf-java9` `flatlaf-java9`
`flatlaf-publish` `flatlaf-publish`
@@ -29,7 +30,7 @@ dependencies {
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" ) testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
// https://github.com/jtulach/netbeans-apitest // https://github.com/jtulach/netbeans-apitest
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" ) sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.7" )
} }
java { java {
@@ -43,11 +44,6 @@ tasks {
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) ) options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
} }
processResources {
// build native libraries
dependsOn( ":flatlaf-natives-windows:assemble" )
}
jar { jar {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
@@ -71,6 +67,9 @@ tasks {
test { test {
useJUnitPlatform() useJUnitPlatform()
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
} }
register( "sigtestGenerate" ) { register( "sigtestGenerate" ) {
@@ -88,7 +87,7 @@ tasks {
"action" to "generate", "action" to "generate",
"fileName" to "${project.name}-sigtest.txt", "fileName" to "${project.name}-sigtest.txt",
"classpath" to jar.get().outputs.files.asPath, "classpath" to jar.get().outputs.files.asPath,
"packages" to "com.formdev.flatlaf,com.formdev.flatlaf.util", "packages" to "com.formdev.flatlaf,com.formdev.flatlaf.themes,com.formdev.flatlaf.util",
"version" to version, "version" to version,
"release" to "1.8", // Java version "release" to "1.8", // Java version
"failonerror" to "true" ) "failonerror" to "true" )

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 2.0 #Version 2.6
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType" fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -11,6 +11,7 @@ fld public final static java.lang.String BUTTON_TYPE_TAB = "tab"
fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton" fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton"
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner" fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect" fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded" fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight" fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth" fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
@@ -30,6 +31,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_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_NEVER = "never"
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once" 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 SQUARE_SIZE = "JButton.squareSize"
fld public final static java.lang.String STYLE = "FlatLaf.style" fld public final static java.lang.String STYLE = "FlatLaf.style"
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass" fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
@@ -72,6 +76,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_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_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_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_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_COMPONENT = "JTextField.leadingComponent"
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon" fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
@@ -181,6 +186,7 @@ meth public java.lang.String getID()
meth public java.util.Map<java.lang.String,java.lang.String> getExtraDefaults() meth public java.util.Map<java.lang.String,java.lang.String> getExtraDefaults()
meth public javax.swing.Icon getDisabledIcon(javax.swing.JComponent,javax.swing.Icon) meth public javax.swing.Icon getDisabledIcon(javax.swing.JComponent,javax.swing.Icon)
meth public javax.swing.UIDefaults getDefaults() meth public javax.swing.UIDefaults getDefaults()
meth public static <%0 extends java.lang.Object> {%%0} getStyleableValue(javax.swing.JComponent,java.lang.String)
meth public static boolean install(javax.swing.LookAndFeel) meth public static boolean install(javax.swing.LookAndFeel)
anno 0 java.lang.Deprecated() anno 0 java.lang.Deprecated()
meth public static boolean isLafDark() meth public static boolean isLafDark()
@@ -189,6 +195,7 @@ meth public static boolean isUseNativeWindowDecorations()
meth public static boolean setup(javax.swing.LookAndFeel) meth public static boolean setup(javax.swing.LookAndFeel)
meth public static boolean supportsNativeWindowDecorations() meth public static boolean supportsNativeWindowDecorations()
meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>) meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>)
meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleableInfos(javax.swing.JComponent)
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults() meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float) meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
meth public static void hideMnemonics() meth public static void hideMnemonics()
@@ -216,7 +223,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
meth public void uninitialize() meth public void uninitialize()
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>) meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
supr javax.swing.plaf.basic.BasicLookAndFeel supr javax.swing.plaf.basic.BasicLookAndFeel
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,uiDefaultsGetters,updateUIPending hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,subMenuUsabilityHelperInstalled,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -255,6 +262,7 @@ fld public final static java.lang.String NATIVE_LIBRARY_PATH = "flatlaf.nativeLi
fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale" fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale"
fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown" fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown"
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled" fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations" fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection" fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont" fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
@@ -596,6 +604,7 @@ supr java.lang.Object
CLSS public com.formdev.flatlaf.util.NativeLibrary CLSS public com.formdev.flatlaf.util.NativeLibrary
cons public init(java.io.File,boolean) cons public init(java.io.File,boolean)
cons public init(java.lang.String,boolean)
cons public init(java.lang.String,java.lang.ClassLoader,boolean) cons public init(java.lang.String,java.lang.ClassLoader,boolean)
meth public boolean isLoaded() meth public boolean isLoaded()
supr java.lang.Object supr java.lang.Object
@@ -662,6 +671,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init() cons public init()
fld public final static boolean isAARCH64 fld public final static boolean isAARCH64
fld public final static boolean isJava_11_orLater 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_15_orLater
fld public final static boolean isJava_17_orLater fld public final static boolean isJava_17_orLater
fld public final static boolean isJava_18_orLater fld public final static boolean isJava_18_orLater
@@ -670,6 +680,7 @@ fld public final static boolean isJetBrainsJVM
fld public final static boolean isJetBrainsJVM_11_orLater fld public final static boolean isJetBrainsJVM_11_orLater
fld public final static boolean isKDE fld public final static boolean isKDE
fld public final static boolean isLinux fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_orLater fld public final static boolean isMacOS_10_11_ElCapitan_orLater
fld public final static boolean isMacOS_10_14_Mojave_orLater fld public final static boolean isMacOS_10_14_Mojave_orLater

View File

@@ -253,6 +253,19 @@ public interface FlatClientProperties
*/ */
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"; String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
/**
* Specifies whether a component in an embedded menu bar should behave as caption
* (left-click allows moving window, right-click shows window system menu).
* The component does not receive mouse pressed/released/clicked/dragged events,
* but it gets mouse entered/exited/moved events.
* <p>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 2.5
*/
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
//---- Popup -------------------------------------------------------------- //---- Popup --------------------------------------------------------------
/** /**
@@ -333,7 +346,7 @@ public interface FlatClientProperties
/** /**
* Specifies whether the window icon should be shown in the window title bar * Specifies whether the window icon should be shown in the window title bar
* (requires enabled window decorations). * (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
* <p> * <p>
* Setting this shows/hides the windows icon * Setting this shows/hides the windows icon
* for the {@code JFrame} or {@code JDialog} that contains the root pane. * for the {@code JFrame} or {@code JDialog} that contains the root pane.
@@ -349,6 +362,62 @@ public interface FlatClientProperties
*/ */
String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"; String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon";
/**
* Specifies whether the window title should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}.
* <p>
* Setting this shows/hides the windows title
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3
*/
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
/**
* Specifies whether the "iconfify" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}.
* <p>
* Setting this shows/hides the "iconfify" button
* for the {@code JFrame} that contains the root pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3
*/
String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify";
/**
* Specifies whether the "maximize/restore" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}.
* <p>
* Setting this shows/hides the "maximize/restore" button
* for the {@code JFrame} that contains the root pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3
*/
String TITLE_BAR_SHOW_MAXIMIZE = "JRootPane.titleBarShowMaximize";
/**
* Specifies whether the "close" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}.
* <p>
* Setting this shows/hides the "close" button
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3
*/
String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose";
/** /**
* Background color of window title bar (requires enabled window decorations). * Background color of window title bar (requires enabled window decorations).
* <p> * <p>
@@ -391,6 +460,40 @@ public interface FlatClientProperties
*/ */
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling"; String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JSplitPane ---------------------------------------------------------
/**
* Specifies what side of the spilt pane is allowed to expand
* via one-touch expanding arrow buttons.
* Requires that one-touch expanding is enabled with
* {@link javax.swing.JSplitPane#setOneTouchExpandable(boolean)}.
* <p>
* <strong>Component</strong> {@link javax.swing.JSplitPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_LEFT} or
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_RIGHT}
*
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide";
/**
* Allow expanding only left/top side of the split pane.
*
* @see #SPLIT_PANE_EXPANDABLE_SIDE
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left";
/**
* Allow expanding only right/bottom side of the split pane.
*
* @see #SPLIT_PANE_EXPANDABLE_SIDE
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
//---- JTabbedPane -------------------------------------------------------- //---- JTabbedPane --------------------------------------------------------
/** /**
@@ -958,7 +1061,22 @@ public interface FlatClientProperties
//---- JToggleButton ------------------------------------------------------ //---- 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> * <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br> * <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer} * <strong>Value type</strong> {@link java.lang.Integer}

View File

@@ -34,6 +34,8 @@ public class FlatDarculaLaf
/** /**
* Sets the application look and feel to this LaF * Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}. * using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/ */
public static boolean setup() { public static boolean setup() {
return setup( new FlatDarculaLaf() ); return setup( new FlatDarculaLaf() );

View File

@@ -33,6 +33,8 @@ public class FlatDarkLaf
/** /**
* Sets the application look and feel to this LaF * Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}. * using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/ */
public static boolean setup() { public static boolean setup() {
return setup( new FlatDarkLaf() ); return setup( new FlatDarkLaf() );

View File

@@ -34,6 +34,8 @@ public class FlatIntelliJLaf
/** /**
* Sets the application look and feel to this LaF * Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}. * using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/ */
public static boolean setup() { public static boolean setup() {
return setup( new FlatIntelliJLaf() ); return setup( new FlatIntelliJLaf() );

View File

@@ -30,6 +30,9 @@ import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@@ -63,6 +66,7 @@ import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException; import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.FontUIResource; import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.IconUIResource; import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -73,6 +77,8 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory; import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.FlatRootPaneUI; import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport; import com.formdev.flatlaf.util.MultiResolutionImageSupport;
@@ -93,6 +99,7 @@ public abstract class FlatLaf
private static List<Object> customDefaultsSources; private static List<Object> customDefaultsSources;
private static Map<String, String> globalExtraDefaults; private static Map<String, String> globalExtraDefaults;
private Map<String, String> extraDefaults; private Map<String, String> extraDefaults;
private static Function<String, Color> systemColorGetter;
private String desktopPropertyName; private String desktopPropertyName;
private String desktopPropertyName2; private String desktopPropertyName2;
@@ -103,14 +110,21 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory; private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler; private MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper; private boolean subMenuUsabilityHelperInstalled;
private Consumer<UIDefaults> postInitialization; private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters; private List<Function<Object, Object>> uiDefaultsGetters;
private static String preferredFontFamily;
private static String preferredLightFontFamily;
private static String preferredSemiboldFontFamily;
private static String preferredMonospacedFontFamily;
/** /**
* Sets the application look and feel to the given LaF * Sets the application look and feel to the given LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}. * using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/ */
public static boolean setup( LookAndFeel newLookAndFeel ) { public static boolean setup( LookAndFeel newLookAndFeel ) {
try { try {
@@ -232,6 +246,15 @@ public abstract class FlatLaf
@Override @Override
public void initialize() { 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 ) if( SystemInfo.isMacOS )
initializeAqua(); initializeAqua();
@@ -246,8 +269,7 @@ public abstract class FlatLaf
mnemonicHandler.install(); mnemonicHandler.install();
// install submenu usability helper // install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper(); subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
subMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes // listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) { if( SystemInfo.isWindows ) {
@@ -266,6 +288,9 @@ public abstract class FlatLaf
} }
if( desktopPropertyName != null ) { if( desktopPropertyName != null ) {
desktopPropertyListener = e -> { desktopPropertyListener = e -> {
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.UPDATE_UI_ON_SYSTEM_FONT_CHANGE, true ) )
return;
String propertyName = e.getPropertyName(); String propertyName = e.getPropertyName();
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) ) if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
reSetLookAndFeel(); reSetLookAndFeel();
@@ -304,6 +329,10 @@ public abstract class FlatLaf
@Override @Override
public void uninitialize() { public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener // remove desktop property listener
if( desktopPropertyListener != null ) { if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit(); Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -329,9 +358,9 @@ public abstract class FlatLaf
} }
// uninstall submenu usability helper // uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) { if( subMenuUsabilityHelperInstalled ) {
subMenuUsabilityHelper.uninstall(); SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null; subMenuUsabilityHelperInstalled = false;
} }
// restore default link color // restore default link color
@@ -559,6 +588,7 @@ public abstract class FlatLaf
// add fonts that are not set in BasicLookAndFeel // add fonts that are not set in BasicLookAndFeel
defaults.put( "RootPane.font", activeFont ); defaults.put( "RootPane.font", activeFont );
defaults.put( "TitlePane.font", activeFont );
} }
private void initDefaultFont( UIDefaults defaults ) { private void initDefaultFont( UIDefaults defaults ) {
@@ -607,6 +637,13 @@ public abstract class FlatLaf
if( uiFont == null ) if( uiFont == null )
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 ); uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
// use preferred font family (if specified)
if( preferredFontFamily != null ) {
FontUIResource preferredFont = createCompositeFont( preferredFontFamily, uiFont.getStyle(), uiFont.getSize() );
if( !ActiveFont.isFallbackFont( preferredFont ) || ActiveFont.isDialogFamily( preferredFontFamily ) )
uiFont = preferredFont;
}
// get/remove "defaultFont" from defaults if set in properties files // get/remove "defaultFont" from defaults if set in properties files
// (use remove() to avoid that ActiveFont.createValue() gets invoked) // (use remove() to avoid that ActiveFont.createValue() gets invoked)
Object defaultFont = defaults.remove( "defaultFont" ); Object defaultFont = defaults.remove( "defaultFont" );
@@ -627,6 +664,9 @@ public abstract class FlatLaf
} }
static FontUIResource createCompositeFont( String family, int style, int size ) { static FontUIResource createCompositeFont( String family, int style, int size ) {
// load lazy font family
FontUtils.loadFontFamily( family );
// using StyleContext.getFont() here because it uses // using StyleContext.getFont() here because it uses
// sun.font.FontUtilities.getCompositeFontUIResource() // sun.font.FontUtilities.getCompositeFontUIResource()
// and creates a composite font that is able to display all Unicode characters // and creates a composite font that is able to display all Unicode characters
@@ -874,14 +914,14 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p> * <p>
* The global extra defaults are useful for smaller additional defaults that may change. * The global extra defaults are useful for smaller additional defaults that may change.
* E.g. accent color. Otherwise, FlatLaf properties files should be used. * Otherwise, FlatLaf properties files should be used.
* See {@link #registerCustomDefaultsSource(String)}. * See {@link #registerCustomDefaultsSource(String)}.
* <p> * <p>
* The keys and values are strings in same format as in FlatLaf properties files. * The keys and values are strings in same format as in FlatLaf properties files.
* <p> * <p>
* Sample that setups "FlatLaf Light" theme with red accent color: * Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code * <pre>{@code
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) ); * FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLightLaf.setup(); * FlatLightLaf.setup();
* }</pre> * }</pre>
* *
@@ -906,15 +946,15 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p> * <p>
* The extra defaults are useful for smaller additional defaults that may change. * The extra defaults are useful for smaller additional defaults that may change.
* E.g. accent color. Otherwise, FlatLaf properties files should be used. * Otherwise, FlatLaf properties files should be used.
* See {@link #registerCustomDefaultsSource(String)}. * See {@link #registerCustomDefaultsSource(String)}.
* <p> * <p>
* The keys and values are strings in same format as in FlatLaf properties files. * The keys and values are strings in same format as in FlatLaf properties files.
* <p> * <p>
* Sample that setups "FlatLaf Light" theme with red accent color: * Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code * <pre>{@code
* FlatLaf laf = new FlatLightLaf(); * FlatLaf laf = new FlatLightLaf();
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) ); * laf.setExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLaf.setup( laf ); * FlatLaf.setup( laf );
* }</pre> * }</pre>
* *
@@ -956,6 +996,36 @@ public abstract class FlatLaf
return val; return val;
} }
/**
* Returns the system color getter function, or {@code null}.
*
* @since 3
*/
public static Function<String, Color> getSystemColorGetter() {
return systemColorGetter;
}
/**
* Sets a system color getter function that is invoked when function
* {@code systemColor()} is used in FlatLaf properties files.
* <p>
* The name of the system color is passed as parameter to the function.
* The function should return {@code null} for unknown system colors.
* <p>
* Can be used to change the accent color:
* <pre>{@code
* FlatLaf.setSystemColorGetter( name -> {
* return name.equals( "accent" ) ? Color.red : null;
* } );
* FlatLightLaf.setup();
* }</pre>
*
* @since 3
*/
public static void setSystemColorGetter( Function<String, Color> systemColorGetter ) {
FlatLaf.systemColorGetter = systemColorGetter;
}
private static void reSetLookAndFeel() { private static void reSetLookAndFeel() {
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
@@ -1213,6 +1283,146 @@ public abstract class FlatLaf
*/ */
public static final Object NULL_VALUE = new Object(); public static final Object NULL_VALUE = new Object();
/**
* Returns information about styleable values of a component.
* <p>
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableInfos(c)}
*
* @since 2.5
*/
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
StyleableUI ui = getStyleableUI( c );
return (ui != null) ? ui.getStyleableInfos( c ) : null;
}
/**
* Returns the (styled) value for the given key from the given component.
* <p>
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableValue(c, key)}
*
* @since 2.5
*/
@SuppressWarnings( "unchecked" )
public static <T> T getStyleableValue( JComponent c, String key ) {
StyleableUI ui = getStyleableUI( c );
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
}
private static StyleableUI getStyleableUI( JComponent c ) {
if( !getUIMethodInitialized ) {
getUIMethodInitialized = true;
if( SystemInfo.isJava_9_orLater ) {
try {
// JComponent.getUI() is available since Java 9
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
MethodType.methodType( ComponentUI.class ) );
} catch( Exception ex ) {
// ignore
}
}
}
try {
Object ui;
if( getUIMethod != null )
ui = getUIMethod.invoke( c );
else
ui = c.getClass().getMethod( "getUI" ).invoke( c );
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
} catch( Throwable ex ) {
// ignore
return null;
}
}
private static boolean getUIMethodInitialized;
private static MethodHandle getUIMethod;
/**
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
*
* @since 3
*/
public static String getPreferredFontFamily() {
return preferredFontFamily;
}
/**
* Sets the preferred font family to be used for (nearly) all fonts.
* <p>
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
* the application look and feel.
*
* @since 3
*/
public static void setPreferredFontFamily( String preferredFontFamily ) {
FlatLaf.preferredFontFamily = preferredFontFamily;
}
/**
* Returns the preferred font family to be used for "light" fonts; or {@code null}.
*
* @since 3
*/
public static String getPreferredLightFontFamily() {
return preferredLightFontFamily;
}
/**
* Sets the preferred font family to be used for "light" fonts.
* <p>
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
* the application look and feel.
*
* @since 3
*/
public static void setPreferredLightFontFamily( String preferredLightFontFamily ) {
FlatLaf.preferredLightFontFamily = preferredLightFontFamily;
}
/**
* Returns the preferred font family to be used for "semibold" fonts; or {@code null}.
*
* @since 3
*/
public static String getPreferredSemiboldFontFamily() {
return preferredSemiboldFontFamily;
}
/**
* Sets the preferred font family to be used for "semibold" fonts.
* <p>
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
* the application look and feel.
*
* @since 3
*/
public static void setPreferredSemiboldFontFamily( String preferredSemiboldFontFamily ) {
FlatLaf.preferredSemiboldFontFamily = preferredSemiboldFontFamily;
}
/**
* Returns the preferred font family to be used for monospaced fonts; or {@code null}.
*
* @since 3
*/
public static String getPreferredMonospacedFontFamily() {
return preferredMonospacedFontFamily;
}
/**
* Sets the preferred font family to be used for monospaced fonts.
* <p>
* <strong>Note</strong>: This must be invoked <strong>before</strong> setting
* the application look and feel.
*
* @since 3
*/
public static void setPreferredMonospacedFontFamily( String preferredMonospacedFontFamily ) {
FlatLaf.preferredMonospacedFontFamily = preferredMonospacedFontFamily;
}
//---- class FlatUIDefaults ----------------------------------------------- //---- class FlatUIDefaults -----------------------------------------------
private class FlatUIDefaults private class FlatUIDefaults
@@ -1347,9 +1557,16 @@ public abstract class FlatLaf
// create font for family // create font for family
if( families != null && !families.isEmpty() ) { if( families != null && !families.isEmpty() ) {
String preferredFamily = preferredFamily( families );
if( preferredFamily != null ) {
Font font = createCompositeFont( preferredFamily, newStyle, newSize );
if( !isFallbackFont( font ) || isDialogFamily( preferredFamily ) )
return toUIResource( font );
}
for( String family : families ) { for( String family : families ) {
Font font = createCompositeFont( family, newStyle, newSize ); Font font = createCompositeFont( family, newStyle, newSize );
if( !isFallbackFont( font ) || family.equalsIgnoreCase( Font.DIALOG ) ) if( !isFallbackFont( font ) || isDialogFamily( family ) )
return toUIResource( font ); return toUIResource( font );
} }
} }
@@ -1378,9 +1595,26 @@ public abstract class FlatLaf
: new FontUIResource( font ); : new FontUIResource( font );
} }
private boolean isFallbackFont( Font font ) { private static boolean isFallbackFont( Font font ) {
return Font.DIALOG.equalsIgnoreCase( font.getFamily() ); return Font.DIALOG.equalsIgnoreCase( font.getFamily() );
} }
private static boolean isDialogFamily( String family ) {
return family.equalsIgnoreCase( Font.DIALOG );
}
private static String preferredFamily( List<String> families ) {
for( String family : families ) {
family = family.toLowerCase( Locale.ENGLISH );
if( family.endsWith( " light" ) || family.endsWith( "-thin" ) )
return preferredLightFontFamily;
if( family.endsWith( " semibold" ) || family.endsWith( "-medium" ) )
return preferredSemiboldFontFamily;
if( family.equals( "monospaced" ) )
return preferredMonospacedFontFamily;
}
return null;
}
} }
//---- class ImageIconUIResource ------------------------------------------ //---- class ImageIconUIResource ------------------------------------------

View File

@@ -33,6 +33,8 @@ public class FlatLightLaf
/** /**
* Sets the application look and feel to this LaF * Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}. * using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/ */
public static boolean setup() { public static boolean setup() {
return setup( new FlatLightLaf() ); return setup( new FlatLightLaf() );

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -140,9 +141,33 @@ public interface FlatSystemProperties
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"; String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
/** /**
* Specifies a directory in which the native FlatLaf library have been extracted. * Specifies whether FlatLaf updates the UI when the system font changes.
* If {@code true}, {@link SwingUtilities#updateComponentTreeUI(java.awt.Component)}
* gets invoked for all windows if the system font has changed.
* This is the similar to when switching to another look and feel (theme).
* Applications that do not work correctly when switching look and feel,
* should disable this option to avoid corrupted UI.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
* @since 2.5
*/
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
/**
* Specifies a directory in which the native FlatLaf libraries have been extracted.
* The path can be absolute or relative to current application working directory. * The path can be absolute or relative to current application working directory.
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime. * This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
* <p>
* If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
* used to load the native library.
* Searches for the native library in classloader of caller
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified
* in system properties {@code sun.boot.library.path} and {@code java.library.path}.
* (supported since FlatLaf 2.6)
* <p>
* If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
* then the embedded native library is extracted to the temporary directory and loaded from there.
* *
* @since 2 * @since 2
*/ */

View File

@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.ColorFunctions; import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils; import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* This class supports loading IntelliJ .theme.json files and using them as a Laf. * This class supports loading IntelliJ .theme.json files and using them as a Laf.
@@ -72,6 +73,8 @@ public class IntelliJTheme
* *
* The input stream is automatically closed. * The input stream is automatically closed.
* Using a buffered input stream is not necessary. * Using a buffered input stream is not necessary.
*
* @since 1.2
*/ */
public static boolean setup( InputStream in ) { public static boolean setup( InputStream in ) {
try { try {
@@ -162,8 +165,11 @@ public class IntelliJTheme
applyCheckBoxColors( defaults ); applyCheckBoxColors( defaults );
// copy values // copy values
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
defaults.put( e.getKey(), defaults.get( e.getValue() ) ); Object value = defaults.get( e.getValue() );
if( value != null )
defaults.put( e.getKey(), value );
}
// IDEA does not paint button background if disabled, but FlatLaf does // IDEA does not paint button background if disabled, but FlatLaf does
Object panelBackground = defaults.get( "Panel.background" ); Object panelBackground = defaults.get( "Panel.background" );
@@ -296,7 +302,7 @@ public class IntelliJTheme
for( Map.Entry<String, String> e : colors.entrySet() ) { for( Map.Entry<String, String> e : colors.entrySet() ) {
String value = e.getValue(); String value = e.getValue();
ColorUIResource color = UIDefaultsLoader.parseColor( value ); ColorUIResource color = parseColor( value );
if( color != null ) { if( color != null ) {
String key = e.getKey(); String key = e.getKey();
namedColors.put( key, color ); namedColors.put( key, color );
@@ -311,8 +317,19 @@ public class IntelliJTheme
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) { private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
if( value instanceof Map ) { if( value instanceof Map ) {
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() ) Map<String, Object> map = (Map<String, Object>)value;
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys ); if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
String osKey = SystemInfo.isWindows ? "os.windows"
: SystemInfo.isMacOS ? "os.mac"
: SystemInfo.isLinux ? "os.linux" : null;
if( osKey != null && map.containsKey( osKey ) )
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
else if( map.containsKey( "os.default" ) )
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
} else {
for( Map.Entry<String, Object> e : map.entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
}
} else { } else {
if( "".equals( value ) ) if( "".equals( value ) )
return; // ignore empty value return; // ignore empty value
@@ -431,7 +448,15 @@ public class IntelliJTheme
ColorUIResource color = namedColors.get( value ); ColorUIResource color = namedColors.get( value );
// parse color // parse color
return (color != null) ? color : UIDefaultsLoader.parseColor( value ); return (color != null) ? color : parseColor( value );
}
private ColorUIResource parseColor( String value ) {
try {
return UIDefaultsLoader.parseColor( value );
} catch( IllegalArgumentException ex ) {
return null;
}
} }
/** /**
@@ -523,7 +548,7 @@ public class IntelliJTheme
// radioFocused.svg and radioSelectedFocused.svg // radioFocused.svg and radioSelectedFocused.svg
// use opacity=".65" for the border // use opacity=".65" for the border
// --> add alpha to focused border colors // --> add alpha to focused border colors
String[] focusedBorderColorKeys = new String[] { String[] focusedBorderColorKeys = {
"CheckBox.icon.focusedBorderColor", "CheckBox.icon.focusedBorderColor",
"CheckBox.icon.focusedSelectedBorderColor", "CheckBox.icon.focusedSelectedBorderColor",
"CheckBox.icon[filled].focusedBorderColor", "CheckBox.icon[filled].focusedBorderColor",
@@ -612,6 +637,11 @@ public class IntelliJTheme
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" ); uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" ); uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
// TabbedPane
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
// TitlePane // TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" ); uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" ); uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );

View File

@@ -59,6 +59,11 @@ class SubMenuUsabilityHelper
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle"; private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle"; 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 SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter; private SafeTrianglePainter safeTrianglePainter;
private boolean changePending; private boolean changePending;
@@ -74,13 +79,22 @@ class SubMenuUsabilityHelper
private Rectangle invokerBounds; private Rectangle invokerBounds;
void install() { static synchronized boolean install() {
MenuSelectionManager.defaultManager().addChangeListener( this ); if( instance != null )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
} }
void uninstall() { static synchronized void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this ); if( instance == null )
uninstallEventQueue(); return;
MenuSelectionManager.defaultManager().removeChangeListener( instance );
instance.uninstallEventQueue();
instance = null;
} }
@Override @Override
@@ -298,7 +312,7 @@ debug*/
if( window instanceof RootPaneContainer ) { if( window instanceof RootPaneContainer ) {
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane(); JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
setSize( layeredPane.getSize() ); setSize( layeredPane.getSize() );
layeredPane.add( this, new Integer( JLayeredPane.POPUP_LAYER + 1 ) ); layeredPane.add( this, Integer.valueOf( JLayeredPane.POPUP_LAYER + 1 ) );
} }
} }

View File

@@ -84,6 +84,7 @@ class UIDefaultsLoader
private static int parseColorDepth; private static int parseColorDepth;
private static Map<String, ColorUIResource> systemColorCache;
private static final SoftCache<String, Object> fontCache = new SoftCache<>(); private static final SoftCache<String, Object> fontCache = new SoftCache<>();
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons, static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
@@ -105,6 +106,10 @@ class UIDefaultsLoader
Properties additionalDefaults, boolean dark, UIDefaults defaults ) Properties additionalDefaults, boolean dark, UIDefaults defaults )
{ {
try { try {
// temporary cache system colors while loading defaults,
// which avoids that system color getter is invoked multiple times
systemColorCache = (FlatLaf.getSystemColorGetter() != null) ? new HashMap<>() : null;
// load core properties files // load core properties files
Properties properties = new Properties(); Properties properties = new Properties();
for( Class<?> lafClass : lafClasses ) { for( Class<?> lafClass : lafClasses ) {
@@ -276,6 +281,9 @@ class UIDefaultsLoader
// remember variables in defaults to allow using them in styles // remember variables in defaults to allow using them in styles
defaults.put( KEY_VARIABLES, variables ); defaults.put( KEY_VARIABLES, variables );
// clear/disable system color cache
systemColorCache = null;
} catch( IOException ex ) { } catch( IOException ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex ); LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
} }
@@ -466,6 +474,10 @@ class UIDefaultsLoader
if( knownValueTypes == null ) { if( knownValueTypes == null ) {
// create lazy // create lazy
knownValueTypes = new HashMap<>(); knownValueTypes = new HashMap<>();
// system colors
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "windowBorder", ValueType.COLOR );
// SplitPane // SplitPane
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER ); knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER ); knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
@@ -521,14 +533,14 @@ class UIDefaultsLoader
case STRING: return value; case STRING: return value;
case BOOLEAN: return parseBoolean( value ); case BOOLEAN: return parseBoolean( value );
case CHARACTER: return parseCharacter( value ); case CHARACTER: return parseCharacter( value );
case INTEGER: return parseInteger( value, true ); case INTEGER: return parseInteger( value );
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true ); case INTEGERORFLOAT:return parseIntegerOrFloat( value );
case FLOAT: return parseFloat( value, true ); case FLOAT: return parseFloat( value );
case BORDER: return parseBorder( value, resolver, addonClassLoaders ); case BORDER: return parseBorder( value, resolver, addonClassLoaders );
case ICON: return parseInstance( value, addonClassLoaders ); case ICON: return parseInstance( value, addonClassLoaders );
case INSETS: return parseInsets( value ); case INSETS: return parseInsets( value );
case DIMENSION: return parseDimension( value ); case DIMENSION: return parseDimension( value );
case COLOR: return parseColorOrFunction( value, resolver, true ); case COLOR: return parseColorOrFunction( value, resolver );
case FONT: return parseFont( value ); case FONT: return parseFont( value );
case SCALEDINTEGER: return parseScaledInteger( value ); case SCALEDINTEGER: return parseScaledInteger( value );
case SCALEDFLOAT: return parseScaledFloat( value ); case SCALEDFLOAT: return parseScaledFloat( value );
@@ -546,24 +558,34 @@ class UIDefaultsLoader
} }
// colors // colors
Object color = parseColorOrFunction( value, resolver, false ); if( value.startsWith( "#" ) || value.endsWith( ")" ) ) {
if( color != null ) { Object color = parseColorOrFunction( value, resolver );
resultValueType[0] = ValueType.COLOR; resultValueType[0] = (color != null) ? ValueType.COLOR : ValueType.NULL;
return color; return color;
} }
// integer // integer or float
Integer integer = parseInteger( value, false ); char firstChar = value.charAt( 0 );
if( integer != null ) { if( (firstChar >= '0' && firstChar <= '9') ||
resultValueType[0] = ValueType.INTEGER; firstChar == '-' || firstChar == '+' || firstChar == '.' )
return integer; {
} // integer
try {
Integer integer = parseInteger( value );
resultValueType[0] = ValueType.INTEGER;
return integer;
} catch( NumberFormatException ex ) {
// ignore
}
// float // float
Float f = parseFloat( value, false ); try {
if( f != null ) { Float f = parseFloat( value );
resultValueType[0] = ValueType.FLOAT; resultValueType[0] = ValueType.FLOAT;
return f; return f;
} catch( NumberFormatException ex ) {
// ignore
}
} }
// string // string
@@ -592,10 +614,10 @@ class UIDefaultsLoader
List<String> parts = splitFunctionParams( value, ',' ); List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value ); Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5) ColorUIResource lineColor = (parts.size() >= 5)
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true ) ? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
: null; : null;
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ), true ) : 1f; float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ) ) : 1f;
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ), true ) : 0; int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 0;
return (LazyValue) t -> { return (LazyValue) t -> {
return (lineColor != null) return (lineColor != null)
@@ -670,30 +692,24 @@ class UIDefaultsLoader
} }
} }
private static Object parseColorOrFunction( String value, Function<String, String> resolver, boolean reportError ) { private static Object parseColorOrFunction( String value, Function<String, String> resolver ) {
if( value.endsWith( ")" ) ) if( value.endsWith( ")" ) )
return parseColorFunctions( value, resolver, reportError ); return parseColorFunctions( value, resolver );
return parseColor( value, reportError ); return parseColor( value );
} }
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as color object.
*
* @throws IllegalArgumentException
*/
static ColorUIResource parseColor( String value ) { static ColorUIResource parseColor( String value ) {
return parseColor( value, false ); int rgba = parseColorRGBA( value );
} return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
private static ColorUIResource parseColor( String value, boolean reportError ) { : new ColorUIResource( new Color( rgba, true ) );
try {
int rgba = parseColorRGBA( value );
return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
: new ColorUIResource( new Color( rgba, true ) );
} catch( IllegalArgumentException ex ) {
if( reportError )
throw new IllegalArgumentException( "invalid color '" + value + "'" );
// not a color --> ignore
}
return null;
} }
/** /**
@@ -706,7 +722,7 @@ class UIDefaultsLoader
static int parseColorRGBA( String value ) { static int parseColorRGBA( String value ) {
int len = value.length(); int len = value.length();
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' ) if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
throw new IllegalArgumentException(); throw newInvalidColorException( value );
// parse hex // parse hex
int n = 0; int n = 0;
@@ -721,7 +737,7 @@ class UIDefaultsLoader
else if( ch >= 'A' && ch <= 'F' ) else if( ch >= 'A' && ch <= 'F' )
digit = ch - 'A' + 10; digit = ch - 'A' + 10;
else else
throw new IllegalArgumentException(); throw newInvalidColorException( value );
n = (n << 4) | digit; n = (n << 4) | digit;
} }
@@ -740,13 +756,14 @@ class UIDefaultsLoader
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte : (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
} }
private static Object parseColorFunctions( String value, Function<String, String> resolver, boolean reportError ) { private static IllegalArgumentException newInvalidColorException( String value ) {
return new IllegalArgumentException( "invalid color '" + value + "'" );
}
private static Object parseColorFunctions( String value, Function<String, String> resolver ) {
int paramsStart = value.indexOf( '(' ); int paramsStart = value.indexOf( '(' );
if( paramsStart < 0 ) { if( paramsStart < 0 )
if( reportError ) throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
return null;
}
String function = StringUtils.substringTrimmed( value, 0, paramsStart ); String function = StringUtils.substringTrimmed( value, 0, paramsStart );
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' ); List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
@@ -759,27 +776,29 @@ class UIDefaultsLoader
parseColorDepth++; parseColorDepth++;
try { try {
switch( function ) { switch( function ) {
case "if": return parseColorIf( value, params, resolver, reportError ); case "if": return parseColorIf( value, params, resolver );
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError ); case "systemColor": return parseColorSystemColor( value, params, resolver );
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError ); case "rgb": return parseColorRgbOrRgba( false, params, resolver );
case "rgba": return parseColorRgbOrRgba( true, params, resolver );
case "hsl": return parseColorHslOrHsla( false, params ); case "hsl": return parseColorHslOrHsla( false, params );
case "hsla": return parseColorHslOrHsla( true, params ); case "hsla": return parseColorHslOrHsla( true, params );
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError ); case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver );
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError ); case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver );
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError ); case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver );
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError ); case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver );
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError ); case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver );
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError ); case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver );
case "fade": return parseColorFade( params, resolver, reportError ); case "fade": return parseColorFade( params, resolver );
case "spin": return parseColorSpin( params, resolver, reportError ); case "spin": return parseColorSpin( params, resolver );
case "changeHue": return parseColorChange( 0, params, resolver, reportError ); case "changeHue": return parseColorChange( 0, params, resolver );
case "changeSaturation":return parseColorChange( 1, params, resolver, reportError ); case "changeSaturation":return parseColorChange( 1, params, resolver );
case "changeLightness": return parseColorChange( 2, params, resolver, reportError ); case "changeLightness": return parseColorChange( 2, params, resolver );
case "changeAlpha": return parseColorChange( 3, params, resolver, reportError ); case "changeAlpha": return parseColorChange( 3, params, resolver );
case "mix": return parseColorMix( null, params, resolver, reportError ); case "mix": return parseColorMix( null, params, resolver );
case "tint": return parseColorMix( "#fff", params, resolver, reportError ); case "tint": return parseColorMix( "#fff", params, resolver );
case "shade": return parseColorMix( "#000", params, resolver, reportError ); case "shade": return parseColorMix( "#000", params, resolver );
case "contrast": return parseColorContrast( params, resolver, reportError ); case "contrast": return parseColorContrast( params, resolver );
case "over": return parseColorOver( params, resolver );
} }
} finally { } finally {
parseColorDepth--; parseColorDepth--;
@@ -794,13 +813,51 @@ class UIDefaultsLoader
* This "if" function is only used if the "if" is passed as parameter to another * This "if" function is only used if the "if" is passed as parameter to another
* color function. Otherwise, the general "if" function is used. * color function. Otherwise, the general "if" function is used.
*/ */
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver ) {
if( params.size() != 3 ) if( params.size() != 3 )
throwMissingParametersException( value ); throwMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() ); boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
String ifValue = params.get( ifCondition ? 1 : 2 ); String ifValue = params.get( ifCondition ? 1 : 2 );
return parseColorOrFunction( resolver.apply( ifValue ), resolver, reportError ); return parseColorOrFunction( resolver.apply( ifValue ), resolver );
}
/**
* Syntax: systemColor(name[,defaultValue])
* - name: system color name
* - defaultValue: default color value used if system color is not available
*/
private static Object parseColorSystemColor( String value, List<String> params, Function<String, String> resolver ) {
if( params.size() < 1 )
throwMissingParametersException( value );
ColorUIResource systemColor = getSystemColor( params.get( 0 ) );
if( systemColor != null )
return systemColor;
String defaultValue = (params.size() > 1) ? params.get( 1 ) : "";
if( defaultValue.equals( "null" ) || defaultValue.isEmpty() )
return null;
return parseColorOrFunction( resolver.apply( defaultValue ), resolver );
}
private static ColorUIResource getSystemColor( String name ) {
Function<String, Color> systemColorGetter = FlatLaf.getSystemColorGetter();
if( systemColorGetter == null )
return null;
// use containsKey() because value may be null
if( systemColorCache != null && systemColorCache.containsKey( name ) )
return systemColorCache.get( name );
Color color = systemColorGetter.apply( name );
ColorUIResource uiColor = (color != null) ? new ColorUIResource( color ) : null;
if( systemColorCache != null )
systemColorCache.put( name, uiColor );
return uiColor;
} }
/** /**
@@ -811,7 +868,7 @@ class UIDefaultsLoader
* - alpha: an integer 0-255 or a percentage 0-100% * - alpha: an integer 0-255 or a percentage 0-100%
*/ */
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params, private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
Function<String, String> resolver, boolean reportError ) Function<String, String> resolver )
{ {
if( hasAlpha && params.size() == 2 ) { if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color // syntax rgba(color,alpha), which allows adding alpha to any color
@@ -820,7 +877,7 @@ class UIDefaultsLoader
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int alpha = parseInteger( params.get( 1 ), 0, 255, true ); int alpha = parseInteger( params.get( 1 ), 0, 255, true );
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError ); ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver );
return new ColorUIResource( new Color( ((alpha & 0xff) << 24) | (color.getRGB() & 0xffffff), true ) ); return new ColorUIResource( new Color( ((alpha & 0xff) << 24) | (color.getRGB() & 0xffffff), true ) );
} }
@@ -847,7 +904,7 @@ class UIDefaultsLoader
int lightness = parsePercentage( params.get( 2 ) ); int lightness = parsePercentage( params.get( 2 ) );
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100; 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 ) ); return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
} }
@@ -860,7 +917,7 @@ class UIDefaultsLoader
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived] * - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
*/ */
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase, private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
List<String> params, Function<String, String> resolver, boolean reportError ) List<String> params, Function<String, String> resolver )
{ {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) ); int amount = parsePercentage( params.get( 1 ) );
@@ -895,7 +952,7 @@ class UIDefaultsLoader
} }
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
/** /**
@@ -904,7 +961,7 @@ class UIDefaultsLoader
* - amount: percentage 0-100% * - amount: percentage 0-100%
* - options: [derived] [lazy] * - options: [derived] [lazy]
*/ */
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorFade( List<String> params, Function<String, String> resolver ) {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) ); int amount = parsePercentage( params.get( 1 ) );
boolean derived = false; boolean derived = false;
@@ -929,7 +986,7 @@ class UIDefaultsLoader
} }
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
/** /**
@@ -938,9 +995,9 @@ class UIDefaultsLoader
* - angle: number of degrees to rotate * - angle: number of degrees to rotate
* - options: [derived] * - options: [derived]
*/ */
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorSpin( List<String> params, Function<String, String> resolver ) {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ), true ); int amount = parseInteger( params.get( 1 ) );
boolean derived = false; boolean derived = false;
if( params.size() > 2 ) { if( params.size() > 2 ) {
@@ -952,7 +1009,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false ); ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
/** /**
@@ -965,11 +1022,11 @@ class UIDefaultsLoader
* - options: [derived] * - options: [derived]
*/ */
private static Object parseColorChange( int hslIndex, private static Object parseColorChange( int hslIndex,
List<String> params, Function<String, String> resolver, boolean reportError ) List<String> params, Function<String, String> resolver )
{ {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int value = (hslIndex == 0) int value = (hslIndex == 0)
? parseInteger( params.get( 1 ), true ) ? parseInteger( params.get( 1 ) )
: parsePercentage( params.get( 1 ) ); : parsePercentage( params.get( 1 ) );
boolean derived = false; boolean derived = false;
@@ -982,7 +1039,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value ); ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value );
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); return parseFunctionBaseColor( colorStr, function, derived, resolver );
} }
/** /**
@@ -994,7 +1051,7 @@ class UIDefaultsLoader
* - weight: the weight (in range 0-100%) to mix the two colors * - weight: the weight (in range 0-100%) to mix the two colors
* larger weight uses more of first color, smaller weight more of second color * larger weight uses more of first color, smaller weight more of second color
*/ */
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver ) {
int i = 0; int i = 0;
if( color1Str == null ) if( color1Str == null )
color1Str = params.get( i++ ); color1Str = params.get( i++ );
@@ -1002,7 +1059,7 @@ class UIDefaultsLoader
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50; int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
// parse second color // parse second color
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver, reportError ); ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver );
if( color2 == null ) if( color2 == null )
return null; return null;
@@ -1010,7 +1067,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.Mix( color2, weight ); ColorFunction function = new ColorFunctions.Mix( color2, weight );
// parse first color, apply function and create mixed color // parse first color, apply function and create mixed color
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError ); return parseFunctionBaseColor( color1Str, function, false, resolver );
} }
/** /**
@@ -1021,14 +1078,14 @@ class UIDefaultsLoader
* - threshold: the threshold (in range 0-100%) to specify where the transition * - threshold: the threshold (in range 0-100%) to specify where the transition
* from "dark" to "light" is (default is 43%) * from "dark" to "light" is (default is 43%)
*/ */
private static Object parseColorContrast( List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorContrast( List<String> params, Function<String, String> resolver ) {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
String darkStr = params.get( 1 ); String darkStr = params.get( 1 );
String lightStr = params.get( 2 ); String lightStr = params.get( 2 );
int threshold = (params.size() > 3) ? parsePercentage( params.get( 3 ) ) : 43; int threshold = (params.size() > 3) ? parsePercentage( params.get( 3 ) ) : 43;
// parse color to compare against // parse color to compare against
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError ); ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver );
if( color == null ) if( color == null )
return null; return null;
@@ -1038,15 +1095,43 @@ class UIDefaultsLoader
: darkStr; : darkStr;
// parse dark or light color // parse dark or light color
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError ); return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver );
}
/**
* 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 ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver ) {
String foregroundStr = params.get( 0 );
String backgroundStr = params.get( 1 );
// parse foreground color
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver );
if( foreground == null || foreground.getAlpha() == 255 )
return foreground;
// foreground color without alpha
ColorUIResource foreground2 = new ColorUIResource( foreground.getRGB() );
// parse background color
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver );
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, private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError ) boolean derived, Function<String, String> resolver )
{ {
// parse base color // parse base color
String resolvedColorStr = resolver.apply( colorStr ); String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError ); ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver );
if( baseColor == null ) if( baseColor == null )
return null; return null;
@@ -1130,11 +1215,11 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "size specified more than once in '" + value + "'" ); throw new IllegalArgumentException( "size specified more than once in '" + value + "'" );
if( firstChar == '+' || firstChar == '-' ) if( firstChar == '+' || firstChar == '-' )
relativeSize = parseInteger( param, true ); relativeSize = parseInteger( param );
else if( param.endsWith( "%" ) ) else if( param.endsWith( "%" ) )
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f; scaleSize = parseInteger( param.substring( 0, param.length() - 1 ) ) / 100f;
else else
absoluteSize = parseInteger( param, true ); absoluteSize = parseInteger( param );
} else if( firstChar == '$' ) { } else if( firstChar == '$' ) {
// reference to base font // reference to base font
if( baseFontKey != null ) if( baseFontKey != null )
@@ -1208,55 +1293,49 @@ class UIDefaultsLoader
return (max * percent) / 100; return (max * percent) / 100;
} }
Integer integer = parseInteger( value, true ); Integer integer = parseInteger( value );
if( integer < min || integer > max ) if( integer < min || integer > max )
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' ); throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
return integer; return integer;
} }
private static Integer parseInteger( String value, boolean reportError ) { private static Integer parseInteger( String value ) {
try { try {
return Integer.parseInt( value ); return Integer.parseInt( value );
} catch( NumberFormatException ex ) { } catch( NumberFormatException ex ) {
if( reportError ) throw new NumberFormatException( "invalid integer '" + value + "'" );
throw new NumberFormatException( "invalid integer '" + value + "'" );
} }
return null;
} }
private static Number parseIntegerOrFloat( String value, boolean reportError ) { private static Number parseIntegerOrFloat( String value ) {
try { try {
return Integer.parseInt( value ); return Integer.parseInt( value );
} catch( NumberFormatException ex ) { } catch( NumberFormatException ex ) {
try { try {
return Float.parseFloat( value ); return Float.parseFloat( value );
} catch( NumberFormatException ex2 ) { } catch( NumberFormatException ex2 ) {
if( reportError ) throw new NumberFormatException( "invalid integer or float '" + value + "'" );
throw new NumberFormatException( "invalid integer or float '" + value + "'" );
} }
} }
return null;
} }
private static Float parseFloat( String value, boolean reportError ) { private static Float parseFloat( String value ) {
try { try {
return Float.parseFloat( value ); return Float.parseFloat( value );
} catch( NumberFormatException ex ) { } catch( NumberFormatException ex ) {
if( reportError ) throw new NumberFormatException( "invalid float '" + value + "'" );
throw new NumberFormatException( "invalid float '" + value + "'" );
} }
return null;
} }
private static ActiveValue parseScaledInteger( String value ) { private static ActiveValue parseScaledInteger( String value ) {
int val = parseInteger( value, true ); int val = parseInteger( value );
return t -> { return t -> {
return UIScale.scale( val ); return UIScale.scale( val );
}; };
} }
private static ActiveValue parseScaledFloat( String value ) { private static ActiveValue parseScaledFloat( String value ) {
float val = parseFloat( value, true ); float val = parseFloat( value );
return t -> { return t -> {
return UIScale.scale( val ); return UIScale.scale( val );
}; };
@@ -1311,7 +1390,11 @@ class UIDefaultsLoader
start = i + 1; start = i + 1;
} }
} }
strs.add( StringUtils.substringTrimmed( str, start ) );
// last param
String s = StringUtils.substringTrimmed( str, start );
if( !s.isEmpty() || !strs.isEmpty() )
strs.add( s );
return strs; return strs;
} }

View File

@@ -16,9 +16,12 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
@@ -36,6 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatCapsLockIcon public class FlatCapsLockIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private Path2D path;
public FlatCapsLockIcon() { public FlatCapsLockIcon() {
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) ); super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
} }
@@ -49,22 +54,36 @@ public class FlatCapsLockIcon
} }
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
switch( key ) {
case "capsLockIconColor": return color;
default: return null;
}
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<rect width="16" height="16" fill="#6E6E6E" rx="3"/> <rect width="16" height="16" fill="#6E6E6E" rx="3"/>
<rect width="6" height="2" x="5" y="11.5" fill="#FFF"/> <rect width="5" height="2" x="5.5" y="11.5" stroke="#FFF" stroke-linejoin="round"/>
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/> <path stroke="#FFF" stroke-linejoin="round" d="M2.5,7.5 L8,2 L13.5,7.5 L10.5,7.5 L10.5,9.5 L5.5,9.5 L5.5,7.5 L2.5,7.5 Z"/>
</g> </g>
</svg> </svg>
*/ */
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false ); BasicStroke stroke = new BasicStroke( 1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND );
path.append( new Rectangle2D.Float( 5, 11.5f, 6, 2 ), false );
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false ); if( path == null ) {
path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
path.append( new Area( stroke.createStrokedShape( new Rectangle2D.Float( 5.5f, 11.5f, 5, 2 ) ) ), false );
path.append( new Area( stroke.createStrokedShape( FlatUIUtils.createPath(
2.5,7.5, 8,2, 13.5,7.5, 10.5,7.5, 10.5,9.5, 5.5,9.5, 5.5,7.5, 2.5,7.5 ) ) ), false );
}
g.fill( path ); g.fill( path );
} }
} }

View File

@@ -172,6 +172,11 @@ public class FlatCheckBoxIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
boolean indeterminate = isIndeterminate( c ); boolean indeterminate = isIndeterminate( c );
@@ -237,7 +242,7 @@ public class FlatCheckBoxIcon
} }
protected void paintCheckmark( Component c, Graphics2D g ) { protected void paintCheckmark( Component c, Graphics2D g ) {
Path2D.Float path = new Path2D.Float(); Path2D.Float path = new Path2D.Float( Path2D.WIND_NON_ZERO, 3 );
path.moveTo( 4.5f, 7.5f ); path.moveTo( 4.5f, 7.5f );
path.lineTo( 6.6f, 10f ); path.lineTo( 6.6f, 10f );
path.lineTo( 11.25f, 3.5f ); path.lineTo( 11.25f, 3.5f );

View File

@@ -59,6 +59,11 @@ public class FlatCheckBoxMenuItemIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g2 ) { protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected(); boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
@@ -71,7 +76,7 @@ public class FlatCheckBoxMenuItemIcon
} }
protected void paintCheckmark( Graphics2D g2 ) { protected void paintCheckmark( Graphics2D g2 ) {
Path2D.Float path = new Path2D.Float(); Path2D.Float path = new Path2D.Float( Path2D.WIND_NON_ZERO, 3 );
path.moveTo( 4.5f, 7.5f ); path.moveTo( 4.5f, 7.5f );
path.lineTo( 6.6f, 10f ); path.lineTo( 6.6f, 10f );
path.lineTo( 11.25f, 3.5f ); path.lineTo( 11.25f, 3.5f );

View File

@@ -20,7 +20,6 @@ import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.util.Map; import java.util.Map;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
@@ -69,6 +68,11 @@ public class FlatClearIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
if( !ignoreButtonState && c instanceof AbstractButton ) { if( !ignoreButtonState && c instanceof AbstractButton ) {
@@ -98,9 +102,11 @@ public class FlatClearIcon
// paint cross // paint cross
g.setColor( clearIconColor ); g.setColor( clearIconColor );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.append( new Line2D.Float( 5,5, 11,11 ), false ); path.moveTo( 5, 5 );
path.append( new Line2D.Float( 5,11, 11,5 ), false ); path.lineTo( 11, 11 );
path.moveTo( 5, 11 );
path.lineTo( 11, 5 );
g.draw( path ); g.draw( path );
} }
} }

View File

@@ -39,21 +39,25 @@ public class FlatFileChooserDetailsViewIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<rect width="2" height="2" x="2" y="3" fill="#6E6E6E"/> <rect width="2" height="1" x="2" y="3" fill="#6E6E6E" rx=".5"/>
<rect width="2" height="2" x="2" y="7" fill="#6E6E6E"/> <rect width="2" height="1" x="2" y="6" fill="#6E6E6E" rx=".5"/>
<rect width="2" height="2" x="2" y="11" fill="#6E6E6E"/> <rect width="2" height="1" x="2" y="9" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="2" x="6" y="3" fill="#6E6E6E"/> <rect width="2" height="1" x="2" y="12" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="2" x="6" y="7" fill="#6E6E6E"/> <rect width="8" height="1" x="6" y="3" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="2" x="6" y="11" fill="#6E6E6E"/> <rect width="8" height="1" x="6" y="6" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="1" x="6" y="9" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="1" x="6" y="12" fill="#6E6E6E" rx=".5"/>
</g> </g>
</svg> </svg>
*/ */
g.fillRect( 2, 3, 2, 2 ); g.fillRoundRect( 2, 3, 2, 1, 1, 1 );
g.fillRect( 2, 7, 2, 2 ); g.fillRoundRect( 2, 6, 2, 1, 1, 1 );
g.fillRect( 2, 11, 2, 2 ); g.fillRoundRect( 2, 9, 2, 1, 1, 1 );
g.fillRect( 6, 3, 8, 2 ); g.fillRoundRect( 2, 12, 2, 1, 1, 1 );
g.fillRect( 6, 7, 8, 2 ); g.fillRoundRect( 6, 3, 8, 1, 1, 1 );
g.fillRect( 6, 11, 8, 2 ); g.fillRoundRect( 6, 6, 8, 1, 1, 1 );
g.fillRoundRect( 6, 9, 8, 1, 1, 1 );
g.fillRoundRect( 6, 12, 8, 1, 1, 1 );
} }
} }

View File

@@ -16,8 +16,10 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -39,10 +41,22 @@ public class FlatFileChooserHomeFolderIcon
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="2 8 8 2 14 8 12 8 12 13 9 13 9 10 7 10 7 13 4 13 4 8"/> <g fill="none" fill-rule="evenodd">
<polyline stroke="#6E6E6E" stroke-linejoin="round" points="6.5 13 6.5 9.5 9.5 9.5 9.5 13"/>
<path stroke="#6E6E6E" d="M3.5,6.5 L3.5,12.5 C3.5,13.0522847 3.94771525,13.5 4.5,13.5 L11.5,13.5 C12.0522847,13.5 12.5,13.0522847 12.5,12.5 L12.5,6.5 L12.5,6.5"/>
<polyline stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round" points="1.5 8.5 8 2 14.5 8.5"/>
</g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 12,8, 12,13, 9,13, 9,10, 7,10, 7,13, 4,13, 4,8 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.draw( FlatUIUtils.createPath( false, 6.5,13, 6.5,9.5, 9.5,9.5, 9.5,13 ) );
g.draw( FlatUIUtils.createPath( false, 3.5,6.5,
3.5,12.5, FlatUIUtils.QUAD_TO, 3.5,13.5, 4.5,13.5,
11.5,13.5, FlatUIUtils.QUAD_TO, 12.5,13.5, 12.5,12.5,
12.5,6.5 ) );
g.draw( FlatUIUtils.createPath( false, 1.5,8.5, 8,2, 14.5,8.5 ) );
} }
} }

View File

@@ -16,8 +16,11 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager; import javax.swing.UIManager;
/** /**
@@ -39,17 +42,20 @@ public class FlatFileChooserListViewIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<rect width="4" height="4" x="3" y="3" fill="#6E6E6E"/> <rect width="4" height="4" x="2.5" y="2.5" stroke="#6E6E6E" rx="1.5"/>
<rect width="4" height="4" x="3" y="9" fill="#6E6E6E"/> <rect width="4" height="4" x="2.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
<rect width="4" height="4" x="9" y="9" fill="#6E6E6E"/> <rect width="4" height="4" x="9.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
<rect width="4" height="4" x="9" y="3" fill="#6E6E6E"/> <rect width="4" height="4" x="9.5" y="2.5" stroke="#6E6E6E" rx="1.5"/>
</g> </g>
</svg> </svg>
*/ */
g.fillRect( 3, 3, 4, 4 ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.fillRect( 3, 9, 4, 4 ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.fillRect( 9, 9, 4, 4 );
g.fillRect( 9, 3, 4, 4 ); g.draw( new RoundRectangle2D.Float( 2.5f, 2.5f, 4, 4, 2, 2 ) );
g.draw( new RoundRectangle2D.Float( 2.5f, 9.5f, 4, 4, 2, 2 ) );
g.draw( new RoundRectangle2D.Float( 9.5f, 9.5f, 4, 4, 2, 2 ) );
g.draw( new RoundRectangle2D.Float( 9.5f, 2.5f, 4, 4, 2, 2 ) );
} }
} }

View File

@@ -16,10 +16,13 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "new folder" icon for {@link javax.swing.JFileChooser}. * "new folder" icon for {@link javax.swing.JFileChooser}.
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileChooserNewFolderIcon public class FlatFileChooserNewFolderIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private final Color greenColor = UIManager.getColor( "Actions.Green" );
public FlatFileChooserNewFolderIcon() { public FlatFileChooserNewFolderIcon() {
super( 16, 16, UIManager.getColor( "Actions.Grey" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@@ -40,13 +45,20 @@ public class FlatFileChooserNewFolderIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<polygon fill="#6E6E6E" points="2 3 5.5 3 7 5 14 5 14 8 11 8 11 10 9 10 9 13 2 13"/> <path stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
<path fill="#59A869" d="M14,11 L16,11 L16,13 L14,13 L14,15 L12,15 L12,13 L10,13 L10,11 L12,11 L12,9 L14,9 L14,11 Z"/> <line x1="5.5" x2="10.5" y1="9" y2="9" stroke="#59A869" stroke-linecap="round"/>
<line x1="8" x2="8" y1="6.5" y2="11.5" stroke="#59A869" stroke-linecap="round"/>
</g> </g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 14,5, 14,8, 11,8, 11,10, 9,10, 9,13, 2,13 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.fill( FlatUIUtils.createPath( 14,11, 16,11, 16,13, 14,13, 14,15, 12,15, 12,13, 10,13, 10,11, 12,11, 12,9, 14,9, 14,11 ) ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.draw( FlatFileViewDirectoryIcon.createFolderPath() );
g.setColor( greenColor );
g.draw( new Line2D.Float( 5.5f, 9, 10.5f, 9 ) );
g.draw( new Line2D.Float( 8, 6.5f, 8, 11.5f ) );
} }
} }

View File

@@ -16,9 +16,12 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -44,15 +47,20 @@ public class FlatFileChooserUpFolderIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<polygon fill="#6E6E6E" points="2 3 5.5 3 7 5 9 5 9 9 13 9 13 5 14 5 14 13 2 13"/> <path stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
<path fill="#389FD6" d="M12,4 L12,8 L10,8 L10,4 L8,4 L11,1 L14,4 L12,4 Z"/> <line x1="8" x2="8" y1="6.5" y2="11.5" stroke="#389FD6" stroke-linecap="round"/>
<polyline stroke="#389FD6" stroke-linecap="round" stroke-linejoin="round" points="5.5 9 8 6.5 10.5 9"/>
</g> </g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 9,5, 9,9, 13,9, 13,5, 14,5, 14,13, 2,13 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.draw( FlatFileViewDirectoryIcon.createFolderPath() );
g.setColor( blueColor ); g.setColor( blueColor );
g.fill( FlatUIUtils.createPath( 12,4, 12,8, 10,8, 10,4, 8,4, 11,1, 14,4, 12,4 ) ); g.draw( new Line2D.Float( 8, 6.5f, 8, 11.5f ) );
g.draw( FlatUIUtils.createPath( false, 5.5,9, 8,6.5, 10.5,9 ) );
} }
} }

View File

@@ -16,10 +16,12 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D; import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D; import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager; import javax.swing.UIManager;
/** /**
@@ -41,17 +43,18 @@ public class FlatFileViewComputerIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<path fill="#6E6E6E" d="M2,3 L14,3 L14,11 L2,11 L2,3 Z M4,5 L4,9 L12,9 L12,5 L4,5 Z"/> <rect width="11" height="7" x="2.5" y="3.5" stroke="#6E6E6E" rx="1"/>
<rect width="12" height="2" x="2" y="12" fill="#6E6E6E"/> <line x1="8" x2="8" y1="11" y2="12" stroke="#6E6E6E"/>
<line x1="4.5" x2="11.5" y1="12.5" y2="12.5" stroke="#6E6E6E" stroke-linecap="round"/>
</g> </g>
</svg> </svg>
*/ */
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
path.append( new Rectangle2D.Float( 2, 3, 12, 8 ), false ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
path.append( new Rectangle2D.Float( 4, 5, 8, 4 ), false );
g.fill( path );
g.fillRect( 2, 12, 12, 2 ); g.draw( new RoundRectangle2D.Float( 2.5f, 3.5f, 11, 7, 2, 2 ) );
g.drawLine( 8, 11, 8, 12 );
g.draw( new Line2D.Float( 4.5f, 12.5f, 11.5f, 12.5f ) );
} }
} }

View File

@@ -18,6 +18,8 @@ package com.formdev.flatlaf.icons;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +33,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileViewDirectoryIcon public class FlatFileViewDirectoryIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private Path2D path;
public FlatFileViewDirectoryIcon() { public FlatFileViewDirectoryIcon() {
super( 16, 16, UIManager.getColor( "Objects.Grey" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@@ -39,10 +43,32 @@ public class FlatFileViewDirectoryIcon
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/> <path fill="none" stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 15,4, 15,13, 1,13 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
if( path == null )
path = createFolderPath();
g.draw( path );
}
static Path2D createFolderPath() {
double arc = 1.5;
double arc2 = 0.5;
return FlatUIUtils.createPath(
// bottom-right
14.5,13.5-arc, FlatUIUtils.QUAD_TO, 14.5,13.5, 14.5-arc,13.5,
// bottom-left
1.5+arc,13.5, FlatUIUtils.QUAD_TO, 1.5,13.5, 1.5,13.5-arc,
// top-left
1.5,2.5+arc, FlatUIUtils.QUAD_TO, 1.5,2.5, 1.5+arc,2.5,
// top-mid-left
6.5-arc2,2.5, FlatUIUtils.QUAD_TO, 6.5,2.5, 6.5+arc2,2.5+arc2,
// top-mid-right
8.5,4.5,
// top-right
14.5-arc,4.5, FlatUIUtils.QUAD_TO, 14.5,4.5, 14.5,4.5+arc );
} }
} }

View File

@@ -16,8 +16,11 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileViewFileIcon public class FlatFileViewFileIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private Path2D path;
public FlatFileViewFileIcon() { public FlatFileViewFileIcon() {
super( 16, 16, UIManager.getColor( "Objects.Grey" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@@ -39,14 +44,33 @@ public class FlatFileViewFileIcon
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/> <path stroke="#6E6E6E" d="M4,1.5 L8.8,1.5 L8.8,1.5 L13.5,6.2 L13.5,13 C13.5,13.8284271 12.8284271,14.5 12,14.5 L4,14.5 C3.17157288,14.5 2.5,13.8284271 2.5,13 L2.5,3 C2.5,2.17157288 3.17157288,1.5 4,1.5 Z"/>
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/> <path stroke="#6E6E6E" d="M8.5,2 L8.5,5 C8.5,5.82842712 9.17157288,6.5 10,6.5 L13,6.5 L13,6.5"/>
</g> </g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
if( path == null ) {
double arc = 1.5;
path = FlatUIUtils.createPath( false,
// top-left
2.5,1.5+arc, FlatUIUtils.QUAD_TO, 2.5,1.5, 2.5+arc,1.5,
// top-right
8.8,1.5, 13.5,6.2,
// bottom-right
13.5,14.5-arc, FlatUIUtils.QUAD_TO, 13.5,14.5, 13.5-arc,14.5,
// bottom-left
2.5+arc,14.5, FlatUIUtils.QUAD_TO, 2.5,14.5, 2.5,14.5-arc,
FlatUIUtils.CLOSE_PATH,
FlatUIUtils.MOVE_TO, 8.5,2,
8.5,6.5-arc, FlatUIUtils.QUAD_TO, 8.5,6.5, 8.5+arc,6.5,
13,6.5 );
}
g.draw( path );
} }
} }

View File

@@ -16,9 +16,10 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D; import java.awt.RenderingHints;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -40,18 +41,22 @@ public class FlatFileViewFloppyDriveIcon
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<path fill="#6E6E6E" d="M11,14 L11,11 L5,11 L5,14 L2,14 L2,2 L14,2 L14,14 L11,14 Z M4,4 L4,8 L12,8 L12,4 L4,4 Z"/> <path stroke="#6E6E6E" d="M3.5,2.5 L11.5,2.5 L11.5,2.5 L13.5,4.5 L13.5,12.5 C13.5,13.0522847 13.0522847,13.5 12.5,13.5 L3.5,13.5 C2.94771525,13.5 2.5,13.0522847 2.5,12.5 L2.5,3.5 C2.5,2.94771525 2.94771525,2.5 3.5,2.5 Z"/>
<rect width="4" height="2" x="6" y="12" fill="#6E6E6E"/> <polyline stroke="#6E6E6E" points="4.5 13 4.5 9.5 11.5 9.5 11.5 13"/>
<polyline stroke="#6E6E6E" points="5.5 3 5.5 5.5 10.5 5.5 10.5 3"/>
</g> </g>
</svg> </svg>
*/ */
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
path.append( FlatUIUtils.createPath( 11,14, 11,11, 5,11, 5,14, 2,14, 2,2, 14,2, 14,14, 11,14 ), false ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
path.append( FlatUIUtils.createPath( 4,4, 4,8, 12,8, 12,4, 4,4 ), false );
g.fill( path );
g.fillRect( 6, 12, 4, 2 ); g.draw( FlatUIUtils.createPath( 3.5,2.5, 11.5,2.5, 11.5,2.5, 13.5,4.5,
13.5,12.5, FlatUIUtils.QUAD_TO, 13.5,13.5, 12.5,13.5,
3.5,13.5, FlatUIUtils.QUAD_TO, 2.5,13.5, 2.5,12.5,
2.5,3.5, FlatUIUtils.QUAD_TO, 2.5,2.5, 3.5,2.5 ) );
g.draw( FlatUIUtils.createPath( false, 4.5,13, 4.5,9.5, 11.5,9.5, 11.5,13 ) );
g.draw( FlatUIUtils.createPath( false, 5.5,3, 5.5,5.5, 10.5,5.5, 10.5,3 ) );
} }
} }

View File

@@ -16,10 +16,12 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D; import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager; import javax.swing.UIManager;
/** /**
@@ -40,14 +42,19 @@ public class FlatFileViewHardDriveIcon
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#6E6E6E" fill-rule="evenodd" d="M2,6 L14,6 L14,10 L2,10 L2,6 Z M12,8 L12,9 L13,9 L13,8 L12,8 Z M10,8 L10,9 L11,9 L11,8 L10,8 Z"/> <g fill="none" fill-rule="evenodd">
<rect width="11" height="5" x="2.5" y="5.5" stroke="#6E6E6E" rx="1"/>
<circle cx="11.5" cy="8.5" r="1" fill="#6E6E6E"/>
<circle cx="9.5" cy="8.5" r="1" fill="#6E6E6E"/>
</g>
</svg> </svg>
*/ */
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
path.append( new Rectangle2D.Float( 2, 6, 12, 4 ), false ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
path.append( new Rectangle2D.Float( 12, 8, 1, 1 ), false );
path.append( new Rectangle2D.Float( 10, 8, 1, 1 ), false ); g.draw( new RoundRectangle2D.Float( 2.5f, 5.5f, 11, 5, 2, 2 ) );
g.fill( path ); g.fill( new Ellipse2D.Float( 10.8f, 7.8f, 1.4f, 1.4f ) );
g.fill( new Ellipse2D.Float( 8.8f, 7.8f, 1.4f, 1.4f ) );
} }
} }

View File

@@ -17,9 +17,11 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import static com.formdev.flatlaf.util.UIScale.*; import static com.formdev.flatlaf.util.UIScale.*;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.util.Map; import java.util.Map;
@@ -84,6 +86,11 @@ public class FlatHelpButtonIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g2 ) { protected void paintIcon( Component c, Graphics2D g2 ) {
/* /*
@@ -91,7 +98,8 @@ public class FlatHelpButtonIcon
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10.5" fill="#6E6E6E"/> <circle cx="11" cy="11" r="10.5" fill="#6E6E6E"/>
<circle cx="11" cy="11" r="9.5" fill="#FFF"/> <circle cx="11" cy="11" r="9.5" fill="#FFF"/>
<path fill="#6E6E6E" d="M10,17 L12,17 L12,15 L10,15 L10,17 Z M11,5 C8.8,5 7,6.8 7,9 L9,9 C9,7.9 9.9,7 11,7 C12.1,7 13,7.9 13,9 C13,11 10,10.75 10,14 L12,14 C12,11.75 15,11.5 15,9 C15,6.8 13.2,5 11,5 Z"/> <path stroke="#6E6E6E" stroke-linecap="round" stroke-width="2" d="M8,8.5 C8.25,7 9.66585007,6 11,6 C12.5,6 14,7 14,8.5 C14,10.5 11,11 11,13"/>
<circle cx="11" cy="16" r="1.2" fill="#6E6E6E"/>
</g> </g>
</svg> </svg>
*/ */
@@ -142,22 +150,19 @@ public class FlatHelpButtonIcon
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) ); g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
// paint question mark // paint question mark
Path2D q = new Path2D.Float(); Path2D q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
q.moveTo( 11, 5 ); q.moveTo( 8,8.5 );
q.curveTo( 8.8,5, 7,6.8, 7,9 ); q.curveTo( 8.25,7, 9.66585007,6, 11,6 );
q.lineTo( 9, 9 ); q.curveTo( 12.5,6, 14,7, 14,8.5 );
q.curveTo( 9,7.9, 9.9,7, 11,7 ); q.curveTo( 14,10.5, 11,11, 11,13 );
q.curveTo( 12.1,7, 13,7.9, 13,9 );
q.curveTo( 13,11, 10,10.75, 10,14 ); g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
q.lineTo( 12, 14 ); g2.setStroke( new BasicStroke( 2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
q.curveTo( 12,11.75, 15,11.5, 15,9 );
q.curveTo( 15,6.8, 13.2,5, 11,5 );
q.closePath();
g2.translate( focusWidth, focusWidth ); g2.translate( focusWidth, focusWidth );
g2.setColor( enabled ? questionMarkColor : disabledQuestionMarkColor ); g2.setColor( enabled ? questionMarkColor : disabledQuestionMarkColor );
g2.fill( q ); g2.draw( q );
g2.fillRect( 10, 15, 2, 2 ); g2.fill( new Ellipse2D.Float( 9.8f, 14.8f, 2.4f, 2.4f ) );
} }
@Override @Override

View File

@@ -20,7 +20,6 @@ import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
@@ -58,9 +57,11 @@ public class FlatInternalFrameCloseIcon
float my = height / 2; float my = height / 2;
float r = 3.25f; float r = 3.25f;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false ); path.moveTo( mx - r, my - r );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false ); path.lineTo( mx + r, my + r );
path.moveTo( mx - r, my + r );
path.lineTo( mx + r, my - r );
g.setStroke( new BasicStroke( 1f ) ); g.setStroke( new BasicStroke( 1f ) );
g.draw( path ); g.draw( path );
} }

View File

@@ -61,6 +61,11 @@ public class FlatMenuArrowIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
if( c != null && !c.getComponentOrientation().isLeftToRight() ) if( c != null && !c.getComponentOrientation().isLeftToRight() )

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D;
/** /**
* "Error" icon for {@link javax.swing.JOptionPane}. * "Error" icon for {@link javax.swing.JOptionPane}.
@@ -40,8 +40,8 @@ public class FlatOptionPaneErrorIcon
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#DB5860"/> <circle cx="16" cy="16" r="14" fill="#DB5860"/>
<rect width="4" height="11" x="14" y="7" fill="#FFF"/> <rect width="4" height="12" x="14" y="7" fill="#FFF" rx="2"/>
<rect width="4" height="4" x="14" y="21" fill="#FFF"/> <circle cx="16" cy="23" r="2" fill="#FFF"/>
</g> </g>
</svg> </svg>
*/ */
@@ -54,8 +54,8 @@ public class FlatOptionPaneErrorIcon
@Override @Override
protected Shape createInside() { protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 7, 4, 11 ), false ); inside.append( new RoundRectangle2D.Float( 14, 7, 4, 12, 4, 4 ), false );
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false ); inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
return inside; return inside;
} }
} }

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D;
/** /**
* "Information" icon for {@link javax.swing.JOptionPane}. * "Information" icon for {@link javax.swing.JOptionPane}.
@@ -40,8 +40,8 @@ public class FlatOptionPaneInformationIcon
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#389FD6"/> <circle cx="16" cy="16" r="14" fill="#389FD6"/>
<rect width="4" height="11" x="14" y="14" fill="#FFF"/> <rect width="4" height="12" x="14" y="13" fill="#FFF" rx="2"/>
<rect width="4" height="4" x="14" y="7" fill="#FFF"/> <circle cx="16" cy="9" r="2" fill="#FFF"/>
</g> </g>
</svg> </svg>
*/ */
@@ -54,8 +54,8 @@ public class FlatOptionPaneInformationIcon
@Override @Override
protected Shape createInside() { protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 14, 4, 11 ), false ); inside.append( new RoundRectangle2D.Float( 14, 13, 4, 12, 4, 4 ), false );
inside.append( new Rectangle2D.Float( 14, 7, 4, 4 ), false ); inside.append( new Ellipse2D.Float( 14, 7, 4, 4 ), false );
return inside; return inside;
} }
} }

View File

@@ -16,10 +16,10 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
/** /**
* "Question" icon for {@link javax.swing.JOptionPane}. * "Question" icon for {@link javax.swing.JOptionPane}.
@@ -40,8 +40,8 @@ public class FlatOptionPaneQuestionIcon
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#389FD6"/> <circle cx="16" cy="16" r="14" fill="#389FD6"/>
<rect width="4" height="4" x="14" y="22" fill="#FFF"/> <circle cx="16" cy="24" r="1.7" fill="#FFF"/>
<path fill="#FFF" d="M14,20 C14,20 18,20 18,20 C18,16 23,16 23,12 C23,8 20,6 16,6 C12,6 9,8 9,12 C9,12 13,12 13,12 C13,10 14,9 16,9 C18,9 19,10 19,12 C19,15 14,15 14,20 Z"/> <path stroke="#FFF" stroke-linecap="round" stroke-width="3" d="M11.5,11.75 C11.75,9.5 13.75,8 16,8 C18.25,8 20.5,9.5 20.5,11.75 C20.5,14.75 16,15.5 16,19"/>
</g> </g>
</svg> </svg>
*/ */
@@ -53,21 +53,17 @@ public class FlatOptionPaneQuestionIcon
@Override @Override
protected Shape createInside() { protected Shape createInside() {
Path2D q = new Path2D.Float(); Path2D q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
q.moveTo( 14, 20 ); q.moveTo( 11.5,11.75 );
q.lineTo( 18, 20 ); q.curveTo( 11.75,9.5, 13.75,8, 16,8 );
q.curveTo( 18, 16, 23, 16, 23, 12 ); q.curveTo( 18.25,8, 20.5,9.5, 20.5,11.75 );
q.curveTo( 23, 8, 20, 6, 16, 6 ); q.curveTo( 20.5,14.75, 16,15.5, 16,19 );
q.curveTo( 12, 6, 9, 8, 9, 12 );
q.curveTo( 9, 12, 13, 12, 13, 12 ); BasicStroke stroke = new BasicStroke( 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER );
q.curveTo( 13, 10, 14, 9, 16, 9 );
q.curveTo( 18, 9, 19, 10, 19, 12 );
q.curveTo( 19, 15, 14, 15, 14, 20 );
q.closePath();
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 22, 4, 4 ), false ); inside.append( new Ellipse2D.Float( 14.3f, 22.3f, 3.4f, 3.4f ), false );
inside.append( q, false ); inside.append( stroke.createStrokedShape( q ), false );
return inside; return inside;
} }
} }

View File

@@ -17,8 +17,9 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
@@ -39,23 +40,24 @@ public class FlatOptionPaneWarningIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<polygon fill="#EDA200" points="16 2 31 28 1 28"/> <path fill="#EDA200" d="M17.7364863,3.038851 L30.2901269,25.0077221 C30.8381469,25.966757 30.5049534,27.1884663 29.5459185,27.7364863 C29.2437231,27.9091694 28.9016945,28 28.5536406,28 L3.44635936,28 C2.34178986,28 1.44635936,27.1045695 1.44635936,26 C1.44635936,25.6519461 1.53718999,25.3099175 1.70987307,25.0077221 L14.2635137,3.038851 C14.8115337,2.0798161 16.033243,1.74662265 16.9922779,2.29464259 C17.3023404,2.47182119 17.5593077,2.72878844 17.7364863,3.038851 Z"/>
<rect width="4" height="8" x="14" y="10" fill="#FFF"/> <rect width="4" height="11" x="14" y="8" fill="#FFF" rx="2"/>
<rect width="4" height="4" x="14" y="21" fill="#FFF"/> <circle cx="16" cy="23" r="2" fill="#FFF"/>
</g> </g>
</svg> </svg>
*/ */
@Override @Override
protected Shape createOutside() { protected Shape createOutside() {
return FlatUIUtils.createPath( 16,2, 31,28, 1,28 ); return FlatUIUtils.createRoundTrianglePath( 16,0, 32,28, 0,28, 4 );
} }
@Override @Override
protected Shape createInside() { protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 10, 4, 8 ), false ); inside.append( new RoundRectangle2D.Float( 14, 8, 4, 11, 4, 4 ), false );
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false ); inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
return inside; return inside;
} }
} }

View File

@@ -46,6 +46,7 @@ public class FlatSearchIcon
@Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" ); @Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
private final boolean ignoreButtonState; private final boolean ignoreButtonState;
private Area area;
public FlatSearchIcon() { public FlatSearchIcon() {
this( false ); this( false );
@@ -67,6 +68,11 @@ public class FlatSearchIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
/* /*
@@ -84,9 +90,11 @@ public class FlatSearchIcon
null, searchIconHoverColor, searchIconPressedColor ) ); null, searchIconHoverColor, searchIconPressedColor ) );
// paint magnifier // paint magnifier
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) ); if( area == null ) {
area.subtract( new Area( new Ellipse2D.Float( 3, 3, 8, 8 ) ) ); area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
area.add( new Area( FlatUIUtils.createPath( 10.813,9.75, 14,12.938, 12.938,14, 9.75,10.813 ) ) ); area.subtract( new Area( new Ellipse2D.Float( 3, 3, 8, 8 ) ) );
area.add( new Area( FlatUIUtils.createPath( 10.813,9.75, 14,12.938, 12.938,14, 9.75,10.813 ) ) );
}
g.fill( area ); g.fill( area );
} }
} }

View File

@@ -21,7 +21,6 @@ import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.util.Map; import java.util.Map;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -76,6 +75,11 @@ public class FlatTabbedPaneCloseIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
// paint background // paint background
@@ -95,9 +99,11 @@ public class FlatTabbedPaneCloseIcon
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2; float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
// paint cross // paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false ); path.moveTo( mx - r, my - r );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false ); path.lineTo( mx + r, my + r );
path.moveTo( mx - r, my + r );
path.lineTo( mx + r, my - r );
g.setStroke( new BasicStroke( closeCrossLineWidth ) ); g.setStroke( new BasicStroke( closeCrossLineWidth ) );
g.draw( path ); g.draw( path );
} }

View File

@@ -18,8 +18,9 @@ package com.formdev.flatlaf.icons;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "closed" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}. * "closed" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}.
@@ -31,6 +32,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatTreeClosedIcon public class FlatTreeClosedIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private Path2D path;
public FlatTreeClosedIcon() { public FlatTreeClosedIcon() {
super( 16, 16, UIManager.getColor( "Tree.icon.closedColor" ) ); super( 16, 16, UIManager.getColor( "Tree.icon.closedColor" ) );
} }
@@ -41,10 +44,14 @@ public class FlatTreeClosedIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#6E6E6E" fill-rule="evenodd" points="1 2 6 2 8 4 15 4 15 13 1 13"/> <path fill="none" stroke="#6E6E6E" d="M13,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L13,4.5 C13.8284271,4.5 14.5,5.17157288 14.5,6 L14.5,12 C14.5,12.8284271 13.8284271,13.5 13,13.5 Z"/>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 15,4, 15,13, 1,13 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
if( path == null )
path = FlatFileViewDirectoryIcon.createFolderPath();
g.draw( path );
} }
} }

View File

@@ -16,9 +16,11 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.JTree; import javax.swing.JTree;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@@ -39,6 +41,7 @@ public class FlatTreeCollapsedIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private final boolean chevron; private final boolean chevron;
private Path2D path;
public FlatTreeCollapsedIcon() { public FlatTreeCollapsedIcon() {
this( UIManager.getColor( "Tree.icon.collapsedColor" ) ); this( UIManager.getColor( "Tree.icon.collapsedColor" ) );
@@ -59,10 +62,15 @@ public class FlatTreeCollapsedIcon
if( chevron ) { if( chevron ) {
// chevron arrow // chevron arrow
g.fill( FlatUIUtils.createPath( 3,1, 3,2.5, 6,5.5, 3,8.5, 3,10, 4.5,10, 9,5.5, 4.5,1 ) ); g.setStroke( new BasicStroke( 1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER ) );
if( path == null )
path = FlatUIUtils.createPath( false, 3.5,1.5, 7.5,5.5, 3.5,9.5 );
g.draw( path );
} else { } else {
// triangle arrow // triangle arrow
g.fill( FlatUIUtils.createPath( 2,1, 2,10, 10,5.5 ) ); if( path == null )
path = FlatUIUtils.createPath( 2,1, 2,10, 10,5.5 );
g.fill( path );
} }
} }

View File

@@ -16,10 +16,14 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.util.ColorFunctions;
/** /**
* "leaf" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}. * "leaf" icon for {@link javax.swing.JTree} used by {@link javax.swing.tree.DefaultTreeCellRenderer}.
@@ -42,13 +46,22 @@ public class FlatTreeLeafIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/> <rect width="11" height="13" x="2.5" y="1.5" stroke="#6E6E6E" rx="1.5"/>
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/> <line x1="5.5" x2="10.5" y1="5.5" y2="5.5" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
<line x1="5.5" x2="10.5" y1="8" y2="8" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
<line x1="5.5" x2="10.5" y1="10.5" y2="10.5" stroke="#6E6E6E" stroke-linecap="round" stroke-opacity=".6"/>
</g> </g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.draw( new RoundRectangle2D.Float( 2.5f, 1.5f, 11, 13, 3, 3 ) );
g.setColor( ColorFunctions.fade( g.getColor(), 0.6f ) );
g.draw( new Line2D.Float( 5.5f, 5.5f, 10.5f, 5.5f ) );
g.draw( new Line2D.Float( 5.5f, 8, 10.5f, 8 ) );
g.draw( new Line2D.Float( 5.5f, 10.5f, 10.5f, 10.5f ) );
} }
} }

View File

@@ -16,8 +16,11 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatTreeOpenIcon public class FlatTreeOpenIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private Path2D path;
public FlatTreeOpenIcon() { public FlatTreeOpenIcon() {
super( 16, 16, UIManager.getColor( "Tree.icon.openColor" ) ); super( 16, 16, UIManager.getColor( "Tree.icon.openColor" ) );
} }
@@ -41,14 +46,38 @@ public class FlatTreeOpenIcon
/* /*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd"> <path fill="none" stroke="#6E6E6E" d="M2,13.5 L4.11538462,8.42307692 C4.34828895,7.86410651 4.89444872,7.5 5.5,7.5 L14.75,7.5 C15.0261424,7.5 15.25,7.72385763 15.25,8 C15.25,8.06601301 15.2369281,8.13137261 15.2115385,8.19230769 L13.3846154,12.5769231 C13.151711,13.1358935 12.6055513,13.5 12,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 L1.5,4 C1.5,3.17157288 2.17157288,2.5 3,2.5 L6.29289322,2.5 C6.42550146,2.5 6.55267842,2.55267842 6.64644661,2.64644661 L8.5,4.5 L8.5,4.5 L12,4.5 C12.8284271,4.5 13.5,5.17157288 13.5,6 L13.5,6.5 L13.5,6.5"/>
<polygon fill="#6E6E6E" points="1 2 6 2 8 4 14 4 14 6 3.5 6 1 11"/>
<polygon fill="#6E6E6E" points="4 7 16 7 13 13 1 13"/>
</g>
</svg> </svg>
*/ */
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 14,4, 14,6, 3.5,6, 1,11 ) ); g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.fill( FlatUIUtils.createPath( 4,7, 16,7, 13,13, 1,13 ) ); g.setStroke( new BasicStroke( 1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER ) );
if( path == null ) {
double arc = 1.5;
double arc2 = 0.5;
path = FlatUIUtils.createPath( false,
// bottom-left of opend part
2,13.5,
// top-left of opend part
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
// top-right of opend part
FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
// bottom-right
FlatUIUtils.ROUNDED, 13,13.5, arc,
// bottom-left
1.5+arc,13.5, FlatUIUtils.QUAD_TO, 1.5,13.5, 1.5,13.5-arc,
// top-left
1.5,2.5+arc, FlatUIUtils.QUAD_TO, 1.5,2.5, 1.5+arc,2.5,
// top-mid-left
6.5-arc2,2.5, FlatUIUtils.QUAD_TO, 6.5,2.5, 6.5+arc2,2.5+arc2,
// top-mid-right
8.5,4.5,
// top-right
13.5-arc,4.5, FlatUIUtils.QUAD_TO, 13.5,4.5, 13.5,4.5+arc,
13.5,6.5 );
}
g.draw( path );
} }
} }

View File

@@ -20,7 +20,6 @@ import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
@@ -57,9 +56,11 @@ public class FlatWindowCloseIcon
int iy2 = iy + iwh - 1; int iy2 = iy + iwh - 1;
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor; float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false ); path.moveTo( ix, iy );
path.append( new Line2D.Float( ix, iy2, ix2, iy ), false ); path.lineTo( ix2, iy2 );
path.moveTo( ix, iy2 );
path.lineTo( ix2, iy );
g.setStroke( new BasicStroke( thickness ) ); g.setStroke( new BasicStroke( thickness ) );
g.draw( path ); g.draw( path );
} }

View File

@@ -0,0 +1,68 @@
/*
* 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.themes;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatDarkLaf;
/**
* A Flat LaF that imitates macOS dark look.
* <p>
* The UI defaults are loaded from {@code FlatMacDarkLaf.properties},
* {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
*
* @author Karl Tauber
* @since 3
*/
public class FlatMacDarkLaf
extends FlatDarkLaf
{
public static final String NAME = "FlatLaf macOS Dark";
/**
* Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean setup() {
return setup( new FlatMacDarkLaf() );
}
/**
* Adds this look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo() {
installLafInfo( NAME, FlatMacDarkLaf.class );
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return "FlatLaf macOS Dark Look and Feel";
}
@Override
public boolean isDark() {
return true;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.themes;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLightLaf;
/**
* A Flat LaF that imitates macOS light look.
* <p>
* The UI defaults are loaded from {@code FlatMacLightLaf.properties},
* {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
*
* @author Karl Tauber
* @since 3
*/
public class FlatMacLightLaf
extends FlatLightLaf
{
public static final String NAME = "FlatLaf macOS Light";
/**
* Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean setup() {
return setup( new FlatMacLightLaf() );
}
/**
* Adds this look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo() {
installLafInfo( NAME, FlatMacLightLaf.class );
}
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return "FlatLaf macOS Light Look and Feel";
}
@Override
public boolean isDark() {
return false;
}
}

View File

@@ -25,6 +25,7 @@ import java.awt.Graphics2D;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicArrowButton;
@@ -48,8 +49,10 @@ public class FlatArrowButton
protected Color pressedBackground; protected Color pressedBackground;
private int arrowWidth = DEFAULT_ARROW_WIDTH; private int arrowWidth = DEFAULT_ARROW_WIDTH;
private float arrowThickness = 1;
private float xOffset = 0; private float xOffset = 0;
private float yOffset = 0; private float yOffset = 0;
private boolean roundBorderAutoXOffset = true;
private boolean hover; private boolean hover;
private boolean pressed; private boolean pressed;
@@ -82,14 +85,18 @@ public class FlatArrowButton
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
pressed = true; if( SwingUtilities.isLeftMouseButton( e ) ) {
repaint(); pressed = true;
repaint();
}
} }
@Override @Override
public void mouseReleased( MouseEvent e ) { public void mouseReleased( MouseEvent e ) {
pressed = false; if( SwingUtilities.isLeftMouseButton( e ) ) {
repaint(); pressed = false;
repaint();
}
} }
} ); } );
} }
@@ -116,6 +123,16 @@ public class FlatArrowButton
this.arrowWidth = arrowWidth; this.arrowWidth = arrowWidth;
} }
/** @since 3 */
public float getArrowThickness() {
return arrowThickness;
}
/** @since 3 */
public void setArrowThickness( float arrowThickness ) {
this.arrowThickness = arrowThickness;
}
protected boolean isHover() { protected boolean isHover() {
return hover; return hover;
} }
@@ -140,6 +157,16 @@ public class FlatArrowButton
this.yOffset = yOffset; this.yOffset = yOffset;
} }
/** @since 3 */
public boolean isRoundBorderAutoXOffset() {
return roundBorderAutoXOffset;
}
/** @since 3 */
public void setRoundBorderAutoXOffset( boolean roundBorderAutoXOffset ) {
this.roundBorderAutoXOffset = roundBorderAutoXOffset;
}
protected Color deriveBackground( Color background ) { protected Color deriveBackground( Color background ) {
return background; return background;
} }
@@ -203,14 +230,17 @@ public class FlatArrowButton
} }
protected void paintArrow( Graphics2D g ) { protected void paintArrow( Graphics2D g ) {
boolean vert = (direction == NORTH || direction == SOUTH);
int x = 0; int x = 0;
// move arrow for round borders // move arrow for round borders
Container parent = getParent(); if( isRoundBorderAutoXOffset() ) {
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) ) Container parent = getParent();
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 ); boolean vert = (direction == NORTH || direction == SOUTH);
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
}
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, getArrowWidth(), getXOffset(), getYOffset() ); FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron,
getArrowWidth(), getArrowThickness(), getXOffset(), getYOffset() );
} }
} }

View File

@@ -101,6 +101,12 @@ public class FlatBorder
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();

View File

@@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
@@ -42,10 +43,13 @@ import javax.swing.JTextField;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.ButtonUI; import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ToolBarUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicButtonUI;
@@ -77,20 +81,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.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.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.focusedBackground Color optional * @uiDefault Button.focusedBackground Color optional
* @uiDefault Button.focusedForeground Color optional
* @uiDefault Button.hoverBackground Color optional * @uiDefault Button.hoverBackground Color optional
* @uiDefault Button.hoverForeground Color optional
* @uiDefault Button.pressedBackground Color optional * @uiDefault Button.pressedBackground Color optional
* @uiDefault Button.pressedForeground Color optional
* @uiDefault Button.selectedBackground Color * @uiDefault Button.selectedBackground Color
* @uiDefault Button.selectedForeground Color * @uiDefault Button.selectedForeground Color
* @uiDefault Button.disabledBackground Color optional * @uiDefault Button.disabledBackground Color optional
* @uiDefault Button.disabledText Color * @uiDefault Button.disabledText Color
* @uiDefault Button.disabledSelectedBackground Color * @uiDefault Button.disabledSelectedBackground Color
* @uiDefault Button.disabledSelectedForeground Color optional
* @uiDefault Button.default.background Color * @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.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.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.default.foreground Color * @uiDefault Button.default.foreground Color
* @uiDefault Button.default.focusedBackground Color optional * @uiDefault Button.default.focusedBackground Color optional
* @uiDefault Button.default.focusedForeground Color optional
* @uiDefault Button.default.hoverBackground Color optional * @uiDefault Button.default.hoverBackground Color optional
* @uiDefault Button.default.hoverForeground Color optional
* @uiDefault Button.default.pressedBackground Color optional * @uiDefault Button.default.pressedBackground Color optional
* @uiDefault Button.default.pressedForeground Color optional
* @uiDefault Button.default.boldText boolean * @uiDefault Button.default.boldText boolean
* @uiDefault Button.paintShadow boolean default is false * @uiDefault Button.paintShadow boolean default is false
* @uiDefault Button.shadowWidth int default is 2 * @uiDefault Button.shadowWidth int default is 2
@@ -98,8 +109,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.shadowColor Color optional * @uiDefault Button.default.shadowColor Color optional
* @uiDefault Button.toolbar.spacingInsets Insets * @uiDefault Button.toolbar.spacingInsets Insets
* @uiDefault Button.toolbar.hoverBackground Color * @uiDefault Button.toolbar.hoverBackground Color
* @uiDefault Button.toolbar.hoverForeground Color optional
* @uiDefault Button.toolbar.pressedBackground Color * @uiDefault Button.toolbar.pressedBackground Color
* @uiDefault Button.toolbar.pressedForeground Color optional
* @uiDefault Button.toolbar.selectedBackground Color * @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 * @author Karl Tauber
*/ */
@@ -116,20 +132,27 @@ public class FlatButtonUI
protected Color startBackground; protected Color startBackground;
protected Color endBackground; protected Color endBackground;
@Styleable protected Color focusedBackground; @Styleable protected Color focusedBackground;
/** @since 2.3 */ @Styleable protected Color focusedForeground;
@Styleable protected Color hoverBackground; @Styleable protected Color hoverBackground;
/** @since 2.3 */ @Styleable protected Color hoverForeground;
@Styleable protected Color pressedBackground; @Styleable protected Color pressedBackground;
/** @since 2.3 */ @Styleable protected Color pressedForeground;
@Styleable protected Color selectedBackground; @Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground; @Styleable protected Color selectedForeground;
@Styleable protected Color disabledBackground; @Styleable protected Color disabledBackground;
@Styleable protected Color disabledText; @Styleable protected Color disabledText;
@Styleable protected Color disabledSelectedBackground; @Styleable protected Color disabledSelectedBackground;
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
@Styleable(dot=true) protected Color defaultBackground; @Styleable(dot=true) protected Color defaultBackground;
protected Color defaultEndBackground; protected Color defaultEndBackground;
@Styleable(dot=true) protected Color defaultForeground; @Styleable(dot=true) protected Color defaultForeground;
@Styleable(dot=true) protected Color defaultFocusedBackground; @Styleable(dot=true) protected Color defaultFocusedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
@Styleable(dot=true) protected Color defaultHoverBackground; @Styleable(dot=true) protected Color defaultHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
@Styleable(dot=true) protected Color defaultPressedBackground; @Styleable(dot=true) protected Color defaultPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
@Styleable(dot=true) protected boolean defaultBoldText; @Styleable(dot=true) protected boolean defaultBoldText;
@Styleable protected boolean paintShadow; @Styleable protected boolean paintShadow;
@@ -138,8 +161,13 @@ public class FlatButtonUI
@Styleable(dot=true) protected Color defaultShadowColor; @Styleable(dot=true) protected Color defaultShadowColor;
@Styleable(dot=true) protected Color toolbarHoverBackground; @Styleable(dot=true) protected Color toolbarHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
@Styleable(dot=true) protected Color toolbarPressedBackground; @Styleable(dot=true) protected Color toolbarPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
@Styleable(dot=true) protected Color toolbarSelectedBackground; @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) // only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected String buttonType; /** @since 2 */ @Styleable protected String buttonType;
@@ -156,7 +184,7 @@ public class FlatButtonUI
private AtomicBoolean borderShared; private AtomicBoolean borderShared;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return FlatUIUtils.canUseSharedUI( c ) return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) ) ? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
: new FlatButtonUI( false ); : new FlatButtonUI( false );
} }
@@ -168,6 +196,13 @@ public class FlatButtonUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
installStyle( (AbstractButton) c ); installStyle( (AbstractButton) c );
@@ -189,20 +224,27 @@ public class FlatButtonUI
startBackground = UIManager.getColor( prefix + "startBackground" ); startBackground = UIManager.getColor( prefix + "startBackground" );
endBackground = UIManager.getColor( prefix + "endBackground" ); endBackground = UIManager.getColor( prefix + "endBackground" );
focusedBackground = UIManager.getColor( prefix + "focusedBackground" ); focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
hoverBackground = UIManager.getColor( prefix + "hoverBackground" ); hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
pressedBackground = UIManager.getColor( prefix + "pressedBackground" ); pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
selectedBackground = UIManager.getColor( prefix + "selectedBackground" ); selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
selectedForeground = UIManager.getColor( prefix + "selectedForeground" ); selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
disabledBackground = UIManager.getColor( prefix + "disabledBackground" ); disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
disabledText = UIManager.getColor( prefix + "disabledText" ); disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" ); disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" ); defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" ); defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" ); defaultForeground = UIManager.getColor( "Button.default.foreground" );
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" ); defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" ); defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" ); defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" ); defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
paintShadow = UIManager.getBoolean( "Button.paintShadow" ); paintShadow = UIManager.getBoolean( "Button.paintShadow" );
@@ -211,8 +253,13 @@ public class FlatButtonUI
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" ); defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" ); toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" ); toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" ); 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" ); helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
defaultMargin = UIManager.getInsets( prefix + "margin" ); defaultMargin = UIManager.getInsets( prefix + "margin" );
@@ -329,6 +376,18 @@ public class FlatButtonUI
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
if( key.startsWith( "help." ) ) {
return (helpButtonIcon instanceof FlatHelpButtonIcon)
? ((FlatHelpButtonIcon)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
: null;
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, c.getBorder(), key );
}
static boolean isContentAreaFilled( Component c ) { static boolean isContentAreaFilled( Component c ) {
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled(); return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
} }
@@ -494,6 +553,23 @@ public class FlatButtonUI
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c ); super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
} }
@Override
protected void paintIcon( Graphics g, JComponent c, Rectangle iconRect ) {
// correct icon location when using bold font for default button
int xOffset = defaultBoldPlainWidthDiff( c ) / 2;
if( xOffset > 0 ) {
boolean ltr = c.getComponentOrientation().isLeftToRight();
switch( ((AbstractButton)c).getHorizontalTextPosition() ) {
case SwingConstants.RIGHT: iconRect.x -= xOffset; break;
case SwingConstants.LEFT: iconRect.x += xOffset; break;
case SwingConstants.TRAILING: iconRect.x -= ltr ? xOffset : -xOffset; break;
case SwingConstants.LEADING: iconRect.x += ltr ? xOffset : -xOffset; break;
}
}
super.paintIcon( g, c, iconRect );
}
@Override @Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) { protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
if( isHelpButton( b ) ) if( isHelpButton( b ) )
@@ -514,6 +590,8 @@ public class FlatButtonUI
} }
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) { 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() ); FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1; int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -527,11 +605,14 @@ public class FlatButtonUI
// selected state // selected state
if( ((AbstractButton)c).isSelected() ) { 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 // we assume that toolbar icon is shown disabled
return buttonStateColor( c, return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground, toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground, toolBarButton
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
: disabledSelectedBackground,
null, null,
null, null,
toolBarButton ? toolbarPressedBackground : pressedBackground ); toolBarButton ? toolbarPressedBackground : pressedBackground );
@@ -558,6 +639,9 @@ public class FlatButtonUI
} }
protected Color getBackgroundBase( JComponent c, boolean def ) { protected Color getBackgroundBase( JComponent c, boolean def ) {
if( FlatUIUtils.isAWTPeer( c ) )
return background;
// use component background if explicitly set // use component background if explicitly set
Color bg = c.getBackground(); Color bg = c.getBackground();
if( isCustomBackground( bg ) ) if( isCustomBackground( bg ) )
@@ -596,18 +680,48 @@ public class FlatButtonUI
} }
protected Color getForeground( JComponent c ) { protected Color getForeground( JComponent c ) {
if( !c.isEnabled() ) boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
return disabledText;
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) ) // selected state
return selectedForeground; 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 // use component foreground if explicitly set
Color fg = c.getForeground(); Color fg = c.getForeground();
if( isCustomForeground( fg ) ) if( isCustomForeground( fg ) )
return fg; return fg;
boolean def = isDefaultButton( c );
return def ? defaultForeground : fg; return def ? defaultForeground : fg;
} }
@@ -624,6 +738,9 @@ public class FlatButtonUI
if( prefSize == null ) if( prefSize == null )
return null; return null;
// increase width when using bold font for default button
prefSize.width += defaultBoldPlainWidthDiff( c );
// make square or apply minimum width/height // make square or apply minimum width/height
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c ); boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) { if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
@@ -644,6 +761,23 @@ public class FlatButtonUI
return prefSize; return prefSize;
} }
private int defaultBoldPlainWidthDiff( JComponent c ) {
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
String text = ((AbstractButton)c).getText();
if( text == null || text.isEmpty() )
return 0;
Font font = c.getFont();
Font boldFont = font.deriveFont( Font.BOLD );
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
if( boldWidth > plainWidth )
return boldWidth - plainWidth;
}
return 0;
}
private boolean hasDefaultMargins( JComponent c ) { private boolean hasDefaultMargins( JComponent c ) {
Insets margin = ((AbstractButton)c).getMargin(); Insets margin = ((AbstractButton)c).getMargin();
return margin instanceof UIResource && Objects.equals( margin, defaultMargin ); return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
@@ -666,5 +800,20 @@ public class FlatButtonUI
super.propertyChange( e ); super.propertyChange( e );
FlatButtonUI.this.propertyChange( b, e ); FlatButtonUI.this.propertyChange( b, e );
} }
@Override
public void stateChanged( ChangeEvent e ) {
super.stateChanged( e );
// if button is in toolbar, repaint button groups
AbstractButton b = (AbstractButton) e.getSource();
Container parent = b.getParent();
if( parent instanceof JToolBar ) {
JToolBar toolBar = (JToolBar) parent;
ToolBarUI ui = toolBar.getUI();
if( ui instanceof FlatToolBarUI )
((FlatToolBarUI)ui).repaintButtonGroup( b );
}
}
} }
} }

View File

@@ -16,18 +16,20 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
/** /**
@@ -58,9 +60,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
public class FlatCheckBoxMenuItemUI public class FlatCheckBoxMenuItemUI
extends BasicCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -119,29 +127,27 @@ public class FlatCheckBoxMenuItemUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
try { return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
Object oldValue;
switch( key ) {
// BasicMenuItemUI
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
}
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
} }
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer ); return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
}
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
} }
@Override @Override

View File

@@ -43,7 +43,7 @@ public class FlatCheckBoxUI
extends FlatRadioButtonUI extends FlatRadioButtonUI
{ {
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return FlatUIUtils.canUseSharedUI( c ) return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) ) ? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
: new FlatCheckBoxUI( false ); : new FlatCheckBoxUI( false );
} }

View File

@@ -42,6 +42,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
@@ -69,7 +70,10 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup; import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
@@ -86,12 +90,17 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.padding Insets * @uiDefault ComboBox.padding Insets
* @uiDefault ComboBox.squareButton boolean default is true * @uiDefault ComboBox.squareButton boolean default is true
* *
* <!-- BasicComboPopup -->
*
* @uiDefault ComboBox.selectionBackground Color
* @uiDefault ComboBox.selectionForeground Color
*
* <!-- FlatComboBoxUI --> * <!-- FlatComboBoxUI -->
* *
* @uiDefault ComboBox.minimumWidth int * @uiDefault ComboBox.minimumWidth int
* @uiDefault ComboBox.editorColumns int * @uiDefault ComboBox.editorColumns int
* @uiDefault ComboBox.maximumRowCount int * @uiDefault ComboBox.maximumRowCount int
* @uiDefault ComboBox.buttonStyle String auto (default), button or none * @uiDefault ComboBox.buttonStyle String auto (default), button, mac or none
* @uiDefault Component.arrowType String chevron (default) or triangle * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background * @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
@@ -109,12 +118,17 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.buttonHoverArrowColor Color * @uiDefault ComboBox.buttonHoverArrowColor Color
* @uiDefault ComboBox.buttonPressedArrowColor Color * @uiDefault ComboBox.buttonPressedArrowColor Color
* @uiDefault ComboBox.popupBackground Color optional * @uiDefault ComboBox.popupBackground Color optional
* @uiDefault ComboBox.popupInsets Insets
* @uiDefault ComboBox.selectionInsets Insets
* @uiDefault ComboBox.selectionArc int
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicComboBoxUI.class, key="padding" )
public class FlatComboBoxUI public class FlatComboBoxUI
extends BasicComboBoxUI extends BasicComboBoxUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
@Styleable protected int minimumWidth; @Styleable protected int minimumWidth;
@Styleable protected int editorColumns; @Styleable protected int editorColumns;
@@ -122,6 +136,7 @@ public class FlatComboBoxUI
@Styleable protected String arrowType; @Styleable protected String arrowType;
protected boolean isIntelliJTheme; protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color editableBackground; @Styleable protected Color editableBackground;
@Styleable protected Color focusedBackground; @Styleable protected Color focusedBackground;
@Styleable protected Color disabledBackground; @Styleable protected Color disabledBackground;
@@ -139,6 +154,9 @@ public class FlatComboBoxUI
@Styleable protected Color buttonPressedArrowColor; @Styleable protected Color buttonPressedArrowColor;
@Styleable protected Color popupBackground; @Styleable protected Color popupBackground;
/** @since 3 */ @Styleable protected Insets popupInsets;
/** @since 3 */ @Styleable protected Insets selectionInsets;
/** @since 3 */ @Styleable protected int selectionArc;
private MouseListener hoverListener; private MouseListener hoverListener;
protected boolean hover; protected boolean hover;
@@ -155,6 +173,13 @@ public class FlatComboBoxUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
installStyle(); installStyle();
@@ -217,6 +242,7 @@ public class FlatComboBoxUI
arrowType = UIManager.getString( "Component.arrowType" ); arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( "ComboBox.background" );
editableBackground = UIManager.getColor( "ComboBox.editableBackground" ); editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" ); focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" ); disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
@@ -234,6 +260,9 @@ public class FlatComboBoxUI
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" ); buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
popupBackground = UIManager.getColor( "ComboBox.popupBackground" ); popupBackground = UIManager.getColor( "ComboBox.popupBackground" );
popupInsets = UIManager.getInsets( "ComboBox.popupInsets" );
selectionInsets = UIManager.getInsets( "ComboBox.selectionInsets" );
selectionArc = UIManager.getInt( "ComboBox.selectionArc" );
// set maximumRowCount // set maximumRowCount
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" ); int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
@@ -249,6 +278,7 @@ public class FlatComboBoxUI
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
background = null;
editableBackground = null; editableBackground = null;
focusedBackground = null; focusedBackground = null;
disabledBackground = null; disabledBackground = null;
@@ -288,11 +318,14 @@ public class FlatComboBoxUI
// limit button width to height of a raw combobox (without insets) // limit button width to height of a raw combobox (without insets)
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() ); FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom ); int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
int minButtonWidth = (maxButtonWidth * 3) / 4;
// make button square (except if width is limited)
Insets insets = getInsets(); Insets insets = getInsets();
int buttonWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth ); int buttonWidth = Math.min( Math.max( parent.getHeight() - insets.top - insets.bottom, minButtonWidth ), maxButtonWidth );
if( buttonWidth != arrowButton.getWidth() ) { if( buttonWidth != arrowButton.getWidth() ) {
// set width of arrow button to preferred height of combobox // set width of arrow button
int xOffset = comboBox.getComponentOrientation().isLeftToRight() int xOffset = comboBox.getComponentOrientation().isLeftToRight()
? arrowButton.getWidth() - buttonWidth ? arrowButton.getWidth() - buttonWidth
: 0; : 0;
@@ -486,13 +519,6 @@ public class FlatComboBoxUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
// BasicComboBoxUI
if( key.equals( "padding" ) ) {
Object oldValue = padding;
padding = (Insets) value;
return oldValue;
}
if( borderShared == null ) if( borderShared == null )
borderShared = new AtomicBoolean( true ); borderShared = new AtomicBoolean( true );
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared ); return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
@@ -501,11 +527,21 @@ public class FlatComboBoxUI
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>(); return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() );
infos.put( "padding", Insets.class ); }
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos ); /** @since 2.5 */
return infos; @Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, comboBox.getBorder(), key );
}
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
} }
@Override @Override
@@ -533,7 +569,9 @@ public class FlatComboBoxUI
int height = c.getHeight(); int height = c.getHeight();
int arrowX = arrowButton.getX(); int arrowX = arrowButton.getX();
int arrowWidth = arrowButton.getWidth(); int arrowWidth = arrowButton.getWidth();
boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) && !"none".equals( buttonStyle ); boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) &&
!"none".equals( buttonStyle ) &&
!isMacStyle();
boolean enabled = comboBox.isEnabled(); boolean enabled = comboBox.isEnabled();
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight(); boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
@@ -551,13 +589,21 @@ public class FlatComboBoxUI
: buttonBackground; : buttonBackground;
if( buttonColor != null ) { if( buttonColor != null ) {
g2.setColor( buttonColor ); g2.setColor( buttonColor );
Shape oldClip = g2.getClip(); if( isMacStyle() ) {
if( isLeftToRight ) Insets insets = comboBox.getInsets();
g2.clipRect( arrowX, 0, width - arrowX, height ); int gap = scale( 2 );
else FlatUIUtils.paintComponentBackground( g2, arrowX + gap, insets.top + gap,
g2.clipRect( 0, 0, arrowX + arrowWidth, height ); arrowWidth - (gap * 2), height - insets.top - insets.bottom - (gap * 2),
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc ); 0, arc - focusWidth );
g2.setClip( oldClip ); } else {
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
} }
} }
@@ -603,7 +649,7 @@ public class FlatComboBoxUI
boolean shouldValidate = (c instanceof JPanel); 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 ); currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
paddingBorder.uninstall(); paddingBorder.uninstall();
@@ -618,6 +664,9 @@ public class FlatComboBoxUI
protected Color getBackground( boolean enabled ) { protected Color getBackground( boolean enabled ) {
if( enabled ) { if( enabled ) {
if( FlatUIUtils.isAWTPeer( comboBox ) )
return background;
Color background = comboBox.getBackground(); Color background = comboBox.getBackground();
// always use explicitly set color // always use explicitly set color
@@ -677,7 +726,7 @@ public class FlatComboBoxUI
@Override @Override
protected Dimension getSizeForComponent( Component comp ) { protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp ); paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp ); Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall(); paddingBorder.uninstall();
return size; return size;
@@ -693,6 +742,10 @@ public class FlatComboBoxUI
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() ); return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
} }
private boolean isMacStyle() {
return "mac".equals( buttonStyle );
}
/** @since 1.3 */ /** @since 1.3 */
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) { public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
if( comboBox.isEditable() ) { if( comboBox.isEditable() ) {
@@ -727,6 +780,21 @@ public class FlatComboBoxUI
buttonHoverArrowColor, null, buttonPressedArrowColor, null ); buttonHoverArrowColor, null, buttonPressedArrowColor, null );
} }
@Override
public int getArrowWidth() {
return isMacStyle() ? (getWidth() % 2 == 0 ? 6 : 7) : super.getArrowWidth();
}
@Override
public float getArrowThickness() {
return isMacStyle() ? 1.5f : super.getArrowThickness();
}
@Override
public boolean isRoundBorderAutoXOffset() {
return isMacStyle() ? false : super.isRoundBorderAutoXOffset();
}
@Override @Override
protected boolean isHover() { protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false); return super.isHover() || (!comboBox.isEditable() ? hover : false);
@@ -744,6 +812,20 @@ public class FlatComboBoxUI
return super.getArrowColor(); return super.getArrowColor();
} }
@Override
protected void paintArrow( Graphics2D g ) {
if( isMacStyle() && !comboBox.isEditable() ) {
// for style "mac", paint up and down arrows if combobox is not editable
int height = getHeight();
int h = Math.round( height / 2f );
FlatUIUtils.paintArrow( g, 0, 0, getWidth(), h, SwingConstants.NORTH, chevron,
getArrowWidth(), getArrowThickness(), getXOffset(), getYOffset() + 1.25f );
FlatUIUtils.paintArrow( g, 0, height - h, getWidth(), h, SwingConstants.SOUTH, chevron,
getArrowWidth(), getArrowThickness(), getXOffset(), getYOffset() - 1.25f );
} else
super.paintArrow( g );
}
} }
//---- class FlatComboPopup ----------------------------------------------- //---- class FlatComboPopup -----------------------------------------------
@@ -778,12 +860,19 @@ public class FlatComboBoxUI
} }
} }
// for style "mac", add width of "checked item" icon
boolean isPopupOverComboBox = isPopupOverComboBox();
int selectedIndex = -1;
if( isPopupOverComboBox && (selectedIndex = comboBox.getSelectedIndex()) >= 0 )
displayWidth += MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( CellPaddingBorder.MAC_STYLE_GAP );
// add width of vertical scroll bar // add width of vertical scroll bar
JScrollBar verticalScrollBar = scroller.getVerticalScrollBar(); JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
if( verticalScrollBar != null ) if( verticalScrollBar != null )
displayWidth += verticalScrollBar.getPreferredSize().width; displayWidth += verticalScrollBar.getPreferredSize().width;
// make popup wider if necessary // make popup wider if necessary
int pw0 = pw;
if( displayWidth > pw ) { if( displayWidth > pw ) {
// limit popup width to screen width // limit popup width to screen width
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration(); GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
@@ -803,6 +892,30 @@ public class FlatComboBoxUI
px -= diff; px -= diff;
} }
// for style "mac", place popup over combobox
Rectangle cellBounds;
if( isPopupOverComboBox && selectedIndex >= 0 &&
(cellBounds = list.getCellBounds( 0, 0 )) != null )
{
Insets comboBoxInsets = comboBox.getInsets();
Insets listInsets = list.getInsets();
Insets popupInsets = getInsets();
// position popup so that selected item is at same Y position as combobox
py -= (cellBounds.height * (selectedIndex + 1)) + comboBoxInsets.top + listInsets.top + popupInsets.top;
// position popup slightly to the left so that a small part of the right side of the combobox stays visible
int offset = Math.min( pw - pw0, MacCheckedItemIcon.INSTANCE.getIconWidth() ) + scale( 4 );
if( comboBox.getComponentOrientation().isLeftToRight() )
px -= offset + comboBoxInsets.right + listInsets.right;
else
px += offset + comboBoxInsets.left + listInsets.left;
// not invoking super.computePopupBounds() here to let
// JPopupMenu.adjustPopupLocationToFitScreen() fix the location if necessary
return new Rectangle( px, py, pw, ph );
}
return super.computePopupBounds( px, py, pw, ph ); return super.computePopupBounds( px, py, pw, ph );
} }
@@ -813,17 +926,12 @@ public class FlatComboBoxUI
// make opaque to avoid that background shines thru border (e.g. at 150% scaling) // make opaque to avoid that background shines thru border (e.g. at 150% scaling)
setOpaque( true ); setOpaque( true );
// set popup border // set popup border
// use non-UIResource to avoid that it is overwritten when making // use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview // popup visible (see JPopupMenu.setInvoker()) in theme editor preview
Border border = UIManager.getBorder( "PopupMenu.border" ); Border border = UIManager.getBorder( "PopupMenu.border" );
if( border != null ) if( border != null )
setBorder( FlatUIUtils.nonUIResource( border ) ); setBorder( FlatUIUtils.nonUIResource( border ) );
}
@Override
protected void configureList() {
super.configureList();
list.setCellRenderer( new PopupListCellRenderer() ); list.setCellRenderer( new PopupListCellRenderer() );
updateStyle(); updateStyle();
@@ -831,12 +939,21 @@ public class FlatComboBoxUI
void updateStyle() { void updateStyle() {
if( popupBackground != null ) if( popupBackground != null )
list.setBackground( popupBackground ); list.setBackground( popupBackground );
// set popup background because it may shine thru when scaled (e.g. at 150%) // set popup background because it may shine thru when scaled (e.g. at 150%)
// use non-UIResource to avoid that it is overwritten when making // use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview // popup visible (see JPopupMenu.setInvoker()) in theme editor preview
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) ); setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
scroller.setViewportBorder( (popupInsets != null) ? new FlatEmptyBorder( popupInsets ) : null );
scroller.setOpaque( false );
if( list.getUI() instanceof FlatListUI ) {
FlatListUI ui = (FlatListUI) list.getUI();
ui.selectionInsets = selectionInsets;
ui.selectionArc = selectionArc;
}
} }
@Override @Override
@@ -878,6 +995,15 @@ public class FlatComboBoxUI
paddingBorder.uninstall(); paddingBorder.uninstall();
} }
private boolean isPopupOverComboBox() {
return isMacStyle() &&
!comboBox.isEditable() &&
comboBox.getItemCount() > 0 &&
comboBox.getItemCount() <= comboBox.getMaximumRowCount() &&
// for compatibility with Aqua Laf
!clientPropertyBoolean( comboBox, "JComboBox.isPopDown", false );
}
//---- class PopupListCellRenderer ----- //---- class PopupListCellRenderer -----
private class PopupListCellRenderer private class PopupListCellRenderer
@@ -895,7 +1021,14 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() ); c.applyComponentOrientation( comboBox.getComponentOrientation() );
paddingBorder.install( c ); // style "mac"
if( isPopupOverComboBox() && c instanceof JComponent ) {
int selectedIndex = comboBox.getSelectedIndex();
((JComponent)c).putClientProperty( CellPaddingBorder.KEY_MAC_STYLE_HINT,
(selectedIndex >= 0) ? (index == selectedIndex) : null );
}
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
return c; return c;
} }
@@ -911,13 +1044,20 @@ public class FlatComboBoxUI
* which vertically aligns text in popup list with text in combobox. * which vertically aligns text in popup list with text in combobox.
* <p> * <p>
* The renderer border is painted on the outer side of this border. * The renderer border is painted on the outer side of this border.
* <p>
* For button style "mac", also used to increase insets on left side for
* "checked item" icon and to paint "checked item" icon for selected combobox item.
*/ */
private static class CellPaddingBorder private static class CellPaddingBorder
extends AbstractBorder extends AbstractBorder
{ {
static final String KEY_MAC_STYLE_HINT = "FlatLaf.internal.FlatComboBoxUI.macStyleHint";
static final int MAC_STYLE_GAP = 4;
private Insets padding; private Insets padding;
private JComponent rendererComponent; private JComponent rendererComponent;
private Border rendererBorder; private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) { CellPaddingBorder( Insets padding ) {
this.padding = padding; this.padding = padding;
@@ -925,10 +1065,12 @@ public class FlatComboBoxUI
// using synchronized to avoid problems with code that modifies combo box // using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done) // (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) ) if( !(c instanceof JComponent) )
return; return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c; JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder(); Border oldBorder = jc.getBorder();
if( oldBorder == this ) if( oldBorder == this )
@@ -961,6 +1103,8 @@ public class FlatComboBoxUI
if( rendererComponent == null ) if( rendererComponent == null )
return; return;
rendererComponent.putClientProperty( KEY_MAC_STYLE_HINT, null );
if( rendererComponent.getBorder() == this ) if( rendererComponent.getBorder() == this )
rendererComponent.setBorder( rendererBorder ); rendererComponent.setBorder( rendererBorder );
rendererComponent = null; rendererComponent = null;
@@ -982,6 +1126,24 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom; insets.bottom = padding.bottom;
insets.right = padding.right; 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;
// style "mac"
if( c instanceof JComponent ) {
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
if( macStyleHint != null ) {
int indent = MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( MAC_STYLE_GAP );
if( c.getComponentOrientation().isLeftToRight() )
insets.left += indent;
else
insets.right += indent;
}
}
return insets; return insets;
} }
@@ -989,6 +1151,35 @@ public class FlatComboBoxUI
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( rendererBorder != null ) if( rendererBorder != null )
rendererBorder.paintBorder( c, g, x, y, width, height ); rendererBorder.paintBorder( c, g, x, y, width, height );
// style "mac"
if( c instanceof JComponent ) {
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
if( macStyleHint == Boolean.TRUE ) {
// paint "checked item" icon
int ix = c.getComponentOrientation().isLeftToRight()
? x + scale( padding.left )
: x + width - scale( padding.right ) - MacCheckedItemIcon.INSTANCE.getIconWidth();
MacCheckedItemIcon.INSTANCE.paintIcon( c, g, ix, y + ((height - MacCheckedItemIcon.INSTANCE.getIconHeight()) / 2) );
}
}
}
}
//---- class MacCheckedItemIcon -------------------------------------------
/**
* Use for style "mac" to mark checked item.
*/
private static class MacCheckedItemIcon
extends FlatCheckBoxMenuItemIcon
{
static MacCheckedItemIcon INSTANCE = new MacCheckedItemIcon();
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
g2.setColor( c.getForeground() );
paintCheckmark( g2 );
} }
} }

View File

@@ -107,6 +107,12 @@ public class FlatDropShadowBorder
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( shadowSize <= 0 ) if( shadowSize <= 0 )

View File

@@ -209,6 +209,12 @@ public class FlatEditorPaneUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private void updateBackground() { private void updateBackground() {
FlatTextFieldUI.updateBackground( getComponent(), background, FlatTextFieldUI.updateBackground( getComponent(), background,
disabledBackground, inactiveBackground, disabledBackground, inactiveBackground,

View File

@@ -56,7 +56,7 @@ public class FlatEmptyBorder
protected static Insets scaleInsets( Component c, Insets insets, protected static Insets scaleInsets( Component c, Insets insets,
int top, int left, int bottom, int right ) 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.left = scale( leftToRight ? left : right );
insets.top = scale( top ); insets.top = scale( top );
insets.right = scale( leftToRight ? right : left ); insets.right = scale( leftToRight ? right : left );
@@ -76,4 +76,9 @@ public class FlatEmptyBorder
right = insets.right; right = insets.right;
return oldInsets; return oldInsets;
} }
/** @since 2.5 */
public Insets getStyleableValue() {
return new Insets( top, left, bottom, right );
}
} }

View File

@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets; 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.io.File;
import java.lang.reflect.Method;
import java.util.function.Function;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView; import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI; import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.ScaledImageIcon; import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault FileChooser.listViewActionLabelText String * @uiDefault FileChooser.listViewActionLabelText String
* @uiDefault FileChooser.detailsViewActionLabelText 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&lt;File[], File[]&gt;
* @uiDefault FileChooser.shortcuts.displayNameFunction Function&lt;File, String&gt;
* @uiDefault FileChooser.shortcuts.iconFunction Function&lt;File, Icon&gt;
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatFileChooserUI public class FlatFileChooserUI
extends MetalFileChooserUI extends MetalFileChooserUI
{ {
private final FlatFileView fileView = new FlatFileView(); private final FlatFileView fileView = new FlatFileView();
private FlatShortcutsPanel shortcutsPanel;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c ); return new FlatFileChooserUI( (JFileChooser) c );
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
super.installComponents( fc ); super.installComponents( fc );
patchUI( 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 ) { private void patchUI( JFileChooser fc ) {
@@ -192,6 +234,27 @@ public class FlatFileChooserUI
} catch( ArrayIndexOutOfBoundsException ex ) { } catch( ArrayIndexOutOfBoundsException ex ) {
// ignore // 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 lineEnd = borderLayout.getLayoutComponent( BorderLayout.LINE_END );
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
if( north != null && lineEnd != null && center != null && south != null ) {
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
p.add( north, BorderLayout.NORTH );
p.add( lineEnd, BorderLayout.LINE_END );
p.add( center, BorderLayout.CENTER );
p.add( south, BorderLayout.SOUTH );
fc.add( p, BorderLayout.CENTER );
}
}
} }
@Override @Override
@@ -250,9 +313,19 @@ public class FlatFileChooserUI
return p; return p;
} }
/** @since 2.3 */
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
return new FlatShortcutsPanel( fc );
}
@Override @Override
public Dimension getPreferredSize( JComponent c ) { 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 @Override
@@ -316,4 +389,234 @@ public class FlatFileChooserUI
return icon; 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

@@ -179,6 +179,12 @@ public class FlatInternalFrameUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, frame.getBorder(), key );
}
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
// The internal frame actually should be opaque and fill its background, // The internal frame actually should be opaque and fill its background,
@@ -253,6 +259,23 @@ public class FlatInternalFrameUI
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
switch( key ) {
case "borderMargins": return getStyleableValue();
case "activeDropShadowColor": return activeDropShadowBorder.getStyleableValue( "shadowColor" );
case "activeDropShadowInsets": return activeDropShadowBorder.getStyleableValue( "shadowInsets" );
case "activeDropShadowOpacity": return activeDropShadowBorder.getStyleableValue( "shadowOpacity" );
case "inactiveDropShadowColor": return inactiveDropShadowBorder.getStyleableValue( "shadowColor" );
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.getStyleableValue( "shadowInsets" );
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.getStyleableValue( "shadowOpacity" );
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) { if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {

View File

@@ -158,6 +158,12 @@ public class FlatLabelUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
/** /**
* Checks whether text contains HTML tags that use "absolute-size" keywords * Checks whether text contains HTML tags that use "absolute-size" keywords
* (e.g. "x-large") for font-size in default style sheet * (e.g. "x-large") for font-size in default style sheet

View File

@@ -17,20 +17,34 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.basic.BasicListUI; import javax.swing.plaf.basic.BasicListUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}. * Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
@@ -59,6 +73,8 @@ import com.formdev.flatlaf.util.LoggingFacade;
* *
* @uiDefault List.selectionInactiveBackground Color * @uiDefault List.selectionInactiveBackground Color
* @uiDefault List.selectionInactiveForeground Color * @uiDefault List.selectionInactiveForeground Color
* @uiDefault List.selectionInsets Insets
* @uiDefault List.selectionArc int
* *
* <!-- FlatListCellBorder --> * <!-- FlatListCellBorder -->
* *
@@ -76,6 +92,8 @@ public class FlatListUI
@Styleable protected Color selectionForeground; @Styleable protected Color selectionForeground;
@Styleable protected Color selectionInactiveBackground; @Styleable protected Color selectionInactiveBackground;
@Styleable protected Color selectionInactiveForeground; @Styleable protected Color selectionInactiveForeground;
/** @since 3 */ @Styleable protected Insets selectionInsets;
/** @since 3 */ @Styleable protected int selectionArc;
// for FlatListCellBorder // for FlatListCellBorder
/** @since 2 */ @Styleable protected Insets cellMargins; /** @since 2 */ @Styleable protected Insets cellMargins;
@@ -90,6 +108,13 @@ public class FlatListUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
installStyle(); installStyle();
@@ -103,6 +128,8 @@ public class FlatListUI
selectionForeground = UIManager.getColor( "List.selectionForeground" ); selectionForeground = UIManager.getColor( "List.selectionForeground" );
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" ); selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" ); selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
selectionInsets = UIManager.getInsets( "List.selectionInsets" );
selectionArc = UIManager.getInt( "List.selectionArc" );
toggleSelectionColors(); toggleSelectionColors();
} }
@@ -161,6 +188,29 @@ public class FlatListUI
}; };
} }
@Override
protected ListSelectionListener createListSelectionListener() {
ListSelectionListener superListener = super.createListSelectionListener();
return e -> {
superListener.valueChanged( e );
// for united rounded selection, repaint parts of the rows/columns that adjoin to the changed rows/columns
if( useUnitedRoundedSelection( true, true ) &&
!list.isSelectionEmpty() &&
(list.getMaxSelectionIndex() - list.getMinSelectionIndex()) >= 1 )
{
int size = list.getModel().getSize();
int firstIndex = Math.min( Math.max( e.getFirstIndex(), 0 ), size - 1 );
int lastIndex = Math.min( Math.max( e.getLastIndex(), 0 ), size - 1 );
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
if( r != null ) {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
}
}
};
}
/** @since 2 */ /** @since 2 */
protected void installStyle() { protected void installStyle() {
try { try {
@@ -209,6 +259,12 @@ public class FlatListUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
/** /**
* Toggle selection colors from focused to inactive and vice versa. * Toggle selection colors from focused to inactive and vice versa.
* *
@@ -234,4 +290,164 @@ public class FlatListUI
list.setSelectionForeground( selectionInactiveForeground ); list.setSelectionForeground( selectionInactiveForeground );
} }
} }
@SuppressWarnings( "rawtypes" )
@Override
protected void paintCell( Graphics g, int row, Rectangle rowBounds, ListCellRenderer cellRenderer,
ListModel dataModel, ListSelectionModel selModel, int leadIndex )
{
boolean isSelected = selModel.isSelectedIndex( row );
// get renderer component
@SuppressWarnings( "unchecked" )
Component rendererComponent = cellRenderer.getListCellRendererComponent( list,
dataModel.getElementAt( row ), row, isSelected, list.hasFocus() && (row == leadIndex) );
//
boolean isFileList = Boolean.TRUE.equals( list.getClientProperty( "List.isFileList" ) );
int cx, cw;
if( isFileList ) {
// see BasicListUI.paintCell()
cw = Math.min( rowBounds.width, rendererComponent.getPreferredSize().width + 4 );
cx = list.getComponentOrientation().isLeftToRight()
? rowBounds.x
: rowBounds.x + (rowBounds.width - cw);
} else {
cx = rowBounds.x;
cw = rowBounds.width;
}
// rounded selection or selection insets
if( isSelected &&
!isFileList && // rounded selection is not supported for file list
(rendererComponent instanceof DefaultListCellRenderer ||
rendererComponent instanceof BasicComboBoxRenderer) &&
(selectionArc > 0 ||
(selectionInsets != null &&
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) )
{
// Because selection painting is done in the cell renderer, it would be
// necessary to require a FlatLaf specific renderer to implement rounded selection.
// Using a LaF specific renderer was avoided because often a custom renderer is
// already used in applications. Then either the rounded selection is not used,
// or the application has to be changed to extend a FlatLaf renderer.
//
// To solve this, a graphics proxy is used that paints rounded selection
// if row is selected and the renderer wants to fill the background.
class RoundedSelectionGraphics extends Graphics2DProxy {
// used to avoid endless loop in case that paintCellSelection() invokes
// g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
private boolean inPaintSelection;
RoundedSelectionGraphics( Graphics delegate ) {
super( (Graphics2D) delegate );
}
@Override
public Graphics create() {
return new RoundedSelectionGraphics( super.create() );
}
@Override
public Graphics create( int x, int y, int width, int height ) {
return new RoundedSelectionGraphics( super.create( x, y, width, height ) );
}
@Override
public void fillRect( int x, int y, int width, int height ) {
if( !inPaintSelection &&
x == 0 && y == 0 && width == rowBounds.width && height == rowBounds.height &&
this.getColor() == rendererComponent.getBackground() )
{
inPaintSelection = true;
paintCellSelection( this, row, x, y, width, height );
inPaintSelection = false;
} else
super.fillRect( x, y, width, height );
}
}
g = new RoundedSelectionGraphics( g );
}
// paint renderer
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
}
/** @since 3 */
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( list.getLayoutOrientation() == JList.VERTICAL ) {
// layout orientation: VERTICAL
if( useUnitedRoundedSelection( true, false ) ) {
if( row > 0 && list.isSelectedIndex( row - 1 ) )
arcTopLeft = arcTopRight = 0;
if( row < list.getModel().getSize() - 1 && list.isSelectedIndex( row + 1 ) )
arcBottomLeft = arcBottomRight = 0;
}
} else {
// layout orientation: VERTICAL_WRAP or HORIZONTAL_WRAP
Rectangle r = null;
if( useUnitedRoundedSelection( true, false ) ) {
// vertical: check whether cells above or below are selected
r = getCellBounds( list, row, row );
int topIndex = locationToIndex( list, new Point( r.x, r.y - 1 ) );
int bottomIndex = locationToIndex( list, new Point( r.x, r.y + r.height ) );
if( topIndex >= 0 && topIndex != row && list.isSelectedIndex( topIndex ) )
arcTopLeft = arcTopRight = 0;
if( bottomIndex >= 0 && bottomIndex != row && list.isSelectedIndex( bottomIndex ) )
arcBottomLeft = arcBottomRight = 0;
}
if( useUnitedRoundedSelection( false, true ) ) {
// horizontal: check whether cells left or right are selected
if( r == null )
r = getCellBounds( list, row, row );
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
// special handling for the case that last column contains less cells than the other columns
boolean ltr = list.getComponentOrientation().isLeftToRight();
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
leftIndex = -1;
if( ltr && rightIndex >= 0 && rightIndex != row && rightIndex == locationToIndex( list, new Point( r.x + r.width, r.y - 1 ) ) )
rightIndex = -1;
if( leftIndex >= 0 && leftIndex != row && list.isSelectedIndex( leftIndex ) )
arcTopLeft = arcBottomLeft = 0;
if( rightIndex >= 0 && rightIndex != row && list.isSelectedIndex( rightIndex ) )
arcTopRight = arcBottomRight = 0;
}
}
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
UIScale.scale( selectionInsets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
}
private boolean useUnitedRoundedSelection( boolean vertical, boolean horizontal ) {
return selectionArc > 0 &&
(selectionInsets == null ||
(vertical && selectionInsets.top == 0 && selectionInsets.bottom == 0) ||
(horizontal && selectionInsets.left == 0 && selectionInsets.right == 0));
}
/**
* Paints a cell selection at the given coordinates.
* The selection color must be set on the graphics context.
* <p>
* This method is intended for use in custom cell renderers.
*
* @since 3
*/
public static void paintCellSelection( JList<?> list, Graphics g, int row, int x, int y, int width, int height ) {
if( !(list.getUI() instanceof FlatListUI) )
return;
FlatListUI ui = (FlatListUI) list.getUI();
ui.paintCellSelection( g, row, x, y, width, height );
}
} }

View File

@@ -51,6 +51,12 @@ public class FlatMenuBarBorder
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( !showBottomSeparator( c ) ) if( !showBottomSeparator( c ) )

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@@ -27,6 +29,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.ActionMap; import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI; import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -70,7 +75,12 @@ public class FlatMenuBarUI
/** @since 2 */ @Styleable protected Insets itemMargins; /** @since 2 */ @Styleable protected Insets itemMargins;
// used in FlatMenuUI // used in FlatMenuUI
/** @since 3 */ @Styleable protected Insets selectionInsets;
/** @since 3 */ @Styleable protected Insets selectionEmbeddedInsets;
/** @since 3 */ @Styleable protected int selectionArc = -1;
/** @since 2 */ @Styleable protected Color hoverBackground; /** @since 2 */ @Styleable protected Color hoverBackground;
/** @since 2.5 */ @Styleable protected Color selectionBackground;
/** @since 2.5 */ @Styleable protected Color selectionForeground;
/** @since 2 */ @Styleable protected Color underlineSelectionBackground; /** @since 2 */ @Styleable protected Color underlineSelectionBackground;
/** @since 2 */ @Styleable protected Color underlineSelectionColor; /** @since 2 */ @Styleable protected Color underlineSelectionColor;
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1; /** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
@@ -100,6 +110,10 @@ public class FlatMenuBarUI
super.installDefaults(); super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false ); LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
} }
@Override @Override
@@ -165,6 +179,12 @@ public class FlatMenuBarUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, menuBar.getBorder(), key );
}
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
// paint background // paint background
@@ -221,6 +241,130 @@ public class FlatMenuBarUI
rootPane.getWindowDecorationStyle() != JRootPane.NONE; 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 ---------------------------------------------------- //---- class TakeFocus ----------------------------------------------------
/** /**

View File

@@ -63,6 +63,8 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuItem.acceleratorArrowGap int * @uiDefault MenuItem.acceleratorArrowGap int
* @uiDefault MenuItem.checkBackground Color * @uiDefault MenuItem.checkBackground Color
* @uiDefault MenuItem.checkMargins Insets * @uiDefault MenuItem.checkMargins Insets
* @uiDefault MenuItem.selectionInsets Insets
* @uiDefault MenuItem.selectionArc int
* @uiDefault MenuItem.selectionType String null (default) or underline * @uiDefault MenuItem.selectionType String null (default) or underline
* @uiDefault MenuItem.underlineSelectionBackground Color * @uiDefault MenuItem.underlineSelectionBackground Color
* @uiDefault MenuItem.underlineSelectionCheckBackground Color * @uiDefault MenuItem.underlineSelectionCheckBackground Color
@@ -91,12 +93,16 @@ public class FlatMenuItemRenderer
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" ); @Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
@Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" ); @Styleable protected Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
/** @since 3 */ @Styleable protected Insets selectionInsets = UIManager.getInsets( "MenuItem.selectionInsets" );
/** @since 3 */ @Styleable protected int selectionArc = UIManager.getInt( "MenuItem.selectionArc" );
@Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" ); @Styleable protected Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
@Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" ); @Styleable protected Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" ); @Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" ); @Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
private boolean iconsShared = true; private boolean iconsShared = true;
private final Font menuFont = UIManager.getFont( "Menu.font" );
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon, protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter ) Font acceleratorFont, String acceleratorDelimiter )
@@ -169,6 +175,19 @@ public class FlatMenuItemRenderer
return infos; return infos;
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
if( key.startsWith( "icon." ) ) {
String key2 = key.substring( "icon.".length() );
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
return ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableValue( key2 );
if( arrowIcon instanceof FlatMenuArrowIcon )
return ((FlatMenuArrowIcon)arrowIcon).getStyleableValue( key2 );
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
protected Dimension getPreferredMenuItemSize() { protected Dimension getPreferredMenuItemSize() {
int width = 0; int width = 0;
int height = 0; int height = 0;
@@ -180,7 +199,8 @@ public class FlatMenuItemRenderer
// layout icon and text // layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem, SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(), menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(), menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(), menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) ); viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
@@ -282,7 +302,8 @@ public class FlatMenuItemRenderer
// layout icon and text // layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem, SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(), menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(), menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(), menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) ); labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
@@ -321,10 +342,16 @@ public class FlatMenuItemRenderer
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 ); g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
debug*/ debug*/
boolean armedOrSelected = isArmedOrSelected( menuItem );
boolean underlineSelection = isUnderlineSelection(); boolean underlineSelection = isUnderlineSelection();
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) ) paintBackground( g );
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight ); if( armedOrSelected ) {
if( underlineSelection )
paintUnderlineSelection( g, underlineSelectionBackground, underlineSelectionColor, underlineSelectionHeight );
else
paintSelection( g, selectionBackground, selectionInsets, selectionArc );
}
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground ); paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground ); paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground ); paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
@@ -332,21 +359,35 @@ debug*/
paintArrowIcon( g, arrowRect, arrowIcon ); paintArrowIcon( g, arrowRect, arrowIcon );
} }
protected void paintBackground( Graphics g, Color selectionBackground ) { /** @since 3 */
boolean armedOrSelected = isArmedOrSelected( menuItem ); protected void paintBackground( Graphics g ) {
if( menuItem.isOpaque() || armedOrSelected ) { if( menuItem.isOpaque() ) {
// paint background g.setColor( menuItem.getBackground() );
g.setColor( armedOrSelected
? deriveBackground( selectionBackground )
: menuItem.getBackground() );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() ); g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
} }
} }
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) { /** @since 3 */
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
float arc = scale( selectionArc / 2f );
g.setColor( deriveBackground( selectionBackground ) );
FlatUIUtils.paintSelection( (Graphics2D) g, 0, 0, menuItem.getWidth(), menuItem.getHeight(),
scale( selectionInsets ), arc, arc, arc, arc, 0 );
}
/** @since 3 */
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionBackground,
Color underlineSelectionColor, int underlineSelectionHeight )
{
int width = menuItem.getWidth(); int width = menuItem.getWidth();
int height = menuItem.getHeight(); int height = menuItem.getHeight();
// paint background
g.setColor( deriveBackground( underlineSelectionBackground ) );
g.fillRect( 0, 0, width, height );
// paint underline
int underlineHeight = scale( underlineSelectionHeight ); int underlineHeight = scale( underlineSelectionHeight );
g.setColor( underlineSelectionColor ); g.setColor( underlineSelectionColor );
if( isTopLevelMenu( menuItem ) ) { if( isTopLevelMenu( menuItem ) ) {
@@ -392,9 +433,10 @@ debug*/
} }
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1; int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
Color foreground = (isTopLevelMenu( menuItem ) ? menuItem.getParent() : menuItem).getForeground(); boolean isTopLevelMenu = isTopLevelMenu( menuItem );
Color foreground = (isTopLevelMenu ? menuItem.getParent() : menuItem).getForeground();
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(), paintText( g, menuItem, textRect, text, mnemonicIndex, isTopLevelMenu ? getTopLevelFont() : menuItem.getFont(),
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground ); foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
} }
@@ -446,6 +488,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem, protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
Rectangle textRect, View htmlView, Color selectionForeground ) 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 ) if( isArmedOrSelected( menuItem ) && selectionForeground != null )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground ); g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
@@ -468,6 +523,15 @@ debug*/
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) ); return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
} }
private Font getTopLevelFont() {
Font font = menuItem.getFont();
// menu item parent may be null if JMenu.isTopLevelMenu() is overridden
// and does not check parent (e.g. com.jidesoft.swing.JideMenu.isTopLevelMenu())
return (font != menuFont || menuItem.getParent() == null)
? font
: menuItem.getParent().getFont();
}
private Icon getIconForPainting() { private Icon getIconForPainting() {
Icon icon = menuItem.getIcon(); Icon icon = menuItem.getIcon();

View File

@@ -16,16 +16,19 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.basic.BasicMenuItemUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -58,9 +61,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
public class FlatMenuItemUI public class FlatMenuItemUI
extends BasicMenuItemUI extends BasicMenuItemUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -119,42 +128,54 @@ public class FlatMenuItemUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
return applyStyleProperty( menuItem, this, renderer, key, value );
}
static Object applyStyleProperty( JMenuItem menuItem, BasicMenuItemUI ui,
FlatMenuItemRenderer renderer, String key, Object value )
{
try { try {
return renderer.applyStyleProperty( key, value ); return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) { } catch ( UnknownStyleException ex ) {
// ignore // ignore
} }
Object oldValue; return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
switch( key ) {
// BasicMenuItemUI
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
}
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
} }
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return getStyleableInfos( renderer ); return getStyleableInfos( this, renderer );
} }
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) { static Map<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>(); Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
infos.put( "selectionBackground", Color.class );
infos.put( "selectionForeground", Color.class );
infos.put( "disabledForeground", Color.class );
infos.put( "acceleratorForeground", Color.class );
infos.put( "acceleratorSelectionForeground", Color.class );
infos.putAll( renderer.getStyleableInfos() ); infos.putAll( renderer.getStyleableInfos() );
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return getStyleableValue( this, renderer, key );
}
static Object getStyleableValue( BasicMenuItemUI ui, FlatMenuItemRenderer renderer, String key ) {
Object value = renderer.getStyleableValue( key );
if( value == null )
value = FlatStylingSupport.getAnnotatedStyleableValue( ui, key );
return value;
}
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
}
@Override @Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) { protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize(); return renderer.getPreferredMenuItemSize();

View File

@@ -20,8 +20,12 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.ButtonModel; import javax.swing.ButtonModel;
@@ -30,14 +34,18 @@ import javax.swing.JComponent;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JRootPane;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.MenuBarUI; import javax.swing.plaf.MenuBarUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
import javax.swing.plaf.basic.BasicMenuUI; import javax.swing.plaf.basic.BasicMenuUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
/** /**
@@ -71,16 +79,27 @@ import com.formdev.flatlaf.util.LoggingFacade;
* *
* <!-- FlatMenuRenderer --> * <!-- FlatMenuRenderer -->
* *
* @uiDefault MenuBar.selectionInsets Insets
* @uiDefault MenuBar.selectionEmbeddedInsets Insets
* @uiDefault MenuBar.selectionArc int
* @uiDefault MenuBar.hoverBackground Color * @uiDefault MenuBar.hoverBackground Color
* @uiDefault MenuBar.selectionBackground Color optional; defaults to Menu.selectionBackground
* @uiDefault MenuBar.selectionForeground Color optional; defaults to Menu.selectionForeground
* @uiDefault MenuBar.underlineSelectionBackground Color * @uiDefault MenuBar.underlineSelectionBackground Color
* @uiDefault MenuBar.underlineSelectionColor Color * @uiDefault MenuBar.underlineSelectionColor Color
* @uiDefault MenuBar.underlineSelectionHeight int * @uiDefault MenuBar.underlineSelectionHeight int
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
public class FlatMenuUI public class FlatMenuUI
extends BasicMenuUI extends BasicMenuUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -166,29 +185,27 @@ public class FlatMenuUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
try { return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
Object oldValue;
switch( key ) {
// BasicMenuItemUI
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
}
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
} }
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer ); return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
}
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
} }
@Override @Override
@@ -215,10 +232,15 @@ public class FlatMenuUI
protected class FlatMenuRenderer protected class FlatMenuRenderer
extends FlatMenuItemRenderer extends FlatMenuItemRenderer
{ {
/** @since 3 */ protected Insets menuBarSelectionInsets = UIManager.getInsets( "MenuBar.selectionInsets" );
/** @since 3 */ protected Insets menuBarSelectionEmbeddedInsets = UIManager.getInsets( "MenuBar.selectionEmbeddedInsets" );
/** @since 3 */ protected int menuBarSelectionArc = UIManager.getInt( "MenuBar.selectionArc" );
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" ); protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground ); /** @since 2.5 */ protected Color menuBarSelectionBackground = UIManager.getColor( "MenuBar.selectionBackground" );
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor ); /** @since 2.5 */ protected Color menuBarSelectionForeground = UIManager.getColor( "MenuBar.selectionForeground" );
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight ); protected Color menuBarUnderlineSelectionBackground = UIManager.getColor( "MenuBar.underlineSelectionBackground" );
protected Color menuBarUnderlineSelectionColor = UIManager.getColor( "MenuBar.underlineSelectionColor" );
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", -1 );
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon, protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter ) Font acceleratorFont, String acceleratorDelimiter )
@@ -226,32 +248,76 @@ public class FlatMenuUI
super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter ); super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
} }
/** @since 3 */
@Override @Override
protected void paintBackground( Graphics g, Color selectionBackground ) { protected void paintBackground( Graphics g ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) { super.paintBackground( g );
if( isUnderlineSelection() )
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
ButtonModel model = menuItem.getModel(); if( ((JMenu)menuItem).isTopLevelMenu() && isHover() ) {
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) { // paint hover background
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) ); Color color = deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) );
if( isUnderlineSelection() ) {
g.setColor( color );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() ); g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
return; } else
} paintSelection( g, color, selectionInsets, selectionArc );
}
}
/** @since 3 */
@Override
protected void paintSelection( Graphics g, Color selectionBackground, Insets selectionInsets, int selectionArc ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
if( !isHover() )
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
JMenuBar menuBar = (JMenuBar) menuItem.getParent();
JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
if( rootPane != null && rootPane.getParent() instanceof Window &&
rootPane.getJMenuBar() == menuBar &&
FlatRootPaneUI.isMenuBarEmbedded( rootPane ) )
{
selectionInsets = getStyleFromMenuBarUI( ui -> ui.selectionEmbeddedInsets, menuBarSelectionEmbeddedInsets );
} else
selectionInsets = getStyleFromMenuBarUI( ui -> ui.selectionInsets, menuBarSelectionInsets );
selectionArc = getStyleFromMenuBarUI( ui -> (ui.selectionArc != -1)
? ui.selectionArc : null, menuBarSelectionArc );
} }
super.paintBackground( g, selectionBackground ); super.paintSelection( g, selectionBackground, selectionInsets, selectionArc );
}
/** @since 3 */
@Override
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionBackground,
Color underlineSelectionColor, int underlineSelectionHeight )
{
if( ((JMenu)menuItem).isTopLevelMenu() ) {
underlineSelectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground, underlineSelectionBackground );
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor, underlineSelectionColor );
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1) ? ui.underlineSelectionHeight : null,
(menuBarUnderlineSelectionHeight != -1) ? menuBarUnderlineSelectionHeight : underlineSelectionHeight );
}
super.paintUnderlineSelection( g, underlineSelectionBackground, underlineSelectionColor, underlineSelectionHeight );
} }
@Override @Override
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) { protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) { if( ((JMenu)menuItem).isTopLevelMenu() && !isUnderlineSelection() )
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor ); selectionForeground = getStyleFromMenuBarUI( ui -> ui.selectionForeground, menuBarSelectionForeground, selectionForeground );
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
}
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight ); super.paintText( g, textRect, text, selectionForeground, disabledForeground );
}
private boolean isHover() {
ButtonModel model = menuItem.getModel();
return model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled();
}
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue, T defaultValue2 ) {
return getStyleFromMenuBarUI( f, (defaultValue != null) ? defaultValue : defaultValue2 );
} }
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) { private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {

View File

@@ -0,0 +1,115 @@
/*
* 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/11 (x86 or x86_64)
libraryName = "flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
// 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,
// but load jawt.dll anyway to be on the safe side.
loadJAWT();
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
// Linux: requires x86_64
libraryName = "flatlaf-linux-x86_64";
// Load libjawt.so (part of JRE) explicitly because it is not found
// in all Java versions/distributions.
// E.g. not found in Java 13 and later from openjdk.java.net.
// There seems to be also differences between distributions.
// E.g. Adoptium Java 17 does not need this, but Java 17 from openjdk.java.net does.
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 ) {
if( "system".equals( libraryPath ) ) {
NativeLibrary library = new NativeLibrary( libraryName, true );
if( library.isLoaded() )
return library;
LoggingFacade.INSTANCE.logSevere( "Did not find library " + libraryName + " in java.library.path, using extracted library instead", null );
} else {
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
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() {
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( message, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import javax.swing.JDialog;
import javax.swing.JFrame;
/**
* Native methods for Linux.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* @author Karl Tauber
* @since 2.5
*/
class FlatNativeLinuxLibrary
{
static boolean isLoaded() {
return FlatNativeLibrary.isLoaded();
}
// direction for _NET_WM_MOVERESIZE message
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
static final int MOVE = 8;
private static Boolean isXWindowSystem;
private static boolean isXWindowSystem() {
if( isXWindowSystem == null )
isXWindowSystem = Toolkit.getDefaultToolkit().getClass().getName().endsWith( ".XToolkit" );
return isXWindowSystem;
}
static boolean isWMUtilsSupported( Window window ) {
return hasCustomDecoration( window ) && isXWindowSystem() && isLoaded();
}
static boolean moveOrResizeWindow( Window window, MouseEvent e, int direction ) {
Point pt = scale( window, e.getLocationOnScreen() );
return xMoveOrResizeWindow( window, pt.x, pt.y, direction );
/*
try {
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
java.lang.reflect.Method m = cls.getMethod( "xMoveOrResizeWindow", Window.class, int.class, int.class, int.class );
return (Boolean) m.invoke( null, window, pt.x, pt.y, direction );
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
*/
}
static boolean showWindowMenu( Window window, MouseEvent e ) {
Point pt = scale( window, e.getLocationOnScreen() );
return xShowWindowMenu( window, pt.x, pt.y );
/*
try {
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
java.lang.reflect.Method m = cls.getMethod( "xShowWindowMenu", Window.class, int.class, int.class );
return (Boolean) m.invoke( null, window, pt.x, pt.y );
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
*/
}
private static Point scale( Window window, Point pt ) {
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
int x = (int) Math.round( pt.x * transform.getScaleX() );
int y = (int) Math.round( pt.y * transform.getScaleY() );
return new Point( x, y );
}
// X Window System
private static native boolean xMoveOrResizeWindow( Window window, int x, int y, int direction );
private static native boolean xShowWindowMenu( Window window, int x, int y );
private static boolean hasCustomDecoration( Window window ) {
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
}
}

View File

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

View File

@@ -127,6 +127,12 @@ public class FlatPanelUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
// fill background // fill background

View File

@@ -23,6 +23,7 @@ import java.awt.Toolkit;
import java.awt.event.KeyAdapter; import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.util.Map; import java.util.Map;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.ActionMap; import javax.swing.ActionMap;
@@ -233,6 +234,14 @@ public class FlatPasswordFieldUI
return infos; return infos;
} }
@Override
public Object getStyleableValue( JComponent c, String key ) {
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon )
return ((FlatCapsLockIcon)capsLockIcon).getStyleableValue( key );
return super.getStyleableValue( c, key );
}
@Override @Override
public View create( Element elem ) { public View create( Element elem ) {
return new PasswordView( elem ); return new PasswordView( elem );
@@ -283,6 +292,7 @@ public class FlatPasswordFieldUI
protected void installRevealButton() { protected void installRevealButton() {
if( showRevealButton ) { if( showRevealButton ) {
revealButton = createRevealButton(); revealButton = createRevealButton();
updateRevealButton();
installLayout(); installLayout();
getComponent().add( revealButton ); getComponent().add( revealButton );
} }
@@ -290,28 +300,64 @@ public class FlatPasswordFieldUI
/** @since 2 */ /** @since 2 */
protected JToggleButton createRevealButton() { protected JToggleButton createRevealButton() {
JToggleButton button = new JToggleButton( revealIcon ); JPasswordField c = (JPasswordField) getComponent();
JToggleButton button = new JToggleButton( revealIcon, !c.echoCharIsSet() );
button.setName( "PasswordField.revealButton" ); button.setName( "PasswordField.revealButton" );
prepareLeadingOrTrailingComponent( button ); prepareLeadingOrTrailingComponent( button );
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" ); button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
if( FlatClientProperties.clientPropertyBoolean( getComponent(), KEY_REVEAL_SELECTED, false ) ) { if( FlatClientProperties.clientPropertyBoolean( c, KEY_REVEAL_SELECTED, false ) ) {
button.setSelected( true ); button.setSelected( true );
updateEchoChar( true ); updateEchoChar( true );
} }
button.addActionListener( e -> { button.addActionListener( e -> {
boolean selected = button.isSelected(); boolean selected = button.isSelected();
updateEchoChar( selected ); updateEchoChar( selected );
getComponent().putClientProperty( KEY_REVEAL_SELECTED, selected ); c.putClientProperty( KEY_REVEAL_SELECTED, selected );
} ); } );
return button; return button;
} }
/** @since 2.5 */
protected void updateRevealButton() {
if( revealButton == null )
return;
JTextComponent c = getComponent();
boolean visible = c.isEnabled();
if( visible != revealButton.isVisible() ) {
revealButton.setVisible( visible );
c.revalidate();
c.repaint();
if( !visible ) {
revealButton.setSelected( false );
updateEchoChar( false );
getComponent().putClientProperty( KEY_REVEAL_SELECTED, null );
}
}
}
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
switch( e.getPropertyName() ) {
case "enabled":
updateRevealButton();
break;
}
}
private void updateEchoChar( boolean selected ) { private void updateEchoChar( boolean selected ) {
char newEchoChar = selected char newEchoChar = selected
? 0 ? 0
: (echoChar != null ? echoChar : '*'); : (echoChar != null ? echoChar : '*');
JPasswordField c = (JPasswordField) getComponent(); JPasswordField c = (JPasswordField) getComponent();
if( newEchoChar == c.getEchoChar() )
return;
// set echo char
LookAndFeel.installProperty( c, "echoChar", newEchoChar ); LookAndFeel.installProperty( c, "echoChar", newEchoChar );
// check whether was able to set echo char via LookAndFeel.installProperty() // check whether was able to set echo char via LookAndFeel.installProperty()

View File

@@ -36,7 +36,9 @@ import java.awt.Window;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
@@ -64,8 +66,8 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatPopupFactory public class FlatPopupFactory
extends PopupFactory extends PopupFactory
{ {
private Method java8getPopupMethod; private MethodHandle java8getPopupMethod;
private Method java9getPopupMethod; private MethodHandle java9getPopupMethod;
@Override @Override
public Popup getPopup( Component owner, Component contents, int x, int y ) public Popup getPopup( Component owner, Component contents, int x, int y )
@@ -192,23 +194,25 @@ public class FlatPopupFactory
{ {
try { try {
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
// Java 9: protected Popup getPopup( Component owner, Component contents, int x, int y, boolean isHeavyWeightPopup )
if( java9getPopupMethod == null ) { if( java9getPopupMethod == null ) {
java9getPopupMethod = PopupFactory.class.getDeclaredMethod( MethodType mt = MethodType.methodType( Popup.class, Component.class, Component.class, int.class, int.class, boolean.class );
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class ); java9getPopupMethod = MethodHandles.lookup().findVirtual( PopupFactory.class, "getPopup", mt );
} }
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true ); return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
} else { } else {
// Java 8 // Java 8: private Popup getPopup( Component owner, Component contents, int ownerX, int ownerY, int popupType )
if( java8getPopupMethod == null ) { if( java8getPopupMethod == null ) {
java8getPopupMethod = PopupFactory.class.getDeclaredMethod( Method m = PopupFactory.class.getDeclaredMethod(
"getPopup", Component.class, Component.class, int.class, int.class, int.class ); "getPopup", Component.class, Component.class, int.class, int.class, int.class );
java8getPopupMethod.setAccessible( true ); m.setAccessible( true );
java8getPopupMethod = MethodHandles.lookup().unreflect( m );
} }
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 ); return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
} }
} catch( NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex ) { } catch( Throwable ex ) {
// ignore // fallback
return null; return super.getPopup( owner, contents, x, y );
} }
} }

View File

@@ -66,6 +66,16 @@ public class FlatPopupMenuBorder
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
switch( key ) {
case "borderInsets": return getStyleableValue();
case "borderColor": return borderColor;
}
return null;
}
@Override @Override
public Color getLineColor() { public Color getLineColor() {
return (borderColor != null) ? borderColor : super.getLineColor(); return (borderColor != null) ? borderColor : super.getLineColor();

View File

@@ -130,7 +130,7 @@ public class FlatPopupMenuUI
LayoutManager layout = popupMenu.getLayout(); LayoutManager layout = popupMenu.getLayout();
if( layout == null || layout instanceof UIResource ) if( layout == null || layout instanceof UIResource )
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
} }
@Override @Override
@@ -184,6 +184,12 @@ public class FlatPopupMenuUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, popupMenu.getBorder(), key );
}
@Override @Override
public Popup getPopup( JPopupMenu popup, int x, int y ) { public Popup getPopup( JPopupMenu popup, int x, int y ) {
// do not add scroller to combobox popups or to popups that already have a scroll pane // do not add scroller to combobox popups or to popups that already have a scroll pane
@@ -230,12 +236,15 @@ public class FlatPopupMenuUI
return screenBounds.height - screenInsets.top - screenInsets.bottom; return screenBounds.height - screenInsets.top - screenInsets.bottom;
} }
//---- class FlatMenuLayout ----------------------------------------------- //---- class FlatPopupMenuLayout ------------------------------------------
protected static class FlatMenuLayout /**
* @since 2.4
*/
protected static class FlatPopupMenuLayout
extends DefaultMenuLayout extends DefaultMenuLayout
{ {
public FlatMenuLayout( Container target, int axis ) { public FlatPopupMenuLayout( Container target, int axis ) {
super( target, axis ); super( target, axis );
} }

View File

@@ -160,6 +160,12 @@ public class FlatProgressBarUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
Dimension size = super.getPreferredSize( c ); Dimension size = super.getPreferredSize( c );

View File

@@ -16,18 +16,20 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
/** /**
@@ -58,9 +60,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
public class FlatRadioButtonMenuItemUI public class FlatRadioButtonMenuItemUI
extends BasicRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
private FlatMenuItemRenderer renderer; private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -119,29 +127,27 @@ public class FlatRadioButtonMenuItemUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
try { return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
return renderer.applyStyleProperty( key, value );
} catch ( UnknownStyleException ex ) {
// ignore
}
Object oldValue;
switch( key ) {
// BasicMenuItemUI
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
}
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
} }
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return FlatMenuItemUI.getStyleableInfos( renderer ); return FlatMenuItemUI.getStyleableInfos( this, renderer );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
}
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
} }
@Override @Override

View File

@@ -18,12 +18,16 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
@@ -31,6 +35,7 @@ import javax.swing.CellRendererPane;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonListener;
@@ -78,7 +83,7 @@ public class FlatRadioButtonUI
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return FlatUIUtils.canUseSharedUI( c ) return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) ) ? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
: new FlatRadioButtonUI( false ); : new FlatRadioButtonUI( false );
} }
@@ -90,11 +95,29 @@ public class FlatRadioButtonUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
if( FlatUIUtils.isAWTPeer( c ) )
AWTPeerMouseExitedFix.install( c );
installStyle( (AbstractButton) c ); installStyle( (AbstractButton) c );
} }
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
if( FlatUIUtils.isAWTPeer( c ) )
AWTPeerMouseExitedFix.uninstall( c );
}
@Override @Override
public void installDefaults( AbstractButton b ) { public void installDefaults( AbstractButton b ) {
super.installDefaults( b ); super.installDefaults( b );
@@ -199,6 +222,19 @@ public class FlatRadioButtonUI
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
// style icon
if( key.startsWith( "icon." ) ) {
return (icon instanceof FlatCheckBoxIcon)
? ((FlatCheckBoxIcon)icon).getStyleableValue( key.substring( "icon.".length() ) )
: null;
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private static final Insets tempInsets = new Insets( 0, 0, 0, 0 ); private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
@Override @Override
@@ -308,4 +344,69 @@ public class FlatRadioButtonUI
FlatRadioButtonUI.this.propertyChange( b, e ); FlatRadioButtonUI.this.propertyChange( b, e );
} }
} }
//---- class AWTPeerMouseExitedFix ----------------------------------------
/**
* Hack for missing mouse-exited event for java.awt.Checkbox on macOS (to fix hover effect).
*
* On macOS, AWT components internally use Swing components.
* This is implemented in class sun.lwawt.LWCheckboxPeer, which uses
* a container component CheckboxDelegate that has a JCheckBox and a JRadioButton
* as children. Only one of them is visible.
*
* The reason that mouse-exited event is not sent to the JCheckBox or JRadioButton
* is that sun.lwawt.LWComponentPeer.createDelegateEvent() uses
* SwingUtilities.getDeepestComponentAt() to find the event target,
* which finds the container component CheckboxDelegate,
* which receives the mouse-exited event.
*
* This class adds listeners and forwards the mouse-exited event
* from CheckboxDelegate to JCheckBox or JRadioButton.
*/
private static class AWTPeerMouseExitedFix
extends MouseAdapter
implements PropertyChangeListener
{
private final JComponent button;
static void install( JComponent button ) {
AWTPeerMouseExitedFix l = new AWTPeerMouseExitedFix( button );
button.addPropertyChangeListener( "ancestor", l );
Container parent = button.getParent();
if( parent != null )
parent.addMouseListener( l );
}
static void uninstall( JComponent button ) {
for( PropertyChangeListener l : button.getPropertyChangeListeners( "ancestor" ) ) {
if( l instanceof AWTPeerMouseExitedFix ) {
button.removePropertyChangeListener( "ancestor", l );
Container parent = button.getParent();
if( parent != null )
parent.removeMouseListener( (AWTPeerMouseExitedFix) l );
break;
}
}
}
AWTPeerMouseExitedFix( JComponent button ) {
this.button = button;
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
if( e.getOldValue() instanceof Component )
((Component)e.getOldValue()).removeMouseListener( this );
if( e.getNewValue() instanceof Component ) {
((Component)e.getNewValue()).removeMouseListener( this ); // avoid duplicate listeners
((Component)e.getNewValue()).addMouseListener( this );
}
}
@Override
public void mouseExited( MouseEvent e ) {
button.dispatchEvent( SwingUtilities.convertMouseEvent( e.getComponent(), e, button ) );
}
}
} }

View File

@@ -349,6 +349,14 @@ public class FlatRootPaneUI
titlePane.updateIcon(); titlePane.updateIcon();
break; break;
case FlatClientProperties.TITLE_BAR_SHOW_TITLE:
case FlatClientProperties.TITLE_BAR_SHOW_ICONIFFY:
case FlatClientProperties.TITLE_BAR_SHOW_MAXIMIZE:
case FlatClientProperties.TITLE_BAR_SHOW_CLOSE:
if( titlePane != null )
titlePane.updateVisibility();
break;
case FlatClientProperties.TITLE_BAR_BACKGROUND: case FlatClientProperties.TITLE_BAR_BACKGROUND:
case FlatClientProperties.TITLE_BAR_FOREGROUND: case FlatClientProperties.TITLE_BAR_FOREGROUND:
if( titlePane != null ) if( titlePane != null )
@@ -364,6 +372,12 @@ public class FlatRootPaneUI
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded(); ((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 ----------------------------------------------- //---- class FlatRootLayout -----------------------------------------------
protected class FlatRootLayout protected class FlatRootLayout

View File

@@ -17,7 +17,10 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics;
import javax.swing.JSpinner;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.SpinnerUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/** /**
@@ -35,6 +38,19 @@ public class FlatRoundBorder
// only used via styling (not in UI defaults, but has likewise client properties) // only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected Boolean roundRect; /** @since 2 */ @Styleable protected Boolean roundRect;
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// make mac style spinner border smaller (border does not surround arrow buttons)
if( isMacStyleSpinner( c ) ) {
int macStyleButtonsWidth = ((FlatSpinnerUI)((JSpinner)c).getUI()).getMacStyleButtonsWidth();
width -= macStyleButtonsWidth;
if( !c.getComponentOrientation().isLeftToRight() )
x += macStyleButtonsWidth;
}
super.paintBorder( c, g, x, y, width, height );
}
@Override @Override
protected int getArc( Component c ) { protected int getArc( Component c ) {
if( isCellEditor( c ) ) if( isCellEditor( c ) )
@@ -43,6 +59,17 @@ public class FlatRoundBorder
Boolean roundRect = FlatUIUtils.isRoundRect( c ); Boolean roundRect = FlatUIUtils.isRoundRect( c );
if( roundRect == null ) if( roundRect == null )
roundRect = this.roundRect; roundRect = this.roundRect;
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc; return roundRect != null
? (roundRect ? Short.MAX_VALUE : 0)
: (isMacStyleSpinner( c ) ? 0 : arc);
}
private boolean isMacStyleSpinner( Component c ) {
if( c instanceof JSpinner ) {
SpinnerUI ui = ((JSpinner)c).getUI();
if( ui instanceof FlatSpinnerUI )
return ((FlatSpinnerUI)ui).isMacStyle();
}
return false;
} }
} }

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
@@ -24,6 +25,7 @@ import java.awt.Rectangle;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.swing.InputMap; import javax.swing.InputMap;
@@ -36,9 +38,13 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI; import javax.swing.plaf.basic.BasicScrollBarUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -77,9 +83,15 @@ import com.formdev.flatlaf.util.UIScale;
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@StyleableField( cls=BasicScrollBarUI.class, key="track", fieldName="trackColor" )
@StyleableField( cls=BasicScrollBarUI.class, key="thumb", fieldName="thumbColor" )
@StyleableField( cls=BasicScrollBarUI.class, key="width", fieldName="scrollBarWidth" )
@StyleableField( cls=BasicScrollBarUI.class, key="minimumThumbSize" )
@StyleableField( cls=BasicScrollBarUI.class, key="maximumThumbSize" )
public class FlatScrollBarUI public class FlatScrollBarUI
extends BasicScrollBarUI extends BasicScrollBarUI
implements StyleableUI implements StyleableUI, StyleableLookupProvider
{ {
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private) // overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
@Styleable protected boolean allowsAbsolutePositioning; @Styleable protected boolean allowsAbsolutePositioning;
@@ -108,6 +120,7 @@ public class FlatScrollBarUI
protected boolean hoverThumb; protected boolean hoverThumb;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
private boolean isAWTPeer;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatScrollBarUI(); return new FlatScrollBarUI();
@@ -221,6 +234,37 @@ public class FlatScrollBarUI
} }
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap ); SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
break; break;
case "ancestor":
// check whether scroll bar is used as AWT peer on macOS
if( SystemInfo.isMacOS ) {
Container p = scrollbar.getParent();
for( int i = 0; i < 2 && p != null; i++, p = p.getParent() ) {
if( FlatUIUtils.isAWTPeer( p ) ) {
// Used to disable hover, which does not work correctly
// because scroll bars do not receive mouse exited event.
// The scroll pane, including its scroll bars, is not part
// of the component hierarchy and does not receive mouse events
// directly. Instead LWComponentPeer receives mouse events
// and delegates them to peers, but entered/exited events
// are sent only for the whole scroll pane.
// Exited event is only sent when mouse leaves scroll pane.
// If mouse enters/exits scroll bar, no entered/exited events are sent.
isAWTPeer = true;
// if dark theme is active, reinstall using light theme
if( FlatLaf.isLafDark() ) {
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> {
JScrollBar scrollbar = this.scrollbar;
uninstallUI( scrollbar );
installUI( scrollbar );
} );
}
break;
}
}
}
break;
} }
}; };
} }
@@ -246,30 +290,27 @@ public class FlatScrollBarUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
Object oldValue;
switch( key ) {
// BasicScrollBarUI
case "track": oldValue = trackColor; trackColor = (Color) value; return oldValue;
case "thumb": oldValue = thumbColor; thumbColor = (Color) value; return oldValue;
case "width": oldValue = scrollBarWidth; scrollBarWidth = (int) value; return oldValue;
case "minimumThumbSize": oldValue = minimumThumbSize; minimumThumbSize = (Dimension) value; return oldValue;
case "maximumThumbSize": oldValue = maximumThumbSize; maximumThumbSize = (Dimension) value; return oldValue;
}
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value ); return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
} }
/** @since 2 */ /** @since 2 */
@Override @Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) { public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>(); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
infos.put( "track", Color.class ); }
infos.put( "thumb", Color.class );
infos.put( "width", int.class ); /** @since 2.5 */
infos.put( "minimumThumbSize", Dimension.class ); @Override
infos.put( "maximumThumbSize", Dimension.class ); public Object getStyleableValue( JComponent c, String key ) {
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos ); return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
return infos; }
/** @since 2.5 */
@Override
public MethodHandles.Lookup getLookupForStyling() {
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
// otherwise it is not possible to access protected fields in JRE superclass
return MethodHandles.lookup();
} }
@Override @Override
@@ -311,6 +352,9 @@ public class FlatScrollBarUI
@Override @Override
protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) { protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) {
if( trackBounds.isEmpty() || !scrollbar.isEnabled() )
return;
g.setColor( getTrackColor( c, hoverTrack, isPressed && hoverTrack && !hoverThumb ) ); g.setColor( getTrackColor( c, hoverTrack, isPressed && hoverTrack && !hoverThumb ) );
paintTrackOrThumb( g, c, trackBounds, trackInsets, trackArc ); paintTrackOrThumb( g, c, trackBounds, trackInsets, trackArc );
} }
@@ -357,7 +401,7 @@ public class FlatScrollBarUI
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() ); Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
return (pressed && pressedTrackColor != null) return (pressed && pressedTrackColor != null)
? FlatUIUtils.deriveColor( pressedTrackColor, trackColor ) ? FlatUIUtils.deriveColor( pressedTrackColor, trackColor )
: ((hover && hoverTrackColor != null) : ((hover && hoverTrackColor != null && !isAWTPeer)
? FlatUIUtils.deriveColor( hoverTrackColor, trackColor ) ? FlatUIUtils.deriveColor( hoverTrackColor, trackColor )
: trackColor); : trackColor);
} }
@@ -367,7 +411,7 @@ public class FlatScrollBarUI
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor ); Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
return (pressed && pressedThumbColor != null) return (pressed && pressedThumbColor != null)
? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor ) ? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor )
: ((hover && hoverThumbColor != null) : ((hover && hoverThumbColor != null && !isAWTPeer)
? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor ) ? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor )
: thumbColor); : thumbColor);
} }
@@ -411,18 +455,31 @@ public class FlatScrollBarUI
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
isPressed = true; if( SwingUtilities.isLeftMouseButton( e ) || isAbsolutePositioning( e ) ) {
repaint(); isPressed = true;
repaint();
// update hover because BasicScrollBarUI.TrackListener.mousePressed()
// moves the track on middle-click (if absolute positioning is enabled)
if( isAbsolutePositioning( e ) )
update( e.getX(), e.getY() );
}
} }
@Override @Override
public void mouseReleased( MouseEvent e ) { public void mouseReleased( MouseEvent e ) {
isPressed = false; if( SwingUtilities.isLeftMouseButton( e ) || isAbsolutePositioning( e ) ) {
repaint(); isPressed = false;
repaint();
}
update( e.getX(), e.getY() ); update( e.getX(), e.getY() );
} }
private boolean isAbsolutePositioning( MouseEvent e ) {
return getSupportsAbsolutePositioning() && SwingUtilities.isMiddleMouseButton( e );
}
private void update( int x, int y ) { private void update( int x, int y ) {
boolean inTrack = getTrackBounds().contains( x, y ); boolean inTrack = getTrackBounds().contains( x, y );
boolean inThumb = getThumbBounds().contains( x, y ); boolean inThumb = getThumbBounds().contains( x, y );

View File

@@ -87,6 +87,13 @@ public class FlatScrollPaneUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
int focusWidth = UIManager.getInt( "Component.focusWidth" ); int focusWidth = UIManager.getInt( "Component.focusWidth" );
@@ -343,6 +350,12 @@ public class FlatScrollPaneUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, scrollpane.getBorder(), key );
}
@Override @Override
protected void updateViewport( PropertyChangeEvent e ) { protected void updateViewport( PropertyChangeEvent e ) {
super.updateViewport( e ); super.updateViewport( e );

View File

@@ -170,6 +170,12 @@ public class FlatSeparatorUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();

View File

@@ -222,6 +222,12 @@ public class FlatSliderUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override @Override
public int getBaseline( JComponent c, int width, int height ) { public int getBaseline( JComponent c, int width, int height ) {
if( c == null ) if( c == null )
@@ -525,7 +531,7 @@ debug*/
public static Shape createDirectionalThumbShape( float x, float y, float w, float h, float arc ) { public static Shape createDirectionalThumbShape( float x, float y, float w, float h, float arc ) {
float wh = w / 2; float wh = w / 2;
Path2D path = new Path2D.Float(); Path2D path = new Path2D.Float( Path2D.WIND_NON_ZERO, 9 );
path.moveTo( x + wh, y + h ); path.moveTo( x + wh, y + h );
path.lineTo( x, y + (h - wh) ); path.lineTo( x, y + (h - wh) );
path.lineTo( x, y + arc ); path.lineTo( x, y + arc );

View File

@@ -65,7 +65,7 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatSpinnerUI --> * <!-- FlatSpinnerUI -->
* *
* @uiDefault Component.minimumWidth int * @uiDefault Component.minimumWidth int
* @uiDefault Spinner.buttonStyle String button (default) or none * @uiDefault Spinner.buttonStyle String button (default), mac or none
* @uiDefault Component.arrowType String chevron (default) or triangle * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Spinner.disabledBackground Color * @uiDefault Spinner.disabledBackground Color
@@ -223,6 +223,12 @@ public class FlatSpinnerUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, spinner.getBorder(), key );
}
@Override @Override
protected JComponent createEditor() { protected JComponent createEditor() {
JComponent editor = super.createEditor(); JComponent editor = super.createEditor();
@@ -334,7 +340,25 @@ public class FlatSpinnerUI
private Component createArrowButton( int direction, String name ) { private Component createArrowButton( int direction, String name ) {
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor, FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null ); buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null )
{
@Override
public int getArrowWidth() {
return isMacStyle() ? 7 : super.getArrowWidth();
}
@Override
public float getArrowThickness() {
return isMacStyle() ? 1.5f : super.getArrowThickness();
}
@Override
public float getYOffset() {
return isMacStyle() ? 0 : super.getYOffset();
}
@Override
public boolean isRoundBorderAutoXOffset() {
return isMacStyle() ? false : super.isRoundBorderAutoXOffset();
}
};
button.setName( name ); button.setName( name );
button.setYOffset( (direction == SwingConstants.NORTH) ? 1.25f : -1.25f ); button.setYOffset( (direction == SwingConstants.NORTH) ? 1.25f : -1.25f );
if( direction == SwingConstants.NORTH ) if( direction == SwingConstants.NORTH )
@@ -368,10 +392,13 @@ public class FlatSpinnerUI
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
boolean enabled = spinner.isEnabled(); boolean enabled = spinner.isEnabled();
boolean ltr = spinner.getComponentOrientation().isLeftToRight();
boolean isMacStyle = isMacStyle();
int macStyleButtonsWidth = isMacStyle ? getMacStyleButtonsWidth() : 0;
// paint background // paint background
g2.setColor( getBackground( enabled ) ); g2.setColor( getBackground( enabled ) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc ); FlatUIUtils.paintComponentBackground( g2, ltr ? 0 : macStyleButtonsWidth, 0, width - macStyleButtonsWidth, height, focusWidth, arc );
// paint button background and separator // paint button background and separator
boolean paintButton = !"none".equals( buttonStyle ); boolean paintButton = !"none".equals( buttonStyle );
@@ -380,27 +407,49 @@ public class FlatSpinnerUI
Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton; Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
int arrowX = button.getX(); int arrowX = button.getX();
int arrowWidth = button.getWidth(); int arrowWidth = button.getWidth();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
// paint arrow buttons background
if( enabled && buttonBackground != null ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint vertical line between value and arrow buttons
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor; Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
g2.setColor( separatorColor ); if( isMacStyle ) {
Insets insets = spinner.getInsets();
int lineWidth = Math.round( FlatUIUtils.getBorderLineWidth( spinner ) );
int bx = arrowX;
int by = insets.top - lineWidth;
int bw = arrowWidth;
int bh = height - insets.top - insets.bottom + (lineWidth * 2);
float lw = scale( buttonSeparatorWidth ); float lw = scale( buttonSeparatorWidth );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) ); // buttons border
FlatUIUtils.paintOutlinedComponent( g2, bx, by, bw, bh,
0, 0, 0, lw, scale( 12 ),
null, separatorColor, buttonBackground );
// separator between buttons
if( separatorColor != null ) {
int thickness = scale( 1 );
g2.setColor( separatorColor );
g2.fill( new Rectangle2D.Float( bx + lw, by + ((bh - thickness) / 2f),
bw - (lw * 2), thickness ) );
}
} else {
// paint arrow buttons background
if( enabled && buttonBackground != null ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( ltr )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint vertical line between value and arrow buttons
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
g2.setColor( separatorColor );
float lw = scale( buttonSeparatorWidth );
float lx = ltr ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
}
} }
} }
@@ -409,6 +458,19 @@ public class FlatSpinnerUI
FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
boolean isMacStyle() {
return "mac".equals( buttonStyle );
}
int getMacStyleButtonsWidth() {
return (handler.nextButton != null || handler.previousButton != null)
? scale( MAC_STEPPER_GAP ) + scale( MAC_STEPPER_WIDTH )
: 0;
}
private static final int MAC_STEPPER_WIDTH = 15;
private static final int MAC_STEPPER_GAP = 3;
//---- class Handler ------------------------------------------------------ //---- class Handler ------------------------------------------------------
private class Handler private class Handler
@@ -465,6 +527,7 @@ public class FlatSpinnerUI
Insets insets = parent.getInsets(); Insets insets = parent.getInsets();
Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( size ), insets ); Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( size ), insets );
// editor gets all space if there are no buttons
if( nextButton == null && previousButton == null ) { if( nextButton == null && previousButton == null ) {
if( editor != null ) if( editor != null )
editor.setBounds( r ); editor.setBounds( r );
@@ -477,22 +540,39 @@ public class FlatSpinnerUI
// limit buttons width to height of a raw spinner (without insets) // limit buttons width to height of a raw spinner (without insets)
FontMetrics fm = spinner.getFontMetrics( spinner.getFont() ); FontMetrics fm = spinner.getFontMetrics( spinner.getFont() );
int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom ); int maxButtonWidth = fm.getHeight() + scale( padding.top ) + scale( padding.bottom );
int minButtonWidth = (maxButtonWidth * 3) / 4;
// make button area square (if spinner has preferred height) // make button area square (except if width is limited)
int buttonsWidth = Math.min( parent.getPreferredSize().height - insets.top - insets.bottom, maxButtonWidth ); boolean isMacStyle = isMacStyle();
int buttonsGap = isMacStyle ? scale( MAC_STEPPER_GAP ) : 0;
int prefButtonWidth = isMacStyle ? scale( MAC_STEPPER_WIDTH ) : buttonsRect.height;
int buttonsWidth = Math.min( Math.max( prefButtonWidth, minButtonWidth ), maxButtonWidth );
// update editor and buttons bounds
buttonsRect.width = buttonsWidth; buttonsRect.width = buttonsWidth;
editorRect.width -= buttonsWidth + buttonsGap;
boolean ltr = parent.getComponentOrientation().isLeftToRight();
if( ltr )
buttonsRect.x += editorRect.width + buttonsGap;
else
editorRect.x += buttonsWidth + buttonsGap;
if( parent.getComponentOrientation().isLeftToRight() ) { // in mac button style increase buttons height and move to the right
editorRect.width -= buttonsWidth; // for exact alignment with border
buttonsRect.x += editorRect.width; if( isMacStyle ) {
} else { int lineWidth = Math.round( FlatUIUtils.getBorderLineWidth( spinner ) );
editorRect.x += buttonsWidth; if( lineWidth > 0 ) {
editorRect.width -= buttonsWidth; buttonsRect.x += ltr ? lineWidth : -lineWidth;
buttonsRect.y -= lineWidth;
buttonsRect.height += lineWidth * 2;
}
} }
// set editor bounds
if( editor != null ) if( editor != null )
editor.setBounds( editorRect ); editor.setBounds( editorRect );
// set buttons bounds
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
if( nextButton != null ) if( nextButton != null )
nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight ); nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight );

View File

@@ -34,6 +34,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
@@ -52,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault SplitPaneDivider.border Border * @uiDefault SplitPaneDivider.border Border
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false * @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
* *
* <!-- BasicSplitPaneDivider -->
*
* @uiDefault SplitPane.oneTouchButtonSize int
* @uiDefault SplitPane.oneTouchButtonOffset int
* @uiDefault SplitPane.centerOneTouchButtons boolean
* @uiDefault SplitPane.supportsOneTouchButtons boolean optional; default is true
*
* <!-- JSplitPane --> * <!-- JSplitPane -->
* *
* @uiDefault SplitPane.continuousLayout boolean * @uiDefault SplitPane.continuousLayout boolean
@@ -175,6 +183,17 @@ public class FlatSplitPaneUI
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
if( divider instanceof FlatSplitPaneDivider ) {
Object value = ((FlatSplitPaneDivider)divider).getStyleableValue( key );
if( value != null )
return value;
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
//---- class FlatSplitPaneDivider ----------------------------------------- //---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider
@@ -192,20 +211,21 @@ public class FlatSplitPaneUI
setLayout( new FlatDividerLayout() ); setLayout( new FlatDividerLayout() );
} }
/** /** @since 2 */
* @since 2
*/
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
return FlatStylingSupport.applyToAnnotatedObject( this, key, value ); return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
} }
/** /** @since 2 */
* @since 2
*/
public Map<String, Class<?>> getStyleableInfos() { public Map<String, Class<?>> getStyleableInfos() {
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
void updateStyle() { void updateStyle() {
if( leftButton instanceof FlatOneTouchButton ) if( leftButton instanceof FlatOneTouchButton )
((FlatOneTouchButton)leftButton).updateStyle(); ((FlatOneTouchButton)leftButton).updateStyle();
@@ -235,7 +255,7 @@ public class FlatSplitPaneUI
switch( e.getPropertyName() ) { switch( e.getPropertyName() ) {
case JSplitPane.DIVIDER_LOCATION_PROPERTY: case JSplitPane.DIVIDER_LOCATION_PROPERTY:
// necessary to show/hide one-touch buttons on expand/collapse // necessary to show/hide one-touch buttons on expand/collapse
revalidate(); doLayout();
break; break;
} }
} }
@@ -345,7 +365,7 @@ public class FlatSplitPaneUI
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() ) if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
return; return;
// increase side of buttons, which makes them easier to hit by the user // increase size of buttons, which makes them easier to hit by the user
// and avoids cut arrows at small divider sizes // and avoids cut arrows at small divider sizes
int extraSize = UIScale.scale( 4 ); int extraSize = UIScale.scale( 4 );
if( orientation == JSplitPane.VERTICAL_SPLIT ) { if( orientation == JSplitPane.VERTICAL_SPLIT ) {
@@ -360,10 +380,19 @@ public class FlatSplitPaneUI
// hide buttons if not applicable // hide buttons if not applicable
boolean leftCollapsed = isLeftCollapsed(); boolean leftCollapsed = isLeftCollapsed();
if( leftCollapsed ) boolean rightCollapsed = isRightCollapsed();
if( leftCollapsed || rightCollapsed ) {
leftButton.setVisible( !leftCollapsed );
rightButton.setVisible( !rightCollapsed );
} else {
Object expandableSide = splitPane.getClientProperty( FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE );
leftButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_LEFT.equals( expandableSide ) );
rightButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_RIGHT.equals( expandableSide ) );
}
// move right button if left button is hidden
if( !leftButton.isVisible() )
rightButton.setLocation( leftButton.getLocation() ); rightButton.setLocation( leftButton.getLocation() );
leftButton.setVisible( !leftCollapsed );
rightButton.setVisible( !isRightCollapsed() );
} }
} }
} }

View File

@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@@ -63,15 +65,55 @@ public class FlatStylingSupport
Class<?> type() default Void.class; Class<?> type() default Void.class;
} }
/**
* Indicates that a field in the specified (super) class
* is intended to be used by FlatLaf styling support.
* <p>
* Use this annotation, instead of {@link Styleable}, to style fields
* in superclasses, where it is not possible to use {@link Styleable}.
* <p>
* Classes using this annotation may implement {@link StyleableLookupProvider}
* to give access to protected fields (in JRE) in modular applications.
*
* @since 2.5
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(StyleableFields.class)
public @interface StyleableField {
Class<?> cls();
String key();
String fieldName() default "";
}
/**
* Container annotation for {@link StyleableField}.
*
* @since 2.5
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StyleableFields {
StyleableField[] value();
}
/** @since 2 */ /** @since 2 */
public interface StyleableUI { public interface StyleableUI {
Map<String, Class<?>> getStyleableInfos( JComponent c ); Map<String, Class<?>> getStyleableInfos( JComponent c );
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key );
} }
/** @since 2 */ /** @since 2 */
public interface StyleableBorder { public interface StyleableBorder {
Object applyStyleProperty( String key, Object value ); Object applyStyleProperty( String key, Object value );
Map<String, Class<?>> getStyleableInfos(); Map<String, Class<?>> getStyleableInfos();
/** @since 2.5 */ Object getStyleableValue( String key );
}
/** @since 2.5 */
public interface StyleableLookupProvider {
MethodHandles.Lookup getLookupForStyling();
} }
@@ -365,21 +407,25 @@ public class FlatStylingSupport
public static Object applyToAnnotatedObject( Object obj, String key, Object value ) public static Object applyToAnnotatedObject( Object obj, String key, Object value )
throws UnknownStyleException, IllegalArgumentException throws UnknownStyleException, IllegalArgumentException
{ {
String fieldName = key; String fieldName = keyToFieldName( key );
int dotIndex = key.indexOf( '.' );
if( dotIndex >= 0 ) {
// remove first dot in key and change subsequent character to uppercase
fieldName = key.substring( 0, dotIndex )
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
+ key.substring( dotIndex + 2 );
}
return applyToField( obj, fieldName, key, value, field -> { return applyToField( obj, fieldName, key, value, field -> {
Styleable styleable = field.getAnnotation( Styleable.class ); Styleable styleable = field.getAnnotation( Styleable.class );
return styleable != null && styleable.dot() == (dotIndex >= 0); return styleable != null && styleable.dot() == (fieldName != key);
} ); } );
} }
private static String keyToFieldName( String key ) {
int dotIndex = key.indexOf( '.' );
if( dotIndex < 0 )
return key;
// remove first dot in key and change subsequent character to uppercase
return key.substring( 0, dotIndex )
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
+ key.substring( dotIndex + 2 );
}
/** /**
* Applies the given value to a field of the given object. * Applies the given value to a field of the given object.
* *
@@ -405,26 +451,17 @@ public class FlatStylingSupport
for(;;) { for(;;) {
try { try {
Field f = cls.getDeclaredField( fieldName ); Field f = cls.getDeclaredField( fieldName );
if( predicate == null || predicate.test( f ) ) { if( predicate == null || predicate.test( f ) )
if( !isValidField( f ) ) return applyToField( f, obj, value, false );
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final or static" );
try {
// necessary to access protected fields in other packages
f.setAccessible( true );
// get old value and set new value
Object oldValue = f.get( obj );
f.set( obj, convertToEnum( value, f.getType() ) );
return oldValue;
} catch( IllegalAccessException ex ) {
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'", ex );
}
}
} catch( NoSuchFieldException ex ) { } catch( NoSuchFieldException ex ) {
// field not found in class --> try superclass // field not found in class --> try superclass
} }
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
if( key.equals( styleableField.key() ) )
return applyToField( getStyleableField( styleableField ), obj, value, true );
}
cls = cls.getSuperclass(); cls = cls.getSuperclass();
if( cls == null ) if( cls == null )
throw new UnknownStyleException( key ); throw new UnknownStyleException( key );
@@ -437,11 +474,83 @@ public class FlatStylingSupport
} }
} }
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles ) {
checkValidField( f );
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
try {
// use method handles to access protected fields in JRE in modular applications
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
// get old value and set new value
Object oldValue = lookup.unreflectGetter( f ).invoke( obj );
lookup.unreflectSetter( f ).invoke( obj, convertToEnum( value, f.getType() ) );
return oldValue;
} catch( Throwable ex ) {
throw newFieldAccessFailed( f, ex );
}
}
try {
// necessary to access protected fields in other packages
f.setAccessible( true );
// get old value and set new value
Object oldValue = f.get( obj );
f.set( obj, convertToEnum( value, f.getType() ) );
return oldValue;
} catch( IllegalAccessException ex ) {
throw newFieldAccessFailed( f, ex );
}
}
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles ) {
checkValidField( f );
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
// use method handles to access protected fields in JRE in modular applications
try {
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
return lookup.unreflectGetter( f ).invoke( obj );
} catch( Throwable ex ) {
throw newFieldAccessFailed( f, ex );
}
}
try {
f.setAccessible( true );
return f.get( obj );
} catch( IllegalAccessException ex ) {
throw newFieldAccessFailed( f, ex );
}
}
private static IllegalArgumentException newFieldAccessFailed( Field f, Throwable ex ) {
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
}
private static void checkValidField( Field f ) {
if( !isValidField( f ) )
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
}
private static boolean isValidField( Field f ) { private static boolean isValidField( Field f ) {
int modifiers = f.getModifiers(); int modifiers = f.getModifiers();
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic(); return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
} }
private static Field getStyleableField( StyleableField styleableField ) {
String fieldName = styleableField.fieldName();
if( fieldName.isEmpty() )
fieldName = styleableField.key();
try {
return styleableField.cls().getDeclaredField( fieldName );
} catch( NoSuchFieldException ex ) {
throw new IllegalArgumentException( "field '" + styleableField.cls().getName() + "." + fieldName + "' not found", ex );
}
}
/** /**
* Applies the given value to a property of the given object. * Applies the given value to a property of the given object.
* Works only for properties that have public getter and setter methods. * Works only for properties that have public getter and setter methods.
@@ -628,6 +737,7 @@ public class FlatStylingSupport
Class<?> cls = obj.getClass(); Class<?> cls = obj.getClass();
for(;;) { for(;;) {
// find fields annotated with 'Styleable'
for( Field f : cls.getDeclaredFields() ) { for( Field f : cls.getDeclaredFields() ) {
if( !isValidField( f ) ) if( !isValidField( f ) )
continue; continue;
@@ -666,6 +776,20 @@ public class FlatStylingSupport
infos.put( name, type ); infos.put( name, type );
} }
// get fields specified in 'StyleableField' annotation
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
String name = styleableField.key();
// for the case that the same field name is used in a class and in
// one of its superclasses, do not process field in superclass
if( processedFields.contains( name ) )
continue;
processedFields.add( name );
Field f = getStyleableField( styleableField );
infos.put( name, f.getType() );
}
cls = cls.getSuperclass(); cls = cls.getSuperclass();
if( cls == null ) if( cls == null )
return; return;
@@ -686,6 +810,52 @@ public class FlatStylingSupport
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() ); infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
} }
public static Object getAnnotatedStyleableValue( Object obj, String key ) {
String fieldName = keyToFieldName( key );
Class<?> cls = obj.getClass();
for(;;) {
try {
// find field annotated with 'Styleable'
Field f = cls.getDeclaredField( fieldName );
Styleable styleable = f.getAnnotation( Styleable.class );
if( styleable != null ) {
if( styleable.dot() != (fieldName != key) )
throw new IllegalArgumentException( "'Styleable.dot' on field '" + fieldName + "' does not match key '" + key + "'" );
if( styleable.type() != Void.class )
throw new IllegalArgumentException( "'Styleable.type' on field '" + fieldName + "' not supported" );
return getFieldValue( f, obj, false );
}
} catch( NoSuchFieldException ex ) {
// field not found in class --> try superclass
}
// find field specified in 'StyleableField' annotation
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
if( key.equals( styleableField.key() ) )
return getFieldValue( getStyleableField( styleableField ), obj, true );
}
cls = cls.getSuperclass();
if( cls == null )
return null;
String superclassName = cls.getName();
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
return null;
}
}
public static Object getAnnotatedStyleableValue( Object obj, Border border, String key ) {
if( border instanceof StyleableBorder ) {
Object value = ((StyleableBorder)border).getStyleableValue( key );
if( value != null )
return value;
}
return getAnnotatedStyleableValue( obj, key );
}
//---- class UnknownStyleException ---------------------------------------- //---- class UnknownStyleException ----------------------------------------
public static class UnknownStyleException public static class UnknownStyleException

View File

@@ -59,6 +59,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
import java.util.function.Predicate;
import javax.accessibility.Accessible; import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleContext;
import javax.swing.Action; import javax.swing.Action;
@@ -82,10 +83,12 @@ import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import javax.swing.text.View; import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon; import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -127,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.selectedBackground Color optional * @uiDefault TabbedPane.selectedBackground Color optional
* @uiDefault TabbedPane.selectedForeground Color * @uiDefault TabbedPane.selectedForeground Color
* @uiDefault TabbedPane.underlineColor Color * @uiDefault TabbedPane.underlineColor Color
* @uiDefault TabbedPane.inactiveUnderlineColor Color
* @uiDefault TabbedPane.disabledUnderlineColor Color * @uiDefault TabbedPane.disabledUnderlineColor Color
* @uiDefault TabbedPane.hoverColor Color * @uiDefault TabbedPane.hoverColor Color
* @uiDefault TabbedPane.focusColor Color * @uiDefault TabbedPane.focusColor Color
@@ -141,7 +145,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.showTabSeparators boolean * @uiDefault TabbedPane.showTabSeparators boolean
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean * @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
* @uiDefault TabbedPane.hasFullBorder boolean * @uiDefault TabbedPane.hasFullBorder boolean
* @uiDefault TabbedPane.activeTabBorder boolean * @uiDefault TabbedPane.rotateTabRuns boolean
* *
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll * @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
* @uiDefault TabbedPane.tabType String underlined (default) or card * @uiDefault TabbedPane.tabType String underlined (default) or card
@@ -198,6 +202,7 @@ public class FlatTabbedPaneUI
@Styleable protected Color selectedBackground; @Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground; @Styleable protected Color selectedForeground;
@Styleable protected Color underlineColor; @Styleable protected Color underlineColor;
/** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor;
@Styleable protected Color disabledUnderlineColor; @Styleable protected Color disabledUnderlineColor;
@Styleable protected Color hoverColor; @Styleable protected Color hoverColor;
@Styleable protected Color focusColor; @Styleable protected Color focusColor;
@@ -215,6 +220,7 @@ public class FlatTabbedPaneUI
@Styleable protected boolean tabSeparatorsFullHeight; @Styleable protected boolean tabSeparatorsFullHeight;
@Styleable protected boolean hasFullBorder; @Styleable protected boolean hasFullBorder;
@Styleable protected boolean tabsOpaque = true; @Styleable protected boolean tabsOpaque = true;
/** @since 2.5 */ @Styleable protected boolean rotateTabRuns = true;
@Styleable(type=String.class) private int tabType; @Styleable(type=String.class) private int tabType;
@Styleable(type=String.class) private int tabsPopupPolicy; @Styleable(type=String.class) private int tabsPopupPolicy;
@@ -288,6 +294,7 @@ public class FlatTabbedPaneUI
super.installUI( c ); super.installUI( c );
FlatSelectedTabRepainter.install();
installStyle(); installStyle();
} }
@@ -318,6 +325,7 @@ public class FlatTabbedPaneUI
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" ); selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" ); selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" ); underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor );
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" ); disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" ); hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
focusColor = UIManager.getColor( "TabbedPane.focusColor" ); focusColor = UIManager.getColor( "TabbedPane.focusColor" );
@@ -335,6 +343,7 @@ public class FlatTabbedPaneUI
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" ); tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" ); hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" ); tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
rotateTabRuns = FlatUIUtils.getUIBoolean( "TabbedPane.rotateTabRuns", true );
tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) ); tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) );
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) ); tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
@@ -385,6 +394,7 @@ public class FlatTabbedPaneUI
selectedBackground = null; selectedBackground = null;
selectedForeground = null; selectedForeground = null;
underlineColor = null; underlineColor = null;
inactiveUnderlineColor = null;
disabledUnderlineColor = null; disabledUnderlineColor = null;
hoverColor = null; hoverColor = null;
focusColor = null; focusColor = null;
@@ -679,6 +689,76 @@ public class FlatTabbedPaneUI
return infos; return infos;
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
// close icon
if( key.startsWith( "close" ) ) {
return (closeIcon instanceof FlatTabbedPaneCloseIcon)
? ((FlatTabbedPaneCloseIcon)closeIcon).getStyleableValue( key )
: null;
}
switch( key ) {
// BasicTabbedPaneUI
case "tabInsets": return tabInsets;
case "tabAreaInsets": return tabAreaInsets;
case "textIconGap": return textIconGapUnscaled;
// FlatTabbedPaneUI
case "tabType":
switch( tabType ) {
default:
case TAB_TYPE_UNDERLINED: return TABBED_PANE_TAB_TYPE_UNDERLINED;
case TAB_TYPE_CARD: return TABBED_PANE_TAB_TYPE_CARD;
}
case "tabsPopupPolicy":
switch( tabsPopupPolicy ) {
default:
case AS_NEEDED: return TABBED_PANE_POLICY_AS_NEEDED;
case NEVER: return TABBED_PANE_POLICY_NEVER;
}
case "scrollButtonsPolicy":
switch( scrollButtonsPolicy ) {
default:
case AS_NEEDED_SINGLE: return TABBED_PANE_POLICY_AS_NEEDED_SINGLE;
case AS_NEEDED: return TABBED_PANE_POLICY_AS_NEEDED;
case NEVER: return TABBED_PANE_POLICY_NEVER;
}
case "scrollButtonsPlacement":
switch( scrollButtonsPlacement ) {
default:
case BOTH: return TABBED_PANE_PLACEMENT_BOTH;
case TRAILING: return TABBED_PANE_PLACEMENT_TRAILING;
}
case "tabAreaAlignment": return alignmentToString( tabAreaAlignment, TABBED_PANE_ALIGN_LEADING );
case "tabAlignment": return alignmentToString( tabAlignment, TABBED_PANE_ALIGN_CENTER );
case "tabWidthMode":
switch( tabWidthMode ) {
default:
case WIDTH_MODE_PREFERRED: return TABBED_PANE_TAB_WIDTH_MODE_PREFERRED;
case WIDTH_MODE_EQUAL: return TABBED_PANE_TAB_WIDTH_MODE_EQUAL;
case WIDTH_MODE_COMPACT: return TABBED_PANE_TAB_WIDTH_MODE_COMPACT;
}
case "tabIconPlacement":
switch( tabIconPlacement ) {
default:
case LEADING: return "leading";
case TRAILING: return "trailing";
case TOP: return "top";
case BOTTOM: return "bottom";
}
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
protected void setRolloverTab( int x, int y ) { protected void setRolloverTab( int x, int y ) {
setRolloverTab( tabForCoordinate( tabPane, x, y ) ); setRolloverTab( tabForCoordinate( tabPane, x, y ) );
} }
@@ -733,7 +813,6 @@ public class FlatTabbedPaneUI
// increase size of repaint region to include part of content border // increase size of repaint region to include part of content border
if( contentSeparatorHeight > 0 && if( contentSeparatorHeight > 0 &&
getTabType() == TAB_TYPE_CARD &&
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) ) clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
{ {
int sh = scale( contentSeparatorHeight ); int sh = scale( contentSeparatorHeight );
@@ -1015,7 +1094,7 @@ public class FlatTabbedPaneUI
// paint selection indicator // paint selection indicator
if( isSelected ) if( isSelected )
paintTabSelection( g, tabPlacement, x, y, w, h ); paintTabSelection( g, tabPlacement, tabIndex, x, y, w, h );
if( tabPane.getTabComponentAt( tabIndex ) != null ) if( tabPane.getTabComponentAt( tabIndex ) != null )
return; return;
@@ -1204,12 +1283,19 @@ public class FlatTabbedPaneUI
} }
} }
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) { protected void paintTabSelection( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor ); g.setColor( tabPane.isEnabled()
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
: disabledUnderlineColor );
// paint underline selection // paint underline selection
boolean atBottom = (getTabType() != TAB_TYPE_CARD); boolean atBottom = (getTabType() != TAB_TYPE_CARD);
Insets contentInsets = getContentBorderInsets( tabPlacement ); Insets contentInsets = atBottom
? ((!rotateTabRuns && runCount > 1 && !isScrollTabLayout() && getRunForTab( tabPane.getTabCount(), tabIndex ) > 0)
? new Insets( 0, 0, 0, 0 )
: getContentBorderInsets( tabPlacement ))
: null;
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight ); int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
int sx, sy; int sx, sy;
switch( tabPlacement ) { switch( tabPlacement ) {
@@ -1236,6 +1322,23 @@ public class FlatTabbedPaneUI
} }
} }
/** @since 2.2 */
@SuppressWarnings( "unchecked" )
protected boolean isTabbedPaneOrChildFocused() {
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Object value = tabPane.getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
if( value instanceof Predicate ) {
return ((Predicate<JComponent>)value).test( tabPane ) &&
FlatUIUtils.isInActiveWindow( tabPane, keyboardFocusManager.getActiveWindow() );
}
Component focusOwner = keyboardFocusManager.getPermanentFocusOwner();
return focusOwner != null &&
SwingUtilities.isDescendingFrom( focusOwner, tabPane ) &&
FlatUIUtils.isInActiveWindow( focusOwner, keyboardFocusManager.getActiveWindow() );
}
/** /**
* Actually does nearly the same as super.paintContentBorder() but * Actually does nearly the same as super.paintContentBorder() but
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly * - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
@@ -1351,7 +1454,7 @@ public class FlatTabbedPaneUI
else else
g.clipRect( 0, vr.y, tabPane.getWidth(), vr.height ); g.clipRect( 0, vr.y, tabPane.getWidth(), vr.height );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height ); paintTabSelection( g, tabPlacement, selectedIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip ); g.setClip( oldClip );
} }
} }
@@ -1504,6 +1607,11 @@ public class FlatTabbedPaneUI
super.getTabRunCount( tabPane ); super.getTabRunCount( tabPane );
} }
@Override
protected boolean shouldRotateTabRuns( int tabPlacement ) {
return rotateTabRuns;
}
private boolean isLastInRun( int tabIndex ) { private boolean isLastInRun( int tabIndex ) {
int run = getRunForTab( tabPane.getTabCount(), tabIndex ); int run = getRunForTab( tabPane.getTabCount(), tabIndex );
return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex; return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex;
@@ -1659,6 +1767,16 @@ public class FlatTabbedPaneUI
} }
} }
private static String alignmentToString( int value, String defaultValue ) {
switch( value ) {
case LEADING: return TABBED_PANE_ALIGN_LEADING;
case TRAILING: return TABBED_PANE_ALIGN_TRAILING;
case CENTER: return TABBED_PANE_ALIGN_CENTER;
case FILL: return TABBED_PANE_ALIGN_FILL;
default: return defaultValue;
}
}
protected static int parseTabWidthMode( String str ) { protected static int parseTabWidthMode( String str ) {
if( str == null ) if( str == null )
return WIDTH_MODE_PREFERRED; return WIDTH_MODE_PREFERRED;
@@ -2471,7 +2589,7 @@ public class FlatTabbedPaneUI
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
updateRollover( e ); updateRollover( e );
if( !isPressedTabClose() ) if( !isPressedTabClose() && SwingUtilities.isLeftMouseButton( e ) )
mouseDelegate.mousePressed( e ); mouseDelegate.mousePressed( e );
} }
@@ -2526,7 +2644,7 @@ public class FlatTabbedPaneUI
// check whether mouse hit tab close area // check whether mouse hit tab close area
boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y ); boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y );
if( e.getID() == MouseEvent.MOUSE_PRESSED ) if( e.getID() == MouseEvent.MOUSE_PRESSED && SwingUtilities.isLeftMouseButton( e ) )
pressedTabIndex = hitClose ? tabIndex : -1; pressedTabIndex = hitClose ? tabIndex : -1;
setRolloverTabClose( hitClose ); setRolloverTabClose( hitClose );
setPressedTabClose( hitClose && tabIndex == pressedTabIndex ); setPressedTabClose( hitClose && tabIndex == pressedTabIndex );
@@ -3341,4 +3459,77 @@ public class FlatTabbedPaneUI
delegate.actionPerformed( e ); delegate.actionPerformed( e );
} }
} }
//---- class FlatSelectedTabRepainter -------------------------------------
private static class FlatSelectedTabRepainter
implements PropertyChangeListener//, Runnable
{
private static FlatSelectedTabRepainter instance;
private KeyboardFocusManager keyboardFocusManager;
static void install() {
synchronized( FlatSelectedTabRepainter.class ) {
if( instance != null )
return;
instance = new FlatSelectedTabRepainter();
}
}
FlatSelectedTabRepainter() {
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyboardFocusManager.addPropertyChangeListener( this );
}
private void uninstall() {
synchronized( FlatSelectedTabRepainter.class ) {
if( instance == null )
return;
keyboardFocusManager.removePropertyChangeListener( this );
keyboardFocusManager = null;
instance = null;
}
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
// uninstall if no longer using FlatLaf
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) {
uninstall();
return;
}
switch( e.getPropertyName() ) {
case "permanentFocusOwner":
Object oldValue = e.getOldValue();
Object newValue = e.getNewValue();
if( oldValue instanceof Component )
repaintSelectedTabs( (Component) oldValue );
if( newValue instanceof Component )
repaintSelectedTabs( (Component) newValue );
break;
case "activeWindow":
repaintSelectedTabs( keyboardFocusManager.getPermanentFocusOwner() );
break;
}
}
private void repaintSelectedTabs( Component c ) {
if( c instanceof JTabbedPane )
repaintSelectedTab( (JTabbedPane) c );
while( (c = SwingUtilities.getAncestorOfClass( JTabbedPane.class, c )) != null )
repaintSelectedTab( (JTabbedPane) c );
}
private void repaintSelectedTab( JTabbedPane tabbedPane ) {
TabbedPaneUI ui = tabbedPane.getUI();
if( ui instanceof FlatTabbedPaneUI )
((FlatTabbedPaneUI) ui).repaintTab( tabbedPane.getSelectedIndex() );
}
}
} }

View File

@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI; import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -169,6 +171,22 @@ public class FlatTableHeaderUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
if( key.equals( "sortIconPosition" ) ) {
switch( sortIconPosition ) {
default:
case SwingConstants.RIGHT: return "right";
case SwingConstants.LEFT: return "left";
case SwingConstants.TOP: return "top";
case SwingConstants.BOTTOM: return "bottom";
}
}
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private static int parseSortIconPosition( String str ) { private static int parseSortIconPosition( String str ) {
if( str == null ) if( str == null )
str = ""; str = "";
@@ -195,6 +213,8 @@ public class FlatTableHeaderUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
fixDraggedAndResizingColumns( header );
TableColumnModel columnModel = header.getColumnModel(); TableColumnModel columnModel = header.getColumnModel();
if( columnModel.getColumnCount() <= 0 ) if( columnModel.getColumnCount() <= 0 )
return; return;
@@ -269,6 +289,32 @@ public class FlatTableHeaderUI
return size; 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 ---------------------------------- //---- class FlatTableCellHeaderRenderer ----------------------------------
/** /**

View File

@@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension * @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color * @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color * @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.paintOutsideAlternateRows boolean
* *
* <!-- FlatTableCellBorder --> * <!-- FlatTableCellBorder -->
* *
@@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatTableUI public class FlatTableUI
extends BasicTableUI extends BasicTableUI
implements StyleableUI implements StyleableUI, FlatViewportUI.ViewportPainter
{ {
protected boolean showHorizontalLines; protected boolean showHorizontalLines;
protected boolean showVerticalLines; protected boolean showVerticalLines;
@@ -285,6 +286,12 @@ public class FlatTableUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
/** /**
* Toggle selection colors from focused to inactive and vice versa. * Toggle selection colors from focused to inactive and vice versa.
* *
@@ -313,6 +320,8 @@ public class FlatTableUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
FlatTableHeaderUI.fixDraggedAndResizingColumns( table.getTableHeader() );
boolean horizontalLines = table.getShowHorizontalLines(); boolean horizontalLines = table.getShowHorizontalLines();
boolean verticalLines = table.getShowVerticalLines(); boolean verticalLines = table.getShowVerticalLines();
if( horizontalLines || verticalLines ) { if( horizontalLines || verticalLines ) {
@@ -421,4 +430,38 @@ public class FlatTableUI
? (viewport != rowHeader) ? (viewport != rowHeader)
: (viewport == rowHeader || rowHeader == null); : (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

@@ -86,6 +86,13 @@ public class FlatTextAreaUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
installStyle(); installStyle();
@@ -183,6 +190,12 @@ public class FlatTextAreaUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private void updateBackground() { private void updateBackground() {
FlatTextFieldUI.updateBackground( getComponent(), background, FlatTextFieldUI.updateBackground( getComponent(), background,
disabledBackground, inactiveBackground, disabledBackground, inactiveBackground,

View File

@@ -128,6 +128,13 @@ public class FlatTextFieldUI
@Override @Override
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c ); super.installUI( c );
leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class ); leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class );
@@ -354,6 +361,12 @@ public class FlatTextFieldUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, getComponent().getBorder(), key );
}
private void updateBackground() { private void updateBackground() {
updateBackground( getComponent(), background, updateBackground( getComponent(), background,
disabledBackground, inactiveBackground, disabledBackground, inactiveBackground,

View File

@@ -191,6 +191,12 @@ public class FlatTextPaneUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this ); return FlatStylingSupport.getAnnotatedStyleableInfos( this );
} }
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private void updateBackground() { private void updateBackground() {
FlatTextFieldUI.updateBackground( getComponent(), background, FlatTextFieldUI.updateBackground( getComponent(), background,
disabledBackground, inactiveBackground, disabledBackground, inactiveBackground,

Some files were not shown because too many files have changed in this diff Show More