Compare commits

...

568 Commits

Author SHA1 Message Date
Karl Tauber
78a8035b92 Linux: rounded borders for popups (issue #949) 2025-01-27 17:15:38 +01:00
Karl Tauber
c32c00a5eb Linux with FlatLaf window decorations:
Some checks are pending
CI / build (11) (push) Waiting to run
CI / build-on (17, ) (push) Blocked by required conditions
CI / build-on (21, ) (push) Blocked by required conditions
CI / build-on (23, ) (push) Blocked by required conditions
CI / build-on (8, ) (push) Blocked by required conditions
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
- moved window resizer components from layered pane to rootpane so that border is included in area where user can resize window
- scale border thickness
2025-01-26 17:48:19 +01:00
Karl Tauber
3a8a55a545 Linux with FlatLaf window decorations: right and bottom window resize drag areas were 2px smaller than at left and top sides 2025-01-26 17:23:59 +01:00
Karl Tauber
6c49b8bc4d Linux: hide popups when window is moved, resized, maximized, restored, iconified or switched to another window (issue #962) 2025-01-25 09:48:52 +01:00
Karl Tauber
cca9707f6b Popup: on Windows 10, update drop shadow of heavy-weight popup if popup moved/resized (issue #942)
Some checks failed
CI / build (11) (push) Has been cancelled
CI / build-on (17, ) (push) Has been cancelled
CI / build-on (21, ) (push) Has been cancelled
CI / build-on (23, ) (push) Has been cancelled
CI / build-on (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2025-01-25 07:45:57 +01:00
Karl Tauber
f30dd876e4 Linux: updated libflatlaf-linux-arm64.so for Linux on ARM64; now built on ARM64 Linux (ubuntu-24.04-arm); previous was cross-compiled on X86_64 Linux (issue #899)
Some checks failed
CI / build (11) (push) Has been cancelled
Native Libraries / Natives (macos-latest) (push) Has been cancelled
Native Libraries / Natives (ubuntu-24.04-arm) (push) Has been cancelled
Native Libraries / Natives (ubuntu-latest) (push) Has been cancelled
Native Libraries / Natives (windows-latest) (push) Has been cancelled
CI / build-on (17, ) (push) Has been cancelled
CI / build-on (21, ) (push) Has been cancelled
CI / build-on (23, ) (push) Has been cancelled
CI / build-on (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
built by GitHub Actions: https://github.com/JFormDesigner/FlatLaf/actions/runs/12932258675
2025-01-23 16:38:16 +01:00
Karl Tauber
c6872d48b3 GitHub Actions:
- build Linux ARM64 native library on ubuntu-24.04-arm
- build on various Java versions only if build on Java 11 succeeded
- build Java version matrix only on main repo
2025-01-23 16:15:22 +01:00
Karl Tauber
5e78b21df7 macOS native rounded borders: (PR #772)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
- removed opacity from contentView.layer
- catch and log exceptions
2025-01-21 11:59:40 +01:00
Karl Tauber
2ef87dc789 macOS: re-enabled rounded popup border (see PR #772) on macOS 14.4+ (was disabled in 3.5.x)
deferred rounded border setup, if popup window is not yet created, could make a difference (see commit 656d25b75e)
2025-01-20 19:42:55 +01:00
Karl Tauber
28904c34cc Linux: added libflatlaf-linux-arm64.so for Linux on ARM64 (issue #899)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
built by GitHub Actions: https://github.com/JFormDesigner/FlatLaf/actions/runs/12845430366
2025-01-18 17:09:32 +01:00
Karl Tauber
91f19bf94c Native Libraries:
- Linux: added dumps
- macOS: replaced deprecated Gradle `exec`
2025-01-18 16:53:30 +01:00
Karl Tauber
0ea188f8db Linux: support cross-compile native library for ARM64 on x86_64 Linux (issue #899) 2025-01-18 14:50:42 +01:00
Karl Tauber
6c77b81277 Linux: support building native library for ARM64 when Gradle build runs on ARM64 Linux (issue #899) 2025-01-16 20:10:57 +01:00
Karl Tauber
ddee4ef526 GitHub Actions: natives.yml: install libxt-dev to fix build error on ubuntu 24.04 (did work without this on ubuntu 22.04)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Fonts / Fonts (inter) (push) Has been cancelled
Fonts / Fonts (jetbrains-mono) (push) Has been cancelled
Fonts / Fonts (roboto) (push) Has been cancelled
Fonts / Fonts (roboto-mono) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2025-01-09 15:57:46 +01:00
Karl Tauber
f11f68282b update to Gradle 8.12 2025-01-09 15:39:27 +01:00
Karl Tauber
ac0cceb09b added Exocharts as Gold sponsor 2025-01-09 15:19:47 +01:00
Karl Tauber
a238fd4505 Button: fixed background and foreground colors for borderless and toolBarButton style default buttons (JButton.isDefaultButton() is true) (issue #947) 2025-01-09 14:38:36 +01:00
Karl Tauber
8dc6242889 FlatLaf window decorations: minimize and maximize icons were not shown for custom scale factors less than 100% (e.g. `-Dflatlaf.uiScale=75%) (issue #951) 2025-01-09 14:19:44 +01:00
Karl Tauber
d17fffb82a PopupFactory: fixed NPE on Windows 10 when owner is null (issue #952) 2025-01-09 12:26:57 +01:00
Karl Tauber
0ad3180b10 FileChooser: improved performance when navigating to large directories with thousands of files (issue #953) 2025-01-09 12:04:25 +01:00
Karl Tauber
80ba75fdeb List:
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
- fixed wrong x/width bounds of alternating rows for multi-column lists (PR #939)
- Demo: added "alternating rows" checkboxes to "Data Components" page
2024-12-18 12:22:04 +01:00
Karl Tauber
7027821c00 Merge PR #939: Support for alternate row color in JList 2024-12-18 00:30:01 +01:00
Karl Tauber
a3a49cef73 Merge PR #936: CheckBox: support styling indeterminate state of tri-state check boxes 2024-12-17 23:46:12 +01:00
Dar
abf77d5399 mod: code format 2024-12-16 15:55:40 +01:00
Dar
6404b8de2a fix: filter combobox by name 2024-12-16 15:49:19 +01:00
Dar
19055d5a18 mod: combobox does not support alternate row color 2024-12-13 09:21:14 +01:00
Dar
c12adf12e7 new: support for alternate row color in JList 2024-12-13 09:20:23 +01:00
Karl Tauber
b9c68fbe77 CheckBox: support styling indeterminate state of tri-state check boxes (issue #919) 2024-12-11 16:54:03 +01:00
Karl Tauber
7bdfd49921 Merge PR #935: Tree: support wide cell renderer
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-12-11 13:33:03 +01:00
Karl Tauber
58fa2a5085 Demo, Testing, Theme Editor: re-generated UI code using JFormDesigner 8.3, which now supports class FlatClientProperties 2024-12-10 19:49:46 +01:00
Karl Tauber
a400799db5 Extras: use default activation keys if passing null to FlatInspector.install() or to FlatUIDefaultsInspector.install() 2024-12-09 23:55:02 +01:00
Karl Tauber
2a8e487c1f Tree: support wide cell renderer (issue #922) 2024-12-09 19:50:00 +01:00
Karl Tauber
145631fd43 FlatComponentsTest: added "transparent background" checkbox
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-12-09 17:10:33 +01:00
Karl Tauber
6a774d8c70 GitHub Actions: use lftp directly to upload FlatLaf demo and theme editor
previously used github action `sebastianpopp/ftp-action`, which uses Docker, was very slow (about two minutes to upload one file)
2024-12-09 13:44:54 +01:00
Karl Tauber
bfd746f981 GitHub Actions: no longer upload theme editor snapshot; update gradle wrapper validation action
Some checks failed
CI / build (11, ) (push) Waiting to run
CI / build (17, ) (push) Waiting to run
CI / build (21, ) (push) Waiting to run
CI / build (23, ) (push) Waiting to run
CI / build (8, ) (push) Waiting to run
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
2024-12-09 01:23:55 +01:00
Karl Tauber
3af54b7215 Merge branch 'flatlaf-3.5.4' into main 2024-12-09 01:12:27 +01:00
Karl Tauber
3ba9fc6c1c release 3.5.4 2024-12-09 00:44:57 +01:00
Karl Tauber
0a9ecd66a9 Linux: fixed NPE when using FlatLaf window decorations and switching theme (issue #933; regression in 3.5.3)
caused by fix for #907; commit d471f08b15
2024-12-09 00:43:44 +01:00
Karl Tauber
6991d6729e Merge PR #931: Fixing NPE when using HTML text on a component with null font
(cherry picked from commit 41332de275)
2024-12-08 23:01:00 +01:00
Karl Tauber
304cb0d57b Merge PR #903: Support for alternate row color in JTree
Some checks are pending
CI / build (11, ) (push) Waiting to run
CI / build (17, ) (push) Waiting to run
CI / build (21, ) (push) Waiting to run
CI / build (23, ) (push) Waiting to run
CI / build (8, ) (push) Waiting to run
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
2024-12-08 13:44:58 +01:00
Karl Tauber
41332de275 Merge PR #931: Fixing NPE when using HTML text on a component with null font 2024-12-08 13:37:02 +01:00
Karl Tauber
ef61ae504b FlatSVGIcon: color filters now can access painting component to implement component state based color mappings (PR #906) 2024-12-07 19:52:23 +01:00
Eduwardo Horibe
f96baf1bc2 Fixing NPE when using HTML text on a component with null font 2024-12-06 18:52:14 -03:00
Karl Tauber
1462636e97 release 3.5.3
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-12-06 12:42:15 +01:00
Karl Tauber
7e59a7f4af FlatPropertiesLaf: support macOS themes as base themes 2024-12-04 23:56:01 +01:00
Karl Tauber
e9a21848bc ComboBox: do not paint arrow button background if it is hidden (issue #915)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-12-04 19:24:10 +01:00
Karl Tauber
1dcb251ecb FlatLaf window decorations: fixed sometimes broken window moving with SplitPane in window title area in "full window content" mode (issue #926) 2024-12-04 18:21:06 +01:00
Karl Tauber
3f33543cee Linux: fixed slightly different font size (or letter width) used to paint HTML text when default font family is _Cantarell_ (e.g. on Fedora) (issue #912)
the removed use of floating point font size is similar to what is done in JDK for GTK Look and Feel:
- https://bugs.openjdk.org/browse/JDK-6979979
- 306f12db9e
2024-12-04 17:18:33 +01:00
Karl Tauber
84bd2088f2 FlatSystemProperties: javadoc fixes 2024-12-04 13:06:37 +01:00
Karl Tauber
4f4a3132c5 UIScale:
- do not use "defaultFont" if current Laf is not FlatLaf
- support custom font size divider to calculate user scale factor from font size

(for special use in JFormDesigner)
2024-12-04 13:06:11 +01:00
Karl Tauber
e064c934cb Windows: fixed detection of Windows 11 if custom exe launcher does not specify Windows 10+ compatibility in application manifest (issue #916)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
Windows binaries built and signed locally in clean workspace
2024-11-28 14:12:56 +01:00
Karl Tauber
16fc3cabf2 Popup: fixed NPE if GraphicsConfiguration is null on Windows (issue #921)
Some checks failed
CI / build (11, ) (push) Waiting to run
CI / build (17, ) (push) Waiting to run
CI / build (21, ) (push) Waiting to run
CI / build (23, ) (push) Waiting to run
CI / build (8, ) (push) Waiting to run
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
Native Libraries / Natives (macos) (push) Waiting to run
Native Libraries / Natives (ubuntu) (push) Waiting to run
Native Libraries / Natives (windows) (push) Waiting to run
Fonts / Fonts (inter) (push) Has been cancelled
Fonts / Fonts (jetbrains-mono) (push) Has been cancelled
Fonts / Fonts (roboto) (push) Has been cancelled
Fonts / Fonts (roboto-mono) (push) Has been cancelled
also check for null GraphicsConfiguration in other classes
2024-11-27 19:27:47 +01:00
Karl Tauber
7e002ff6c2 Theme Editor: fixed using color picker on secondary screen 2024-11-27 19:02:13 +01:00
Karl Tauber
323c0c62c3 update to Gradle 8.11.1 2024-11-20 20:11:34 +01:00
Karl Tauber
ff5bd301cc Popup: on Windows 10, fixed misplaced popup drop shadow (issue #911; regression in 3.5 since commit a311bac89b)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-11-19 23:25:50 +01:00
Karl Tauber
c37712b0f0 Windows: fixed wrong layout in maximized frame after changing screen scale factor (issue #904)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
Windows binaries built and signed locally in clean workspace
2024-11-17 19:41:54 +01:00
Karl Tauber
ee9e238592 Windows: fixed possible deadlock with TabbedPane in window title area in "full window content" mode (issue #909)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
Fonts / Fonts (inter) (push) Has been cancelled
Fonts / Fonts (jetbrains-mono) (push) Has been cancelled
Fonts / Fonts (roboto) (push) Has been cancelled
Fonts / Fonts (roboto-mono) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-11-14 19:34:31 +01:00
Karl Tauber
da5d6fa157 update to Gradle 8.11 2024-11-14 18:39:37 +01:00
Karl Tauber
d471f08b15 Linux: fixed continuous cursor toggling between resize and standard cursor when resizing window with FlatLaf window decorations (issue #907) 2024-11-14 18:34:55 +01:00
Karl Tauber
b97424f767 HTML: fixed wrong rendering if HTML text contains <style> tag with attributes (e.g. <style type='text/css'>) (issue #905; regression in 3.5)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-11-10 13:28:02 +01:00
Dar
a20cfa6db3 mod: style tester 2024-10-23 15:36:17 +02:00
Dar
6ac6698ecf mod: added 3.6 tag 2024-10-23 15:32:16 +02:00
Dar
8004d2761a fix: respect the selection arc 2024-10-23 15:30:06 +02:00
Dar
25c2bbc851 fix: alternate row color paint 2024-10-22 18:05:14 +02:00
Dar
33e37a7167 new: support for alternate row color in jtree
see: https://github.com/JFormDesigner/FlatLaf/issues/900
2024-10-22 16:46:30 +02:00
Karl Tauber
c29a276188 release 3.5.2
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-10-18 13:28:53 +02:00
Karl Tauber
d1694aa8bd FlatClientProperties and FlatSystemProperties: javadoc fixes 2024-10-18 13:28:07 +02:00
Karl Tauber
570cf6fc51 FlatLaf window decorations: added client property JRootPane.titleBarHeight to allow specifying a (larger) preferred height for the title bar (issue #897)
Some checks are pending
CI / build (11, ) (push) Waiting to run
CI / build (17, ) (push) Waiting to run
CI / build (21, ) (push) Waiting to run
CI / build (23, ) (push) Waiting to run
CI / build (8, ) (push) Waiting to run
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
2024-10-17 19:58:58 +02:00
Karl Tauber
8eab86e489 FlatLaf window decorations: strech iconify/maximize/close buttons to always fill whole title bar height (issue #897) 2024-10-17 19:49:51 +02:00
Karl Tauber
566568f61a Windows: fixed repaint issues (ghosting) on some systems by setting sun.java2d.d3d.onscreen to false (issue #887) 2024-10-17 13:19:04 +02:00
Karl Tauber
56a73a4d17 Popup: added system property flatlaf.useRoundedPopupBorder to allow disabling native rounded popup borders (PRs #643 and #772) 2024-10-15 00:29:15 +02:00
Karl Tauber
656d25b75e Popup: setup rounded popup border after window was created (no longer create window ourself using addNotify()) to (hopefully) fix repaint issues on some Windows 11 systems after first showing a popup (issue #887, PR #643)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-10-12 23:25:59 +02:00
Karl Tauber
dcdc80ade3 Testing: FlatOptionPaneTest: test option pane with custom title bar icon (issue #886) 2024-10-12 00:28:19 +02:00
Karl Tauber
09f2d65d5e change snapshot version from 3.6-SNAPSHOT to 3.5.2-SNAPSHOT
Some checks failed
CI / build (11, ) (push) Waiting to run
CI / build (17, ) (push) Waiting to run
CI / build (21, ) (push) Waiting to run
CI / build (23, ) (push) Waiting to run
CI / build (8, ) (push) Waiting to run
CI / snapshot (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
Fonts / Fonts (inter) (push) Has been cancelled
Fonts / Fonts (jetbrains-mono) (push) Has been cancelled
Fonts / Fonts (roboto) (push) Has been cancelled
Fonts / Fonts (roboto-mono) (push) Has been cancelled
Native Libraries / Natives (macos) (push) Has been cancelled
Native Libraries / Natives (ubuntu) (push) Has been cancelled
Native Libraries / Natives (windows) (push) Has been cancelled
2024-10-11 19:27:23 +02:00
Karl Tauber
b304d46f7e TextComponents: fixed too fast scrolling in multi-line text components when using touchpads (e.g. on macOS) (issue #892) 2024-10-11 19:18:00 +02:00
Karl Tauber
3391f971ec GitHub Actions: build using Java 23 2024-10-11 15:16:49 +02:00
Karl Tauber
778fed27a5 update to Gradle 8.10.2 2024-10-11 15:14:11 +02:00
Karl Tauber
1755dbc877 README.md updated 2024-10-11 15:11:56 +02:00
Karl Tauber
4e6f538519 ToolBar: fixed endless loop if button in Toolbar has focus and is made invisible (issue #884)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (21, 22) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
2024-09-29 19:26:37 +02:00
Karl Tauber
a6ecb0ef85 FlatLaf window decorations on Windows: fixed possible application freeze when using custom component that overrides Component.contains(int x, int y) and invokes SwingUtilities.convertPoint() (or similar) from the overridden method (issue #878) 2024-09-04 00:48:42 +02:00
Karl Tauber
438ec6ac5c release 3.5.1 2024-08-05 18:17:34 +02:00
Karl Tauber
8089e66642 SubMenuUsabilityHelper: added system property flatlaf.useSubMenuSafeTriangle to allow disabling submenu safe triangle for SWTSwing (issue #870)
also check whether EventQueue.push() succeeded; if not, disable submenu safe triangle
2024-08-05 13:56:43 +02:00
Karl Tauber
d27e0561f2 HiDPI: fixed occasional wrong repaint areas when using HiDPIUtils.installHiDPIRepaintManager() (see PR #864) 2024-08-04 15:14:46 +02:00
Karl Tauber
97b21bfa8b HTML: fixed occasional cutoff wrapped text when using multi-line text in HTML tags <h1>...<h6>, <code>, <kbd>, <big>, <small> or <samp> (issue #873; regression in 3.5) 2024-08-04 14:45:17 +02:00
Karl Tauber
ec4343ed30 TabbedPane: fixed ArrayIndexOutOfBoundsException in case of using "card" tab type and using a custom tab selection model that returns -1 for selected tab (issue #875) 2024-08-03 00:00:00 +02:00
Karl Tauber
948decb3b5 Popup: fixed UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency is not supported exception on Haiku OS when showing popup (partly) outside of window (issue #869) 2024-08-02 23:47:29 +02:00
Karl Tauber
d510fee7f6 CHANGELOG.md: moved note regarding disabled rounded popup border on macOS from 3.4.1 to 3.5 (wrong in commit 32b0f1ba10) 2024-07-17 00:23:41 +02:00
Karl Tauber
70b7a3d662 release 3.5 2024-07-16 22:17:04 +02:00
Karl Tauber
b142a6f31e FlatInspector: added FlatLaf style to tooltip 2024-07-16 13:58:53 +02:00
Karl Tauber
14705a9b30 Menu: show arrow icon for top-level JMenu if used in vertical JMenuBar (issue #867) 2024-07-16 13:48:47 +02:00
Karl Tauber
32b0f1ba10 macOS: (temporary) disabled rounded popup border (see PR #772) on macOS 14.4+ because it may freeze the application and crash the macOS WindowServer process 2024-07-16 01:25:10 +02:00
Karl Tauber
cbffdf4900 HiDPIUtils: fixed javadoc error 2024-07-15 19:01:41 +02:00
Karl Tauber
1238da5e54 Merge PR #864: HiDPI: fix incomplete component repainting at 125% or 175% scaling on Windows 2024-07-15 18:43:46 +02:00
Karl Tauber
cba203be09 Merge PR #856: Table: rounded selection 2024-07-15 18:39:12 +02:00
Karl Tauber
d89c6156b9 HiDPI: introduced (optional) repaint manager that fixes incomplete component paintings at 125% or 175% scaling on Windows (issues #860 and #582) 2024-07-10 23:02:19 +02:00
Karl Tauber
e06475b3b7 HiDPIUtils: javadoc fixes for previous commit 2024-07-05 23:29:38 +02:00
Karl Tauber
5ff99bd45e HiDPI: fixed incomplete component paintings at 125% or 175% scaling on Windows (issues #860 and #582) 2024-07-05 22:18:27 +02:00
Karl Tauber
127dd6ac41 Table: fixed repainting of rounded selection when selection changes 2024-06-27 23:48:14 +02:00
Karl Tauber
9e05384513 Table: fixed location of painted grid lines when scaled with fractional scale factors (e.g. 125%-175%) 2024-06-24 19:22:03 +02:00
Karl Tauber
9ffda72ae3 Table: support rounded selection (issue #844) 2024-06-24 19:20:55 +02:00
Karl Tauber
72a4c00e72 Window decorations: fixed black line sometimes painted on top of (native) window border on Windows 11 (issue #852)
Windows binaries built and signed locally in clean workspace
2024-06-21 19:58:36 +02:00
Karl Tauber
c95e95ef67 FileChooser: wrap shortcuts in scroll pane (issue #828)
added tooltips to shortcuts
disabled group hover effect on shortcuts
2024-06-21 19:10:39 +02:00
Karl Tauber
0c0d4bffbf ScrollPane: fixed/improved border painting at 125% - 175% scaling to avoid different border thicknesses (issue #743) 2024-06-14 19:19:22 +02:00
Karl Tauber
2a494b1d60 Testing: easier testing of various system scale factors using Alt+Shift+F1...F12 2024-06-13 12:20:32 +02:00
Karl Tauber
1463723e52 Table: Fixed painting of alternating rows below table if auto-resize mode is JTable.AUTO_RESIZE_OFF and table is smaller than scroll pane. Below alternating rows were not updated when table width changed and were painted on wrong side in right-to-left component orientation 2024-06-08 11:10:46 +02:00
Karl Tauber
9ade48d078 FlatHTML: javadoc fixes 2024-06-04 13:10:52 +02:00
Karl Tauber
7ba8274fd4 FlatLineBorder: use arc from Label or Panel, if not specified in border (issue #842) 2024-06-01 14:53:44 +02:00
Karl Tauber
238443074c Theme Editor: updated rsyntaxtextarea to latest version 2024-05-31 14:06:57 +02:00
Karl Tauber
0decbec595 Theme Editor: on macOS, use larger window title bar (PR #779) 2024-05-31 13:57:01 +02:00
Karl Tauber
0eb77c7f72 Theme Editor: fixed occasional empty window on startup on macOS 2024-05-31 13:54:50 +02:00
Karl Tauber
f05df0db0a Button and ToggleButton: added more missing border colors for selected states (issue #848) 2024-05-31 10:41:33 +02:00
Karl Tauber
13fbaf1f74 fixed errors reported by Error Prone in commit 261d2b1fe8 2024-05-30 19:33:18 +02:00
Karl Tauber
969d2642de Button and ToggleButton: added missing border colors for pressed and selected states (issue #848) 2024-05-30 19:22:25 +02:00
Karl Tauber
17ce6d39b4 Button and ToggleButton: UI properties [Toggle]Button.selectedForeground and [Toggle]Button.pressedForeground did not work for HTML text (issue #848) 2024-05-30 17:45:28 +02:00
Karl Tauber
261d2b1fe8 HTML: Fixed font sizes for HTML tags <h1>...<h6>, <code>, <kbd>, <big>, <small> and <samp> in HTML text for components Button, CheckBox, RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and JXHyperlink. Also fixed for Label and ToolTip if using Java 11+. 2024-05-30 16:39:23 +02:00
Karl Tauber
a54aeb3838 FlatTestFrame: automatically add scroll pane if content is very large (or screen is small) 2024-05-29 11:14:52 +02:00
Karl Tauber
cc4f9a9db5 Window decorations: window top border on Windows 10 in "full window content" mode was not fully repainted when activating or deactivating window (issue #809) 2024-05-28 16:16:03 +02:00
Karl Tauber
a311bac89b Popup: fixed flicker of popups (e.g. tooltips) while they are moving (e.g. following mouse pointer) (issues #832 and #672) 2024-05-22 14:02:40 +02:00
Karl Tauber
029f273dd9 Label: support painting background with rounded corners (issue #842)
Demo: added rounded panels and labels to "More Components" tab
2024-05-21 13:37:11 +02:00
Karl Tauber
bbbdd7e4d3 Panel: rounded background of panel with rounded corners is now painted even if panel is not opaque (issue #840) 2024-05-20 18:57:19 +02:00
Karl Tauber
3f3ef6b24f ProgressBar: log warning (including stack trace) when uninstalling indeterminate progress bar UI or using JProgressBar.setIndeterminate(false) not on AWT thread, because this may throw NPE in FlatProgressBarUI.paint() (issues #841 and #830) 2024-05-09 11:35:49 +02:00
Karl Tauber
8c3dfd4a36 Merge PR #834: Update flatlaf-intellij-themes/README.md 2024-04-23 13:04:13 +02:00
Hirun Chamara
af57599df9 Update README.md
install(); is deprecated
2024-04-23 11:22:13 +05:30
Karl Tauber
bde25f6ac8 release 3.4.1 2024-03-29 13:28:58 +01:00
Karl Tauber
c989b97ffa fixed custom properties file names for nested classes (e.g. IntelliJTheme$ThemeLaf.properties instead of ThemeLaf.properties)
(see issue #824)
2024-03-29 13:14:29 +01:00
Karl Tauber
5f5c225300 macOS: fixed crash when running in WebSwing (issue #826; regression in 3.4) 2024-03-26 13:24:05 +01:00
Karl Tauber
36e4071b7f FlatSVGIcon: use log level CONFIG instead of SEVERE and allow disabling logging (issue #823) 2024-03-24 17:29:57 +01:00
Karl Tauber
1068884bce change snapshot version from 3.5-SNAPSHOT to 3.4.1-SNAPSHOT 2024-03-24 17:10:43 +01:00
Karl Tauber
32d102dbc9 Native Libraries: added API version to:
- test whether native library matches the JAR (bad builds could e.g. ship a newer JAR with an older incompatible native library)
- invoke a method (to get API version) to check whether native library works correctly

if API version do not match, or method could not invoked correctly, disable usage of FlatLaf native library

Windows and macOS binaries built and signed locally in clean workspace
Linux binary built by GitHub Actions
2024-03-24 16:37:11 +01:00
Karl Tauber
4e1f092b98 FlatClientProperties: javadoc fixes 2024-03-24 12:45:48 +01:00
Karl Tauber
bd60a18ff4 SplitPane:
- update divider when client property `JSplitPane.expandableSide` changed
- Extras: added support for `JSplitPane.expandableSide` client property to `FlatSplitPane`
2024-03-24 12:42:39 +01:00
Karl Tauber
3b3d7d76eb Testing: added tab with random background color to FlatContainerTest and FlatJideOssContainerTest 2024-03-23 16:33:27 +01:00
Karl Tauber
ec76448e9f jsvg: updated to 1.4.0
jide-oss: updated to 3.7.15
2024-03-23 15:31:45 +01:00
Karl Tauber
872c84974c Merge PR #822: Request to add MCreator to list of apps using FlatLAF 2024-03-23 15:08:20 +01:00
Karl Tauber
5dd2008969 GitHub Actions: build using Java 22 (use toolchain because Gradle 8.7 does not support running on Java 22) 2024-03-23 14:13:43 +01:00
Karl Tauber
55ddac2bc7 Gradle: use simpler Kotlin DSL property assignment
https://blog.gradle.org/simpler-kotlin-dsl-property-assignment
2024-03-23 13:56:47 +01:00
Karl Tauber
a62dd22f83 Gradle: fixed warning regarding using deprecation Gradle features, making it incompatible with Gradle 9.0 2024-03-23 13:46:51 +01:00
Karl Tauber
a503879858 update to Gradle 8.7 2024-03-23 13:10:58 +01:00
Klemen
14f19dd735 Update README.md 2024-03-18 09:07:56 +01:00
Karl Tauber
9a727f68ce Window decorations: fixed missing window top border on Windows 10 in "full window content" mode (issue #809) 2024-03-17 18:09:19 +01:00
Karl Tauber
d26819d268 TabbedPane:
- updated comment regarding unsupported scroll buttons in right-to-left component orientation (see issue #815)
- removed unused on wrong code that tries to layout scroll buttons for right-to-left
2024-03-13 00:03:05 +01:00
Karl Tauber
44752cc9aa TabbedPane: fixed swapped back and forward scroll buttons when using TabbedPane.scrollButtonsPlacement = trailing (regression in FlatLaf 3.3 since commit 97495a6093) 2024-03-12 18:41:47 +01:00
Karl Tauber
11e0757387 FlatSVGIcon:
- setColorFilter() now returns `this`
- added method to enable/disable and clear SVGDocument cache
- do not filter red color in `paintSvgError()`
2024-03-12 13:36:56 +01:00
Karl Tauber
1f1ebc3c44 FlatSVGIcon: some additions to PR #817:
- create new `LinearGradientPaint` only if color has changed
- preserve old `LinearGradientPaint.colorSpace` and `transform`
2024-03-12 12:20:31 +01:00
Karl Tauber
4be97b6ea6 Merge PR #817: Add LinearGradient paint in svg color filter 2024-03-11 19:04:47 +01:00
laing raven
3facca5499 add LinearGradient paint in svg color filter 2024-03-10 19:12:29 +07:00
Karl Tauber
bfbd25012a release 3.4 2024-02-19 23:29:00 +01:00
Karl Tauber
063fff2ab4 gradle: fixed dependency of task ':flatlaf-core:signMavenPublication' (type 'Sign') 2024-02-19 23:28:31 +01:00
Karl Tauber
fbdc8d5b99 Merge branch 'windows-full-window-content' 2024-02-19 22:25:04 +01:00
Karl Tauber
625c0a3321 Tree: detect tree cell editor in FlatUIUtils.isCellEditor() 2024-02-19 22:12:29 +01:00
Karl Tauber
2972300112 Table: select all text in cell editor when starting editing using F2 key; can be disabled using Table.editorSelectAllOnStartEditing (issue 652)
also added missing `Table.paintOutsideAlternateRows`
2024-02-19 22:03:19 +01:00
Karl Tauber
a8e71895ee gradle: use AbstractPublishToMaven instead of PublishToMavenRepository to support publishing to local Maven repo (PublishToMavenLocal) 2024-02-19 18:47:48 +01:00
Karl Tauber
d7a76081e3 FileChooser: extended FlatFileChooserTest to support testing various locales (issue #795) 2024-02-12 12:05:01 +01:00
Karl Tauber
fd925a6718 FileChooser: extended FlatFileChooserTest to support testing all JFileChooser properties (issue #795) 2024-02-10 19:35:53 +01:00
Karl Tauber
4fc890a77c Testing: split FlatChooserTest into FlatFileChooserTest and FlatChooserTest 2024-02-10 14:16:45 +01:00
Karl Tauber
b804463b73 Window decorations:
- fixed updating of client property `FULL_WINDOW_CONTENT_BUTTONS_BOUNDS` when resizing window
- fixed title bar buttons placeholder debug painting
2024-02-06 11:30:11 +01:00
Karl Tauber
8f161b4b5a introduced FlatUIAction 2024-02-05 19:03:29 +01:00
Karl Tauber
c6338169f3 Testing: updated lafs.properties 2024-02-05 14:20:21 +01:00
Karl Tauber
6cea24ed9e GitHub Actions: updated versions of used actions 2024-02-05 11:34:58 +01:00
Karl Tauber
3d8eb9eb66 UIDefaultsKeysDump: exclude unused UI properties 2024-02-05 11:33:25 +01:00
Karl Tauber
a84aceb1ba Window decorations: improved caption hit testing to better support TabbedPane, SplitPane and ToolBar in title bar area (e.g. for fullWindowContent mode) 2024-02-04 16:30:38 +01:00
Karl Tauber
8adb7e3021 Native libraries: support Gradle cache when running in development environment (issue #800) 2024-02-02 19:02:56 +01:00
Karl Tauber
bc0d5dc9b5 Native libraries: publish flatlaf-<version>-no-natives.jar to Maven Central; this JAR is equal to flatlaf-<version>.jar, except that it does not contain the FlatLaf native libraries (issue #800) 2024-02-02 17:51:06 +01:00
Karl Tauber
1d935d6659 Window decorations: support fullWindowContent mode on Windows and Linux 2024-02-02 10:06:25 +01:00
Karl Tauber
445466acd0 Demo: removed toolbar button listeners used to test PR #779 2024-01-28 12:14:28 +01:00
Karl Tauber
30af74f806 macOS: setting window background (of undecorated window) to translucent color (alpha < 255) did not show the window translucent (issue #705) 2024-01-28 11:57:17 +01:00
Karl Tauber
16ddd100d3 RootPane: undone commits 7b248427f0 and c6d1ed91a7 because it seems to have no effect anymore
- on Windows with FlatLaf window decorations, maybe because of 7f02eb9cf0
- on Windows with standard window decorations, it seems not to work (still shows "white lines" @2.25x on Windows 10; Java 17)
- macOS and Linux do not support fractional scaling and "white lines" shown never be shown on that platforms
2024-01-27 11:19:18 +01:00
Karl Tauber
c946ec170d macOS window buttons spacing:
- uninstall when switching from FlatLaf to another Laf
- install when switching from another Laf to FlatLaf

(for PR #779)
2024-01-27 11:09:16 +01:00
Karl Tauber
ca514dd76e Merge PR #779: macOS: window title bar close/minimize/zoom buttons spacing 2024-01-25 14:06:38 +01:00
Karl Tauber
cf44a5c50b GitHub Actions: publish shapshots of PRs to Sonatype OSSRH 2024-01-25 13:26:31 +01:00
Karl Tauber
91b8c02c7f GitHub Actions: ignore changes to files that are not related to builds (e.g. readmes, etc.) 2024-01-24 19:23:47 +01:00
Karl Tauber
ca3b2b4b07 Fonts: updated Inter to v4.0 2024-01-24 15:45:43 +01:00
Karl Tauber
722dde63df Native libraries: system property flatlaf.nativeLibraryPath now supports loading native libraries named the same as on Maven central; improved log messages for loading fails (issue #797) 2024-01-24 01:02:44 +01:00
Karl Tauber
c85baf4dc6 flatlaf-natives-windows: fixed invocation of GetMonitorInfo() (issue #798)
also removed unnecessary setting `mii.fType`
2024-01-23 15:54:44 +01:00
Karl Tauber
96b7770ab2 CHANGELOG.md: added PR #794 2024-01-23 14:29:22 +01:00
rogerbj
0c00117820 Remove the dependency with JMenuBar to support, for example, the CommandMenuBar in JIDE OSS
(cherry picked from commit 4d4b90c989)
2024-01-23 13:47:12 +01:00
Karl Tauber
3465fa68b4 macOS window buttons spacing:
- renamed client property `MACOS_WINDOW_BUTTON_STYLE` to `MACOS_WINDOW_BUTTONS_SPACING`
- no longer allow value `true` for that client property
- enable using `MACOS_WINDOW_BUTTONS_SPACING` without `apple.awt.fullWindowContent`
- remove client property `FULL_WINDOW_CONTENT_BUTTONS_BOUNDS` when `apple.awt.fullWindowContent` is set to false or null
- added placeholder options `zeroInFullScreen`, `leftToRight` and `rightToLeft`
- hide close/min/max buttons during the transition from full-screen to non-full-screen to avoid that they "jump" when the nsToolbar is made visible
- fixed: full-screen listeners where added multiple times
- updated macOS native libraries
- added `FlatMacOSTest`
2024-01-22 00:31:40 +01:00
Karl Tauber
28278a75a7 macOS fullWindowContent mode:
- added title bar buttons placeholder
- added client property to root pane that contains title bar buttons bounds
- undone toolbar extensions from commit ea2447dcb7
2024-01-21 19:19:46 +01:00
Karl Tauber
f68a871dd6 macOS window button style: fixed javadoc 2024-01-12 22:49:49 +01:00
Karl Tauber
93d424cfe1 macOS native: added FlatNativeMacLibrary.windowToggleFullScreen() for easier testing 2024-01-12 22:49:49 +01:00
Karl Tauber
a1adde0888 macOS window button style: support NSWindowToolbarStyleUnified (availaible since macOS 11+; standard in macOS Finder, etc) to allow even larger space around close/minimize/zoom buttons 2024-01-12 22:48:31 +01:00
Karl Tauber
13528b49cb macOS large title bar: fixed wrong "main" JToolBar height and left inset after leaving full screen 2024-01-12 22:48:31 +01:00
Karl Tauber
f3be3f2d1c macOS large title bar: added client property FlatClientProperties.MACOS_LARGE_WINDOW_TITLE_BAR (or "FlatLaf.macOS.largeWindowTitleBar") 2024-01-12 22:48:31 +01:00
Karl Tauber
241fe855cc macOS large title bar: hide NSToolbar of window title bar when window becomes full screen 2024-01-12 22:47:33 +01:00
Karl Tauber
ea2447dcb7 macOS large title bar: main JToolBar automatically:
- uses height of macOS window title bar
- adds left insets for close/minimize/zoom buttons (except if full screen, where those buttons are hidden)
2024-01-12 22:47:33 +01:00
Karl Tauber
f40baed65e macOS large title bar: add NSToolbar to NSWindow 2024-01-12 22:45:54 +01:00
Karl Tauber
eed11d211b GitHub Actions: updated versions of used actions 2024-01-11 23:07:21 +01:00
Karl Tauber
19f27a8d56 release 3.3 2024-01-11 18:14:17 +01:00
Karl Tauber
cf3fa17666 fixed typos and grammar 2024-01-11 18:11:09 +01:00
Karl Tauber
6fdc56f2d3 Merge PR #790: Table header background is never restored after hover when the renderer background is null 2024-01-10 19:26:22 +01:00
Karl Tauber
9f17a5b26d TableHeader: restore also renderer foreground after hover 2024-01-10 19:19:01 +01:00
Karl Tauber
fa53e90847 Merge PR #788: Support for hover/pressed on SplitPane divider 2024-01-10 16:43:28 +01:00
Karl Tauber
50c630f403 Table: fixed border arc of components in complex table cell editors (issue #786) 2024-01-10 16:37:32 +01:00
Dar
5630c161ea fix: restore the renderer background after hover
The table header background is never restored after hover when the label renderer is opaque
2024-01-09 16:58:21 +01:00
Karl Tauber
7d16ff9e79 SplitPane:
- removed `SplitPaneDivider.showHover` (hover is now enabled if hover color is specified)
- added `SplitPaneDivider.pressedColor` (for pressed/dragging in continuous layout)
- fixed painting of dragging divider in non-continuous layout (was 1px too small; added grip)
- updated styling unit tests
2024-01-08 22:39:01 +01:00
Karl Tauber
a9ea9daec3 FileChooser: catch NPE in Java 21 when getting icon for .exe files that use default Windows exe icon (see https://bugs.openjdk.org/browse/JDK-8320692) 2024-01-08 20:29:20 +01:00
Karl Tauber
45bdd40dce Viewport: use method handle, instead of reflection, to get view UI faster 2024-01-08 20:12:26 +01:00
Karl Tauber
a2bca88eec removed support for JetBrains custom decorations 2024-01-08 16:22:20 +01:00
Karl Tauber
c0dd02ee13 TabbedPane: paint rounded tab area background for rounded cards (issue #717) 2024-01-08 13:22:59 +01:00
Karl Tauber
97495a6093 TabbedPane: avoid "jumping" tab area when switching L&F or when mouse wheel scrolling to last tab (regression in commit 4ad45088c4) 2023-12-31 12:56:43 +01:00
Karl Tauber
4ad45088c4 TabbedPane: fixed "endless" layouting and repainting when using nested tabbed panes (top and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping) as tab content (see https://github.com/skylot/jadx/issues/2030)
instead of using `BasicTabbedPaneUI.TabbedPaneLayout.layoutContainer()`, now layouting all components ourself and avoid double moving/resizing of some components
2023-12-30 12:56:58 +01:00
Dar
d6d1d4b1b7 fix: missing properties in the expected map 2023-12-29 15:12:06 +01:00
Dar
6e453c170f new: support for hover on splitpane divider 2023-12-29 12:38:34 +01:00
Karl Tauber
beb2deee52 TabbedPane: further reduced duplicate code in nested classes FlatTabbedPaneLayout and FlatTabbedPaneScrollLayout 2023-12-23 14:06:47 +01:00
Karl Tauber
9c69043b2c TabbedPane: reduced duplicate code in nested classes FlatTabbedPaneLayout and FlatTabbedPaneScrollLayout 2023-12-23 00:28:44 +01:00
Karl Tauber
4df34b3f9d TableHeader: no longer temporary replace header cell renderer while painting (issue https://github.com/apache/netbeans/issues/6835) 2023-12-22 14:08:49 +01:00
Karl Tauber
ee01756188 TabbedPane:
- avoid unnecessary repainting whole tabbed pane content area when layouting leading/trailing components
- avoid unnecessary repainting of selected tab on temporary changes
2023-12-21 18:59:11 +01:00
Karl Tauber
0386aaa18b Merge PR #758: TabbedPane: support rotated/vertical tabs 2023-12-21 17:41:49 +01:00
Karl Tauber
92c4230cde Merge PR #713: Scrollpane rounded border 2023-12-21 17:40:00 +01:00
Karl Tauber
26165999e0 Table: fixed background of boolean columns when using alternating row colors (issue #780) 2023-12-21 16:58:49 +01:00
Karl Tauber
38b2641078 NativeLibrary: in development environment, load native library from 'src' folder 2023-12-14 15:29:02 +01:00
Karl Tauber
4e19169312 Merge PR #772: macOS: native rounded borders for popups 2023-12-14 15:19:01 +01:00
Karl Tauber
46de81c1c9 macOS native:
- removed `FlatNativeMacLibrary.getWindowPtr()` because it is too dangerous to use `windowPtr` (which is `NSWindow*`) in Java (using invalid window pointer would crash app)
- made `getNSWindow()` 20x faster
- catch exceptions in `getNSWindow()`
- digitally signed dylibs
2023-12-14 14:39:26 +01:00
Karl Tauber
9bf4da7acf Native macOS libraries updated for PR #772
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/7151969281
2023-12-09 17:04:03 +01:00
Karl Tauber
6f32236fb7 macOS: native rounded borders for popups (issue #715) 2023-12-09 16:12:35 +01:00
Karl Tauber
c25d857e78 UI defaults dumps updated on macOS for commits 9fef2f9d05 and ce527329a6 2023-12-08 19:07:32 +01:00
Karl Tauber
8cc2925fd0 FlatPopupFactory: reordered some methods (nothing else changed) 2023-12-08 17:53:51 +01:00
Karl Tauber
2b87d3c4db Native window libraries: updated Windows DLLs for commit 7f6f366744
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/7132082543

locally signed Windows DLLs with FormDev Software code signing certificate
2023-12-07 19:02:43 +01:00
Karl Tauber
7f6f366744 flatlaf-natives-windows: support DWM attributes DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWA_CAPTION_COLOR and DWMWA_TEXT_COLOR (all unused in FlatLaf core) 2023-12-07 18:53:45 +01:00
Karl Tauber
b1fdbde5cd ScrollPane: allow specifying scroll pane border arc for multi-line text components, lists, tables and trees 2023-11-25 23:29:39 +01:00
Karl Tauber
417f0f5f1c fixed broken rendering after resizing window to minimum size and then increasing size again (issue #767) 2023-11-25 19:14:39 +01:00
Karl Tauber
ec7673790c Demo: added macOS themes to control bar combobox (F5 and F6) 2023-11-14 11:42:54 +01:00
Karl Tauber
7d0bdf3b9e OptionPane: fixed styling custom panel background in JOptionPane (issue #761) 2023-11-14 10:38:28 +01:00
Karl Tauber
2ef5270095 TabbedPane: support rotated/vertical tabs (issue #633) 2023-11-04 19:55:01 +01:00
Karl Tauber
61ba011c3b Testing: introduced FlatTestEnumSelector (a toolbar with a single button group) for easier testing 2023-11-01 14:53:06 +01:00
Karl Tauber
8d8b9f3e98 macOS themes: fixed ToolBar.hoverButtonGroupArc 2023-11-01 14:52:17 +01:00
Karl Tauber
69899ec29f ToolBar: added styling properties separatorWidth and separatorColor 2023-11-01 12:55:06 +01:00
Karl Tauber
5063621c95 junit updated to 5.10.0 2023-11-01 12:07:03 +01:00
Karl Tauber
030177f739 Theme Editore: updated rsyntaxtextarea and autocomplete to latest versions 2023-10-31 16:58:32 +01:00
Karl Tauber
808f5a6381 Button and ToggleButton: selected buttons did not use explicitly set foreground color (issue #756) 2023-10-31 16:14:48 +01:00
Karl Tauber
9602b191a9 jide-oss: updated to 3.7.14 2023-10-22 17:56:36 +02:00
Karl Tauber
34bd2d781c Table: switching theme looses table grid and intercell spacing (issues #733 and #750) 2023-10-22 17:55:10 +02:00
Karl Tauber
cf364c1264 release 3.2.5 2023-10-21 23:53:46 +02:00
Karl Tauber
a997820bb6 Merge PR #754: Fixing NPE when showing a popup without an invoker on Windows 10 2023-10-21 23:52:02 +02:00
Eduwardo Horibe
b8fabd59c0 Fixing NPE when showing a popup without an invoker on Windows 10 2023-10-21 15:26:55 -03:00
Karl Tauber
97d290795e release 3.2.4 2023-10-21 18:23:17 +02:00
Karl Tauber
2a237ff5fc Popup: fixed potential NPE in (unusual) case that the popup invoker is null (only on Linux with Wayland and Java 21; regression in 3.2.3) (issue #752) 2023-10-21 18:21:59 +02:00
Karl Tauber
13a418f74e release 3.2.3 2023-10-21 17:11:32 +02:00
Karl Tauber
5c56dbfed6 Popup: popups that request focus were not shown on Linux with Wayland and Java 21 (issue #752) 2023-10-21 17:04:22 +02:00
Karl Tauber
0d2f37e1da release 3.2.2 2023-10-15 18:12:11 +02:00
Karl Tauber
0494c2161c FileChooser: avoid unnecessary logging of InaccessibleObjectException when running in Java 16 (issue #741) 2023-10-15 18:00:33 +02:00
Karl Tauber
635a620439 jsvg: updated to 1.2.0 2023-10-15 16:42:15 +02:00
Karl Tauber
0a7c76ec72 GitHub Actions:
- build using Java 21 (use toolchain because Gradle 8.4 does not support running on Java 21)
- no longer build on Java 19 and 20
2023-10-15 16:38:28 +02:00
Karl Tauber
9ad8fb38e8 update to Gradle 8.4 2023-10-15 16:34:42 +02:00
Karl Tauber
1dbe968952 TabbedPane: fixed NPE when using focusable component as tab component and switching theme (issue #745) 2023-10-15 11:43:58 +02:00
Karl Tauber
460b6492cb Button: fixed painting icon and text at wrong location when using HTML text, left/right vertical alignment and running in Java 19+ (issue #746) 2023-10-14 19:16:23 +02:00
Karl Tauber
67b0faa9ae Merge PR #738: Fix typo 2023-09-28 16:25:11 +02:00
Karl Tauber
5553425a1a CheckBox and RadioButton: fixed cut off right side when border is removed and horizontal alignment is set to right (issue #734) 2023-09-28 16:22:52 +02:00
valerakostin
8ff516e43a Fix typo 2023-09-22 11:14:50 +02:00
Karl Tauber
b6207bafde release 3.2.1 2023-09-04 13:22:50 +02:00
Karl Tauber
b9f43fd560 jsvg: updated to 1.1.0 2023-09-04 13:13:14 +02:00
Karl Tauber
c617d9f569 Theme Editor: removed shapshot download link from README.md 2023-09-04 13:09:49 +02:00
Karl Tauber
9efb9761c6 MultiResolutionImageSupport optimizations:
- ProducerMultiResolutionImage: avoid creation of base image to get image width and height, because if screen is scaled then the base image would be never painted, but consumes memory and takes time to create it
- MappedMultiResolutionImage: delegate getting width/height/source/property to original image, to defer/avoid creation of mapped image
2023-09-04 13:08:21 +02:00
Karl Tauber
03f9115fbf MultiResolutionImageSupport: fixed memory leak in create(int,Dimension[],Function<Dimension,Image>) (issue #726) 2023-09-04 12:22:40 +02:00
Karl Tauber
a2859cedb5 Popop: fixed drop shadow if popup overlaps a heavyweight component (Windows 10 only; issue #626) 2023-09-02 12:48:46 +02:00
Karl Tauber
0c604b1023 ScrollPane: increase viewport width for rounded border to remove/reduce gap between view and vertical scrollbar 2023-08-27 18:14:03 +02:00
Karl Tauber
cdee0594f8 TextField: fixed placeholder text painting, which did not respect horizontal alignment property of JTextField (issue #721) 2023-08-27 16:30:16 +02:00
Karl Tauber
808833d749 UIDefaultsDump: dump action maps (ActionMap) 2023-08-25 14:02:22 +02:00
Karl Tauber
581c64b601 FileChooser: fixed occasional NPE in FlatShortcutsPanel on Windows (issue #718) 2023-08-23 19:40:59 +02:00
Karl Tauber
40418607e5 ScrollPane: fixed lost styling on ScrollPane border if using Table as view component 2023-08-13 23:23:03 +02:00
Karl Tauber
5436ea88d8 ScrollPane: improved/fixed calculation of left/right padding for rounded border 2023-08-13 17:01:56 +02:00
Karl Tauber
7bec5ec6dc ScrollPane: support rounded border 2023-08-13 14:01:09 +02:00
Karl Tauber
c953ff84d0 added explicit file encoding for Eclipse projects 2023-08-12 22:45:02 +02:00
Karl Tauber
96cd207df3 README.md: minor updates 2023-08-12 17:04:21 +02:00
Karl Tauber
7a75f62a6a README.md: new applications using FlatLaf:
- Constellation
- Ghidra
- jadx
- muCommander
- Guiffy
- HashGarten
- MediathekView
- Astah
- Big Faceless (BFO) PDF Viewer
- Chatty
- BGBlitz
- Linux Task Manager (LTM)
2023-08-12 16:35:00 +02:00
Karl Tauber
61e5fe58c2 README.md: organized applications using FlatLaf into categories 2023-08-12 15:57:32 +02:00
Karl Tauber
1a3baba702 README.md: removed versions and 'New' badge from applications using FlatLaf 2023-08-12 14:04:23 +02:00
Karl Tauber
58dc14bb46 Gradle: use System.getProperty( "org.gradle.parallel" ) instead of rootProject.property( "org.gradle.parallel" ) because this did not work with VM command-line option -Dorg.gradle.parallel=false 2023-08-12 13:38:42 +02:00
Karl Tauber
a5b7e04943 Gradle: check whether parallel build is enabled when running 'publish' task
https://stackoverflow.com/questions/72664149/gradle-maven-publish-sonatype-creates-multiple-repositories-that-cant-be-clos
2023-08-12 13:23:11 +02:00
Karl Tauber
22f2aa5475 README.md: introduced "Sponsor" badge icon
SVGO option `convertPathData` enabled
2023-08-11 13:21:56 +02:00
Karl Tauber
d4e9cb12be updated sigtest for FlatLaf 3.2
(generated in clean workspace with gradle task `sigtestGenerate`)
2023-08-10 23:31:26 +02:00
Karl Tauber
75da361480 GitHub Actions: disable parallel build for publishing to maven central; otherwise two staging repos are created, which can not be closed and released
https://stackoverflow.com/questions/72664149/gradle-maven-publish-sonatype-creates-multiple-repositories-that-cant-be-clos
2023-08-10 19:31:55 +02:00
Karl Tauber
7488bcb7b0 update to Gradle 8.2.1 2023-08-10 19:06:10 +02:00
Karl Tauber
1b1a9be107 release 3.2 2023-08-10 18:34:38 +02:00
Karl Tauber
db2f94aa53 IntelliJ Themes: fixed Table background in "One Dark" theme 2023-08-10 16:06:11 +02:00
Karl Tauber
810146b993 Demo: auto-reload current theme in development environment if .properties files have changed 2023-08-09 14:22:05 +02:00
Karl Tauber
93091662ab IntelliJ Themes: fixed colors for selection background/foreground, Separator, Slider track and ProgressBar background in various themes 2023-08-09 13:58:40 +02:00
Karl Tauber
d349227fbf IntelliJ Themes: fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light Flat", "Nord", "Solarized Dark" and "Solarized Light" themes 2023-08-08 17:13:30 +02:00
Karl Tauber
c9423e3aa8 CHANGELOG.md: fixed type on previous commit 2023-08-07 20:00:00 +02:00
Karl Tauber
b9737ca4f1 Merge PR #709: x86: Narrow version range for not using system icons 2023-08-07 19:55:11 +02:00
Karl Tauber
4b4990635d FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17 64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue #403) 2023-08-07 19:35:04 +02:00
Karl Tauber
afaa2c8c78 FileChooser: show localized text for all locales supported by Metal (issue #680) 2023-08-06 18:35:25 +02:00
Karl Tauber
f506ef0d4f Theme Editor: improve order of directories in combobox 2023-08-06 18:34:29 +02:00
Sung Ho Yoon
d30fe66cac Narrow version range for not using system icons 2023-08-06 09:35:00 +09:00
Karl Tauber
270e998e86 Theme Editor:
- fixed missing icon on "open" button (regression in commit 35fa3197c8)
- added icons to "File" menu for add and save actions
2023-08-05 19:18:51 +02:00
Karl Tauber
c395386c05 Merge PR #702: Window decorations: support toolbox-style "small" window title bar 2023-08-05 17:05:54 +02:00
Karl Tauber
4f1207b0db Merge PR #703: TabbedPane: support rounded underline selection and rounded card tabs 2023-08-05 16:59:57 +02:00
Karl Tauber
dc3878e290 Native window libraries: added flatlaf-windows-arm64.dll (for issue #443, PR #707)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/5771160235

locally signed Windows DLLs with FormDev Software GmbH code signing certificate
2023-08-05 16:26:11 +02:00
Karl Tauber
be2876149d Merge PR #707: Windows on ARM Support 2023-08-05 16:14:00 +02:00
Karl Tauber
52bae9dfb0 Windows on ARM:
- changed DLL filename from aarch64 to arm64
- publish ARM DLL to Maven Central
2023-08-05 16:11:46 +02:00
Karl Tauber
bb636bac3f IntelliJ Themes: fixed ModifyCollectionInEnhancedForLoop Error Prone error 2023-08-04 16:29:21 +02:00
Sung Ho Yoon
502b18fa86 Remove check for x86
Now that the aarch64 library is added, this
check is unnecessary.
2023-08-04 22:10:48 +09:00
Sung Ho Yoon
e0a5450264 Load Windows on ARM (aarch64) native library 2023-08-04 22:10:13 +09:00
Karl Tauber
5ffb23c37f Merge PR #704: Tweak ZAP's name 2023-08-04 12:33:23 +02:00
Sung Ho Yoon
b75f22b7bd Add windows-aarch64 build configuration 2023-08-04 19:19:06 +09:00
Karl Tauber
35fa3197c8 Demo: moved SVG icons to JFormDesigner forms 2023-08-04 12:08:04 +02:00
Karl Tauber
f03725ae36 IntelliJ Themes: fixed ComboBox backgrounds in all "Material UI Lite" themes and in some other themes 2023-08-04 11:18:28 +02:00
Karl Tauber
2b640e2129 IntelliJ Themes: fixed foreground colors of disabled text in "Vuesion" theme 2023-08-04 00:59:41 +02:00
Karl Tauber
2a983f5c03 IntelliJ Themes: fixed background colors of enabled text components, to distinguish from disabled (issue #528) 2023-08-04 00:25:51 +02:00
Karl Tauber
cacc5daa14 IntelliJ Themes: updated theme "Monokai Pro Theme" from version 1.10 2023-08-03 00:37:42 +02:00
Karl Tauber
593502287d IntelliJ Themes: removed all "Contrast" themes from "Material UI Lite" 2023-08-03 00:26:59 +02:00
Karl Tauber
7a9bdf9be0 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2023-08-02 15:03:57 +02:00
Karl Tauber
170c22c5ed IntelliJ Themes: fixed unselected CheckBox and RadioButton icon colors for themes "Atom One Light", "Cyan Light", "GitHub", "Light Owl", "Material Lighter" and "Solarized Light" 2023-08-02 14:25:15 +02:00
Karl Tauber
7e8fa58bd7 IntelliJ Themes: reduced memory footprint and improved setup speed by ignoring IntelliJ UI properties that are not used in FlatLaf
also fixed `ToggleButton.tab.selectedBackground`
2023-08-02 00:40:49 +02:00
Rick M
046200625c Make world possessive not plural 2023-08-01 16:20:11 -04:00
Rick M
710ed55152 Tweak ZAP's name
Per: https://www.zaproxy.org/blog/2023-08-01-zap-is-joining-the-software-security-project/
2023-08-01 16:18:41 -04:00
Karl Tauber
ce527329a6 ComboBox: fixed search in item list for text with spaces (issue #691) 2023-08-01 15:19:15 +02:00
Karl Tauber
b455dd41ab TabbedPane: going back to 3px tab selection for macOS themes 2023-07-31 22:36:59 +02:00
Karl Tauber
b47ed94f40 PopupMenu: make sure that popup menu does not overlap any operating system task bar (issue #701) 2023-07-31 22:21:35 +02:00
Karl Tauber
f1351a2093 TabbedPane: support rounded underline selection and rounded card tabs 2023-07-31 13:36:18 +02:00
Karl Tauber
c1c5e81df0 fixed error reported by Error Prone 2023-07-30 16:07:26 +02:00
Karl Tauber
8e3c8ba6c5 Window decorations: support toolbox-style "small" window title bar (issue #659) 2023-07-30 15:26:44 +02:00
Karl Tauber
dfe4404a17 fixed build error in flatlaf-testing-modular-app caused by moving to JSVG (PR #684) 2023-07-30 14:33:24 +02:00
Karl Tauber
b3fb63c9f5 ComboBox: improved location of selected item in popup if list is large and scrollable 2023-07-30 14:01:24 +02:00
Karl Tauber
9db3dfff26 CHANGELOG.md: added info about recently merged PR #684 2023-07-30 13:58:49 +02:00
Karl Tauber
3c9051e7de Merge PR #684: Replace svgSalamander with jsvg 2023-07-30 13:46:06 +02:00
Karl Tauber
798a6d061c jsvg: use String instead of URL as cache key to avoid this problem: https://errorprone.info/bugpattern/URLEqualsHashCode 2023-07-17 23:42:26 +02:00
Karl Tauber
19afbe99d9 FormattedTextField: On Linux, fixed IllegalArgumentException: Invalid location if JFormattedTextField.setDocument() is invoked in a focus gained listener on that formatted text field. (issue #698) 2023-07-17 15:46:11 +02:00
Karl Tauber
4715d8d16c jsvg: use RenderingHints.VALUE_STROKE_PURE for correct line rendering 2023-07-17 12:43:09 +02:00
Karl Tauber
193da2bc4d jsvg: updated flatlaf-extras/README.md; removed svgSalamander from libs.versions.toml 2023-07-10 13:49:30 +02:00
Karl Tauber
799f8efe22 jsvg: simplified/fixed loading from input stream; replaced internal usage of URI with URL 2023-07-10 13:41:54 +02:00
Karl Tauber
f6062e1ec4 jsvg: fixed color filter in FlatSVGIcon 2023-07-09 23:16:50 +02:00
Karl Tauber
c790778a46 Window decorations: support moving/resizing JInternalFrame that is child of JLayeredPane and overlaps FlatLaf title bar (issue #658) 2023-07-09 18:23:20 +02:00
Karl Tauber
4344f1b3a0 IntelliJ Themes: fixed focused tab background color for themes "Arc *", "Material Design Dark", "Monocai", "One Dark", "Spacegray" and "Xcode-Dark" (issue #697) 2023-07-09 14:09:01 +02:00
Karl Tauber
d520b30500 TestFlatStylingScale unit tests added for commit fde65b2730, issue #682 2023-07-02 18:47:10 +02:00
Karl Tauber
11c02e5f50 FlatWindowDecorationsTest: redesigned UI; added "FlatLaf window decorations" checkbox 2023-07-02 18:03:39 +02:00
Karl Tauber
aa4c6ee9da Native window libraries: updated Windows DLLs (for issue #673)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/5431957508

locally signed Windows DLLs with FormDev Software GmbH code signing certificate
2023-07-01 18:37:32 +02:00
Karl Tauber
98f8557392 flatlaf-natives-windows: reworked linking/loading of jawt.dll; now loading jawt.dll when first used (issue #673) 2023-07-01 18:16:59 +02:00
Karl Tauber
6f6a860887 IntelliJ Themes: "Monocai" theme: fixed unreadable text color of default buttons (issue #693) 2023-06-21 17:30:59 +02:00
Karl Tauber
38695e9e16 updated Error Prone to 2.20.0 2023-06-21 17:16:22 +02:00
Karl Tauber
242c478cb3 GitHub Actions:
- build using Java 20 (use toolchain because Gradle 8.1.1 does not support running and compiling on Java 20 because Kotlin does not support 20 as target version)
- use temurin distribution as default because it is pre-installed on ubuntu-latest
2023-06-21 17:14:29 +02:00
Karl Tauber
f003e835bd macOS themes: changing @accentColor variable in FlatLaf properties files did not change all accent related colors for all components 2023-06-21 12:15:12 +02:00
Karl Tauber
267defb321 added system property flatlaf.useNativeLibrary to allow disabling loading of FlatLaf native library (issue #674) 2023-06-21 00:13:35 +02:00
Karl Tauber
4392c7627b IntelliJ Themes:
- "Light Owl" theme: Fixed wrong (unreadable) text color in selected menu
  items, selected text in text components, and selection in ComboBox popup
  list. (issue #687)
- "Gradianto Midnight Blue" theme: Fixed color of ScrollBar track, which was
  not visible. (issue #686)
2023-06-21 00:02:01 +02:00
Karl Tauber
fde65b2730 Styling: fixed scaling of some styling properties (rowHeight for Table and Tree; iconTextGap for Button, CheckBox and RadioButton) (issue #682) 2023-06-20 23:45:41 +02:00
Karl Tauber
e908362f0a fixed IllegalComponentStateException when invoker is not showing in SubMenuUsabilityHelper (issue #692) 2023-06-20 23:15:11 +02:00
Jannis Weis
a40b837634 Replace svgSalamander with jsvg 2023-05-29 16:30:12 +02:00
Karl Tauber
b391465fbf Gradle:
- moved FlatLaf version numbers from build.gradle.kts to gradle.properties
- enabled Gradle parallel build
2023-05-20 12:30:23 +02:00
Karl Tauber
bad0428f5b UIDefaultsLoader and FlatStylingSupport: explicitly specify throws IllegalArgumentException and improved catching 2023-05-20 12:26:46 +02:00
Karl Tauber
97018df2f9 added Error Prone (https://errorprone.info/) and fixed reported errors and warnings
- CI runs Error Prone with Java 11
- use Gradle task `errorprone` to run it on development machine
- fixes are mostly cosmetic except:
  - use Locale.ENGLISH for String.toLowerCase()
  - use explicit character encoding when reading/writing files
  - TabbedPane: wrong logic in mouse-wheel scrolling
  - SplitPane: simplified property change listener (fixes hiding field `propertyChangeListener` of superclass)
2023-05-19 22:58:12 +02:00
Karl Tauber
9d84501bc8 Gradle: moved declaration of all external dependencies to libs.versions.toml and use Gradle version catalog 2023-05-18 15:37:03 +02:00
Karl Tauber
e9fb2b3fdc update to Gradle 8.1.1 2023-05-18 15:32:05 +02:00
Karl T
f60250fd8a Merge pull request #681 from Plyha/spark
Add Spark to Applications
2023-05-18 14:58:39 +02:00
ilya khlevnoy
5fc3cae28a Add Spark to Applications 2023-05-17 23:07:35 +03:00
Karl T
e7935be85b Merge pull request #671 from kumait/main
Add Kafka Visualizer to Applications
2023-04-23 17:01:56 +02:00
kumait
89363b2ea1 Add Kafka Visualizer to Applications 2023-04-22 18:11:55 -04:00
Karl Tauber
e84390ee46 release 3.1.1 2023-04-18 15:01:44 +02:00
Karl Tauber
65a0f467ae Native libraries: Fixed IllegalArgumentException: URI scheme is not "file" when using FlatLaf in WebStart. (issue #668; regression in FlatLaf 3.1) 2023-04-17 21:33:56 +02:00
Karl Tauber
4afb150106 IntelliJ Themes:
- Fixed too large menu item paddings and too large table/tree row heights (all
  "Material Theme UI Lite" themes; issue #667; regression in FlatLaf 3.1).
- Fixed too large tree row height in "Carbon", "Dark Purple", "Gray",
  "Material Design Dark", "Monokai Pro", "One Dark" and "Spacegray" themes.
2023-04-17 13:45:05 +02:00
Karl Tauber
0f6702217e updated CHANGELOG.md and README.md for FlatLaf 3.1 2023-04-12 13:55:22 +02:00
Karl Tauber
13a0097858 updated sigtest for FlatLaf 3.1
(generated in clean workspace with gradle task `sigtestGenerate`)
2023-04-03 12:24:38 +02:00
Karl Tauber
01c830ad92 release 3.1 2023-04-03 11:02:33 +02:00
Karl Tauber
dce4f4623c SystemInfo.isMacFullWindowContentSupported now includes isMacOS; updated comments regarding system property apple.awt.application.appearance 2023-04-03 10:59:30 +02:00
Karl Tauber
d530624362 Table: improved cell focus indicator border hiding (issue #654)
- never for cell selection mode
- for single selected column if contains editable cell
2023-03-28 18:33:31 +02:00
Karl Tauber
2e878b62d1 Table: fixed cell focus indicator border hiding for boolean columns (issue #654) 2023-03-28 13:32:33 +02:00
Karl Tauber
d27a246dfe Table: fixed potential performance issue with paint cell focus indicator border (issue #654) 2023-03-27 16:37:02 +02:00
Karl Tauber
778def118a List, Table, Tree: improved color of cell focus indicator border
- was black
- now derived from selection color (usually accent color)
  - darker in light themes
  - lighter in dark themes

(issue #654)
2023-03-26 14:56:15 +02:00
Karl Tauber
bc5587477b Theme Editor: Preview: fixed preview of cell focus indicators in List, Table and Tree 2023-03-26 13:05:50 +02:00
Karl Tauber
03a775cd31 List: use FlatUIUtils.isPermanentFocusOwner() instead of hasFocus() for cell renderer (similar to Tree) 2023-03-25 18:28:31 +01:00
Karl Tauber
875083a924 GitHub Actions: don't use toolchain for Java 19 (because this fails on Gradle 8.0.2) 2023-03-06 13:41:44 +01:00
Karl Tauber
f6fc925c9e update to Gradle 8.0.2 2023-03-06 13:13:05 +01:00
Karl Tauber
74e1972781 Linux window decoration: check whether native move/resize was successfully started before maximizing window in mouseClicked() (issue #637) 2023-03-02 23:55:06 +01:00
Karl Tauber
2f5c54bb49 Tree: Fixed missing custom closed/opened/leaf icons of a custom DefaultTreeCellRenderer. (issue #653; regression since implementing PR #609 in FlatLaf 3.0) 2023-03-02 23:06:35 +01:00
geroyche
465798ee3d catch npe 2023-02-23 17:34:40 +01:00
Karl Tauber
425f3acced Window decorations on Linux: fixed behavior of maximize/restore button when tiling window to left or right half of screen (issue #647) 2023-02-23 15:37:26 +01:00
Karl Tauber
546382e471 Linux: fixed UnsatisfiedLinkError: com.formdev.flatlaf.ui.FlatNativeWindowsLibrary.getOSBuildNumberImpl() (regression in PR #643, commit 07ad467c73)
added additional platform checks to `FlatNative<platform>Library.isLoaded()` methods
2023-02-10 21:33:26 +01:00
Karl Tauber
7e91d78633 Native window decorations: updated Windows DLLs (for PR #643)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/4097364667

locally signed Windows DLLs with FormDev Software GmbH code signing certificate
2023-02-05 17:32:15 +01:00
Karl Tauber
136e1e4e30 Merge PR #643: Windows 11: Rounded popup windows 2023-02-05 16:59:16 +01:00
Karl Tauber
f5f6850172 fixed HiDPIUtils.paintAtScale1x(), which painted at wrong location if graphics is rotated, is scaled and x or y parameters are not zero (issue #646) 2023-02-05 16:56:04 +01:00
Karl Tauber
28cdde3f17 Tree: fixed truncated node text and too small painted non-wide node background if custom cell renderer sets icon, but not disabled icon, and tree is disabled (issue #640) 2023-02-02 11:50:13 +01:00
Karl Tauber
29b801e13d TabbedPane: support hover and focused tab foreground colors (issue #627)
changed background behavior: `tabbedPane.getBackgroundAt(tabIndex)` now has higher priority than `TabbedPane.focusColor` and `TabbedPane.selectedBackground`
2023-01-31 13:58:42 +01:00
Karl Tauber
1435469ee5 TableHeader: support column hover and pressed background and foreground colors (issue #636) 2023-01-30 14:21:44 +01:00
Karl Tauber
4a0bd2c09f MenuItem: fixed horizontal alignment of icons (issue #631) 2023-01-27 22:43:45 +01:00
Karl Tauber
f8d67f863f UI defaults dumps updated for commits 9fef2f9d05 2023-01-27 22:27:36 +01:00
Karl Tauber
0291dd5416 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2023-01-27 22:27:26 +01:00
Karl Tauber
9014435d4d Windows 11: made rounded popup border configurable via UI properties and client property 2023-01-27 15:00:11 +01:00
Karl Tauber
07ad467c73 Windows 11: use rounded popups with system border and system drop shadow 2023-01-26 18:13:26 +01:00
Karl Tauber
35e23574cf Native libraries: (issue #624)
- publish to maven central
- load from same location as flatlaf.jar (if available, otherwise extract from jar to temporary directory)
2023-01-21 18:35:54 +01:00
Karl Tauber
9b62b8395f Theme Editor: support macOS light and dark themes 2023-01-19 11:10:42 +01:00
Karl Tauber
45e7022deb Fonts: updated JetBrains Mono to v2.304 2023-01-19 00:06:00 +01:00
Karl Tauber
32dce16363 Styling: fixed resolving of UI variables in styles that use other variables 2023-01-18 23:26:34 +01:00
Karl Tauber
e34b5eafe1 Merge PR #639: Fonts: Roboto Mono 2023-01-12 15:28:06 +01:00
Karl Tauber
4e1e749094 Fonts: added Roboto Mono (issue #638) 2023-01-12 14:08:08 +01:00
Karl Tauber
ede9293377 README.md: added macOS theme screenshots 2023-01-03 20:11:55 +01:00
Karl Tauber
9101324a1f Window decorations: glass pane no longer overlaps the FlatLaf window title bar (issue #630) 2023-01-03 19:48:21 +01:00
Karl Tauber
4b844353ee Window decorations: fixed broken window resizing on Linux multi-screen setups (issue #632) 2023-01-02 19:54:05 +01:00
Karl Tauber
2134c19c58 IntelliJ Themes: avoid that accent color affect some colors in some IntelliJ themes (issue #625) 2023-01-02 18:58:18 +01:00
Karl Tauber
c974784ebb IntelliJ Themes: fixed default button hover background in "Solarized Light" theme (issue #628) 2023-01-02 18:43:12 +01:00
Karl Tauber
5eb6961023 updated sigtest for FlatLaf 3.0
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-12-16 01:30:50 +01:00
Karl Tauber
07cbd8b97b Fonts: enabled "Download" section in README.md 2022-12-16 01:29:11 +01:00
Karl Tauber
09c7f15364 Fonts: added missing description in POM (required for Maven Central) 2022-12-16 00:51:35 +01:00
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
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
567 changed files with 53473 additions and 33882 deletions

2
.gitattributes vendored
View File

@@ -20,7 +20,9 @@
*.gif binary
*.jar binary
*.lib binary
*.otf binary
*.png binary
*.sketch binary
*.so binary
*.ttf 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
@@ -8,132 +9,141 @@ on:
- '*'
tags:
- '[0-9]*'
pull_request:
branches:
- '*'
paths-ignore:
- '**.md'
- '.*'
- '**/.settings/**'
- 'flatlaf-core/svg/**'
- 'flatlaf-testing/dumps/**'
- 'flatlaf-testing/misc/**'
- 'images/**'
jobs:
build:
name: build (11)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
- name: Setup Java 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Check with Error Prone
run: ./gradlew errorprone clean
- name: Build with Gradle
run: ./gradlew build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: FlatLaf-build-artifacts
path: |
flatlaf-*/build/libs
flatlaf-*/flatlaf-*/build/libs
!**/*-javadoc.jar
!**/*-sources.jar
build-on:
runs-on: ubuntu-latest
needs: build
if: github.repository == 'JFormDesigner/FlatLaf'
strategy:
matrix:
# test against
# - Java 8 (minimum requirement)
# - Java LTS versions (11, 17, ...)
# - lastest Java version(s)
# - latest Java version(s)
java:
- 8
- 11 # LTS
- 17 # LTS
- 21 # LTS
- 23 # latest
toolchain: [""]
# include:
# - java: 21
# toolchain: 22 # latest
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
if: matrix.java == '8'
- uses: actions/checkout@v4
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
distribution: temurin # Java 8, 11, 17 and 21 are pre-installed on ubuntu-latest
cache: gradle
- name: Build with Gradle
run: ./gradlew build
- name: Upload artifacts
uses: actions/upload-artifact@v3
if: matrix.java == '11'
with:
name: FlatLaf-build-artifacts
path: |
flatlaf-*/build/libs
!**/*-javadoc.jar
!**/*-sources.jar
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
snapshot:
runs-on: ubuntu-latest
needs: build
needs: build-on
if: |
github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || startsWith( github.ref, 'refs/heads/develop-' )) &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Publish snapshot to oss.sonatype.org
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
run: ./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
- name: Upload theme editor
uses: sebastianpopp/ftp-action@releases/v2
with:
host: ${{ secrets.FTP_SERVER }}
user: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
forceSsl: true
localDir: "flatlaf-theme-editor/build/libs"
remoteDir: "snapshots"
options: "--only-newer --no-recursion --verbose=1"
release:
runs-on: ubuntu-latest
needs: build
needs: build-on
if: |
github.event_name == 'push' &&
startsWith( github.ref, 'refs/tags/' ) &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- 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 -Dorg.gradle.parallel=false
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
- name: Upload demo
uses: sebastianpopp/ftp-action@releases/v2
with:
host: ${{ secrets.FTP_SERVER }}
user: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
forceSsl: true
localDir: "flatlaf-demo/build/libs"
remoteDir: "."
options: "--only-newer --no-recursion --verbose=1"
- name: Install lftp
run: sudo apt-get -y install lftp
- name: Upload theme editor
uses: sebastianpopp/ftp-action@releases/v2
with:
host: ${{ secrets.FTP_SERVER }}
user: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
forceSsl: true
localDir: "flatlaf-theme-editor/build/libs"
remoteDir: "."
options: "--only-newer --no-recursion --verbose=1"
- name: Upload demo and theme editor
run: >
lftp -c "set ftp:ssl-force true;
open -u ${{ secrets.FTP_USERNAME }},${{ secrets.FTP_PASSWORD }} ${{ secrets.FTP_SERVER }};
mput flatlaf-demo/build/libs/flatlaf-demo-*.jar;
mput flatlaf-theme-editor/build/libs/flatlaf-theme-editor-*.jar"

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

@@ -0,0 +1,62 @@
# 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'
- '!**.md'
- '!**/.settings/**'
jobs:
Fonts:
strategy:
matrix:
font:
- inter
- jetbrains-mono
- roboto
- roboto-mono
runs-on: ubuntu-latest
if: |
github.event_name == 'push' &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # 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
@@ -9,42 +10,53 @@ on:
tags:
- '[0-9]*'
paths:
- 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
pull_request:
branches:
- '*'
paths:
- 'flatlaf-natives/flatlaf-natives-windows/**'
- 'flatlaf-natives/**'
- '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
- '!**.md'
- '!**/.settings/**'
jobs:
Windows:
runs-on: windows-latest
Natives:
strategy:
matrix:
os:
- windows-latest
- macos-latest
- ubuntu-latest
- ubuntu-24.04-arm
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
- uses: gradle/actions/wrapper-validation@v4
- name: install libxt-dev
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
run: sudo apt install libxt-dev
- name: install g++-aarch64-linux-gnu
if: matrix.os == 'ubuntu-latest'
run: sudo apt install g++-aarch64-linux-gnu
- name: Setup Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: adopt
distribution: temurin
cache: 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
run: ./gradlew :flatlaf-natives-windows:build-natives --no-daemon
run: ./gradlew build-natives --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: FlatLaf-natives-windows-build-artifacts
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
path: |
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
flatlaf-natives/flatlaf-natives-windows/build
flatlaf-natives/flatlaf-natives-*/build

37
.github/workflows/pr-snapshots.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
name: PR Snapshots
on:
pull_request:
paths-ignore:
- '**.md'
- '.*'
- '**/.settings/**'
- 'flatlaf-core/svg/**'
- 'flatlaf-testing/dumps/**'
- 'flatlaf-testing/misc/**'
- 'images/**'
jobs:
snapshot:
runs-on: ubuntu-latest
if: github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v4
- name: Setup Java 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Publish PR snapshot to oss.sonatype.org
run: >
./gradlew publish -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
-Pgithub.event.pull_request.number=${{ github.event.pull_request.number }}
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

2
.gitignore vendored
View File

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

View File

@@ -1,6 +1,655 @@
FlatLaf Change Log
==================
## 3.6-SNAPSHOT
#### New features and improvements
- macOS: Re-enabled rounded popup border (see PR #772) on macOS 14.4+ (was
disabled in 3.5.x).
- CheckBox: Support styling indeterminate state of
[tri-state check boxes](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/components/FlatTriStateCheckBox.html).
(PR #936; issue #919)
- List: Support for alternate row highlighting. (PR #939)
- Tree: Support for alternate row highlighting. (PR #903)
- Tree: Support wide cell renderer. (issue #922)
- Extras: `FlatSVGIcon` color filters now can access painting component to
implement component state based color mappings. (issue #906)
- Linux: Added `libflatlaf-linux-arm64.so` for Linux on ARM64. (issue #899)
#### Fixed bugs
- Button: Fixed background and foreground colors for `borderless` and
`toolBarButton` style default buttons (`JButton.isDefaultButton()` is `true`).
(issue #947)
- FileChooser: Improved performance when navigating to large directories with
thousands of files. (issue #953)
- PopupFactory: Fixed NPE on Windows 10 when `owner` is `null`. (issue #952)
- Popup: On Windows 10, drop shadow of heavy-weight popup was not updated if
popup moved/resized. (issue #942)
- FlatLaf window decorations: Minimize and maximize icons were not shown for
custom scale factors less than 100% (e.g. `-Dflatlaf.uiScale=75%`). (issue
#951)
- Linux: Popups (menus and combobox lists) were not hidden when window is moved,
resized, maximized, restored, iconified or switched to another window. (issue
#962)
## 3.5.4
#### Fixed bugs
- HTML: Fixed NPE when using HTML text on a component with `null` font. (issue
#930; PR #931; regression in 3.5)
- Linux: Fixed NPE when using FlatLaf window decorations and switching theme.
(issue #933; regression in 3.5.3)
## 3.5.3
#### Fixed bugs
- HTML: Fixed wrong rendering if HTML text contains `<style>` tag with
attributes (e.g. `<style type='text/css'>`). (issue #905; regression in 3.5.1)
- FlatLaf window decorations:
- Windows: Fixed possible deadlock with TabbedPane in window title area in
"full window content" mode. (issue #909)
- Windows: Fixed wrong layout in maximized frame after changing screen scale
factor. (issue #904)
- Linux: Fixed continuous cursor toggling between resize and standard cursor
when resizing window. (issue #907)
- Fixed sometimes broken window moving with SplitPane in window title area in
"full window content" mode. (issue #926)
- Popup: On Windows 10, fixed misplaced popup drop shadow. (issue #911;
regression in 3.5)
- Popup: Fixed NPE if `GraphicsConfiguration` is `null` on Windows. (issue #921)
- Theme Editor: Fixed using color picker on secondary screen.
- Fixed detection of Windows 11 if custom exe launcher does not specify Windows
10+ compatibility in application manifest. (issue #916)
- Linux: Fixed slightly different font size (or letter width) used to paint HTML
text when default font family is _Cantarell_ (e.g. on Fedora). (issue #912)
#### Other Changes
- Class `FlatPropertiesLaf` now supports FlatLaf macOS themes as base themes.
## 3.5.2
#### Fixed bugs
- Windows: Fixed repaint issues (ghosting) on some systems (probably depending
on graphics card/driver). This is done by setting Java system property
`sun.java2d.d3d.onscreen` to `false` (but only if `sun.java2d.d3d.onscreen`,
`sun.java2d.d3d` and `sun.java2d.noddraw` are not yet set), which disables
usage of Windows Direct3D (DirectX) onscreen surfaces. Component rendering
still uses Direct3D. (issue #887)
- FlatLaf window decorations:
- Iconify/maximize/close buttons did not fill whole title bar height, if some
custom component in menu bar increases title bar height. (issue #897)
- Windows: Fixed possible application freeze when using custom component that
overrides `Component.contains(int x, int y)` and invokes
`SwingUtilities.convertPoint()` (or similar) from the overridden method.
(issue #878)
- TextComponents: Fixed too fast scrolling in multi-line text components when
using touchpads (e.g. on macOS). (issue #892)
- ToolBar: Fixed endless loop if button in Toolbar has focus and is made
invisible. (issue #884)
#### Other Changes
- FlatLaf window decorations: Added client property `JRootPane.titleBarHeight`
to allow specifying a (larger) preferred height for the title bar. (issue
#897)
- Added system property `flatlaf.useRoundedPopupBorder` to allow disabling
native rounded popup borders on Windows 11 and macOS. On macOS 14.4+, where
rounded popup borders are disabled since FlatLaf 3.5 because of occasional
problems, you can use this to enable rounded popup borders (at your risk).
## 3.5.1
#### Fixed bugs
- HTML: Fixed occasional cutoff wrapped text when using multi-line text in HTML
tags `<h1>`...`<h6>`, `<code>`, `<kbd>`, `<big>`, `<small>` or `<samp>`.
(issue #873; regression in 3.5)
- Popup: Fixed `UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency
is not supported` exception on Haiku OS when showing popup (partly) outside of
window. (issue #869)
- HiDPI: Fixed occasional wrong repaint areas when using
`HiDPIUtils.installHiDPIRepaintManager()`. (see PR #864)
- Added system property `flatlaf.useSubMenuSafeTriangle` to allow disabling
submenu safe triangle (PR #490) for
[SWTSwing](https://github.com/Chrriis/SWTSwing). (issue #870)
## 3.5
#### New features and improvements
- Table: Support rounded selection. (PR #856)
- Button and ToggleButton: Added border colors for pressed and selected states.
(issue #848)
- Label: Support painting background with rounded corners. (issue #842)
- Popup: Fixed flicker of popups (e.g. tooltips) while they are moving (e.g.
following mouse pointer). (issues #832 and #672)
- FileChooser: Wrap shortcuts in scroll pane. (issue #828)
- Theme Editor: On macOS, use larger window title bar. (PR #779)
#### Fixed bugs
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
may freeze the application and crash the macOS WindowServer process (reports
vary from Finder restarts to OS restarts). This is a temporary change until a
solution is found. See NetBeans issues
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
and
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
- FlatLaf window decorations: Window top border on Windows 10 in "full window
content" mode was not fully repainted when activating or deactivating window.
(issue #809)
- Button and ToggleButton: UI properties `[Toggle]Button.selectedForeground` and
`[Toggle]Button.pressedForeground` did not work for HTML text. (issue #848)
- HTML: Fixed font sizes for HTML tags `<h1>`...`<h6>`, `<code>`, `<kbd>`,
`<big>`, `<small>` and `<samp>` in HTML text for components Button, CheckBox,
RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and
JXHyperlink. Also fixed for Label and ToolTip if using Java 11+.
- ScrollPane: Fixed/improved border painting at 125% - 175% scaling to avoid
different border thicknesses. (issue #743)
- Table: Fixed painting of alternating rows below table if auto-resize mode is
`JTable.AUTO_RESIZE_OFF` and table width is smaller than scroll pane (was not
updated when table width changed and was painted on wrong side in
right-to-left component orientation).
- Theme Editor: Fixed occasional empty window on startup on macOS.
- FlatLaf window decorations: Fixed black line sometimes painted on top of
(native) window border on Windows 11. (issue #852)
- HiDPI: Fixed incomplete component paintings at 125% or 175% scaling on Windows
where sometimes a 1px wide area at the right or bottom component edge is not
repainted. E.g. ScrollPane focus indicator border. (issues #860 and #582)
#### Incompatibilities
- ProgressBar: Log warning (including stack trace) when uninstalling
indeterminate progress bar UI or using `JProgressBar.setIndeterminate(false)`
not on AWT thread, because this may throw NPE in `FlatProgressBarUI.paint()`.
(issues #841 and #830)
- Panel: Rounded background of panel with rounded corners is now painted even if
panel is not opaque. (issue #840)
## 3.4.1
#### Fixed bugs
- SplitPane: Update divider when client property `JSplitPane.expandableSide`
changed.
- TabbedPane: Fixed swapped back and forward scroll buttons when using
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
- Fixed missing window top border on Windows 10 in "full window content" mode.
(issue #809)
- Extras:
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
disabling logging. (issue #823)
- Added support for `JSplitPane.expandableSide` client property to
`FlatSplitPane`.
- Native libraries: Added API version check to test whether native library
matches the JAR (bad builds could e.g. ship a newer JAR with an older
incompatible native library) and to test whether native methods can be invoked
(some security software allows loading native library but blocks method
invocation).
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
#### Incompatibilities
- File names of custom properties files for nested Laf classes now must include
name of enclosing class name. E.g. nested Laf class `IntelliJTheme.ThemeLaf`
used `ThemeLaf.properties` in previous versions, but now needs to be named
`IntelliJTheme$ThemeLaf.properties`.
## 3.4
#### New features and improvements
- FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
content" mode, which allows you to extend the content into the window title
bar. (PR #801)
- macOS: Support larger window title bar close/minimize/zoom buttons spacing in
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
mode and introduced "buttons placeholder". (PR #779)
- Native libraries:
- System property `flatlaf.nativeLibraryPath` now supports loading native
libraries named the same as on Maven central.
- Published `flatlaf-<version>-no-natives.jar` to Maven Central. This JAR is
equal to `flatlaf-<version>.jar`, except that it does not contain the
FlatLaf native libraries. The Maven "classifier" to use this JAR is
`no-natives`. You need to distribute the FlatLaf native libraries with your
application.
See https://www.formdev.com/flatlaf/native-libraries/ for more details.
- Improved log messages for loading fails.
- Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
- Table: Select all text in cell editor when starting editing using `F2` key on
Windows or Linux. (issue #652)
#### Fixed bugs
- macOS: Setting window background (of undecorated window) to translucent color
(alpha < 255) did not show the window translucent. (issue #705)
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794)
## 3.3
#### New features and improvements
- macOS (10.14+): Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
native macOS rounded borders. (PR #772; issue #715)
- Native libraries: Added `libflatlaf-macos-arm64.dylib` and
`libflatlaf-macos-x86_64.dylib`. See also
https://www.formdev.com/flatlaf/native-libraries/.
- ScrollPane: Support rounded border. (PR #713)
- SplitPane: Support divider hover and pressed background colors. (PR #788)
- TabbedPane: Support vertical tabs. (PR #758, issue #633)
- TabbedPane: Paint rounded tab area background for rounded cards. (issue #717)
- ToolBar: Added styling properties `separatorWidth` and `separatorColor`.
#### Fixed bugs
- Button and ToggleButton: Selected buttons did not use explicitly set
foreground color. (issue #756)
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
default Windows exe icon. (see
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
- OptionPane: Fixed styling custom panel background in `JOptionPane`. (issue
#761)
- ScrollPane: Styling ScrollPane border properties did not work if view
component is a Table.
- Table:
- Switching theme looses table grid and intercell spacing. (issues #733 and
#750)
- Fixed background of `boolean` columns when using alternating row colors.
(issue #780)
- Fixed border arc of components in complex table cell editors. (issue #786)
- TableHeader:
- No longer temporary replace header cell renderer while painting. This avoids
a `StackOverflowError` in case that custom renderer does this too. (see
[NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)) This
also improves compatibility with custom table header implementations.
- Header cell renderer background/foreground colors were not restored after
hover if renderer uses `null` for background/foreground. (PR #790)
- TabbedPane:
- Avoid unnecessary repainting whole tabbed pane content area when layouting
leading/trailing components.
- Avoid unnecessary repainting of selected tab on temporary changes.
- Fixed "endless" layouting and repainting when using nested tabbed panes (top
and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping)
as tab content. (see
[jadx issue #2030](https://github.com/skylot/jadx/issues/2030))
- Fixed broken rendering after resizing window to minimum size and then
increasing size again. (issue #767)
#### Incompatibilities
- Removed support for JetBrains custom decorations, which required
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki) (JBR)
8 or 11. It did not work for JBR 17. System property
`flatlaf.useJetBrainsCustomDecorations` is now ignored. **Note**: FlatLaf
window decorations continue to work with JBR.
## 3.2.5
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Windows 10. (issue #753;
regression in 3.2.1 in fix for #626)
## 3.2.4
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Linux with Wayland and Java 21.
(issue #752; regression in 3.2.3)
## 3.2.3
#### Fixed bugs
- Popup: Popups that request focus were not shown on Linux with Wayland and Java 21.
(issue #752)
## 3.2.2
#### Fixed bugs
- Button: Fixed painting icon and text at wrong location when using HTML text,
left/right vertical alignment and running in Java 19+. (issue #746)
- CheckBox and RadioButton: Fixed cut off right side when border is removed and
horizontal alignment is set to `right`. (issue #734)
- TabbedPane: Fixed NPE when using focusable component as tab component and
switching theme. (issue #745)
## 3.2.1
#### Fixed bugs
- Fixed memory leak in
`MultiResolutionImageSupport.create(int,Dimension[],Function<Dimension,Image>)`,
which caches images created by the producer function. Used by
`FlatSVGIcon.getImage()` and `FlatSVGUtils.createWindowIconImages()`. If you
use one of these methods, it is **strongly recommended** to upgrade to this
version, because if the returned image is larger and painted very often it may
result in an out-of-memory situation. (issue #726)
- FileChooser: Fixed occasional NPE in `FlatShortcutsPanel` on Windows. (issue
#718)
- TextField: Fixed placeholder text painting, which did not respect horizontal
alignment property of `JTextField`. (issue #721)
- Popup: Fixed drop shadow if popup overlaps a heavyweight component. (Windows
10 only; issue #626)
## 3.2
#### New features and improvements
- TabbedPane: Support rounded underline selection and rounded card tabs. (PR
#703)
- FlatLaf window decorations:
- Support for Windows on ARM 64-bit. (issue #443, PR #707)
- Support toolbox-style "small" window title bar. (issue #659, PR #702)
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
library (instead of svgSalamander) for rendering. JSVG provides improved SVG
rendering and uses less memory compared to svgSalamander. (PR #684)
- ComboBox: Improved location of selected item in popup if list is large and
scrollable.
- FileChooser: Show localized text for all locales supported by Java's Metal
look and feel. (issue #680)
- Added system property `flatlaf.useNativeLibrary` to allow disabling loading of
FlatLaf native library. (issue #674)
- IntelliJ Themes:
- Reduced memory footprint by releasing Json data and ignoring IntelliJ UI
properties that are not used in FlatLaf.
- Updated "Hiberbee Dark" and "Gradianto" themes.
#### Fixed bugs
- Styling: Fixed scaling of some styling properties (`rowHeight` for Table and
Tree; `iconTextGap` for Button, CheckBox and RadioButton). (issue #682)
- Fixed `IllegalComponentStateException` when invoker is not showing in
`SubMenuUsabilityHelper`. (issue #692)
- macOS themes: Changing `@accentColor` variable in FlatLaf properties files did
not change all accent related colors for all components.
- IntelliJ Themes:
- "Light Owl" theme: Fixed wrong (unreadable) text color in selected menu
items, selected text in text components, and selection in ComboBox popup
list. (issue #687)
- "Gradianto Midnight Blue" theme: Fixed color of ScrollBar track, which was
not visible. (issue #686)
- "Monocai" theme: Fixed unreadable text color of default buttons. (issue
#693)
- "Vuesion" theme: Fixed foreground colors of disabled text.
- "Material UI Lite" themes: Fixed non-editable ComboBox button background.
- CheckBox and RadioButton: Fixed unselected icon colors for themes "Atom One
Light", "Cyan Light", "GitHub", "Light Owl", "Material Lighter" and
"Solarized Light".
- TabbedPane: Fixed focused tab background color for themes "Arc *", "Material
Design Dark", "Monocai", "One Dark", "Spacegray" and "Xcode-Dark". (issue
#697)
- TextComponents, ComboBox and Spinner: Fixed background colors of enabled
text components, to distinguish from disabled, for themes "Carbon", "Cobalt
2", "Gradianto *", "Gruvbox *", "Monocai", "Spacegray", "Vuesion",
"Xcode-Dark", "GitHub", and "Light Owl". (issue #528)
- Fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light
Flat", "Nord", "Solarized Dark" and "Solarized Light" themes.
- Fixed colors for selection background/foreground, Separator, Slider track
and ProgressBar background in various themes.
- Native Windows libraries: Fixed crash when running in Java 8 and newer Java
version is installed in `PATH` environment variable and using class
`SystemInfo` before AWT initialization. (issue #673)
- ComboBox: Fixed search in item list for text with spaces. (issue #691)
- FormattedTextField: On Linux, fixed `IllegalArgumentException: Invalid
location` if `JFormattedTextField.setDocument()` is invoked in a focus gained
listener on that formatted text field. (issue #698)
- PopupMenu: Make sure that popup menu does not overlap any operating system
task bar. (issue #701)
- FileChooser: Use system icons on Windows with Java 17.0.3 (and later) 32-bit.
Only Java 17 - 17.0.2 32-bit do not use system icons because of a bug in Java
32-bit that crashes the application. (PR #709)
- FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17
64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue
#403)
#### Incompatibilities
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
library for SVG rendering. You need to replace svgSalamander with JSVG in your
build scripts and distribute `jsvg.jar` with your application. Also replace
`com.kitfox.svg` with `com.github.weisj.jsvg` in `module-info.java` files.
- IntelliJ Themes: Removed all "Contrast" themes from "Material UI Lite".
## 3.1.1
- IntelliJ Themes:
- Fixed too large menu item paddings and too large table/tree row heights (all
"Material Theme UI Lite" themes; issue #667; regression in FlatLaf 3.1).
- Fixed too large tree row height in "Carbon", "Dark Purple", "Gray",
"Material Design Dark", "Monokai Pro", "One Dark" and "Spacegray" themes.
- Native libraries: Fixed `IllegalArgumentException: URI scheme is not "file"`
when using FlatLaf in WebStart. (issue #668; regression in FlatLaf 3.1)
## 3.1
#### New features and improvements
- Windows 11: Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
native Windows 11 rounded borders and drop shadows. (PR #643)
- Fonts:
- Added **Roboto Mono** (https://fonts.google.com/specimen/Roboto+Mono). (PR
#639, issue #638)
- Updated **JetBrains Mono** to
[v2.304](https://github.com/JetBrains/JetBrainsMono/releases/tag/v2.304).
- Theme Editor: Support macOS light and dark themes.
- TabbedPane: Support hover and focused tab foreground colors. (issue #627)
- TabbedPane: `tabbedPane.getBackgroundAt(tabIndex)` now has higher priority
than `TabbedPane.focusColor` and `TabbedPane.selectedBackground`. If
`tabbedPane.setBackgroundAt(tabIndex)` is used to set a color for a single
tab, then this color is now used even if the tab is focused or selected.
- TableHeader: Support column hover and pressed background and foreground
colors. (issue #636)
- Native libraries: Made it easier to distribute FlatLaf native libraries
(Windows `.dll` and Linux `.so`) to avoid problems on operating systems with
enabled execution restrictions.
See https://www.formdev.com/flatlaf/native-libraries/ for more details. (issue #624)
- Published native libraries to Maven Central for easy using them as
dependencies in Gradle and Maven.
- If available, native libraries are now loaded from same location as
`flatlaf.jar`, otherwise they are extract from `flatlaf.jar` to temporary
folder and loaded from there.
- Windows DLLs are now digitally signed with FormDev Software GmbH
certificate.
#### Fixed bugs
- FlatLaf window decorations:
- Fixed inconsistent size of glass pane depending on whether FlatLaf window
decorations are used (e.g. Windows 10/11) or not (e.g. macOS). Now the glass
pane no longer overlaps the FlatLaf window title bar. (issue #630)
- Linux: Fixed broken window resizing on multi-screen setups. (issue #632)
- Linux: Fixed behavior of maximize/restore button when tiling window to left
or right half of screen. (issue #647)
- IntelliJ Themes:
- Fixed default button hover background in "Solarized Light" theme. (issue
#628)
- Avoid that accent color affect some colors in some IntelliJ themes. (issue
#625)
- Updated "Hiberbee Dark" and "Material Theme UI Lite" themes.
- Styling: Fixed resolving of UI variables in styles that use other variables.
- MenuItem: Fixed horizontal alignment of icons. (issue #631)
- Table: Fixed potential performance issue with paint cell focus indicator
border. (issue #654)
- Tree: Fixed missing custom closed/opened/leaf icons of a custom
`DefaultTreeCellRenderer`. (issue #653; regression since implementing PR #609
in FlatLaf 3.0)
- Tree: Fixed truncated node text and too small painted non-wide node background
if custom cell renderer sets icon, but not disabled icon, and tree is
disabled. (issue #640)
- Fixed `HiDPIUtils.paintAtScale1x()`, which painted at wrong location if
graphics is rotated, is scaled and `x` or `y` parameters are not zero. (issue
#646)
## 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
@@ -364,7 +1013,7 @@ FlatLaf Change Log
- Native window decorations (Windows 10 only):
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
- When window is initially shown, fill background with window background color
(instead of white), which avoids flickering in dark themes. (issue 339)
(instead of white), which avoids flickering in dark themes. (issue #339)
- When resizing a window at the right/bottom edge, then first fill the new
space with the window background color (instead of black) before the layout
is updated.

296
README.md
View File

@@ -6,7 +6,7 @@ Swing desktop applications.
It looks almost flat (no shadows or gradients), clean, simple and elegant.
FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes,
scales on **HiDPI** displays and runs on Java 8 or newer.
scales on **HiDPI** displays and runs on Java 8 or newer (LTS and latest).
The look is heavily inspired by **Darcula** and **IntelliJ** themes from
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
@@ -15,6 +15,11 @@ IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
![FlatLaf Dark](images/flat_dark.png)
macOS Themes
------------
![FlatLaf macOS themes](images/flat_macos_themes.png)
IntelliJ Platform Themes
------------------------
@@ -25,6 +30,25 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
![IntelliJ Platform Themes](images/intellij_platform_themes.png)
Sponsors
--------
### Current Sponsors
<a href="https://exocharts.com/"><img src="https://www.formdev.com/flatlaf/sponsor/Exocharts.png" width="200" alt="Exocharts" title="Exocharts - Professional Grade OrderFlow"></a>
<!-- [![None Sponsors](images/none-sponsors.png)](https://www.formdev.com/flatlaf/sponsor/) -->
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
### Previous 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>
Demo
----
@@ -46,10 +70,15 @@ build script:
artifactId: flatlaf
version: (see button below)
Otherwise download `flatlaf-<version>.jar` here:
Otherwise, download `flatlaf-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
See also
[Native Libraries distribution](https://www.formdev.com/flatlaf/native-libraries/)
for instructions on how to redistribute FlatLaf native libraries with your
application.
### Snapshots
@@ -74,6 +103,8 @@ Addons
- [SwingX](flatlaf-swingx) - support for SwingX components
- [JIDE Common Layer](flatlaf-jide-oss) - support for JIDE Common Layer
components
- [Fonts](flatlaf-fonts) - some font families bundled in easy-to-use and
redistributable JARs
Getting started
@@ -118,7 +149,7 @@ details and downloads.
Buzz
----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
@@ -126,99 +157,196 @@ Buzz
Applications using FlatLaf
--------------------------
- ![New](images/new.svg) [Ultorg](https://www.ultorg.com/) (**commercial**) - a
visual query system for relational databases
- ![New](images/new.svg) [MooInfo](https://github.com/rememberber/MooInfo) -
visual implementation of OSHI, to view information about the system and
hardware
- ![New](images/new.svg) [Jailer](https://github.com/Wisser/Jailer) 11.2 -
database subsetting and relational data browsing tool
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
and much more
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- ![New](images/new.svg)
### Featured
- ![Sponsor](images/sponsor.svg) [JFormDesigner](https://www.formdev.com/)
(**commercial**) - Java/Swing GUI Designer (from the FlatLaf creators)
- ![Sponsor](images/sponsor.svg)
[JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)
(**commercial**) - the award-winning all-in-one Java profiler
- ![Sponsor](images/sponsor.svg)
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
9.0 (**commercial**) - the powerful multi-platform Java installer builder
- ![New](images/new.svg) [DbVisualizer](https://www.dbvis.com/) 12.0
(**commercial**) - the powerful multi-platform Java installer builder
- ![Sponsor](images/sponsor.svg) [DbVisualizer](https://www.dbvis.com/)
(**commercial**) - the universal database tool for developers, analysts and
DBAs
- ![New](images/new.svg) [MagicPlot](https://magicplot.com/) 3.0
(**commercial**) - Software for nonlinear fitting, plotting and data analysis
- ![New](images/new.svg)
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) 2021a
(**commercial**) - Thermodynamics and Properties Software
- [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds most widely used web
app scanner
- ![Hot](images/hot.svg) [Apache NetBeans](https://netbeans.apache.org/) - IDE
for Java, PHP, HTML and much more
- ![Sponsor](images/sponsor.svg)
[Thermo-Calc](https://thermocalc.com/products/thermo-calc/) (**commercial**) -
Thermodynamics and Properties Software
### Data
- ![Hot](images/hot.svg) [Ultorg](https://www.ultorg.com/) (**commercial**) - a
visual query system for relational databases
- [Jailer](https://github.com/Wisser/Jailer) - database subsetting and
relational data browsing tool
- ![Hot](images/hot.svg) [MagicPlot](https://magicplot.com/) (**commercial**) -
Software for nonlinear fitting, plotting and data analysis
- ![New](images/new.svg) [Constellation](https://www.constellation-app.com/) -
Data Visualization and Analytics (based on NetBeans platform)
- ![New](images/new.svg)
[Kafka Visualizer](https://github.com/kumait/kafkavisualizer) - Kafka GUI
client
### Security
- ![Hot](images/hot.svg) [ZAP](https://www.zaproxy.org/) - the world's most
widely used web app scanner
- ![Hot](images/hot.svg)
[Burp Suite Professional and Community Edition](https://portswigger.net/burp/pro)
2020.11.2 (**commercial**) - the leading software for web security testing
(**commercial**) - the leading software for web security testing
- ![New](images/new.svg)
[BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
[Ghidra](https://github.com/NationalSecurityAgency/ghidra) - a software
reverse engineering (SRE) framework
- ![New](images/new.svg) [jadx](https://github.com/skylot/jadx) - Dex to Java
decompiler
- [BurpCustomizer](https://github.com/CoreyD97/BurpCustomizer) - adds more
FlatLaf themes to Burp Suite
- [JOSM](https://josm.openstreetmap.de/) - an extensible editor for
[OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf JOSM plugin)
- [jAlbum](https://jalbum.net/) 21 (**commercial**) - creates photo album
websites
- ![New](images/new.svg) [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**)
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
checks your website
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
- [MegaMek](https://github.com/MegaMek/megamek),
[MegaMekLab](https://github.com/MegaMek/megameklab) and
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5+ - a sci-fi tabletop
BattleTech simulator suite handling battles, unit building, and campaigns
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
0.13.b024 - GUI builder for
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
framework for embedded displays
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
gamepad mapping software
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
connections manager
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
easy
- [Total Validator](https://www.totalvalidator.com/) (**commercial**) - checks
your website
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
encryption
### Software Development
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib)
- [KeyStore Explorer](https://keystore-explorer.org/)
- ![New](images/new.svg)
[muCommander](https://github.com/mucommander/mucommander) - lightweight
cross-platform file manager
- ![New](images/new.svg) [Guiffy](https://www.guiffy.com/) (**commercial**) -
advanced cross-platform Diff/Merge
- ![New](images/new.svg) [HashGarten](https://github.com/jonelo/HashGarten) -
cross-platform Swing GUI for Jacksum
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
IDE for Pseudo-Assembler
- [Linotte](https://github.com/cpc6128/LangageLinotte) - French programming
language created to learn programming
- [lsfusion platform](https://github.com/lsfusion/platform) - information
systems development platform
### Electrical
- [Antares](https://www.antarescircuit.io/) - a free, powerful platform for
designing, simulating and explaining digital circuits
- [Logisim-evolution](https://github.com/logisim-evolution/logisim-evolution) -
Digital logic design tool and simulator
- [Makelangelo Software](https://github.com/MarginallyClever/Makelangelo-software) -
for plotters, especially the wall-hanging polargraph
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder) - GUI
builder for [GUIslice](https://github.com/ImpulseAdventure/GUIslice), a
lightweight GUI framework for embedded displays
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
Arduino-based telescope focuser
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
control software
### Media
- ![Hot](images/hot.svg) [jAlbum](https://jalbum.net/) (**commercial**) -
creates photo album websites
- ![New](images/new.svg) [MediathekView](https://mediathekview.de/) - search in
media libraries of various German broadcasters
- [Cinecred](https://loadingbyte.com/cinecred/) - create beautiful film credit
sequences
- [tinyMediaManager](https://www.tinymediamanager.org/) (**commercial**) - a
media management tool
- [Weasis](https://nroduit.github.io/) - medical DICOM viewer used in healthcare
by hospitals, health networks, etc
- [Shutter Encoder](https://www.shutterencoder.com/)
([source code](https://github.com/paulpacifico/shutter-encoder)) -
professional video converter and compression tool
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
sound files in time or frequency domain
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
from any webnovel and lightnovel site
- [lectureStudio](https://www.lecturestudio.org/) - digitize your lectures with
ease
### Modelling
- ![New](images/new.svg) [Astah](https://astah.net/) (**commercial**) - create
UML, ER Diagram, Flowchart, Data Flow Diagram, Requirement Diagram, SysML
diagrams and more
- [IGMAS+](https://www.gfz-potsdam.de/igmas) - Interactive Gravity and Magnetic
Application System
### Documents
- ![New](images/new.svg) [Big Faceless (BFO) PDF Viewer](https://bfo.com/)
(**commercial**) - Swing PDF Viewer
- [PDF Studio](https://www.qoppa.com/pdfstudio/) (**commercial**) - create,
review and edit PDF documents
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) (**commercial**)
### Geo
- ![Hot](images/hot.svg) [JOSM](https://josm.openstreetmap.de/) - an extensible
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
JOSM plugin)
- [Mapton](https://mapton.org/)
([source code](https://github.com/trixon/mapton)) - some kind of map
application (based on NetBeans platform)
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) - GIS and scientific
computation environment for meteorological community
### Business / Legal
- ![Sponsor](images/sponsor.svg)
[j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
- ![Sponsor](images/sponsor.svg) [Jeyla Studio](https://www.jeylastudio.com/) -
Salon Software
- [Fanurio](https://www.fanuriotimetracking.com/) (**commercial**) - time
tracking and billing for freelancers and teams
- [Jes](https://www.jes-eur.de) - Die Java-EÜR
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
[mendelson AS2](https://mendelson-e-c.com/as2/),
[AS4](https://mendelson-e-c.com/as4/) and
[OFTP2](https://mendelson-e-c.com/oftp2) (**commercial**)
- ![New](images/new.svg) [IGMAS+](https://www.gfz-potsdam.de/igmas) -
Interactive Gravity and Magnetic Application System
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
computation environment for meteorological community
- [lsfusion platform](https://github.com/lsfusion/platform) 4 - information
systems development platform
- [JPass](https://github.com/gaborbata/jpass) - password manager with strong
encryption
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
- [Mapton](https://mapton.org/) 2.0
([source code](https://github.com/trixon/mapton)) - some kind of map
application (based on NetBeans platform)
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
IDE for Pseudo-Assembler
- [Linotte](https://github.com/cpc6128/LangageLinotte) 3.1 - French programming
language created to learn programming
- [MEKA](https://github.com/Waikato/meka) 1.9.3 - multi-label classifiers and
evaluation procedures using the Weka machine learning framework
- [Shutter Encoder](https://www.shutterencoder.com/) 14.2
([source code](https://github.com/paulpacifico/shutter-encoder)) -
professional video converter and compression tool (screenshots show **old**
look)
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
sound files in time or frequency domain
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
control software
- [ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
Arduino-based telescope focuser
- [Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
from any webnovel and lightnovel site
- [lectureStudio](https://www.lecturestudio.org/) 4.3.1060 - digitize your
lectures with ease
### Messaging
- ![New](images/new.svg) [Spark](https://github.com/igniterealtime/Spark) -
cross-platform IM client optimized for businesses and organizations
- ![New](images/new.svg) [Chatty](https://github.com/chatty/chatty) - Twitch
Chat Client
### Gaming
- ![New](images/new.svg) ![Sponsor](images/sponsor.svg)
[BGBlitz](https://www.bgblitz.com/) (**commercial**) - professional Backgammon
- ![New](images/new.svg) [MCreator](https://github.com/MCreator/MCreator) -
software used to make Minecraft Java Edition mods, Minecraft Bedrock Edition Add-Ons,
and data packs without programming knowledge
- ![New](images/new.svg) [MapTool](https://github.com/RPTools/maptool) - virtual
Tabletop for playing role-playing games
- [MegaMek](https://github.com/MegaMek/megamek),
[MegaMekLab](https://github.com/MegaMek/megameklab) and
[MekHQ](https://github.com/MegaMek/mekhq) - a sci-fi tabletop BattleTech
simulator suite handling battles, unit building, and campaigns
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
gamepad mapping software
### Utilities
- [MooInfo](https://github.com/rememberber/MooInfo) - visual implementation of
OSHI, to view information about the system and hardware
- ![New](images/new.svg)
[Linux Task Manager (LTM)](https://github.com/ajee10x/LTM-LinuxTaskManager) -
GUI for monitoring and managing various aspects of a Linux system
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
connections manager
- [jEnTunnel](https://github.com/ggrandes/jentunnel) - manage SSH Tunnels made
easy
- [Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
and fastboot commands easier to use
- and more...
### Miscellaneous
- [MEKA](https://github.com/Waikato/meka) - multi-label classifiers and
evaluation procedures using the Weka machine learning framework

View File

@@ -14,10 +14,15 @@
* limitations under the License.
*/
val releaseVersion = "2.3"
val developmentVersion = "2.4-SNAPSHOT"
import net.ltgt.gradle.errorprone.errorprone
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
// for PR snapshots change version to 'PR-<pr_number>-SNAPSHOT'
val pullRequestNumber = findProperty( "github.event.pull_request.number" )
if( pullRequestNumber != null )
version = "PR-${pullRequestNumber}-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
allprojects {
version = rootProject.version
@@ -37,9 +42,16 @@ println( "----------------------------------------------------------------------
println( "FlatLaf Version: ${version}" )
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
println( "Java ${System.getProperty( "java.version" )}" )
val toolchainJavaVersion = System.getProperty( "toolchain" )
if( !toolchainJavaVersion.isNullOrEmpty() )
println( "Java toolchain ${toolchainJavaVersion}" )
println()
plugins {
alias( libs.plugins.errorprone ) apply false
}
allprojects {
tasks {
withType<JavaCompile>().configureEach {
@@ -78,4 +90,56 @@ allprojects {
isFailOnError = false
}
}
//---- Error Prone ----
tasks.register( "errorprone" ) {
group = "verification"
tasks.withType<JavaCompile>().forEach {
dependsOn( it )
}
}
val useErrorProne = gradle.startParameter.taskNames.contains( "errorprone" )
if( useErrorProne ) {
plugins.withType<JavaPlugin> {
apply( plugin = libs.plugins.errorprone.get().pluginId )
dependencies {
"errorprone"( libs.errorprone )
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add( "-Werror" )
options.errorprone {
disable(
"ReferenceEquality", // reports usage of '==' for objects
"StringSplitter", // reports String.split()
"JavaTimeDefaultTimeZone", // reports Year.now()
"MissingSummary", // reports `/** @since 2 */`
"InvalidBlockTag", // reports @uiDefault in javadoc
"AlreadyChecked", // reports false positives
"InlineMeSuggester", // suggests using Error Prone annotations for deprecated methods
"TypeParameterUnusedInFormals",
"UnsynchronizedOverridesSynchronized",
"NonApiType", // reports ArrayList/HashSet in parameter or return type
)
when( project.name ) {
"flatlaf-intellij-themes" -> disable(
"MutablePublicArray", // reports FlatAllIJThemes.INFOS
)
"flatlaf-theme-editor" -> disable(
"CatchAndPrintStackTrace",
)
"flatlaf-testing" -> disable(
"CatchAndPrintStackTrace",
"JdkObsolete", // reports Hashtable used for JSlider.setLabelTable()
"JavaUtilDate", // reports usage of class Date
)
}
}
}
}
}
}

View File

@@ -45,7 +45,7 @@ public class ReorderJarEntries
// 1st pass: copy .properties files
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
// 2st pass: copy other files
// 2nd pass: copy other files
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
}

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 = 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" ).layout.buildDirectory.dir( "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 {
manifest.attributes( "Multi-Release" to "true" )
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}

View File

@@ -15,10 +15,13 @@
*/
open class NativeArtifact( val fileName: String, val classifier: String, val type: String ) {}
open class PublishExtension {
var artifactId: String? = null
var name: String? = null
var description: String? = null
var nativeArtifacts: List<NativeArtifact>? = null
}
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
@@ -41,34 +44,43 @@ publishing {
pom {
afterEvaluate {
this@pom.name.set( extension.name )
this@pom.description.set( extension.description )
this@pom.name = extension.name
this@pom.description = extension.description
}
url.set( "https://github.com/JFormDesigner/FlatLaf" )
url = "https://github.com/JFormDesigner/FlatLaf"
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
name.set( "Karl Tauber" )
organization.set( "FormDev Software GmbH" )
organizationUrl.set( "https://www.formdev.com/" )
name = "Karl Tauber"
organization = "FormDev Software GmbH"
organizationUrl = "https://www.formdev.com/"
}
}
scm {
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
url.set( "https://github.com/JFormDesigner/FlatLaf" )
connection = "scm:git:git://github.com/JFormDesigner/FlatLaf.git"
url = "https://github.com/JFormDesigner/FlatLaf"
}
issueManagement {
system.set( "GitHub" )
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
system = "GitHub"
url = "https://github.com/JFormDesigner/FlatLaf/issues"
}
}
afterEvaluate {
extension.nativeArtifacts?.forEach {
artifact( artifacts.add( "archives", file( it.fileName ) ) {
classifier = it.classifier
type = it.type
} )
}
}
}
@@ -80,7 +92,7 @@ publishing {
val releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
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 {
// get from gradle.properties
@@ -108,5 +120,13 @@ signing {
// disable signing of snapshots
tasks.withType<Sign>().configureEach {
onlyIf { java.lang.Boolean.getBoolean( "release" ) }
onlyIf { rootProject.hasProperty( "release" ) }
}
// check whether parallel build is enabled
tasks.withType<AbstractPublishToMaven>().configureEach {
doFirst {
if( System.getProperty( "org.gradle.parallel" ) == "true" )
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )
}
}

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 = JavaLanguageVersion.of( toolchainJavaVersion )
}
}

View File

@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=ISO-8859-1

View File

@@ -14,8 +14,11 @@
* limitations under the License.
*/
import Flatlaf_publish_gradle.NativeArtifact
plugins {
`java-library`
`flatlaf-toolchain`
`flatlaf-module-info`
`flatlaf-java9`
`flatlaf-publish`
@@ -24,12 +27,11 @@ plugins {
val sigtest = configurations.create( "sigtest" )
dependencies {
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
testImplementation( libs.junit )
testRuntimeOnly( libs.junit.launcher )
// https://github.com/jtulach/netbeans-apitest
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" )
sigtest( libs.sigtest )
}
java {
@@ -40,11 +42,11 @@ java {
tasks {
compileJava {
// generate JNI headers
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
options.headerOutputDirectory = layout.buildDirectory.dir( "generated/jni-headers" )
}
jar {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
doLast {
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
@@ -52,11 +54,32 @@ tasks {
}
named<Jar>( "sourcesJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
named<Jar>( "javadocJar" ) {
archiveBaseName.set( "flatlaf" )
archiveBaseName = "flatlaf"
}
register<Zip>( "jarNoNatives" ) {
group = "build"
dependsOn( "jar" )
archiveBaseName = "flatlaf"
archiveClassifier = "no-natives"
archiveExtension = "jar"
destinationDirectory = layout.buildDirectory.dir( "libs" )
from( zipTree( jar.get().archiveFile.get().asFile ) )
exclude( "com/formdev/flatlaf/natives/**" )
}
withType<AbstractPublishToMaven>().configureEach {
dependsOn( "jarNoNatives" )
}
withType<Sign>().configureEach {
dependsOn( "jarNoNatives" )
}
check {
@@ -66,6 +89,9 @@ tasks {
test {
useJUnitPlatform()
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" ) {
@@ -83,7 +109,7 @@ tasks {
"action" to "generate",
"fileName" to "${project.name}-sigtest.txt",
"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,
"release" to "1.8", // Java version
"failonerror" to "true" )
@@ -119,4 +145,17 @@ flatlafPublish {
artifactId = "flatlaf"
name = "FlatLaf"
description = "Flat Look and Feel"
val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf(
NativeArtifact( tasks.getByName( "jarNoNatives" ).outputs.files.asPath, "no-natives", "jar" ),
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-macos-arm64.dylib", "macos-arm64", "dylib" ),
NativeArtifact( "${natives}/libflatlaf-macos-x86_64.dylib", "macos-x86_64", "dylib" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
NativeArtifact( "${natives}/libflatlaf-linux-arm64.so", "linux-arm64", "so" ),
)
}

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 2.2
#Version 3.5.2
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -11,6 +11,14 @@ 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 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_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
fld public final static java.lang.String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent"
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds"
fld public final static java.lang.String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder"
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large"
fld public final static java.lang.String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium"
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_WIDTH = "JComponent.minimumWidth"
@@ -18,8 +26,10 @@ fld public final static java.lang.String OUTLINE = "JComponent.outline"
fld public final static java.lang.String OUTLINE_ERROR = "error"
fld public final static java.lang.String OUTLINE_WARNING = "warning"
fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placeholderText"
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
fld public final static java.lang.String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth"
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
@@ -64,6 +74,11 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_AUTO = "auto"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_LEFT = "left"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_NONE = "none"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_RIGHT = "right"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
@@ -75,6 +90,7 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement"
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
@@ -84,10 +100,17 @@ fld public final static java.lang.String TEXT_FIELD_TRAILING_COMPONENT = "JTextF
fld public final static java.lang.String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"
fld public final static java.lang.String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
fld public final static java.lang.String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight"
fld public final static java.lang.String TITLE_BAR_SHOW_CLOSE = "JRootPane.titleBarShowClose"
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
fld public final static java.lang.String TITLE_BAR_SHOW_ICONIFFY = "JRootPane.titleBarShowIconify"
fld public final static java.lang.String TITLE_BAR_SHOW_MAXIMIZE = "JRootPane.titleBarShowMaximize"
fld public final static java.lang.String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle"
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
fld public final static java.lang.String WINDOW_STYLE = "Window.style"
fld public final static java.lang.String WINDOW_STYLE_SMALL = "small"
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
@@ -184,6 +207,7 @@ meth public java.lang.String getID()
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.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)
anno 0 java.lang.Deprecated()
meth public static boolean isLafDark()
@@ -192,8 +216,15 @@ meth public static boolean isUseNativeWindowDecorations()
meth public static boolean setup(javax.swing.LookAndFeel)
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.String getPreferredFontFamily()
meth public static java.lang.String getPreferredLightFontFamily()
meth public static java.lang.String getPreferredMonospacedFontFamily()
meth public static java.lang.String getPreferredSemiboldFontFamily()
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.function.Function<java.lang.String,java.awt.Color> getSystemColorGetter()
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
meth public static void disableWindowsD3Donscreen()
meth public static void hideMnemonics()
meth public static void initIconColors(javax.swing.UIDefaults,boolean)
meth public static void installLafInfo(java.lang.String,java.lang.Class<? extends javax.swing.LookAndFeel>)
@@ -205,6 +236,11 @@ meth public static void repaintAllFramesAndDialogs()
meth public static void revalidateAndRepaintAllFramesAndDialogs()
meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable)
meth public static void setGlobalExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
meth public static void setPreferredFontFamily(java.lang.String)
meth public static void setPreferredLightFontFamily(java.lang.String)
meth public static void setPreferredMonospacedFontFamily(java.lang.String)
meth public static void setPreferredSemiboldFontFamily(java.lang.String)
meth public static void setSystemColorGetter(java.util.function.Function<java.lang.String,java.awt.Color>)
meth public static void setUseNativeWindowDecorations(boolean)
meth public static void showMnemonics(java.awt.Component)
meth public static void unregisterCustomDefaultsSource(java.io.File)
@@ -219,7 +255,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
meth public void uninitialize()
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
supr javax.swing.plaf.basic.BasicLookAndFeel
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,subMenuUsabilityHelper,uiDefaultsGetters,updateUIPending
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -258,7 +294,12 @@ 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_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 UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
anno 0 java.lang.Deprecated()
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder"
fld public final static java.lang.String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle"
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_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
@@ -277,7 +318,7 @@ meth public static boolean setup(java.io.InputStream)
meth public static com.formdev.flatlaf.FlatLaf createLaf(com.formdev.flatlaf.IntelliJTheme)
meth public static com.formdev.flatlaf.FlatLaf createLaf(java.io.InputStream) throws java.io.IOException
supr java.lang.Object
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyInverseMapping,uiKeyMapping
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyDoNotOverride,uiKeyExcludes,uiKeyInverseMapping,uiKeyMapping
CLSS public static com.formdev.flatlaf.IntelliJTheme$ThemeLaf
outer com.formdev.flatlaf.IntelliJTheme
@@ -290,6 +331,26 @@ meth public java.lang.String getName()
supr com.formdev.flatlaf.FlatLaf
hfds theme
CLSS public com.formdev.flatlaf.themes.FlatMacDarkLaf
cons public init()
fld public final static java.lang.String NAME = "FlatLaf macOS Dark"
meth public boolean isDark()
meth public java.lang.String getDescription()
meth public java.lang.String getName()
meth public static boolean setup()
meth public static void installLafInfo()
supr com.formdev.flatlaf.FlatDarkLaf
CLSS public com.formdev.flatlaf.themes.FlatMacLightLaf
cons public init()
fld public final static java.lang.String NAME = "FlatLaf macOS Light"
meth public boolean isDark()
meth public java.lang.String getDescription()
meth public java.lang.String getName()
meth public static boolean setup()
meth public static void installLafInfo()
supr com.formdev.flatlaf.FlatLightLaf
CLSS public abstract interface com.formdev.flatlaf.util.AnimatedIcon
innr public static AnimationSupport
intf javax.swing.Icon
@@ -357,6 +418,7 @@ meth public static float clamp(float)
meth public static float luma(java.awt.Color)
meth public static java.awt.Color darken(java.awt.Color,float)
meth public static java.awt.Color desaturate(java.awt.Color,float)
meth public static java.awt.Color fade(java.awt.Color,float)
meth public static java.awt.Color lighten(java.awt.Color,float)
meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float)
meth public static java.awt.Color saturate(java.awt.Color,float)
@@ -432,6 +494,17 @@ meth public java.lang.String toString()
supr javax.swing.plaf.ColorUIResource
hfds baseOfDefaultColorRGB,functions,hasBaseOfDefaultColor
CLSS public com.formdev.flatlaf.util.FontUtils
cons public init()
meth public static boolean installFont(java.net.URL)
meth public static java.awt.Font getCompositeFont(java.lang.String,int,int)
meth public static java.awt.Font[] getAllFonts()
meth public static java.lang.String[] getAvailableFontFamilyNames()
meth public static void loadFontFamily(java.lang.String)
meth public static void registerFontFamilyLoader(java.lang.String,java.lang.Runnable)
supr java.lang.Object
hfds loadersMap
CLSS public com.formdev.flatlaf.util.Graphics2DProxy
cons public init(java.awt.Graphics2D)
meth public boolean drawImage(java.awt.Image,int,int,int,int,int,int,int,int,java.awt.Color,java.awt.image.ImageObserver)
@@ -560,15 +633,32 @@ hfds alpha,hsl,rgb
CLSS public com.formdev.flatlaf.util.HiDPIUtils
cons public init()
innr public abstract interface static DirtyRegionCallback
innr public abstract interface static Painter
innr public static HiDPIRepaintManager
meth public static float computeTextYCorrection(java.awt.Graphics2D)
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
meth public static void addDirtyRegion(javax.swing.JComponent,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback)
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
meth public static void installHiDPIRepaintManager()
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
meth public static void repaint(java.awt.Component)
meth public static void repaint(java.awt.Component,int,int,int,int)
meth public static void repaint(java.awt.Component,java.awt.Rectangle)
supr java.lang.Object
hfds useTextYCorrection
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback
outer com.formdev.flatlaf.util.HiDPIUtils
meth public abstract void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
CLSS public static com.formdev.flatlaf.util.HiDPIUtils$HiDPIRepaintManager
outer com.formdev.flatlaf.util.HiDPIUtils
cons public init()
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
supr javax.swing.RepaintManager
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
outer com.formdev.flatlaf.util.HiDPIUtils
@@ -599,6 +689,7 @@ supr java.lang.Object
CLSS public com.formdev.flatlaf.util.NativeLibrary
cons public init(java.io.File,boolean)
cons public init(java.lang.String,boolean)
cons public init(java.lang.String,java.lang.ClassLoader,boolean)
meth public boolean isLoaded()
supr java.lang.Object
@@ -665,6 +756,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init()
fld public final static boolean isAARCH64
fld public final static boolean isJava_11_orLater
fld public final static boolean isJava_12_orLater
fld public final static boolean isJava_15_orLater
fld public final static boolean isJava_17_orLater
fld public final static boolean isJava_18_orLater
@@ -673,6 +765,7 @@ fld public final static boolean isJetBrainsJVM
fld public final static boolean isJetBrainsJVM_11_orLater
fld public final static boolean isKDE
fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
fld public final static boolean isMacOS_10_14_Mojave_orLater
@@ -1065,6 +1158,31 @@ meth public void provideErrorFeedback(java.awt.Component)
meth public void uninitialize()
supr java.lang.Object
CLSS public javax.swing.RepaintManager
cons public init()
meth public boolean isCompletelyDirty(javax.swing.JComponent)
meth public boolean isDoubleBufferingEnabled()
meth public java.awt.Dimension getDoubleBufferMaximumSize()
meth public java.awt.Image getOffscreenBuffer(java.awt.Component,int,int)
meth public java.awt.Image getVolatileOffscreenBuffer(java.awt.Component,int,int)
meth public java.awt.Rectangle getDirtyRegion(javax.swing.JComponent)
meth public java.lang.String toString()
meth public static javax.swing.RepaintManager currentManager(java.awt.Component)
meth public static javax.swing.RepaintManager currentManager(javax.swing.JComponent)
meth public static void setCurrentManager(javax.swing.RepaintManager)
meth public void addDirtyRegion(java.applet.Applet,int,int,int,int)
meth public void addDirtyRegion(java.awt.Window,int,int,int,int)
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
meth public void addInvalidComponent(javax.swing.JComponent)
meth public void markCompletelyClean(javax.swing.JComponent)
meth public void markCompletelyDirty(javax.swing.JComponent)
meth public void paintDirtyRegions()
meth public void removeInvalidComponent(javax.swing.JComponent)
meth public void setDoubleBufferMaximumSize(java.awt.Dimension)
meth public void setDoubleBufferingEnabled(boolean)
meth public void validateInvalidComponents()
supr java.lang.Object
CLSS public abstract javax.swing.border.AbstractBorder
cons public init()
intf java.io.Serializable

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.IllegalComponentStateException;
import java.awt.Window;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
@@ -31,7 +33,7 @@ public interface FlatClientProperties
//---- JButton ------------------------------------------------------------
/**
* Specifies type of a button.
* Specifies type of button.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
@@ -100,6 +102,17 @@ public interface FlatClientProperties
*/
String BUTTON_TYPE_BORDERLESS = "borderless";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JCheckBox ----------------------------------------------------------
/**
* Specifies selected state of a checkbox.
* <p>
@@ -116,13 +129,6 @@ public interface FlatClientProperties
*/
String SELECTED_STATE_INDETERMINATE = "indeterminate";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
@@ -253,8 +259,159 @@ public interface FlatClientProperties
*/
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
/**
* Specifies whether a component shown in a window title bar area should behave as caption
* (left-click allows moving window, right-click shows window system menu).
* The caption component does not receive mouse pressed/released/clicked/dragged events,
* but it gets mouse entered/exited/moved events.
* <p>
* Since 3.4, this client property also supports using a function that can check
* whether a given location in the component should behave as caption.
* Useful for components that do not use mouse input on whole component bounds.
*
* <pre>{@code
* myComponent.putClientProperty( "JComponent.titleBarCaption",
* (Function<Point, Boolean>) pt -> {
* // parameter pt contains mouse location (in myComponent coordinates)
* // return true if the component is not interested in mouse input at the given location
* // return false if the component wants process mouse input at the given location
* // return null if the component children should be checked
* return ...; // check here
* } );
* }</pre>
* <b>Warning</b>:
* <ul>
* <li>This function is invoked often when mouse is moved over window title bar area
* and should therefore return quickly.
* <li>This function is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
* while processing Windows messages.
* It <b>must not</b> change any component property or layout because this could cause a dead lock.
* </ul>
* <p>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Boolean} or {@link java.util.function.Function}&lt;Point, Boolean&gt;
*
* @since 2.5
*/
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
//---- Panel --------------------------------------------------------------
/**
* Marks the panel as placeholder for the iconfify/maximize/close buttons
* in fullWindowContent mode. See {@link #FULL_WINDOW_CONTENT}.
* <p>
* If fullWindowContent mode is enabled, the preferred size of the panel is equal
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
* <p>
* You're responsible to layout that panel at the top-left or top-right corner,
* depending on platform, where the iconfify/maximize/close buttons are located.
* <p>
* Syntax of the value string is: {@code "win|mac [horizontal|vertical] [zeroInFullScreen] [leftToRight|rightToLeft]"}.
* <p>
* The string must start with {@code "win"} (for Windows or Linux) or
* with {@code "mac"} (for macOS) and specifies the platform where the placeholder
* should be used. On macOS, you need the placeholder in the top-left corner,
* but on Windows/Linux you need it in the top-right corner. So if your application supports
* fullWindowContent mode on both platforms, you can add two placeholders to your layout
* and FlatLaf automatically uses only one of them. The other gets size {@code 0,0}.
* <p>
* Optionally, you can append following options to the value string (separated by space characters):
* <ul>
* <li>{@code "horizontal"} - preferred height is zero
* <li>{@code "vertical"} - preferred width is zero
* <li>{@code "zeroInFullScreen"} - in full-screen mode on macOS, preferred size is {@code 0,0}
* <li>{@code "leftToRight"} - in right-to-left component orientation, preferred size is {@code 0,0}
* <li>{@code "rightToLeft"} - in left-to-right component orientation, preferred size is {@code 0,0}
* </ul>
*
* Example for adding placeholder to top-left corner on macOS:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* JToolBar toolBar = new JToolBar();
* // add tool bar items
*
* JPanel toolBarPanel = new JPanel( new BorderLayout() );
* toolBarPanel.add( placeholder, BorderLayout.WEST );
* toolBarPanel.add( toolBar, BorderLayout.CENTER );
*
* frame.getContentPane().add( toolBarPanel, BorderLayout.NORTH );
* }</pre>
*
* Or add placeholder as first item to the tool bar:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* JToolBar toolBar = new JToolBar();
* toolBar.add( placeholder );
* // add tool bar items
*
* frame.getContentPane().add( toolBar, BorderLayout.NORTH );
* }</pre>
*
* If a tabbed pane is located at the top, you can add the placeholder
* as leading component to that tabbed pane:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_LEADING_COMPONENT, placeholder );
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JPanel}<br>
* <strong>Value type</strong> {@link java.lang.String}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder";
//---- Popup --------------------------------------------------------------
/**
* Specifies the popup border corner radius if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
* <p>
* Note that this is not available on all platforms since it requires special support.
* Supported platforms:
* <ul>
* <li><strong>Windows 11</strong>: Only two corner radiuses are supported
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
* <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported.
* <li><strong>Linux</strong> (since FlatLaf 3.6): Any corner radius is supported.
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
*
* @since 3.1
*/
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
/**
* Specifies the popup rounded border width if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
* <p>
* Only used if popup uses rounded border.
* <p>
* Note that this is not available on all platforms since it requires special support.
* Supported platforms:
* <ul>
* <li><strong>macOS</strong> (10.14 and later)
* <li><strong>Linux</strong> (since FlatLaf 3.6)
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.Float}<br>
*
* @since 3.3
*/
String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth";
/**
* Specifies whether a drop shadow is painted if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
@@ -273,6 +430,7 @@ public interface FlatClientProperties
*/
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
//---- JProgressBar -------------------------------------------------------
/**
@@ -291,6 +449,7 @@ public interface FlatClientProperties
*/
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
//---- JRootPane ----------------------------------------------------------
/**
@@ -304,7 +463,7 @@ public interface FlatClientProperties
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
* than UI default {@code TitlePane.useWindowDecorations}.
* <p>
* (requires Window 10)
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
@@ -324,23 +483,65 @@ public interface FlatClientProperties
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
* than UI default {@code TitlePane.menuBarEmbedded}.
* <p>
* (requires Window 10)
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Specifies whether the content pane (and the glass pane) should be extended
* into the window title bar area
* (requires enabled window decorations). Default is {@code false}.
* <p>
* On macOS, use client property {@code apple.awt.fullWindowContent}
* (see <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">macOS Full window content</a>).
* <p>
* Setting this enables/disables full window content
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* If {@code true}, the content pane (and the glass pane) is extended into
* the title bar area. The window icon and title are hidden.
* Only the iconfify/maximize/close buttons stay visible in the upper right corner
* (and overlap the content pane).
* <p>
* The user can left-click-and-drag on the title bar area to move the window,
* except when clicking on a component that processes mouse events (e.g. buttons or menus).
* <p>
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent";
/**
* Contains the current bounds of the iconfify/maximize/close buttons
* (in root pane coordinates) if fullWindowContent mode is enabled.
* Otherwise its value is {@code null}.
* <p>
* <b>Note</b>: Do not set this client property. It is set by FlatLaf.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Rectangle}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds";
/**
* 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>
* Setting this shows/hides the windows icon
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
* <p>
* (requires Window 10)
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
@@ -349,10 +550,74 @@ public interface FlatClientProperties
*/
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>
* (requires Windows 10/11)
* <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 "iconify" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}.
* <p>
* Setting this shows/hides the "iconify" button
* for the {@code JFrame} that contains the root pane.
* <p>
* (requires Windows 10/11)
* <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>
* (requires Windows 10/11)
* <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>
* (requires Windows 10/11)
* <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).
* <p>
* (requires Window 10)
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Color}
@@ -364,7 +629,7 @@ public interface FlatClientProperties
/**
* Foreground color of window title bar (requires enabled window decorations).
* <p>
* (requires Window 10)
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Color}
@@ -373,6 +638,62 @@ public interface FlatClientProperties
*/
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
/**
* Specifies the preferred height of title bar (requires enabled window decorations).
* <p>
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer}
*
* @since 3.5.2
*/
String TITLE_BAR_HEIGHT = "JRootPane.titleBarHeight";
/**
* Specifies whether the glass pane should have full height and overlap the title bar,
* if FlatLaf window decorations are enabled. Default is {@code false}.
* <p>
* (requires Windows 10/11)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.1
*/
String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight";
/**
* Specifies the style of the window title bar.
* Besides the default title bar style, you can use a Utility-style title bar,
* which is smaller than the default title bar.
* <p>
* On Windows 10/11, this requires FlatLaf window decorations.
* On macOS, Java supports this out of the box.
* <p>
* Note that this client property must be set before the window becomes displayable.
* Otherwise, an {@link IllegalComponentStateException} is thrown.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #WINDOW_STYLE_SMALL}
*
* @since 3.2
*/
String WINDOW_STYLE = "Window.style";
/**
* The window has Utility-style title bar, which is smaller than default title bar.
* <p>
* This is the same as using {@link Window#setType}( {@link Window.Type#UTILITY} ).
*
* @see #WINDOW_STYLE
* @since 3.2
*/
String WINDOW_STYLE_SMALL = "small";
//---- JScrollBar / JScrollPane -------------------------------------------
/**
@@ -391,6 +712,7 @@ public interface FlatClientProperties
*/
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JSplitPane ---------------------------------------------------------
/**
@@ -425,6 +747,7 @@ public interface FlatClientProperties
*/
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
//---- JTabbedPane --------------------------------------------------------
/**
@@ -690,7 +1013,7 @@ public interface FlatClientProperties
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING} (default)
* {@link SwingConstants#LEADING} (default),
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#CENTER},
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
@@ -794,6 +1117,59 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
/**
* Specifies the rotation of the tabs (title, icon, etc.).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEFT},
* {@link SwingConstants#RIGHT},
* {@link #TABBED_PANE_TAB_ROTATION_NONE} (default),
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
*
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation";
/**
* Tabs are not rotated.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_NONE = "none";
/**
* Tabs are rotated depending on tab placement.
* <p>
* For top and bottom tab placement, the tabs are not rotated.<br>
* For left tab placement, the tabs are rotated counter-clockwise.<br>
* For right tab placement, the tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_AUTO = "auto";
/**
* Tabs are rotated counter-clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_LEFT = "left";
/**
* Tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_RIGHT = "right";
/**
* Specifies a component that will be placed at the leading edge of the tabs area.
* <p>
@@ -820,6 +1196,7 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
//---- JTextField ---------------------------------------------------------
/**
@@ -989,6 +1366,7 @@ public interface FlatClientProperties
*/
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
//---- JToggleButton ------------------------------------------------------
/**
@@ -996,8 +1374,8 @@ public interface FlatClientProperties
* <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)
* <strong>Allowed Values</strong>
* {@link SwingConstants#BOTTOM} (default),
* {@link SwingConstants#TOP},
* {@link SwingConstants#LEFT} or
* {@link SwingConstants#RIGHT}
@@ -1030,16 +1408,27 @@ public interface FlatClientProperties
*/
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
//---- JTree --------------------------------------------------------------
/**
* Override if a tree shows a wide selection. Default is {@code true}.
* Specifies whether tree shows a wide selection. Default is {@code true}.
* <p>
* <strong>Component</strong> {@link javax.swing.JTree}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String TREE_WIDE_SELECTION = "JTree.wideSelection";
/**
* Specifies whether tree uses a wide cell renderer. Default is {@code false}.
* <p>
* <strong>Component</strong> {@link javax.swing.JTree}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.6
*/
String TREE_WIDE_CELL_RENDERER = "JTree.wideCellRenderer";
/**
* Specifies whether tree item selection is painted. Default is {@code true}.
* If set to {@code false}, then the tree cell renderer is responsible for painting selection.
@@ -1049,6 +1438,45 @@ public interface FlatClientProperties
*/
String TREE_PAINT_SELECTION = "JTree.paintSelection";
//---- macOS --------------------------------------------------------------
/**
* Specifies the spacing around the macOS window close/minimize/zoom buttons.
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
* is enabled.
* <p>
* (requires macOS 10.14+ for "medium" spacing and macOS 11+ for "large" spacing, requires Java 17+)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #MACOS_WINDOW_BUTTONS_SPACING_MEDIUM} or
* {@link #MACOS_WINDOW_BUTTONS_SPACING_LARGE} (requires macOS 11+)
*
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing";
/**
* Add medium spacing around the macOS window close/minimize/zoom buttons.
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium";
/**
* Add large spacing around the macOS window close/minimize/zoom buttons.
* <p>
* (requires macOS 11+; "medium" is used on older systems)
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large";
//---- helper methods -----------------------------------------------------
/**

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ public abstract class FlatDefaultsAddon
public InputStream getDefaults( Class<?> lafClass ) {
Class<?> addonClass = this.getClass();
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
+ '/' + lafClass.getSimpleName() + ".properties";
+ '/' + UIDefaultsLoader.simpleClassName( lafClass ) + ".properties";
return addonClass.getResourceAsStream( propertiesName );
}

View File

@@ -50,7 +50,8 @@ class FlatInputMaps
}
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
"SPACE", "spacePopup",
// Space key still shows popup, but from FlatComboBoxUI.FlatKeySelectionManager
// "SPACE", "spacePopup",
"UP", mac( "selectPrevious2", "selectPrevious" ),
"DOWN", mac( "selectNext2", "selectNext" ),
@@ -70,7 +71,7 @@ class FlatInputMaps
);
}
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
// join ltr and rtl bindings to fix up/down/etc. keys in right-to-left component orientation
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
if( bindings != null && rtlBindings != null ) {

View File

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

View File

@@ -20,6 +20,7 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
@@ -63,16 +64,21 @@ import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.text.StyleContext;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.JavaCompatibility2;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
@@ -93,6 +99,7 @@ public abstract class FlatLaf
private static List<Object> customDefaultsSources;
private static Map<String, String> globalExtraDefaults;
private Map<String, String> extraDefaults;
private static Function<String, Color> systemColorGetter;
private String desktopPropertyName;
private String desktopPropertyName2;
@@ -103,14 +110,62 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper;
private boolean subMenuUsabilityHelperInstalled;
private LinuxPopupMenuCanceler linuxPopupMenuCanceler;
private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters;
private static String preferredFontFamily;
private static String preferredLightFontFamily;
private static String preferredSemiboldFontFamily;
private static String preferredMonospacedFontFamily;
static {
// see disableWindowsD3Donscreen() for details
// https://github.com/JFormDesigner/FlatLaf/issues/887
if( SystemInfo.isWindows &&
System.getProperty( "sun.java2d.d3d.onscreen" ) == null &&
System.getProperty( "sun.java2d.d3d" ) == null &&
System.getProperty( "sun.java2d.noddraw" ) == null )
System.setProperty( "sun.java2d.d3d.onscreen", "false" );
}
/**
* Disable usage of Windows Direct3D (DirectX) onscreen surfaces because this may lead to
* repaint issues (ghosting) on some systems (probably depending on graphics card/driver).
* Problem occurs usually when a small heavy-weight popup window (menu, combobox, tooltip) is shown.
* <p>
* Sets system property {@code sun.java2d.d3d.onscreen} to {@code false},
* but only if {@code sun.java2d.d3d.onscreen}, {@code sun.java2d.d3d}
* and {@code sun.java2d.noddraw} are not yet set.
* <p>
* <strong>Note</strong>: Must be invoked very early before the graphics environment is created.
* <p>
* This method is automatically invoked when loading this class,
* which is usually before the graphics environment is created.
* E.g. when doing {@code FlatLightLaf.setup()} or
* {@code UIManager.setLookAndFeel( "com.formdev.flatlaf.FlatLightLaf" )}.
* <p>
* However, it may be invoked too late if you use some methods from {@link UIManager}
* of {@link GraphicsEnvironment} before setting look and feel.
* E.g. {@link UIManager#put(Object, Object)}.
* In that case invoke this method yourself very early.
* <p>
* <strong>Tip</strong>: How to find out when the graphics environment is created?
* Set a breakpoint at constructor of class {@link GraphicsEnvironment} and look at the stack.
*
* @since 3.5.2
*/
public static void disableWindowsD3Donscreen() {
// dummy method used to trigger invocation of "static {...}" block
}
/**
* Sets the application look and feel to the given LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*
* @since 1.2
*/
public static boolean setup( LookAndFeel newLookAndFeel ) {
try {
@@ -168,17 +223,11 @@ public abstract class FlatLaf
* This depends on the operating system and on the used Java runtime.
* <p>
* This method returns {@code true} on Windows 10/11 (see exception below)
* and on Linux, {@code false} otherwise.
* and on Linux, otherwise returns {@code false}.
* <p>
* Returns also {@code false} on Windows 10/11 if
* FlatLaf native window border support is available (requires Windows 10/11).
* <p>
* Returns also {@code false} on Windows 10/11 if:
* <ul>
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
* <li>running in
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
* and JBR supports custom window decorations
* </li>
* </ul>
* In these cases, custom decorations are enabled by the root pane.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
@@ -232,6 +281,15 @@ public abstract class FlatLaf
@Override
public void initialize() {
// do not initialize if this is not the current look and feel
// This is only necessary for special Laf usage. E.g. in GUI builders,
// which may use multiple Lafs and may invoke this method directly.
// This avoids that listeners and factories are installed multiple times.
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
// this also avoids that listeners stay registered in the system.
if( UIManager.getLookAndFeel() != this )
return;
if( SystemInfo.isMacOS )
initializeAqua();
@@ -246,8 +304,11 @@ public abstract class FlatLaf
mnemonicHandler.install();
// install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper();
subMenuUsabilityHelper.install();
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
// install Linux popup menu canceler
if( SystemInfo.isLinux )
linuxPopupMenuCanceler = new LinuxPopupMenuCanceler();
// listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) {
@@ -266,6 +327,9 @@ public abstract class FlatLaf
}
if( desktopPropertyName != null ) {
desktopPropertyListener = e -> {
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.UPDATE_UI_ON_SYSTEM_FONT_CHANGE, true ) )
return;
String propertyName = e.getPropertyName();
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
reSetLookAndFeel();
@@ -304,6 +368,10 @@ public abstract class FlatLaf
@Override
public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener
if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -329,9 +397,15 @@ public abstract class FlatLaf
}
// uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) {
subMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null;
if( subMenuUsabilityHelperInstalled ) {
SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelperInstalled = false;
}
// uninstall Linux popup menu canceler
if( linuxPopupMenuCanceler != null ) {
linuxPopupMenuCanceler.uninstall();
linuxPopupMenuCanceler = null;
}
// restore default link color
@@ -362,7 +436,7 @@ public abstract class FlatLaf
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
} else
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().newInstance();
aquaLaf = Class.forName( aquaLafClassName ).asSubclass( BasicLookAndFeel.class ).getDeclaredConstructor().newInstance();
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
throw new IllegalStateException();
@@ -514,7 +588,7 @@ public abstract class FlatLaf
// which can happen in applications that use some plugin system
// and load FlatLaf in a plugin that uses its own classloader.
// (e.g. Apache NetBeans)
if( defaults.get( "FileChooser.fileNameHeaderText" ) != null )
if( defaults.get( "TabbedPane.moreTabsButtonToolTipText" ) != null )
return;
// load FlatLaf resource bundle and add content to defaults
@@ -552,13 +626,17 @@ public abstract class FlatLaf
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
// override fonts
List<String> fontKeys = new ArrayList<>( 50 );
for( Object key : defaults.keySet() ) {
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
defaults.put( key, activeFont );
fontKeys.add( (String) key );
}
for( String key : fontKeys )
defaults.put( key, activeFont );
// add fonts that are not set in BasicLookAndFeel
defaults.put( "RootPane.font", activeFont );
defaults.put( "TitlePane.font", activeFont );
}
private void initDefaultFont( UIDefaults defaults ) {
@@ -607,6 +685,13 @@ public abstract class FlatLaf
if( uiFont == null )
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
// (use remove() to avoid that ActiveFont.createValue() gets invoked)
Object defaultFont = defaults.remove( "defaultFont" );
@@ -627,6 +712,9 @@ public abstract class FlatLaf
}
static FontUIResource createCompositeFont( String family, int style, int size ) {
// load lazy font family
FontUtils.loadFontFamily( family );
// using StyleContext.getFont() here because it uses
// sun.font.FontUtilities.getCompositeFontUIResource()
// and creates a composite font that is able to display all Unicode characters
@@ -874,14 +962,14 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
* <p>
* Sample that setups "FlatLaf Light" theme with red accent color:
* Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
* FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLightLaf.setup();
* }</pre>
*
@@ -906,15 +994,15 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
* <p>
* Sample that setups "FlatLaf Light" theme with red accent color:
* Sample that setups "FlatLaf Light" theme with white background color:
* <pre>{@code
* FlatLaf laf = new FlatLightLaf();
* laf.setExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
* laf.setExtraDefaults( Collections.singletonMap( "@background", "#fff" ) );
* FlatLaf.setup( laf );
* }</pre>
*
@@ -956,6 +1044,36 @@ public abstract class FlatLaf
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() {
EventQueue.invokeLater( () -> {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
@@ -1213,31 +1331,150 @@ public abstract class FlatLaf
*/
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 ) {
ComponentUI ui = JavaCompatibility2.getUI( c );
return (ui instanceof StyleableUI) ? ((StyleableUI)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 ) {
ComponentUI ui = JavaCompatibility2.getUI( c );
return (ui instanceof StyleableUI) ? (T) ((StyleableUI)ui).getStyleableValue( c, key ) : null;
}
/**
* 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 -----------------------------------------------
private class FlatUIDefaults
extends UIDefaults
{
private UIDefaults metalDefaults;
FlatUIDefaults( int initialCapacity, float loadFactor ) {
super( initialCapacity, loadFactor );
}
@Override
public Object get( Object key ) {
Object value = getValue( key );
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key );
return get( key, null );
}
@Override
public Object get( Object key, Locale l ) {
Object value = getValue( key );
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key, l );
Object value = getFromUIDefaultsGetters( key );
if( value != null )
return (value != NULL_VALUE) ? value : null;
value = super.get( key, l );
if( value != null )
return value;
// get file chooser texts from Metal
return (key instanceof String && ((String)key).startsWith( "FileChooser." ))
? getFromMetal( (String) key, l )
: null;
}
private Object getValue( Object key ) {
private Object getFromUIDefaultsGetters( Object key ) {
// use local variable for getters to avoid potential multi-threading issues
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
if( uiDefaultsGetters == null )
return null;
@@ -1249,6 +1486,22 @@ public abstract class FlatLaf
return null;
}
private synchronized Object getFromMetal( String key, Locale l ) {
if( metalDefaults == null ) {
metalDefaults = new MetalLookAndFeel() {
// avoid unnecessary initialization
@Override protected void initClassDefaults( UIDefaults table ) {}
@Override protected void initSystemColorDefaults( UIDefaults table ) {}
}.getDefaults();
// empty not needed defaults (to save memory) because we're only interested
// in resource bundle strings, which are stored in another internal map
metalDefaults.clear();
}
return metalDefaults.get( key, l );
}
}
//---- class ActiveFont ---------------------------------------------------
@@ -1331,7 +1584,7 @@ public abstract class FlatLaf
int newStyle = (style != -1)
? style
: (styleChange != 0)
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
? (baseStyle & ~((styleChange >> 16) & 0xffff)) | (styleChange & 0xffff)
: baseStyle;
// new size
@@ -1347,9 +1600,16 @@ public abstract class FlatLaf
// create font for family
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 ) {
Font font = createCompositeFont( family, newStyle, newSize );
if( !isFallbackFont( font ) || family.equalsIgnoreCase( Font.DIALOG ) )
if( !isFallbackFont( font ) || isDialogFamily( family ) )
return toUIResource( font );
}
}
@@ -1378,9 +1638,26 @@ public abstract class FlatLaf
: new FontUIResource( font );
}
private boolean isFallbackFont( Font font ) {
private static boolean isFallbackFont( Font font ) {
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 ------------------------------------------

View File

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

View File

@@ -21,14 +21,17 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Properties;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
/**
* A Flat LaF that is able to load UI defaults from properties passed to the constructor.
* <p>
* Specify the base theme in the properties with {@code @baseTheme=<baseTheme>}.
* Allowed values for {@code <baseTheme>} are {@code light} (the default), {@code dark},
* {@code intellij} or {@code darcula}.
* {@code intellij}, {@code darcula}, {@code maclight} or {@code macdark}.
* <p>
* The properties are applied after loading the base theme and may overwrite base properties.
* All features of FlatLaf properties files are available.
@@ -70,7 +73,8 @@ public class FlatPropertiesLaf
this.properties = properties;
baseTheme = properties.getProperty( "@baseTheme", "light" );
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme );
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme ) ||
"macdark".equalsIgnoreCase( baseTheme );
}
@Override
@@ -96,7 +100,7 @@ public class FlatPropertiesLaf
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
switch( baseTheme.toLowerCase() ) {
switch( baseTheme.toLowerCase( Locale.ENGLISH ) ) {
default:
case "light":
lafClasses.add( FlatLightLaf.class );
@@ -115,6 +119,16 @@ public class FlatPropertiesLaf
lafClasses.add( FlatDarkLaf.class );
lafClasses.add( FlatDarculaLaf.class );
break;
case "maclight":
lafClasses.add( FlatLightLaf.class );
lafClasses.add( FlatMacLightLaf.class );
break;
case "macdark":
lafClasses.add( FlatDarkLaf.class );
lafClasses.add( FlatMacDarkLaf.class );
break;
}
return lafClasses;
}

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -81,7 +82,7 @@ public interface FlatSystemProperties
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
* UI default {@code TitlePane.useWindowDecorations}.
* <p>
* (requires Window 10/11)
* (requires Windows 10/11)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> none
@@ -98,11 +99,14 @@ public interface FlatSystemProperties
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
* Then FlatLaf native window decorations are used.
* <p>
* (requires Window 10/11)
* (requires Windows 10/11)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
*
* @deprecated No longer used since FlatLaf 3.3. Retained for API compatibility.
*/
@Deprecated
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
/**
@@ -116,7 +120,7 @@ public interface FlatSystemProperties
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
* UI default {@code TitlePane.menuBarEmbedded}.
* <p>
* (requires Window 10/11)
* (requires Windows 10/11)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> none
@@ -131,6 +135,18 @@ public interface FlatSystemProperties
*/
String ANIMATION = "flatlaf.animation";
/**
* Specifies whether native rounded popup borders should be used (if supported by operating system).
* <p>
* (requires Windows 11 or macOS)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}; except in FlatLaf 3.5.x on macOS 14.4+ where it was {@code false}
*
* @since 3.5.2
*/
String USE_ROUNDED_POPUP_BORDER = "flatlaf.useRoundedPopupBorder";
/**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p>
@@ -140,14 +156,76 @@ public interface FlatSystemProperties
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 whether FlatLaf native library should be used.
* <p>
* Setting this to {@code false} disables loading native library,
* which also disables some features that depend on the native library.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*
* @since 3.2
*/
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
/**
* Specifies a directory in which the FlatLaf native libraries are searched for.
* 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.
* <p>
* If the value is {@code "system"} (supported since FlatLaf 2.6),
* then {@link System#loadLibrary(String)} is used to load the native library.
* This 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}.
* <p>
* If the native library can not be 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.
* <p>
* The file names of the native libraries must be either:
* <ul>
* <li>the same as in flatlaf.jar in package 'com/formdev/flatlaf/natives' (required for "system") or
* <li>when downloaded from Maven central then as described here:
* <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* (requires FlatLaf 3.4)
* </ul>
* <p>
* <strong>Note</strong>: Since FlatLaf 3.1 it is recommended to download the
* FlatLaf native libraries from Maven central and distribute them with your
* application in the same directory as flatlaf.jar.
* Then it is <strong>not necessary</strong> to set this system property.
* See <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* for details.
*
* @since 2
*/
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
/**
* Specifies whether safe triangle is used to improve usability of submenus.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*
* @since 3.5.1
*/
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";
/**
* Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.

View File

@@ -23,13 +23,17 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.swing.UIDefaults;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.json.Json;
@@ -61,9 +65,9 @@ public class IntelliJTheme
private final boolean isMaterialUILite;
private final Map<String, String> colors;
private final Map<String, Object> ui;
private final Map<String, Object> icons;
private Map<String, String> colors;
private Map<String, Object> ui;
private Map<String, Object> icons;
private Map<String, ColorUIResource> namedColors = Collections.emptyMap();
@@ -73,6 +77,8 @@ public class IntelliJTheme
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*
* @since 1.2
*/
public static boolean setup( InputStream in ) {
try {
@@ -194,8 +200,9 @@ public class IntelliJTheme
defaults.put( "HelpButton.focusedBackground", defaults.get( "Button.focusedBackground" ) );
// IDEA uses TextField.background for editable ComboBox and Spinner
defaults.put( "ComboBox.editableBackground", defaults.get( "TextField.background" ) );
defaults.put( "Spinner.background", defaults.get( "TextField.background" ) );
Object textFieldBackground = get( defaults, themeSpecificDefaults, "TextField.background" );
defaults.put( "ComboBox.editableBackground", textFieldBackground );
defaults.put( "Spinner.background", textFieldBackground );
// Spinner arrow button always has same colors as ComboBox arrow button
defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonEditableBackground" ) );
@@ -203,22 +210,41 @@ public class IntelliJTheme
defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) );
// some themes specify colors for TextField.background, but forget to specify it for other components
// (probably because those components are not used in IntelliJ)
if( uiKeys.contains( "TextField.background" ) ) {
Object textFieldBackground = defaults.get( "TextField.background" );
if( !uiKeys.contains( "FormattedTextField.background" ) )
defaults.put( "FormattedTextField.background", textFieldBackground );
if( !uiKeys.contains( "PasswordField.background" ) )
defaults.put( "PasswordField.background", textFieldBackground );
if( !uiKeys.contains( "EditorPane.background" ) )
defaults.put( "EditorPane.background", textFieldBackground );
if( !uiKeys.contains( "TextArea.background" ) )
defaults.put( "TextArea.background", textFieldBackground );
if( !uiKeys.contains( "TextPane.background" ) )
defaults.put( "TextPane.background", textFieldBackground );
if( !uiKeys.contains( "Spinner.background" ) )
defaults.put( "Spinner.background", textFieldBackground );
}
// (probably because those components are not used in IntelliJ IDEA)
putAll( defaults, textFieldBackground,
"EditorPane.background",
"FormattedTextField.background",
"PasswordField.background",
"TextArea.background",
"TextPane.background"
);
putAll( defaults, get( defaults, themeSpecificDefaults, "TextField.selectionBackground" ),
"EditorPane.selectionBackground",
"FormattedTextField.selectionBackground",
"PasswordField.selectionBackground",
"TextArea.selectionBackground",
"TextPane.selectionBackground"
);
putAll( defaults, get( defaults, themeSpecificDefaults, "TextField.selectionForeground" ),
"EditorPane.selectionForeground",
"FormattedTextField.selectionForeground",
"PasswordField.selectionForeground",
"TextArea.selectionForeground",
"TextPane.selectionForeground"
);
// fix disabled and not-editable backgrounds for text components, combobox and spinner
// (IntelliJ IDEA does not use those colors; instead it used background color of parent)
putAll( defaults, panelBackground,
"ComboBox.disabledBackground",
"EditorPane.disabledBackground", "EditorPane.inactiveBackground",
"FormattedTextField.disabledBackground", "FormattedTextField.inactiveBackground",
"PasswordField.disabledBackground", "PasswordField.inactiveBackground",
"Spinner.disabledBackground",
"TextArea.disabledBackground", "TextArea.inactiveBackground",
"TextField.disabledBackground", "TextField.inactiveBackground",
"TextPane.disabledBackground", "TextPane.inactiveBackground"
);
// fix ToggleButton
if( !uiKeys.contains( "ToggleButton.startBackground" ) && !uiKeys.contains( "*.startBackground" ) )
@@ -245,6 +271,33 @@ public class IntelliJTheme
if( rowHeight > 22 )
defaults.put( "Tree.rowHeight", 22 );
// get (and remove) theme specific wildcard replacements, which override all other defaults that end with same suffix
HashMap<String, Object> wildcards = new HashMap<>();
Iterator<Entry<Object, Object>> it = themeSpecificDefaults.entrySet().iterator();
while( it.hasNext() ) {
Entry<Object, Object> e = it.next();
String key = (String) e.getKey();
if( key.startsWith( "*." ) ) {
wildcards.put( key.substring( "*.".length() ), e.getValue() );
it.remove();
}
}
// override UI defaults with theme specific wildcard replacements
if( !wildcards.isEmpty() ) {
for( Object key : defaults.keySet().toArray() ) {
int dot;
if( !(key instanceof String) ||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
continue;
String wildcardKey = ((String)key).substring( dot + 1 );
Object wildcardValue = wildcards.get( wildcardKey );
if( wildcardValue != null )
defaults.put( key, wildcardValue );
}
}
// apply theme specific UI defaults at the end to allow overwriting
for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
Object key = e.getKey();
@@ -259,6 +312,20 @@ public class IntelliJTheme
defaults.put( key, value );
}
// let Java release memory
colors = null;
ui = null;
icons = null;
}
private Object get( UIDefaults defaults, Map<Object, Object> themeSpecificDefaults, String key ) {
return themeSpecificDefaults.getOrDefault( key, defaults.get( key ) );
}
private void putAll( UIDefaults defaults, Object value, String... keys ) {
for( String key : keys )
defaults.put( key, value );
}
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
@@ -300,7 +367,7 @@ public class IntelliJTheme
for( Map.Entry<String, String> e : colors.entrySet() ) {
String value = e.getValue();
ColorUIResource color = UIDefaultsLoader.parseColor( value );
ColorUIResource color = parseColor( value );
if( color != null ) {
String key = e.getKey();
namedColors.put( key, color );
@@ -332,17 +399,30 @@ public class IntelliJTheme
if( "".equals( value ) )
return; // ignore empty value
uiKeys.add( key );
// fix ComboBox size and Spinner border in all Material UI Lite themes
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
return; // ignore
// ignore some properties that affect sizes
if( key.endsWith( ".border" ) ||
key.endsWith( ".rowHeight" ) ||
key.equals( "ComboBox.padding" ) ||
key.equals( "Spinner.padding" ) ||
key.equals( "Tree.leftChildIndent" ) ||
key.equals( "Tree.rightChildIndent" ) )
return; // ignore
// map keys
key = uiKeyMapping.getOrDefault( key, key );
if( key.isEmpty() )
return; // ignore key
// exclude properties
int dot = key.indexOf( '.' );
if( dot > 0 && uiKeyExcludes.contains( key.substring( 0, dot + 1 ) ) )
return;
if( uiKeyDoNotOverride.contains( key ) && uiKeys.contains( key ) )
return;
uiKeys.add( key );
String valueStr = value.toString();
// map named colors
@@ -388,7 +468,8 @@ public class IntelliJTheme
// replace all values in UI defaults that match the wildcard key
for( Object k : defaultsKeysCache ) {
if( k.equals( "Desktop.background" ) ||
k.equals( "DesktopIcon.background" ) )
k.equals( "DesktopIcon.background" ) ||
k.equals( "TabbedPane.focusColor" ) )
continue;
if( k instanceof String ) {
@@ -446,12 +527,20 @@ public class IntelliJTheme
ColorUIResource color = namedColors.get( value );
// 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;
}
}
/**
* Because IDEA uses SVGs for check boxes and radio buttons, the colors for
* this two components are specified in "icons > ColorPalette".
* these two components are specified in "icons > ColorPalette".
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
*/
private void applyCheckBoxColors( UIDefaults defaults ) {
@@ -471,18 +560,6 @@ public class IntelliJTheme
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
continue;
if( key.equals( "Checkbox.Background.Default" ) ||
key.equals( "Checkbox.Foreground.Selected" ) )
{
// This two keys do not work correctly in IDEA because they
// map SVG color "#ffffff" to another color, but checkBox.svg and
// radio.svg (in package com.intellij.ide.ui.laf.icons.intellij)
// use "#fff". So use white to get same appearance as in IDEA.
value = "#ffffff";
}
String key2 = checkboxDuplicateColors.get( key );
if( dark )
key = StringUtils.removeTrailing( key, ".Dark" );
@@ -496,6 +573,7 @@ public class IntelliJTheme
if( color != null ) {
defaults.put( newKey, color );
String key2 = checkboxDuplicateColors.get( key + ".Dark");
if( key2 != null ) {
// When IDEA replaces colors in SVGs it uses color values and not the keys
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
@@ -538,7 +616,7 @@ public class IntelliJTheme
// radioFocused.svg and radioSelectedFocused.svg
// use opacity=".65" for the border
// --> add alpha to focused border colors
String[] focusedBorderColorKeys = new String[] {
String[] focusedBorderColorKeys = {
"CheckBox.icon.focusedBorderColor",
"CheckBox.icon.focusedSelectedBorderColor",
"CheckBox.icon[filled].focusedBorderColor",
@@ -560,17 +638,59 @@ public class IntelliJTheme
defaults.put( destKey, defaults.get( srcKey ) );
}
private static final Set<String> uiKeyExcludes;
private static final Set<String> uiKeyDoNotOverride;
/** Rename UI default keys (key --> value). */
private static final Map<String, String> uiKeyMapping = new HashMap<>();
/** Copy UI default keys (value --> key). */
private static final Map<String, String> uiKeyCopying = new HashMap<>();
private static final Map<String, String> uiKeyCopying = new LinkedHashMap<>();
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
static {
// IntelliJ UI properties that are not used in FlatLaf
uiKeyExcludes = new HashSet<>( Arrays.asList(
"ActionButton.", "ActionToolbar.", "ActionsList.", "AppInspector.", "AssignedMnemonic.", "Autocomplete.",
"AvailableMnemonic.",
"BigSpinner.", "Bookmark.", "BookmarkIcon.", "BookmarkMnemonicAssigned.", "BookmarkMnemonicAvailable.",
"BookmarkMnemonicCurrent.", "BookmarkMnemonicIcon.", "Borders.", "Breakpoint.",
"Canvas.", "CodeWithMe.", "ComboBoxButton.", "CompletionPopup.", "ComplexPopup.", "Content.",
"CurrentMnemonic.", "Counter.",
"Debugger.", "DebuggerPopup.", "DebuggerTabs.", "DefaultTabs.", "Dialog.", "DialogWrapper.", "DragAndDrop.",
"Editor.", "EditorGroupsTabs.", "EditorTabs.",
"FileColor.", "FlameGraph.", "Focus.",
"Git.", "Github.", "GotItTooltip.", "Group.", "Gutter.", "GutterTooltip.",
"HeaderColor.", "HelpTooltip.", "Hg.",
"IconBadge.", "InformationHint.", "InplaceRefactoringPopup.",
"Lesson.", "Link.", "LiveIndicator.",
"MainMenu.", "MainToolbar.", "MemoryIndicator.", "MlModelBinding.", "MnemonicIcon.",
"NavBar.", "NewClass.", "NewPSD.", "Notification.", "Notifications.", "NotificationsToolwindow.",
"OnePixelDivider.", "OptionButton.", "Outline.",
"ParameterInfo.", "Plugins.", "ProgressIcon.", "PsiViewer.",
"ReviewList.", "RunWidget.",
"ScreenView.", "SearchEverywhere.", "SearchFieldWithExtension.", "SearchMatch.", "SearchOption.",
"SearchResults.", "SegmentedButton.", "Settings.", "SidePanel.", "Space.", "SpeedSearch.", "StateWidget.",
"StatusBar.",
"Tag.", "TipOfTheDay.", "ToolbarComboWidget.", "ToolWindow.",
"UIDesigner.", "UnattendedHostStatus.",
"ValidationTooltip.", "VersionControl.",
"WelcomeScreen.",
// lower case
"darcula.", "dropArea.", "icons.", "intellijlaf.", "macOSWindow.", "material.", "tooltips.",
// possible typos in .theme.json files
"Checkbox.", "Toolbar.", "Tooltip.", "UiDesigner.", "link."
) );
uiKeyDoNotOverride = new HashSet<>( Arrays.asList(
"TabbedPane.selectedForeground"
) );
// ComboBox
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
uiKeyMapping.put( "ComboBox.buttonBackground", "" ); // ignore
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
@@ -628,9 +748,9 @@ public class IntelliJTheme
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" );
uiKeyMapping.put( "DefaultTabs.underlinedTabBackground", "TabbedPane.selectedBackground" );
uiKeyMapping.put( "DefaultTabs.underlinedTabForeground", "TabbedPane.selectedForeground" );
uiKeyMapping.put( "DefaultTabs.inactiveUnderlineColor", "TabbedPane.inactiveUnderlineColor" );
// TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );

View File

@@ -23,11 +23,14 @@ import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -68,7 +71,7 @@ class LinuxFontPolicy
if( word.endsWith( "," ) )
word = word.substring( 0, word.length() - 1 ).trim();
String lword = word.toLowerCase();
String lword = word.toLowerCase( Locale.ENGLISH );
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
style |= Font.ITALIC;
else if( lword.equals( "bold" ) )
@@ -104,11 +107,11 @@ class LinuxFontPolicy
size = 1;
// handle logical font names
String logicalFamily = mapFcName( family.toLowerCase() );
String logicalFamily = mapFcName( family.toLowerCase( Locale.ENGLISH ) );
if( logicalFamily != null )
family = logicalFamily;
return createFontEx( family, style, size, dsize );
return createFontEx( family, style, size );
}
/**
@@ -118,9 +121,9 @@ class LinuxFontPolicy
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
* If still not found, then font of family 'Dialog' is returned.
*/
private static Font createFontEx( String family, int style, int size, double dsize ) {
private static Font createFontEx( String family, int style, int size ) {
for(;;) {
Font font = createFont( family, style, size, dsize );
Font font = FlatLaf.createCompositeFont( family, style, size );
if( Font.DIALOG.equals( family ) )
return font;
@@ -132,7 +135,7 @@ class LinuxFontPolicy
// - character width is zero (e.g. font Cantarell; Fedora; Oracle Java 8)
FontMetrics fm = StyleContext.getDefaultStyleContext().getFontMetrics( font );
if( fm.getHeight() > size * 2 || fm.stringWidth( "a" ) == 0 )
return createFont( Font.DIALOG, style, size, dsize );
return FlatLaf.createCompositeFont( Font.DIALOG, style, size );
return font;
}
@@ -140,10 +143,10 @@ class LinuxFontPolicy
// find last word in family
int index = family.lastIndexOf( ' ' );
if( index < 0 )
return createFont( Font.DIALOG, style, size, dsize );
return FlatLaf.createCompositeFont( Font.DIALOG, style, size );
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
String lastWord = family.substring( index + 1 ).toLowerCase();
String lastWord = family.substring( index + 1 ).toLowerCase( Locale.ENGLISH );
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
style |= Font.BOLD;
@@ -152,15 +155,6 @@ class LinuxFontPolicy
}
}
private static Font createFont( String family, int style, int size, double dsize ) {
Font font = FlatLaf.createCompositeFont( family, style, size );
// set font size in floating points
font = font.deriveFont( style, (float) dsize );
return font;
}
private static double getGnomeFontScale() {
// do not scale font here if JRE scales
if( isSystemScaling() )
@@ -200,7 +194,7 @@ class LinuxFontPolicy
* Gets the default font for KDE from KDE configuration files.
*
* The Swing fonts are not updated when the user changes system font size
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
* (System Settings > Fonts > Force Font DPI). An application restart is necessary.
* This is the same behavior as in native KDE applications.
*
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used
@@ -254,9 +248,10 @@ class LinuxFontPolicy
if( size < 1 )
size = 1;
return createFont( family, style, size, dsize );
return FlatLaf.createCompositeFont( family, style, size );
}
@SuppressWarnings( "MixedMutabilityReturnType" ) // Error Prone
private static List<String> readConfig( String filename ) {
File userHome = new File( System.getProperty( "user.home" ) );
@@ -277,7 +272,9 @@ class LinuxFontPolicy
// read config file
ArrayList<String> lines = new ArrayList<>( 200 );
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
try( BufferedReader reader = new BufferedReader( new InputStreamReader(
new FileInputStream( file ), StandardCharsets.US_ASCII ) ) )
{
String line;
while( (line = reader.readLine()) != null )
lines.add( line );

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2025 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;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* Cancels (hides) popup menus on Linux.
* <p>
* On Linux, popups are not hidden under following conditions, which results in
* misplaced popups:
* <ul>
* <li>window moved or resized
* <li>window maximized or restored
* <li>window iconified
* <li>window deactivated (e.g. activated other application)
* </ul>
*
* On Windows and macOS, popups are automatically hidden.
* <p>
* The implementation is similar to what's done in
* {@code javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber},
* but only hides popup in some conditions.
*
* @author Karl Tauber
*/
class LinuxPopupMenuCanceler
extends WindowAdapter
implements ChangeListener, ComponentListener
{
private MenuElement[] lastPathSelectedPath;
private Window window;
LinuxPopupMenuCanceler() {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
msm.addChangeListener( this );
lastPathSelectedPath = msm.getSelectedPath();
if( lastPathSelectedPath.length > 0 )
addWindowListeners( lastPathSelectedPath[0] );
}
void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this );
}
private void addWindowListeners( MenuElement selected ) {
// see BasicPopupMenuUI.MouseGrabber.grabWindow()
Component invoker = selected.getComponent();
if( invoker instanceof JPopupMenu )
invoker = ((JPopupMenu)invoker).getInvoker();
window = (invoker instanceof Window)
? (Window) invoker
: SwingUtilities.windowForComponent( invoker );
if( window != null ) {
window.addWindowListener( this );
window.addComponentListener( this );
}
}
private void removeWindowListeners() {
if( window != null ) {
window.removeWindowListener( this );
window.removeComponentListener( this );
window = null;
}
}
private void cancelPopupMenu() {
try {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
MenuElement[] selectedPath = msm.getSelectedPath();
for( MenuElement e : selectedPath ) {
if( e instanceof JPopupMenu )
((JPopupMenu)e).putClientProperty( "JPopupMenu.firePopupMenuCanceled", true );
}
msm.clearSelectedPath();
} catch( RuntimeException ex ) {
removeWindowListeners();
throw ex;
} catch( Error ex ) {
removeWindowListeners();
throw ex;
}
}
//---- ChangeListener ----
@Override
public void stateChanged( ChangeEvent e ) {
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
if( selectedPath.length == 0 )
removeWindowListeners();
else if( lastPathSelectedPath.length == 0 )
addWindowListeners( selectedPath[0] );
lastPathSelectedPath = selectedPath;
}
//---- WindowListener ----
@Override
public void windowIconified( WindowEvent e ) {
cancelPopupMenu();
}
@Override
public void windowDeactivated( WindowEvent e ) {
cancelPopupMenu();
}
@Override
public void windowClosing( WindowEvent e ) {
cancelPopupMenu();
}
//---- ComponentListener ----
@Override
public void componentResized( ComponentEvent e ) {
cancelPopupMenu();
}
@Override
public void componentMoved( ComponentEvent e ) {
cancelPopupMenu();
}
@Override
public void componentShown( ComponentEvent e ) {
}
@Override
public void componentHidden( ComponentEvent e ) {
cancelPopupMenu();
}
}

View File

@@ -45,6 +45,7 @@ import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
* Improves usability of submenus by using a
@@ -59,6 +60,12 @@ class SubMenuUsabilityHelper
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
// Using a static field to ensure that there is only one instance in the system.
// Multiple instances would freeze the application.
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
private static SubMenuUsabilityHelper instance;
private boolean eventQueuePushNotSupported;
private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter;
private boolean changePending;
@@ -74,18 +81,30 @@ class SubMenuUsabilityHelper
private Rectangle invokerBounds;
void install() {
MenuSelectionManager.defaultManager().addChangeListener( this );
static synchronized boolean install() {
if( instance != null )
return false;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SUB_MENU_SAFE_TRIANGLE, true ) )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
}
void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this );
uninstallEventQueue();
static synchronized void uninstall() {
if( instance == null )
return;
MenuSelectionManager.defaultManager().removeChangeListener( instance );
instance.uninstallEventQueue();
instance = null;
}
@Override
public void stateChanged( ChangeEvent e ) {
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
if( eventQueuePushNotSupported || !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
return;
// handle menu selection change later, but only once in case of temporary changes
@@ -139,7 +158,7 @@ debug*/
// get invoker screen bounds
Component invoker = popup.getInvoker();
invokerBounds = (invoker != null)
invokerBounds = (invoker != null && invoker.isShowing())
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
: null;
@@ -158,9 +177,30 @@ debug*/
targetTopY = popupLocation.y;
targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to supress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue();
// install own event queue to suppress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null ) {
SubMenuEventQueue queue = new SubMenuEventQueue();
try {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemEventQueue().push( queue );
// check whether push() worked
// (e.g. SWTSwing uses own event queue that does not support push())
if( toolkit.getSystemEventQueue() != queue ) {
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", null );
return;
}
subMenuEventQueue = queue;
} catch( RuntimeException ex ) {
// catch runtime exception from EventQueue.push()
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", ex );
return;
}
}
// create safe triangle painter
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
@@ -233,8 +273,6 @@ debug*/
}
} );
timeoutTimer.setRepeats( false );
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
}
void uninstall() {
@@ -298,7 +336,7 @@ debug*/
if( window instanceof RootPaneContainer ) {
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
setSize( layeredPane.getSize() );
layeredPane.add( this, new Integer( JLayeredPane.POPUP_LAYER + 1 ) );
layeredPane.add( this, Integer.valueOf( JLayeredPane.POPUP_LAYER + 1 ) );
}
}

View File

@@ -27,8 +27,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -84,6 +88,7 @@ class UIDefaultsLoader
private static int parseColorDepth;
private static Map<String, ColorUIResource> systemColorCache;
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
@@ -105,6 +110,10 @@ class UIDefaultsLoader
Properties additionalDefaults, boolean dark, UIDefaults defaults )
{
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
Properties properties = new Properties();
for( Class<?> lafClass : lafClasses ) {
@@ -152,7 +161,7 @@ class UIDefaultsLoader
classLoader = FlatLaf.class.getClassLoader();
for( Class<?> lafClass : lafClasses ) {
String propertiesName = packageName + '/' + lafClass.getSimpleName() + ".properties";
String propertiesName = packageName + '/' + simpleClassName( lafClass ) + ".properties";
try( InputStream in = classLoader.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
@@ -162,7 +171,7 @@ class UIDefaultsLoader
// load from package URL
URL packageUrl = (URL) source;
for( Class<?> lafClass : lafClasses ) {
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
URL propertiesUrl = new URL( packageUrl + simpleClassName( lafClass ) + ".properties" );
try( InputStream in = propertiesUrl.openStream() ) {
properties.load( in );
@@ -174,7 +183,7 @@ class UIDefaultsLoader
// load from folder
File folder = (File) source;
for( Class<?> lafClass : lafClasses ) {
File propertiesFile = new File( folder, lafClass.getSimpleName() + ".properties" );
File propertiesFile = new File( folder, simpleClassName( lafClass ) + ".properties" );
if( !propertiesFile.isFile() )
continue;
@@ -266,8 +275,9 @@ class UIDefaultsLoader
continue;
}
String value = resolveValue( (String) e.getValue(), propertiesGetter );
String value = (String) e.getValue();
try {
value = resolveValue( value, propertiesGetter );
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex, true );
@@ -276,11 +286,22 @@ class UIDefaultsLoader
// remember variables in defaults to allow using them in styles
defaults.put( KEY_VARIABLES, variables );
// clear/disable system color cache
systemColorCache = null;
} catch( IOException ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
}
}
/**
* Similar to Class.getSimpleName(), but includes enclosing class for nested classes.
*/
static String simpleClassName( Class<?> cls ) {
String className = cls.getName();
return className.substring( className.lastIndexOf( '.' ) + 1 );
}
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
if( severe )
@@ -289,7 +310,9 @@ class UIDefaultsLoader
LoggingFacade.INSTANCE.logConfig( message, ex );
}
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
static String resolveValue( String value, Function<String, String> propertiesGetter )
throws IllegalArgumentException
{
value = value.trim();
String value0 = value;
@@ -318,7 +341,9 @@ class UIDefaultsLoader
return resolveValue( newValue, propertiesGetter );
}
static String resolveValueFromUIManager( String value ) {
static String resolveValueFromUIManager( String value )
throws IllegalArgumentException
{
if( value.startsWith( VARIABLE_PREFIX ) ) {
@SuppressWarnings( "unchecked" )
Map<String, String> variables = (Map<String, String>) UIManager.get( KEY_VARIABLES );
@@ -326,7 +351,7 @@ class UIDefaultsLoader
if( newValue == null )
throw new IllegalArgumentException( "variable '" + value + "' not found" );
return newValue;
return resolveValueFromUIManager( newValue );
}
if( !value.startsWith( PROPERTY_PREFIX ) )
@@ -340,8 +365,11 @@ class UIDefaultsLoader
// convert binary color to string
if( newValue instanceof Color ) {
Color color = (Color) newValue;
int rgb = color.getRGB() & 0xffffff;
int alpha = color.getAlpha();
return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
return (alpha != 255)
? String.format( "#%06x%02x", rgb, alpha )
: String.format( "#%06x", rgb );
}
throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
@@ -354,12 +382,15 @@ class UIDefaultsLoader
private static Map<Class<?>, ValueType> javaValueTypes;
private static Map<String, ValueType> knownValueTypes;
static Object parseValue( String key, String value, Class<?> valueType ) {
static Object parseValue( String key, String value, Class<?> valueType )
throws IllegalArgumentException
{
return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
}
static Object parseValue( String key, String value, Class<?> javaValueType, ValueType[] resultValueType,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
throws IllegalArgumentException
{
if( resultValueType == null )
resultValueType = tempResultValueType;
@@ -389,7 +420,7 @@ class UIDefaultsLoader
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
if( params.size() != 3 )
throwMissingParametersException( value );
throw newMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
String ifValue = params.get( ifCondition ? 1 : 2 );
@@ -525,20 +556,20 @@ class UIDefaultsLoader
case STRING: return value;
case BOOLEAN: return parseBoolean( value );
case CHARACTER: return parseCharacter( value );
case INTEGER: return parseInteger( value, true );
case INTEGERORFLOAT:return parseIntegerOrFloat( value, true );
case FLOAT: return parseFloat( value, true );
case INTEGER: return parseInteger( value );
case INTEGERORFLOAT:return parseIntegerOrFloat( value );
case FLOAT: return parseFloat( value );
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
case ICON: return parseInstance( value, addonClassLoaders );
case ICON: return parseInstance( value, resolver, addonClassLoaders );
case INSETS: return parseInsets( value );
case DIMENSION: return parseDimension( value );
case COLOR: return parseColorOrFunction( value, resolver, true );
case COLOR: return parseColorOrFunction( value, resolver );
case FONT: return parseFont( value );
case SCALEDINTEGER: return parseScaledInteger( value );
case SCALEDFLOAT: return parseScaledFloat( value );
case SCALEDINSETS: return parseScaledInsets( value );
case SCALEDDIMENSION:return parseScaledDimension( value );
case INSTANCE: return parseInstance( value, addonClassLoaders );
case INSTANCE: return parseInstance( value, resolver, addonClassLoaders );
case CLASS: return parseClass( value, addonClassLoaders );
case GRAYFILTER: return parseGrayFilter( value );
case UNKNOWN:
@@ -550,24 +581,34 @@ class UIDefaultsLoader
}
// colors
Object color = parseColorOrFunction( value, resolver, false );
if( color != null ) {
resultValueType[0] = ValueType.COLOR;
if( value.startsWith( "#" ) || value.endsWith( ")" ) ) {
Object color = parseColorOrFunction( value, resolver );
resultValueType[0] = (color != null) ? ValueType.COLOR : ValueType.NULL;
return color;
}
// integer
Integer integer = parseInteger( value, false );
if( integer != null ) {
resultValueType[0] = ValueType.INTEGER;
return integer;
}
// integer or float
char firstChar = value.charAt( 0 );
if( (firstChar >= '0' && firstChar <= '9') ||
firstChar == '-' || firstChar == '+' || firstChar == '.' )
{
// integer
try {
Integer integer = parseInteger( value );
resultValueType[0] = ValueType.INTEGER;
return integer;
} catch( NumberFormatException ex ) {
// ignore
}
// float
Float f = parseFloat( value, false );
if( f != null ) {
resultValueType[0] = ValueType.FLOAT;
return f;
// float
try {
Float f = parseFloat( value );
resultValueType[0] = ValueType.FLOAT;
return f;
} catch( NumberFormatException ex ) {
// ignore
}
}
// string
@@ -590,30 +631,52 @@ class UIDefaultsLoader
}
}
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
throws IllegalArgumentException
{
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
ColorUIResource lineColor = (parts.size() >= 5 && !parts.get( 4 ).isEmpty())
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
: null;
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ), true ) : 1f;
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ), true ) : 0;
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty())
? parseFloat( parts.get( 5 ) )
: 1f;
int arc = (parts.size() >= 7) && !parts.get( 6 ).isEmpty()
? parseInteger( parts.get( 6 ) )
: -1;
return (LazyValue) t -> {
return (lineColor != null)
return (lineColor != null || arc > 0)
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value, addonClassLoaders );
return parseInstance( value, resolver, addonClassLoaders );
}
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
private static Object parseInstance( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
return (LazyValue) t -> {
try {
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
if( value.indexOf( ',' ) >= 0 ) {
// Syntax: className,param1,param2,...
List<String> parts = splitFunctionParams( value, ',' );
String className = parts.get( 0 );
Class<?> cls = findClass( className, addonClassLoaders );
Constructor<?>[] constructors = cls.getDeclaredConstructors();
Object result = invokeConstructorOrStaticMethod( constructors, parts, resolver );
if( result != null )
return result;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + className
+ "': no constructor found for parameters '"
+ value.substring( value.indexOf( ',' + 1 ) ) + "'.", null );
return null;
} else
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
return null;
@@ -650,7 +713,9 @@ class UIDefaultsLoader
}
}
private static Insets parseInsets( String value ) {
private static Insets parseInsets( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
return new InsetsUIResource(
@@ -663,7 +728,9 @@ class UIDefaultsLoader
}
}
private static Dimension parseDimension( String value ) {
private static Dimension parseDimension( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
return new DimensionUIResource(
@@ -674,43 +741,39 @@ class UIDefaultsLoader
}
}
private static Object parseColorOrFunction( String value, Function<String, String> resolver, boolean reportError ) {
private static Object parseColorOrFunction( String value, Function<String, String> resolver )
throws IllegalArgumentException
{
if( value.endsWith( ")" ) )
return parseColorFunctions( value, resolver, reportError );
return parseColorFunctions( value, resolver );
return parseColor( value, reportError );
return parseColor( value );
}
static ColorUIResource parseColor( String value ) {
return parseColor( value, false );
}
private static ColorUIResource parseColor( String value, boolean reportError ) {
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;
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as color object.
*/
static ColorUIResource parseColor( String value )
throws IllegalArgumentException
{
int rgba = parseColorRGBA( value );
return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
: new ColorUIResource( new Color( rgba, true ) );
}
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as {@code rgba} integer suitable for {@link java.awt.Color},
* which includes alpha component in bits 24-31.
*
* @throws IllegalArgumentException
*/
static int parseColorRGBA( String value ) {
static int parseColorRGBA( String value )
throws IllegalArgumentException
{
int len = value.length();
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
throw new IllegalArgumentException();
throw newInvalidColorException( value );
// parse hex
int n = 0;
@@ -725,7 +788,7 @@ class UIDefaultsLoader
else if( ch >= 'A' && ch <= 'F' )
digit = ch - 'A' + 10;
else
throw new IllegalArgumentException();
throw newInvalidColorException( value );
n = (n << 4) | digit;
}
@@ -744,18 +807,21 @@ class UIDefaultsLoader
: (((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 )
throws IllegalArgumentException
{
int paramsStart = value.indexOf( '(' );
if( paramsStart < 0 ) {
if( reportError )
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
return null;
}
if( paramsStart < 0 )
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
String function = StringUtils.substringTrimmed( value, 0, paramsStart );
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
if( params.isEmpty() )
throwMissingParametersException( value );
throw newMissingParametersException( value );
if( parseColorDepth > 100 )
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
@@ -763,28 +829,29 @@ class UIDefaultsLoader
parseColorDepth++;
try {
switch( function ) {
case "if": return parseColorIf( value, params, resolver, reportError );
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
case "if": return parseColorIf( value, params, resolver );
case "systemColor": return parseColorSystemColor( value, params, resolver );
case "rgb": return parseColorRgbOrRgba( false, params, resolver );
case "rgba": return parseColorRgbOrRgba( true, params, resolver );
case "hsl": return parseColorHslOrHsla( false, params );
case "hsla": return parseColorHslOrHsla( true, params );
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
case "fade": return parseColorFade( params, resolver, reportError );
case "spin": return parseColorSpin( params, resolver, reportError );
case "changeHue": return parseColorChange( 0, params, resolver, reportError );
case "changeSaturation":return parseColorChange( 1, params, resolver, reportError );
case "changeLightness": return parseColorChange( 2, params, resolver, reportError );
case "changeAlpha": return parseColorChange( 3, params, resolver, reportError );
case "mix": return parseColorMix( null, params, resolver, reportError );
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
case "shade": return parseColorMix( "#000", params, resolver, reportError );
case "contrast": return parseColorContrast( params, resolver, reportError );
case "over": return parseColorOver( params, resolver, reportError );
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver );
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver );
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver );
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver );
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver );
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver );
case "fade": return parseColorFade( params, resolver );
case "spin": return parseColorSpin( params, resolver );
case "changeHue": return parseColorChange( 0, params, resolver );
case "changeSaturation":return parseColorChange( 1, params, resolver );
case "changeLightness": return parseColorChange( 2, params, resolver );
case "changeAlpha": return parseColorChange( 3, params, resolver );
case "mix": return parseColorMix( null, params, resolver );
case "tint": return parseColorMix( "#fff", params, resolver );
case "shade": return parseColorMix( "#000", params, resolver );
case "contrast": return parseColorContrast( params, resolver );
case "over": return parseColorOver( params, resolver );
}
} finally {
parseColorDepth--;
@@ -799,13 +866,55 @@ class UIDefaultsLoader
* This "if" function is only used if the "if" is passed as parameter to another
* 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 )
throws IllegalArgumentException
{
if( params.size() != 3 )
throwMissingParametersException( value );
throw newMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
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 )
throws IllegalArgumentException
{
if( params.size() < 1 )
throw newMissingParametersException( 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;
}
/**
@@ -816,7 +925,8 @@ class UIDefaultsLoader
* - alpha: an integer 0-255 or a percentage 0-100%
*/
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
Function<String, String> resolver, boolean reportError )
Function<String, String> resolver )
throws IllegalArgumentException
{
if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color
@@ -825,7 +935,7 @@ class UIDefaultsLoader
String colorStr = params.get( 0 );
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 ) );
}
@@ -846,7 +956,9 @@ class UIDefaultsLoader
* - lightness: a percentage 0-100%
* - alpha: a percentage 0-100%
*/
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params )
throws IllegalArgumentException
{
int hue = parseInteger( params.get( 0 ), 0, 360, false );
int saturation = parsePercentage( params.get( 1 ) );
int lightness = parsePercentage( params.get( 2 ) );
@@ -865,7 +977,8 @@ class UIDefaultsLoader
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
*/
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
List<String> params, Function<String, String> resolver, boolean reportError )
List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
@@ -900,7 +1013,7 @@ class UIDefaultsLoader
}
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
return parseFunctionBaseColor( colorStr, function, derived, resolver );
}
/**
@@ -909,7 +1022,9 @@ class UIDefaultsLoader
* - amount: percentage 0-100%
* - 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 )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
boolean derived = false;
@@ -934,7 +1049,7 @@ class UIDefaultsLoader
}
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
return parseFunctionBaseColor( colorStr, function, derived, resolver );
}
/**
@@ -943,9 +1058,11 @@ class UIDefaultsLoader
* - angle: number of degrees to rotate
* - 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 )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ), true );
int amount = parseInteger( params.get( 1 ) );
boolean derived = false;
if( params.size() > 2 ) {
@@ -957,7 +1074,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
return parseFunctionBaseColor( colorStr, function, derived, resolver );
}
/**
@@ -970,11 +1087,12 @@ class UIDefaultsLoader
* - options: [derived]
*/
private static Object parseColorChange( int hslIndex,
List<String> params, Function<String, String> resolver, boolean reportError )
List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int value = (hslIndex == 0)
? parseInteger( params.get( 1 ), true )
? parseInteger( params.get( 1 ) )
: parsePercentage( params.get( 1 ) );
boolean derived = false;
@@ -987,7 +1105,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.HSLChange( hslIndex, value );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
return parseFunctionBaseColor( colorStr, function, derived, resolver );
}
/**
@@ -999,7 +1117,9 @@ class UIDefaultsLoader
* - 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
*/
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 )
throws IllegalArgumentException
{
int i = 0;
if( color1Str == null )
color1Str = params.get( i++ );
@@ -1007,7 +1127,7 @@ class UIDefaultsLoader
int weight = (params.size() > i) ? parsePercentage( params.get( i ) ) : 50;
// parse second color
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver, reportError );
ColorUIResource color2 = (ColorUIResource) parseColorOrFunction( resolver.apply( color2Str ), resolver );
if( color2 == null )
return null;
@@ -1015,7 +1135,7 @@ class UIDefaultsLoader
ColorFunction function = new ColorFunctions.Mix( color2, weight );
// parse first color, apply function and create mixed color
return parseFunctionBaseColor( color1Str, function, false, resolver, reportError );
return parseFunctionBaseColor( color1Str, function, false, resolver );
}
/**
@@ -1026,14 +1146,16 @@ class UIDefaultsLoader
* - threshold: the threshold (in range 0-100%) to specify where the transition
* 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 )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
String darkStr = params.get( 1 );
String lightStr = params.get( 2 );
int threshold = (params.size() > 3) ? parsePercentage( params.get( 3 ) ) : 43;
// 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 )
return null;
@@ -1043,7 +1165,7 @@ class UIDefaultsLoader
: darkStr;
// parse dark or light color
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver );
}
/**
@@ -1052,19 +1174,22 @@ class UIDefaultsLoader
* the alpha of this color is used as weight to mix the two colors
* - background: a background color (e.g. #f00) or a color function
*/
private static Object parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String foregroundStr = params.get( 0 );
String backgroundStr = params.get( 1 );
// parse foreground color
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver );
if( foreground == null || foreground.getAlpha() == 255 )
return foreground;
Color foreground2 = new Color( foreground.getRGB() );
// foreground color without alpha
ColorUIResource foreground2 = new ColorUIResource( foreground.getRGB() );
// parse background color
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver );
if( background == null )
return foreground2;
@@ -1074,11 +1199,12 @@ class UIDefaultsLoader
}
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError )
boolean derived, Function<String, String> resolver )
throws IllegalArgumentException
{
// parse base color
String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver );
if( baseColor == null )
return null;
@@ -1106,7 +1232,9 @@ class UIDefaultsLoader
/**
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
*/
private static Object parseFont( String value ) {
private static Object parseFont( String value )
throws IllegalArgumentException
{
Object font = fontCache.get( value );
if( font != null )
return font;
@@ -1162,11 +1290,11 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "size specified more than once in '" + value + "'" );
if( firstChar == '+' || firstChar == '-' )
relativeSize = parseInteger( param, true );
relativeSize = parseInteger( param );
else if( param.endsWith( "%" ) )
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f;
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ) ) / 100f;
else
absoluteSize = parseInteger( param, true );
absoluteSize = parseInteger( param );
} else if( firstChar == '$' ) {
// reference to base font
if( baseFontKey != null )
@@ -1204,7 +1332,9 @@ class UIDefaultsLoader
return font;
}
private static int parsePercentage( String value ) {
private static int parsePercentage( String value )
throws IllegalArgumentException, NumberFormatException
{
if( !value.endsWith( "%" ) )
throw new NumberFormatException( "invalid percentage '" + value + "'" );
@@ -1220,7 +1350,9 @@ class UIDefaultsLoader
return val;
}
private static Boolean parseBoolean( String value ) {
private static Boolean parseBoolean( String value )
throws IllegalArgumentException
{
switch( value ) {
case "false": return false;
case "true": return true;
@@ -1228,87 +1360,101 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
}
private static Character parseCharacter( String value ) {
private static Character parseCharacter( String value )
throws IllegalArgumentException
{
if( value.length() != 1 )
throw new IllegalArgumentException( "invalid character '" + value + "'" );
return value.charAt( 0 );
}
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage ) {
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage )
throws IllegalArgumentException, NumberFormatException
{
if( allowPercentage && value.endsWith( "%" ) ) {
int percent = parsePercentage( value );
return (max * percent) / 100;
}
Integer integer = parseInteger( value, true );
Integer integer = parseInteger( value );
if( integer < min || integer > max )
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
return integer;
}
private static Integer parseInteger( String value, boolean reportError ) {
private static Integer parseInteger( String value )
throws NumberFormatException
{
try {
return Integer.parseInt( value );
} 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 )
throws NumberFormatException
{
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
try {
return Float.parseFloat( value );
} 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 )
throws NumberFormatException
{
try {
return Float.parseFloat( value );
} 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 ) {
int val = parseInteger( value, true );
private static ActiveValue parseScaledInteger( String value )
throws NumberFormatException
{
int val = parseInteger( value );
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledFloat( String value ) {
float val = parseFloat( value, true );
private static ActiveValue parseScaledFloat( String value )
throws NumberFormatException
{
float val = parseFloat( value );
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledInsets( String value ) {
private static ActiveValue parseScaledInsets( String value )
throws IllegalArgumentException
{
Insets insets = parseInsets( value );
return t -> {
return UIScale.scale( insets );
};
}
private static ActiveValue parseScaledDimension( String value ) {
private static ActiveValue parseScaledDimension( String value )
throws IllegalArgumentException
{
Dimension dimension = parseDimension( value );
return t -> {
return UIScale.scale( dimension );
};
}
private static Object parseGrayFilter( String value ) {
private static Object parseGrayFilter( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
int brightness = Integer.parseInt( numbers.get( 0 ) );
@@ -1343,11 +1489,95 @@ class UIDefaultsLoader
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;
}
private static Object invokeConstructorOrStaticMethod( Executable[] constructorsOrMethods,
List<String> parts, Function<String, String> resolver )
throws Exception
{
// order constructors/methods by parameter types:
// - String parameters to the end
// - int before float parameters
constructorsOrMethods = constructorsOrMethods.clone();
Arrays.sort( constructorsOrMethods, (c1, c2) -> {
Class<?>[] ptypes1 = c1.getParameterTypes();
Class<?>[] ptypes2 = c2.getParameterTypes();
if( ptypes1.length != ptypes2.length )
return ptypes1.length - ptypes2.length;
for( int i = 0; i < ptypes1.length; i++ ) {
Class<?> pt1 = ptypes1[i];
Class<?> pt2 = ptypes2[i];
if( pt1 == pt2 )
continue;
// order methods with String parameters to the end
if( pt1 == String.class )
return 2;
if( pt2 == String.class )
return -2;
// order int before float
if( pt1 == int.class )
return -1;
if( pt2 == int.class )
return 1;
}
return 0;
} );
// search for best constructor/method for given parameter values
for( Executable cm : constructorsOrMethods ) {
if( cm.getParameterCount() != parts.size() - 1 )
continue;
Object[] params = parseMethodParams( cm.getParameterTypes(), parts, resolver );
if( params == null )
continue;
// invoke constructor or static method
if( cm instanceof Constructor )
return ((Constructor<?>)cm).newInstance( params );
else
return ((Method)cm).invoke( null, params );
}
return null;
}
private static Object[] parseMethodParams( Class<?>[] paramTypes, List<String> parts, Function<String, String> resolver ) {
Object[] params = new Object[paramTypes.length];
try {
for( int i = 0; i < params.length; i++ ) {
Class<?> paramType = paramTypes[i];
String paramValue = parts.get( i + 1 );
if( paramType == String.class )
params[i] = paramValue;
else if( paramType == boolean.class )
params[i] = parseBoolean( paramValue );
else if( paramType == int.class )
params[i] = parseInteger( paramValue );
else if( paramType == float.class )
params[i] = parseFloat( paramValue );
else if( paramType == Color.class )
params[i] = parseColorOrFunction( resolver.apply( paramValue ), resolver );
else
return null; // unsupported parameter type
}
} catch( IllegalArgumentException ex ) {
return null; // failed to parse parameter for expected parameter type
}
return params;
}
/**
* For use in LazyValue to get value for given key from UIManager and report error
* if not found. If key is prefixed by '?', then no error is reported.
@@ -1365,7 +1595,7 @@ class UIDefaultsLoader
return value;
}
private static void throwMissingParametersException( String value ) {
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
private static IllegalArgumentException newMissingParametersException( String value ) {
return new IllegalArgumentException( "missing parameters in function '" + value + "'" );
}
}

View File

@@ -57,6 +57,8 @@ public abstract class FlatAbstractIcon
// g2.setColor( Color.blue );
// g2.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 );
paintBackground( c, g2, x, y );
g2.translate( x, y );
UIScale.scaleGraphics( g2 );
@@ -69,7 +71,11 @@ public abstract class FlatAbstractIcon
}
}
protected abstract void paintIcon( Component c, Graphics2D g2 );
/** @since 3.5.2 */
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
}
protected abstract void paintIcon( Component c, Graphics2D g );
@Override
public int getIconWidth() {

View File

@@ -16,9 +16,12 @@
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
@@ -36,6 +39,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatCapsLockIcon
extends FlatAbstractIcon
{
private Path2D path;
public FlatCapsLockIcon() {
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
protected void paintIcon( Component c, Graphics2D g ) {
/*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
<rect width="6" height="2" x="5" y="11.5" fill="#FFF"/>
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
<rect width="5" height="2" x="5.5" y="11.5" stroke="#FFF" stroke-linejoin="round"/>
<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>
</svg>
*/
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
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 );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
BasicStroke stroke = new BasicStroke( 1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND );
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 );
}
}

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.icons;
import static com.formdev.flatlaf.FlatClientProperties.*;
import static com.formdev.flatlaf.ui.FlatUIUtils.stateColor;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
@@ -48,6 +49,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @uiDefault CheckBox.icon.borderWidth int or float optional; defaults to Component.borderWidth
* @uiDefault CheckBox.icon.selectedBorderWidth int or float optional; defaults to CheckBox.icon.borderWidth
* @uiDefault CheckBox.icon.disabledSelectedBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
* @uiDefault CheckBox.icon.indeterminateBorderWidth int or float optional; defaults to CheckBox.icon.selectedBorderWidth
* @uiDefault CheckBox.icon.disabledIndeterminateBorderWidth int or float optional; defaults to CheckBox.icon.disabledSelectedBorderWidth
* @uiDefault CheckBox.arc int
*
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
@@ -56,30 +59,45 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @uiDefault CheckBox.icon.selectedBorderColor Color
* @uiDefault CheckBox.icon.selectedBackground Color
* @uiDefault CheckBox.icon.checkmarkColor Color
* @uiDefault CheckBox.icon.indeterminateBorderColor Color optional; defaults to CheckBox.icon.selectedBorderColor
* @uiDefault CheckBox.icon.indeterminateBackground Color optional; defaults to CheckBox.icon.selectedBackground
* @uiDefault CheckBox.icon.indeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
*
* @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.disabledBackground Color
* @uiDefault CheckBox.icon.disabledSelectedBorderColor Color optional; CheckBox.icon.disabledBorderColor is used if not specified
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; CheckBox.icon.disabledBackground is used if not specified
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
* @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.disabledBackground Color
* @uiDefault CheckBox.icon.disabledSelectedBorderColor Color optional; defaults to CheckBox.icon.disabledBorderColor
* @uiDefault CheckBox.icon.disabledSelectedBackground Color optional; defaults to CheckBox.icon.disabledBackground
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
* @uiDefault CheckBox.icon.disabledIndeterminateBorderColor Color optional; defaults to CheckBox.icon.disabledSelectedBorderColor
* @uiDefault CheckBox.icon.disabledIndeterminateBackground Color optional; defaults to CheckBox.icon.disabledSelectedBackground
* @uiDefault CheckBox.icon.disabledIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.disabledCheckmarkColor
*
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
* @uiDefault CheckBox.icon.focusedBackground Color optional
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
* @uiDefault CheckBox.icon.focusedBorderColor Color optional
* @uiDefault CheckBox.icon.focusedBackground Color optional
* @uiDefault CheckBox.icon.focusedSelectedBorderColor Color optional; defaults to CheckBox.icon.focusedBorderColor
* @uiDefault CheckBox.icon.focusedSelectedBackground Color optional; defaults to CheckBox.icon.focusedBackground
* @uiDefault CheckBox.icon.focusedCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
* @uiDefault CheckBox.icon.focusedIndeterminateBorderColor Color optional; defaults to CheckBox.icon.focusedSelectedBorderColor
* @uiDefault CheckBox.icon.focusedIndeterminateBackground Color optional; defaults to CheckBox.icon.focusedSelectedBackground
* @uiDefault CheckBox.icon.focusedIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.focusedCheckmarkColor
*
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
* @uiDefault CheckBox.icon.hoverBackground Color optional
* @uiDefault CheckBox.icon.hoverSelectedBorderColor Color optional; CheckBox.icon.hoverBorderColor is used if not specified
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
* @uiDefault CheckBox.icon.hoverBackground Color optional
* @uiDefault CheckBox.icon.hoverSelectedBorderColor Color optional; defaults to CheckBox.icon.hoverBorderColor
* @uiDefault CheckBox.icon.hoverSelectedBackground Color optional; defaults to CheckBox.icon.hoverBackground
* @uiDefault CheckBox.icon.hoverCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
* @uiDefault CheckBox.icon.hoverIndeterminateBorderColor Color optional; defaults to CheckBox.icon.hoverSelectedBorderColor
* @uiDefault CheckBox.icon.hoverIndeterminateBackground Color optional; defaults to CheckBox.icon.hoverSelectedBackground
* @uiDefault CheckBox.icon.hoverIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.hoverCheckmarkColor
*
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
* @uiDefault CheckBox.icon.pressedBackground Color optional
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; CheckBox.icon.pressedBorderColor is used if not specified
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
* @uiDefault CheckBox.icon.pressedBorderColor Color optional
* @uiDefault CheckBox.icon.pressedBackground Color optional
* @uiDefault CheckBox.icon.pressedSelectedBorderColor Color optional; defaults to CheckBox.icon.pressedBorderColor
* @uiDefault CheckBox.icon.pressedSelectedBackground Color optional; defaults to CheckBox.icon.pressedBackground
* @uiDefault CheckBox.icon.pressedCheckmarkColor Color optional; defaults to CheckBox.icon.checkmarkColor
* @uiDefault CheckBox.icon.pressedIndeterminateBorderColor Color optional; defaults to CheckBox.icon.pressedSelectedBorderColor
* @uiDefault CheckBox.icon.pressedIndeterminateBackground Color optional; defaults to CheckBox.icon.pressedSelectedBackground
* @uiDefault CheckBox.icon.pressedIndeterminateCheckmarkColor Color optional; defaults to CheckBox.icon.pressedCheckmarkColor
*
* @author Karl Tauber
*/
@@ -92,6 +110,8 @@ public class FlatCheckBoxIcon
/** @since 2 */ @Styleable protected float borderWidth = getUIFloat( "CheckBox.icon.borderWidth", FlatUIUtils.getUIFloat( "Component.borderWidth", 1 ), style );
/** @since 2 */ @Styleable protected float selectedBorderWidth = getUIFloat( "CheckBox.icon.selectedBorderWidth", Float.MIN_VALUE, style );
/** @since 2 */ @Styleable protected float disabledSelectedBorderWidth = getUIFloat( "CheckBox.icon.disabledSelectedBorderWidth", Float.MIN_VALUE, style );
/** @since 3.6 */ @Styleable protected float indeterminateBorderWidth = getUIFloat( "CheckBox.icon.indeterminateBorderWidth", Float.MIN_VALUE, style );
/** @since 3.6 */ @Styleable protected float disabledIndeterminateBorderWidth = getUIFloat( "CheckBox.icon.disabledIndeterminateBorderWidth", Float.MIN_VALUE, style );
@Styleable protected int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
// enabled
@@ -100,6 +120,9 @@ public class FlatCheckBoxIcon
@Styleable protected Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
@Styleable protected Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
@Styleable protected Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
/** @since 3.6 */ @Styleable protected Color indeterminateBorderColor = getUIColor( "CheckBox.icon.indeterminateBorderColor", style );
/** @since 3.6 */ @Styleable protected Color indeterminateBackground = getUIColor( "CheckBox.icon.indeterminateBackground", style );
/** @since 3.6 */ @Styleable protected Color indeterminateCheckmarkColor = getUIColor( "CheckBox.icon.indeterminateCheckmarkColor", style );
// disabled
@Styleable protected Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
@@ -107,6 +130,9 @@ public class FlatCheckBoxIcon
/** @since 2 */ @Styleable protected Color disabledSelectedBorderColor = getUIColor( "CheckBox.icon.disabledSelectedBorderColor", style );
/** @since 2 */ @Styleable protected Color disabledSelectedBackground = getUIColor( "CheckBox.icon.disabledSelectedBackground", style );
@Styleable protected Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateBorderColor = getUIColor( "CheckBox.icon.disabledIndeterminateBorderColor", style );
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateBackground = getUIColor( "CheckBox.icon.disabledIndeterminateBackground", style );
/** @since 3.6 */ @Styleable protected Color disabledIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.disabledIndeterminateCheckmarkColor", style );
// focused
@Styleable protected Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
@@ -114,6 +140,9 @@ public class FlatCheckBoxIcon
/** @since 2 */ @Styleable protected Color focusedSelectedBorderColor = getUIColor( "CheckBox.icon.focusedSelectedBorderColor", style );
/** @since 2 */ @Styleable protected Color focusedSelectedBackground = getUIColor( "CheckBox.icon.focusedSelectedBackground", style );
/** @since 2 */ @Styleable protected Color focusedCheckmarkColor = getUIColor( "CheckBox.icon.focusedCheckmarkColor", style );
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateBorderColor = getUIColor( "CheckBox.icon.focusedIndeterminateBorderColor", style );
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateBackground = getUIColor( "CheckBox.icon.focusedIndeterminateBackground", style );
/** @since 3.6 */ @Styleable protected Color focusedIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.focusedIndeterminateCheckmarkColor", style );
// hover
@Styleable protected Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
@@ -121,6 +150,9 @@ public class FlatCheckBoxIcon
/** @since 2 */ @Styleable protected Color hoverSelectedBorderColor = getUIColor( "CheckBox.icon.hoverSelectedBorderColor", style );
/** @since 2 */ @Styleable protected Color hoverSelectedBackground = getUIColor( "CheckBox.icon.hoverSelectedBackground", style );
/** @since 2 */ @Styleable protected Color hoverCheckmarkColor = getUIColor( "CheckBox.icon.hoverCheckmarkColor", style );
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateBorderColor = getUIColor( "CheckBox.icon.hoverIndeterminateBorderColor", style );
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateBackground = getUIColor( "CheckBox.icon.hoverIndeterminateBackground", style );
/** @since 3.6 */ @Styleable protected Color hoverIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.hoverIndeterminateCheckmarkColor", style );
// pressed
/** @since 2 */ @Styleable protected Color pressedBorderColor = getUIColor( "CheckBox.icon.pressedBorderColor", style );
@@ -128,6 +160,9 @@ public class FlatCheckBoxIcon
/** @since 2 */ @Styleable protected Color pressedSelectedBorderColor = getUIColor( "CheckBox.icon.pressedSelectedBorderColor", style );
/** @since 2 */ @Styleable protected Color pressedSelectedBackground = getUIColor( "CheckBox.icon.pressedSelectedBackground", style );
/** @since 2 */ @Styleable protected Color pressedCheckmarkColor = getUIColor( "CheckBox.icon.pressedCheckmarkColor", style );
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateBorderColor = getUIColor( "CheckBox.icon.pressedIndeterminateBorderColor", style );
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateBackground = getUIColor( "CheckBox.icon.pressedIndeterminateBackground", style );
/** @since 3.6 */ @Styleable protected Color pressedIndeterminateCheckmarkColor = getUIColor( "CheckBox.icon.pressedIndeterminateCheckmarkColor", style );
protected String getPropertyPrefix() {
return "CheckBox.";
@@ -172,16 +207,27 @@ public class FlatCheckBoxIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
boolean indeterminate = isIndeterminate( c );
boolean selected = indeterminate || isSelected( c );
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
float bw = selected
? (disabledSelectedBorderWidth != Float.MIN_VALUE && !c.isEnabled()
? disabledSelectedBorderWidth
: (selectedBorderWidth != Float.MIN_VALUE ? selectedBorderWidth : borderWidth))
: borderWidth;
float bw = Float.MIN_VALUE;
if( !c.isEnabled() ) {
bw = (indeterminate && disabledIndeterminateBorderWidth != Float.MIN_VALUE)
? disabledIndeterminateBorderWidth
: (selected ? disabledSelectedBorderWidth : selectedBorderWidth);
}
if( bw == Float.MIN_VALUE ) {
bw = (indeterminate && indeterminateBorderWidth != Float.MIN_VALUE)
? indeterminateBorderWidth
: ((selected && selectedBorderWidth != Float.MIN_VALUE) ? selectedBorderWidth : borderWidth);
}
// paint focused border
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
@@ -190,15 +236,15 @@ public class FlatCheckBoxIcon
}
// paint border
g.setColor( getBorderColor( c, selected ) );
g.setColor( getBorderColor( c, selected, indeterminate ) );
paintBorder( c, g, bw );
// paint background
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected ),
selected ? selectedBackground : background );
Color baseBg = stateColor( indeterminate, indeterminateBackground, selected, selectedBackground, background );
Color bg = FlatUIUtils.deriveColor( getBackground( c, selected, indeterminate ), baseBg );
if( bg.getAlpha() < 255 ) {
// fill background with default color before filling with non-opaque background
g.setColor( selected ? selectedBackground : background );
g.setColor( baseBg );
paintBackground( c, g, bw );
}
g.setColor( bg );
@@ -206,7 +252,7 @@ public class FlatCheckBoxIcon
// paint checkmark
if( selected ) {
g.setColor( getCheckmarkColor( c ) );
g.setColor( getCheckmarkColor( c, indeterminate ) );
if( indeterminate )
paintIndeterminate( c, g );
else
@@ -237,7 +283,7 @@ public class FlatCheckBoxIcon
}
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.lineTo( 6.6f, 10f );
path.lineTo( 11.25f, 3.5f );
@@ -267,30 +313,33 @@ public class FlatCheckBoxIcon
return focusColor;
}
protected Color getBorderColor( Component c, boolean selected ) {
/** @since 3.6 */
protected Color getBorderColor( Component c, boolean selected, boolean indeterminate ) {
return FlatButtonUI.buttonStateColor( c,
selected ? selectedBorderColor : borderColor,
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
(selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor,
(selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor,
(selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor );
stateColor( indeterminate, indeterminateBorderColor, selected, selectedBorderColor, borderColor ),
stateColor( indeterminate, disabledIndeterminateBorderColor, selected, disabledSelectedBorderColor, disabledBorderColor ),
stateColor( indeterminate, focusedIndeterminateBorderColor, selected, focusedSelectedBorderColor, focusedBorderColor ),
stateColor( indeterminate, hoverIndeterminateBorderColor, selected, hoverSelectedBorderColor, hoverBorderColor ),
stateColor( indeterminate, pressedIndeterminateBorderColor, selected, pressedSelectedBorderColor, pressedBorderColor ) );
}
protected Color getBackground( Component c, boolean selected ) {
/** @since 3.6 */
protected Color getBackground( Component c, boolean selected, boolean indeterminate ) {
return FlatButtonUI.buttonStateColor( c,
selected ? selectedBackground : background,
(selected && disabledSelectedBackground != null) ? disabledSelectedBackground : disabledBackground,
(selected && focusedSelectedBackground != null) ? focusedSelectedBackground : focusedBackground,
(selected && hoverSelectedBackground != null) ? hoverSelectedBackground : hoverBackground,
(selected && pressedSelectedBackground != null) ? pressedSelectedBackground : pressedBackground );
stateColor( indeterminate, indeterminateBackground, selected, selectedBackground, background ),
stateColor( indeterminate, disabledIndeterminateBackground, selected, disabledSelectedBackground, disabledBackground ),
stateColor( indeterminate, focusedIndeterminateBackground, selected, focusedSelectedBackground, focusedBackground ),
stateColor( indeterminate, hoverIndeterminateBackground, selected, hoverSelectedBackground, hoverBackground ),
stateColor( indeterminate, pressedIndeterminateBackground, selected, pressedSelectedBackground, pressedBackground ) );
}
protected Color getCheckmarkColor( Component c ) {
/** @since 3.6 */
protected Color getCheckmarkColor( Component c, boolean indeterminate ) {
return FlatButtonUI.buttonStateColor( c,
checkmarkColor,
disabledCheckmarkColor,
focusedCheckmarkColor,
hoverCheckmarkColor,
pressedCheckmarkColor );
stateColor( indeterminate, indeterminateCheckmarkColor, checkmarkColor ),
stateColor( indeterminate, disabledIndeterminateCheckmarkColor, disabledCheckmarkColor ),
stateColor( indeterminate, focusedIndeterminateCheckmarkColor, focusedCheckmarkColor ),
stateColor( indeterminate, hoverIndeterminateCheckmarkColor, hoverCheckmarkColor ),
stateColor( indeterminate, pressedIndeterminateCheckmarkColor, pressedCheckmarkColor ) );
}
}

View File

@@ -59,6 +59,11 @@ public class FlatCheckBoxMenuItemIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
@@ -71,7 +76,7 @@ public class FlatCheckBoxMenuItemIcon
}
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.lineTo( 6.6f, 10f );
path.lineTo( 11.25f, 3.5f );

View File

@@ -20,7 +20,6 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.Map;
import javax.swing.AbstractButton;
@@ -69,6 +68,11 @@ public class FlatClearIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
if( !ignoreButtonState && c instanceof AbstractButton ) {
@@ -98,9 +102,11 @@ public class FlatClearIcon
// paint cross
g.setColor( clearIconColor );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( 5,5, 11,11 ), false );
path.append( new Line2D.Float( 5,11, 11,5 ), false );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.moveTo( 5, 5 );
path.lineTo( 11, 11 );
path.moveTo( 5, 11 );
path.lineTo( 11, 5 );
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">
<g fill="none" fill-rule="evenodd">
<rect width="2" height="2" x="2" y="3" fill="#6E6E6E"/>
<rect width="2" height="2" x="2" y="7" fill="#6E6E6E"/>
<rect width="2" height="2" x="2" y="11" fill="#6E6E6E"/>
<rect width="8" height="2" x="6" y="3" fill="#6E6E6E"/>
<rect width="8" height="2" x="6" y="7" fill="#6E6E6E"/>
<rect width="8" height="2" x="6" y="11" fill="#6E6E6E"/>
<rect width="2" height="1" x="2" y="3" fill="#6E6E6E" rx=".5"/>
<rect width="2" height="1" x="2" y="6" fill="#6E6E6E" rx=".5"/>
<rect width="2" height="1" x="2" y="9" fill="#6E6E6E" rx=".5"/>
<rect width="2" height="1" x="2" y="12" fill="#6E6E6E" rx=".5"/>
<rect width="8" height="1" x="6" y="3" fill="#6E6E6E" rx=".5"/>
<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>
</svg>
*/
g.fillRect( 2, 3, 2, 2 );
g.fillRect( 2, 7, 2, 2 );
g.fillRect( 2, 11, 2, 2 );
g.fillRect( 6, 3, 8, 2 );
g.fillRect( 6, 7, 8, 2 );
g.fillRect( 6, 11, 8, 2 );
g.fillRoundRect( 2, 3, 2, 1, 1, 1 );
g.fillRoundRect( 2, 6, 2, 1, 1, 1 );
g.fillRoundRect( 2, 9, 2, 1, 1, 1 );
g.fillRoundRect( 2, 12, 2, 1, 1, 1 );
g.fillRoundRect( 6, 3, 8, 1, 1, 1 );
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -39,10 +41,22 @@ public class FlatFileChooserHomeFolderIcon
protected void paintIcon( Component c, Graphics2D g ) {
/*
<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>
*/
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
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">
<g fill="none" fill-rule="evenodd">
<rect width="4" height="4" x="3" y="3" fill="#6E6E6E"/>
<rect width="4" height="4" x="3" y="9" fill="#6E6E6E"/>
<rect width="4" height="4" x="9" y="9" fill="#6E6E6E"/>
<rect width="4" height="4" x="9" 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="2.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
<rect width="4" height="4" x="9.5" y="9.5" stroke="#6E6E6E" rx="1.5"/>
<rect width="4" height="4" x="9.5" y="2.5" stroke="#6E6E6E" rx="1.5"/>
</g>
</svg>
*/
g.fillRect( 3, 3, 4, 4 );
g.fillRect( 3, 9, 4, 4 );
g.fillRect( 9, 9, 4, 4 );
g.fillRect( 9, 3, 4, 4 );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
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;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "new folder" icon for {@link javax.swing.JFileChooser}.
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileChooserNewFolderIcon
extends FlatAbstractIcon
{
private final Color greenColor = UIManager.getColor( "Actions.Green" );
public FlatFileChooserNewFolderIcon() {
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">
<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 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"/>
<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"/>
<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>
</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.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.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( 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;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import javax.swing.UIManager;
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">
<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 fill="#389FD6" d="M12,4 L12,8 L10,8 L10,4 L8,4 L11,1 L14,4 L12,4 Z"/>
<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"/>
<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>
</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.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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
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">
<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="12" height="2" x="2" y="12" fill="#6E6E6E"/>
<rect width="11" height="7" x="2.5" y="3.5" stroke="#6E6E6E" rx="1"/>
<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>
</svg>
*/
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Rectangle2D.Float( 2, 3, 12, 8 ), false );
path.append( new Rectangle2D.Float( 4, 5, 8, 4 ), false );
g.fill( path );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
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.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +33,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileViewDirectoryIcon
extends FlatAbstractIcon
{
private Path2D path;
public FlatFileViewDirectoryIcon() {
super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
}
@@ -39,10 +43,32 @@ public class FlatFileViewDirectoryIcon
protected void paintIcon( Component c, Graphics2D g ) {
/*
<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>
*/
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatFileViewFileIcon
extends FlatAbstractIcon
{
private Path2D path;
public FlatFileViewFileIcon() {
super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
}
@@ -39,14 +44,33 @@ public class FlatFileViewFileIcon
protected void paintIcon( Component c, Graphics2D g ) {
/*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/>
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/>
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<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"/>
<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>
</svg>
*/
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) );
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.RenderingHints;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -40,18 +41,22 @@ public class FlatFileViewFloppyDriveIcon
protected void paintIcon( Component c, Graphics2D g ) {
/*
<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="#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"/>
<rect width="4" height="2" x="6" y="12" fill="#6E6E6E"/>
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<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"/>
<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>
</svg>
*/
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( FlatUIUtils.createPath( 11,14, 11,11, 5,11, 5,14, 2,14, 2,2, 14,2, 14,14, 11,14 ), false );
path.append( FlatUIUtils.createPath( 4,4, 4,8, 12,8, 12,4, 4,4 ), false );
g.fill( path );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager;
/**
@@ -40,14 +42,19 @@ public class FlatFileViewHardDriveIcon
protected void paintIcon( Component c, Graphics2D g ) {
/*
<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>
*/
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Rectangle2D.Float( 2, 6, 12, 4 ), false );
path.append( new Rectangle2D.Float( 12, 8, 1, 1 ), false );
path.append( new Rectangle2D.Float( 10, 8, 1, 1 ), false );
g.fill( path );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.setStroke( new BasicStroke( 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g.draw( new RoundRectangle2D.Float( 2.5f, 5.5f, 11, 5, 2, 2 ) );
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;
import static com.formdev.flatlaf.util.UIScale.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.Map;
@@ -84,6 +86,11 @@ public class FlatHelpButtonIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
/*
@@ -91,7 +98,8 @@ public class FlatHelpButtonIcon
<g fill="none" fill-rule="evenodd">
<circle cx="11" cy="11" r="10.5" fill="#6E6E6E"/>
<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>
</svg>
*/
@@ -142,22 +150,19 @@ public class FlatHelpButtonIcon
g2.fill( new Ellipse2D.Float( xy, xy, wh, wh ) );
// paint question mark
Path2D q = new Path2D.Float();
q.moveTo( 11, 5 );
q.curveTo( 8.8,5, 7,6.8, 7,9 );
q.lineTo( 9, 9 );
q.curveTo( 9,7.9, 9.9,7, 11,7 );
q.curveTo( 12.1,7, 13,7.9, 13,9 );
q.curveTo( 13,11, 10,10.75, 10,14 );
q.lineTo( 12, 14 );
q.curveTo( 12,11.75, 15,11.5, 15,9 );
q.curveTo( 15,6.8, 13.2,5, 11,5 );
q.closePath();
Path2D q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
q.moveTo( 8,8.5 );
q.curveTo( 8.25,7, 9.66585007,6, 11,6 );
q.curveTo( 12.5,6, 14,7, 14,8.5 );
q.curveTo( 14,10.5, 11,11, 11,13 );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g2.setStroke( new BasicStroke( 2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g2.translate( focusWidth, focusWidth );
g2.setColor( enabled ? questionMarkColor : disabledQuestionMarkColor );
g2.fill( q );
g2.fillRect( 10, 15, 2, 2 );
g2.draw( q );
g2.fill( new Ellipse2D.Float( 9.8f, 14.8f, 2.4f, 2.4f ) );
}
@Override

View File

@@ -20,7 +20,6 @@ import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
@@ -54,13 +53,15 @@ public class FlatInternalFrameCloseIcon
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
float mx = width / 2;
float my = height / 2;
float mx = width / 2f;
float my = height / 2f;
float r = 3.25f;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.moveTo( mx - r, my - r );
path.lineTo( mx + r, my + r );
path.moveTo( mx - r, my + r );
path.lineTo( mx + r, my - r );
g.setStroke( new BasicStroke( 1f ) );
g.draw( path );
}

View File

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

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
/**
* "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">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#DB5860"/>
<rect width="4" height="11" x="14" y="7" fill="#FFF"/>
<rect width="4" height="4" x="14" y="21" fill="#FFF"/>
<rect width="4" height="12" x="14" y="7" fill="#FFF" rx="2"/>
<circle cx="16" cy="23" r="2" fill="#FFF"/>
</g>
</svg>
*/
@@ -54,8 +54,8 @@ public class FlatOptionPaneErrorIcon
@Override
protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 7, 4, 11 ), false );
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false );
inside.append( new RoundRectangle2D.Float( 14, 7, 4, 12, 4, 4 ), false );
inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
return inside;
}
}

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
/**
* "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">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#389FD6"/>
<rect width="4" height="11" x="14" y="14" fill="#FFF"/>
<rect width="4" height="4" x="14" y="7" fill="#FFF"/>
<rect width="4" height="12" x="14" y="13" fill="#FFF" rx="2"/>
<circle cx="16" cy="9" r="2" fill="#FFF"/>
</g>
</svg>
*/
@@ -54,8 +54,8 @@ public class FlatOptionPaneInformationIcon
@Override
protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 14, 4, 11 ), false );
inside.append( new Rectangle2D.Float( 14, 7, 4, 4 ), false );
inside.append( new RoundRectangle2D.Float( 14, 13, 4, 12, 4, 4 ), false );
inside.append( new Ellipse2D.Float( 14, 7, 4, 4 ), false );
return inside;
}
}

View File

@@ -16,10 +16,10 @@
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
/**
* "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">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14" fill="#389FD6"/>
<rect width="4" height="4" x="14" y="22" 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"/>
<circle cx="16" cy="24" r="1.7" fill="#FFF"/>
<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>
</svg>
*/
@@ -53,21 +53,17 @@ public class FlatOptionPaneQuestionIcon
@Override
protected Shape createInside() {
Path2D q = new Path2D.Float();
q.moveTo( 14, 20 );
q.lineTo( 18, 20 );
q.curveTo( 18, 16, 23, 16, 23, 12 );
q.curveTo( 23, 8, 20, 6, 16, 6 );
q.curveTo( 12, 6, 9, 8, 9, 12 );
q.curveTo( 9, 12, 13, 12, 13, 12 );
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 q = new Path2D.Float( Path2D.WIND_NON_ZERO, 10 );
q.moveTo( 11.5,11.75 );
q.curveTo( 11.75,9.5, 13.75,8, 16,8 );
q.curveTo( 18.25,8, 20.5,9.5, 20.5,11.75 );
q.curveTo( 20.5,14.75, 16,15.5, 16,19 );
BasicStroke stroke = new BasicStroke( 3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER );
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 22, 4, 4 ), false );
inside.append( q, false );
inside.append( new Ellipse2D.Float( 14.3f, 22.3f, 3.4f, 3.4f ), false );
inside.append( stroke.createStrokedShape( q ), false );
return inside;
}
}

View File

@@ -17,8 +17,9 @@
package com.formdev.flatlaf.icons;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
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">
<g fill="none" fill-rule="evenodd">
<polygon fill="#EDA200" points="16 2 31 28 1 28"/>
<rect width="4" height="8" x="14" y="10" fill="#FFF"/>
<rect width="4" height="4" x="14" y="21" fill="#FFF"/>
<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="11" x="14" y="8" fill="#FFF" rx="2"/>
<circle cx="16" cy="23" r="2" fill="#FFF"/>
</g>
</svg>
*/
@Override
protected Shape createOutside() {
return FlatUIUtils.createPath( 16,2, 31,28, 1,28 );
return FlatUIUtils.createRoundTrianglePath( 16,0, 32,28, 0,28, 4 );
}
@Override
protected Shape createInside() {
Path2D inside = new Path2D.Float( Path2D.WIND_EVEN_ODD );
inside.append( new Rectangle2D.Float( 14, 10, 4, 8 ), false );
inside.append( new Rectangle2D.Float( 14, 21, 4, 4 ), false );
inside.append( new RoundRectangle2D.Float( 14, 8, 4, 11, 4, 4 ), false );
inside.append( new Ellipse2D.Float( 14, 21, 4, 4 ), false );
return inside;
}
}

View File

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

View File

@@ -21,7 +21,6 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.Map;
import javax.swing.UIManager;
@@ -76,6 +75,11 @@ public class FlatTabbedPaneCloseIcon
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
// paint background
@@ -86,18 +90,20 @@ public class FlatTabbedPaneCloseIcon
closeSize.width, closeSize.height, closeArc, closeArc );
}
// set cross color
// set color of cross
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
float mx = width / 2;
float my = height / 2;
float mx = width / 2f;
float my = height / 2f;
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
// paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.moveTo( mx - r, my - r );
path.lineTo( mx + r, my + r );
path.moveTo( mx - r, my + r );
path.lineTo( mx + r, my - r );
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
g.draw( path );
}

View File

@@ -18,8 +18,9 @@ package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "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
extends FlatAbstractIcon
{
private Path2D path;
public FlatTreeClosedIcon() {
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">
<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>
*/
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;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.util.function.Function;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
@@ -39,6 +41,7 @@ public class FlatTreeCollapsedIcon
extends FlatAbstractIcon
{
private final boolean chevron;
private Path2D path;
public FlatTreeCollapsedIcon() {
this( UIManager.getColor( "Tree.icon.collapsedColor" ) );
@@ -59,10 +62,15 @@ public class FlatTreeCollapsedIcon
if( chevron ) {
// 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 {
// 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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
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}.
@@ -42,13 +46,22 @@ public class FlatTreeLeafIcon
/*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<polygon fill="#6E6E6E" points="8 6 8 1 13 1 13 15 3 15 3 6"/>
<polygon fill="#6E6E6E" points="3 5 7 5 7 1"/>
<rect width="11" height="13" x="2.5" y="1.5" stroke="#6E6E6E" rx="1.5"/>
<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>
</svg>
*/
g.fill( FlatUIUtils.createPath( 8,6, 8,1, 13,1, 13,15, 3,15, 3,6 ) );
g.fill( FlatUIUtils.createPath( 3,5, 7,5, 7,1 ) );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
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;
import java.awt.BasicStroke;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -31,6 +34,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatTreeOpenIcon
extends FlatAbstractIcon
{
private Path2D path;
public FlatTreeOpenIcon() {
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">
<g fill="none" fill-rule="evenodd">
<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>
<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"/>
</svg>
*/
g.fill( FlatUIUtils.createPath( 1,2, 6,2, 8,4, 14,4, 14,6, 3.5,6, 1,11 ) );
g.fill( FlatUIUtils.createPath( 4,7, 16,7, 13,13, 1,13 ) );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
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 opened part
2,13.5,
// top-left of opened part
FlatUIUtils.ROUNDED, 4.5,7.5, arc,
// top-right of opened 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

@@ -21,7 +21,6 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
@@ -30,6 +29,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* Base class for window icons.
*
* @uiDefault TitlePane.buttonSize Dimension
* @uiDefault TitlePane.buttonSymbolHeight int
* @uiDefault TitlePane.buttonHoverBackground Color
* @uiDefault TitlePane.buttonPressedBackground Color
*
@@ -38,40 +38,46 @@ import com.formdev.flatlaf.util.HiDPIUtils;
public abstract class FlatWindowAbstractIcon
extends FlatAbstractIcon
{
private final int symbolHeight;
private final Color hoverBackground;
private final Color pressedBackground;
public FlatWindowAbstractIcon() {
this( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
/** @since 3.2 */
protected FlatWindowAbstractIcon( String windowStyle ) {
this( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedBackground", windowStyle ) );
}
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
/** @since 3.2 */
protected FlatWindowAbstractIcon( Dimension size, int symbolHeight, Color hoverBackground, Color pressedBackground ) {
super( size.width, size.height, null );
this.symbolHeight = symbolHeight;
this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( getForeground( c ) );
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
}
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
protected void paintBackground( Component c, Graphics2D g ) {
/** @since 3.5.2 */
@Override
protected void paintBackground( Component c, Graphics2D g, int x, int y ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) {
// disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%)
// disable antialiasing for background rectangle painting to avoid blurry edges when scaled (e.g. at 125% or 175%)
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
// fill background of whole component
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
g.fillRect( 0, 0, width, height );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
}
@@ -80,4 +86,9 @@ public abstract class FlatWindowAbstractIcon
protected Color getForeground( Component c ) {
return c.getForeground();
}
/** @since 3.2 */
protected int getSymbolHeight() {
return symbolHeight;
}
}

View File

@@ -20,10 +20,9 @@ import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -39,27 +38,38 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatWindowCloseIcon
extends FlatWindowAbstractIcon
{
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
private final Color hoverForeground;
private final Color pressedForeground;
public FlatWindowCloseIcon() {
super( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.closeHoverBackground" ),
UIManager.getColor( "TitlePane.closePressedBackground" ) );
this( null );
}
/** @since 3.2 */
public FlatWindowCloseIcon( String windowStyle ) {
super( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.closePressedBackground", windowStyle ) );
hoverForeground = FlatUIUtils.getSubUIColor( "TitlePane.closeHoverForeground", windowStyle );
pressedForeground = FlatUIUtils.getSubUIColor( "TitlePane.closePressedForeground", windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int iwh = (int) (getSymbolHeight() * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int ix2 = ix + iwh - 1;
int iy2 = iy + iwh - 1;
float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
float thickness = Math.max( SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor, 1 );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
path.append( new Line2D.Float( ix, iy2, ix2, iy ), false );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );
path.moveTo( ix, iy );
path.lineTo( ix2, iy2 );
path.moveTo( ix, iy2 );
path.lineTo( ix2, iy );
g.setStroke( new BasicStroke( thickness ) );
g.draw( path );
}

View File

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

View File

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

View File

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

View File

@@ -502,9 +502,9 @@ class JsonParser {
}
private boolean isHexDigit() {
return current >= '0' && current <= '9'
|| current >= 'a' && current <= 'f'
|| current >= 'A' && current <= 'F';
return (current >= '0' && current <= '9')
|| (current >= 'a' && current <= 'f')
|| (current >= 'A' && current <= 'F');
}
private boolean isEndOfText() {

View File

@@ -69,7 +69,7 @@ public class Location {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (!(obj instanceof Location)) {
return false;
}
Location other = (Location)obj;

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

@@ -28,7 +28,6 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders;
import com.formdev.flatlaf.FlatClientProperties;
@@ -101,6 +100,12 @@ public class FlatBorder
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create();
@@ -130,7 +135,7 @@ public class FlatBorder
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
focusWidth, 1, focusInnerWidth, borderWidth, arc,
focusColor, borderColor, null );
focusColor, borderColor, null, c instanceof JScrollPane );
} finally {
g2.dispose();
}
@@ -189,8 +194,7 @@ public class FlatBorder
protected boolean isEnabled( Component c ) {
if( c instanceof JScrollPane ) {
// check whether view component is disabled
JViewport viewport = ((JScrollPane)c).getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
if( view != null && !isEnabled( view ) )
return false;
}
@@ -273,7 +277,7 @@ public class FlatBorder
}
/**
* Returns the (unscaled) arc diameter of the border.
* Returns the (unscaled) arc diameter of the border corners.
*/
protected int getArc( Component c ) {
return 0;

View File

@@ -42,6 +42,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.disabledBorderColor Color
* @uiDefault Button.focusedBorderColor Color
* @uiDefault Button.hoverBorderColor Color optional
* @uiDefault Button.pressedBorderColor Color optional
*
* @uiDefault Button.selectedBorderColor Color optional
* @uiDefault Button.disabledSelectedBorderColor Color optional
* @uiDefault Button.focusedSelectedBorderColor Color optional
* @uiDefault Button.hoverSelectedBorderColor Color optional
* @uiDefault Button.pressedSelectedBorderColor Color optional
*
* @uiDefault Button.default.borderWidth int or float
* @uiDefault Button.default.borderColor Color
@@ -49,6 +56,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
* @uiDefault Button.default.focusedBorderColor Color
* @uiDefault Button.default.focusColor Color
* @uiDefault Button.default.pressedBorderColor Color optional
* @uiDefault Button.default.hoverBorderColor Color optional
*
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
@@ -65,6 +73,13 @@ public class FlatButtonBorder
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
/** @since 3.5 */ @Styleable protected Color pressedBorderColor = UIManager.getColor( "Button.pressedBorderColor" );
/** @since 3.5 */ @Styleable protected Color selectedBorderColor = UIManager.getColor( "Button.selectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color disabledSelectedBorderColor = UIManager.getColor( "Button.disabledSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color focusedSelectedBorderColor = UIManager.getColor( "Button.focusedSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color hoverSelectedBorderColor = UIManager.getColor( "Button.hoverSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color pressedSelectedBorderColor = UIManager.getColor( "Button.pressedSelectedBorderColor" );
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
@@ -72,6 +87,7 @@ public class FlatButtonBorder
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
/** @since 3.5 */ @Styleable(dot=true) protected Color defaultPressedBorderColor = UIManager.getColor( "Button.default.pressedBorderColor" );
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
@@ -139,12 +155,13 @@ public class FlatButtonBorder
@Override
protected Paint getBorderColor( Component c ) {
boolean def = FlatButtonUI.isDefaultButton( c );
boolean selected = (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
Paint color = FlatButtonUI.buttonStateColor( c,
def ? defaultBorderColor : borderColor,
disabledBorderColor,
def ? defaultFocusedBorderColor : focusedBorderColor,
def ? defaultHoverBorderColor : hoverBorderColor,
null );
def ? defaultBorderColor : ((selected && selectedBorderColor != null) ? selectedBorderColor : borderColor),
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
def ? defaultFocusedBorderColor : ((selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor),
def ? defaultHoverBorderColor : ((selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor),
def ? defaultPressedBorderColor : ((selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor) );
// change to gradient paint if start/end colors are specified
Color startBg = def ? defaultBorderColor : borderColor;

View File

@@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
@@ -28,6 +29,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.util.Map;
@@ -45,17 +47,22 @@ import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ToolBarUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -181,7 +188,7 @@ public class FlatButtonUI
private AtomicBoolean borderShared;
public static ComponentUI createUI( JComponent c ) {
return FlatUIUtils.canUseSharedUI( c )
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
: new FlatButtonUI( false );
}
@@ -193,6 +200,13 @@ public class FlatButtonUI
@Override
public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c );
installStyle( (AbstractButton) c );
@@ -288,6 +302,10 @@ public class FlatButtonUI
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case BasicHTML.propertyKey:
FlatHTML.updateRendererCSSFontBaseSize( b );
break;
case SQUARE_SIZE:
case MINIMUM_WIDTH:
case MINIMUM_HEIGHT:
@@ -296,11 +314,11 @@ public class FlatButtonUI
case BUTTON_TYPE:
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
case OUTLINE:
b.repaint();
HiDPIUtils.repaint( b );
break;
case STYLE:
@@ -312,7 +330,7 @@ public class FlatButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}
@@ -352,6 +370,9 @@ public class FlatButtonUI
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
}
if( "iconTextGap".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
if( borderShared == null )
borderShared = new AtomicBoolean( true );
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );
@@ -366,6 +387,18 @@ public class FlatButtonUI
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 ) {
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
}
@@ -526,9 +559,52 @@ public class FlatButtonUI
}
}
/**
* Similar to BasicButtonUI.paint(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
@Override
public void paint( Graphics g, JComponent c ) {
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
g = FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c );
AbstractButton b = (AbstractButton) c;
// layout
String clippedText = layout( b, b.getFontMetrics( b.getFont() ), b.getWidth(), b.getHeight() );
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
clearTextShiftOffset();
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
ButtonModel model = b.getModel();
if( model.isArmed() && model.isPressed() )
paintButtonPressed( g, b );
// paint icon
if( b.getIcon() != null )
paintIcon( g, b, iconR );
// paint text
if( clippedText != null && !clippedText.isEmpty() ) {
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null ) {
// update foreground color in HTML view, which is necessary
// for selected and pressed states
// (only for enabled buttons, because UIManager.getColor("textInactiveText")
// is used for disabled components; see: javax.swing.text.GlyphView.paint())
if( b.isEnabled() )
FlatHTML.updateRendererCSSForeground( view, getForeground( b ) );
view.paint( g, textR ); // HTML text
} else
paintText( g, b, textR, clippedText );
}
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
if( b.isFocusPainted() && b.hasFocus() )
paintFocus( g, b, viewR, textR, iconR );
}
@Override
@@ -568,8 +644,6 @@ public class FlatButtonUI
}
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
if(foreground == null)
foreground=Color.red;
FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -579,7 +653,8 @@ public class FlatButtonUI
}
protected Color getBackground( JComponent c ) {
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
boolean def = isDefaultButton( c );
boolean toolBarButton = !def && (isToolBarButton( c ) || isBorderlessButton( c ));
// selected state
if( ((AbstractButton)c).isSelected() ) {
@@ -607,7 +682,6 @@ public class FlatButtonUI
toolbarPressedBackground );
}
boolean def = isDefaultButton( c );
return buttonStateColor( c,
getBackgroundBase( c, def ),
disabledBackground,
@@ -617,6 +691,9 @@ public class FlatButtonUI
}
protected Color getBackgroundBase( JComponent c, boolean def ) {
if( FlatUIUtils.isAWTPeer( c ) )
return background;
// use component background if explicitly set
Color bg = c.getBackground();
if( isCustomBackground( bg ) )
@@ -655,14 +732,16 @@ public class FlatButtonUI
}
protected Color getForeground( JComponent c ) {
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
Color fg = c.getForeground();
boolean def = isDefaultButton( c );
boolean toolBarButton = !def && (isToolBarButton( c ) || isBorderlessButton( c ));
// selected state
if( ((AbstractButton)c).isSelected() ) {
return buttonStateColor( c,
toolBarButton
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
: selectedForeground,
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : fg)
: (isCustomForeground( fg ) ? fg : selectedForeground),
toolBarButton
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
@@ -674,18 +753,17 @@ public class FlatButtonUI
// toolbar button
if( toolBarButton ) {
return buttonStateColor( c,
c.getForeground(),
fg,
disabledText,
null,
toolbarHoverForeground,
toolbarPressedForeground );
}
boolean def = isDefaultButton( c );
return buttonStateColor( c,
getForegroundBase( c, def ),
disabledText,
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
isCustomForeground( fg ) ? null : (def ? defaultFocusedForeground : focusedForeground),
def ? defaultHoverForeground : hoverForeground,
def ? defaultPressedForeground : pressedForeground );
}
@@ -758,6 +836,67 @@ public class FlatButtonUI
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
}
@Override
public int getBaseline( JComponent c, int width, int height ) {
return getBaselineImpl( c, width, height );
}
/**
* Similar to BasicButtonUI.getBaseline(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
static int getBaselineImpl( JComponent c, int width, int height ) {
if( width < 0 || height < 0 )
throw new IllegalArgumentException();
AbstractButton b = (AbstractButton) c;
String text = b.getText();
if( text == null || text.isEmpty() )
return -1;
FontMetrics fm = b.getFontMetrics( b.getFont() );
layout( b, fm, width, height );
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null ) {
// HTML text
int baseline = BasicHTML.getHTMLBaseline( view, textR.width, textR.height );
return (baseline >= 0) ? textR.y + baseline : baseline;
} else
return textR.y + fm.getAscent();
}
/**
* Similar to BasicButtonUI.layout(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
private static String layout( AbstractButton b, FontMetrics fm, int width, int height ) {
// compute view rectangle
Insets insets = b.getInsets();
viewR.setBounds( insets.left, insets.top,
width - insets.left - insets.right,
height - insets.top - insets.bottom );
// reset rectangles
textR.setBounds( 0, 0, 0, 0 );
iconR.setBounds( 0, 0, 0, 0 );
String text = b.getText();
return SwingUtilities.layoutCompoundLabel( b, fm, text, b.getIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewR, iconR, textR,
(text != null) ? b.getIconTextGap() : 0 );
}
private static Rectangle viewR = new Rectangle();
private static Rectangle textR = new Rectangle();
private static Rectangle iconR = new Rectangle();
//---- class FlatButtonListener -------------------------------------------
protected class FlatButtonListener
@@ -775,5 +914,32 @@ public class FlatButtonUI
super.propertyChange( e );
FlatButtonUI.this.propertyChange( b, e );
}
@Override
public void stateChanged( ChangeEvent e ) {
HiDPIUtils.repaint( b );
// 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 );
}
}
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( b );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( b );
}
}
}

View File

@@ -256,10 +256,14 @@ public class FlatCaret
// select all
if( c instanceof JFormattedTextField ) {
EventQueue.invokeLater( () -> {
if( getComponent() == null )
// Warning: do not use variables from outside of this runnable
// because they may be out-of-date when this runnable is executed
JTextComponent c2 = getComponent();
if( c2 == null )
return; // was deinstalled
select( 0, doc.getLength() );
select( 0, c2.getDocument().getLength() );
} );
} else {
select( 0, doc.getLength() );

View File

@@ -16,18 +16,21 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
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.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -58,9 +61,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
*
* @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
extends BasicCheckBoxMenuItemUI
implements StyleableUI
implements StyleableUI, StyleableLookupProvider
{
private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
@@ -94,13 +103,23 @@ public class FlatCheckBoxMenuItemUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */
@@ -119,29 +138,27 @@ public class FlatCheckBoxMenuItemUI
/** @since 2 */
protected Object applyStyleProperty( String key, Object value ) {
try {
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 );
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
}
/** @since 2 */
@Override
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

View File

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

View File

@@ -24,6 +24,7 @@ import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -42,15 +43,18 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.CellRendererPane;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComboBox.KeySelectionManager;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
@@ -69,8 +73,12 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
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.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
@@ -96,9 +104,8 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.minimumWidth int
* @uiDefault ComboBox.editorColumns 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.isIntelliJTheme boolean
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
* @uiDefault ComboBox.focusedBackground Color optional
* @uiDefault ComboBox.disabledBackground Color
@@ -114,19 +121,24 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.buttonHoverArrowColor Color
* @uiDefault ComboBox.buttonPressedArrowColor Color
* @uiDefault ComboBox.popupBackground Color optional
* @uiDefault ComboBox.popupInsets Insets
* @uiDefault ComboBox.selectionInsets Insets
* @uiDefault ComboBox.selectionArc int
*
* @author Karl Tauber
*/
@StyleableField( cls=BasicComboBoxUI.class, key="padding" )
public class FlatComboBoxUI
extends BasicComboBoxUI
implements StyleableUI
implements StyleableUI, StyleableLookupProvider
{
@Styleable protected int minimumWidth;
@Styleable protected int editorColumns;
@Styleable protected String buttonStyle;
@Styleable protected String arrowType;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color editableBackground;
@Styleable protected Color focusedBackground;
@Styleable protected Color disabledBackground;
@@ -144,6 +156,9 @@ public class FlatComboBoxUI
@Styleable protected Color buttonPressedArrowColor;
@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;
protected boolean hover;
@@ -160,8 +175,18 @@ public class FlatComboBoxUI
@Override
public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c );
// install key selection manager that shows popup when Space key is pressed
comboBox.setKeySelectionManager( new FlatKeySelectionManager( comboBox.getKeySelectionManager() ) );
installStyle();
}
@@ -196,7 +221,7 @@ public class FlatComboBoxUI
private void repaintArrowButton() {
if( arrowButton != null && !comboBox.isEditable() )
arrowButton.repaint();
HiDPIUtils.repaint( arrowButton );
}
};
comboBox.addMouseListener( hoverListener );
@@ -220,8 +245,8 @@ public class FlatComboBoxUI
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( "ComboBox.background" );
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
@@ -239,6 +264,9 @@ public class FlatComboBoxUI
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
popupBackground = UIManager.getColor( "ComboBox.popupBackground" );
popupInsets = UIManager.getInsets( "ComboBox.popupInsets" );
selectionInsets = UIManager.getInsets( "ComboBox.selectionInsets" );
selectionArc = UIManager.getInt( "ComboBox.selectionArc" );
// set maximumRowCount
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
@@ -254,6 +282,7 @@ public class FlatComboBoxUI
protected void uninstallDefaults() {
super.uninstallDefaults();
background = null;
editableBackground = null;
focusedBackground = null;
disabledBackground = null;
@@ -293,11 +322,14 @@ public class FlatComboBoxUI
// limit button width to height of a raw combobox (without insets)
FontMetrics fm = comboBox.getFontMetrics( comboBox.getFont() );
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();
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() ) {
// set width of arrow button to preferred height of combobox
// set width of arrow button
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
? arrowButton.getWidth() - buttonWidth
: 0;
@@ -320,15 +352,15 @@ public class FlatComboBoxUI
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
};
}
@@ -355,12 +387,12 @@ public class FlatComboBoxUI
switch( propertyName ) {
case PLACEHOLDER_TEXT:
if( editor != null )
editor.repaint();
HiDPIUtils.repaint( editor );
break;
case COMPONENT_ROUND_RECT:
case OUTLINE:
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
case MINIMUM_WIDTH:
@@ -371,7 +403,7 @@ public class FlatComboBoxUI
case STYLE_CLASS:
installStyle();
comboBox.revalidate();
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
}
}
@@ -491,13 +523,6 @@ public class FlatComboBoxUI
/** @since 2 */
protected Object applyStyleProperty( String key, Object value ) {
// BasicComboBoxUI
if( key.equals( "padding" ) ) {
Object oldValue = padding;
padding = (Insets) value;
return oldValue;
}
if( borderShared == null )
borderShared = new AtomicBoolean( true );
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
@@ -506,11 +531,21 @@ public class FlatComboBoxUI
/** @since 2 */
@Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
infos.put( "padding", Insets.class );
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos );
return infos;
return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() );
}
/** @since 2.5 */
@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
@@ -538,7 +573,9 @@ public class FlatComboBoxUI
int height = c.getHeight();
int arrowX = arrowButton.getX();
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 isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
@@ -548,7 +585,7 @@ public class FlatComboBoxUI
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
// paint arrow button background
if( enabled && !isCellRenderer ) {
if( enabled && !isCellRenderer && arrowButton.isVisible() ) {
Color buttonColor = paintButton
? buttonEditableBackground
: (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox )
@@ -556,18 +593,26 @@ public class FlatComboBoxUI
: buttonBackground;
if( buttonColor != null ) {
g2.setColor( buttonColor );
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 );
if( isMacStyle() ) {
Insets insets = comboBox.getInsets();
int gap = scale( 2 );
FlatUIUtils.paintComponentBackground( g2, arrowX + gap, insets.top + gap,
arrowWidth - (gap * 2), height - insets.top - insets.bottom - (gap * 2),
0, arc - focusWidth );
} 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 );
}
}
}
// paint vertical line between value and arrow button
if( paintButton ) {
if( paintButton && arrowButton.isVisible() ) {
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
g2.setColor( separatorColor );
@@ -608,7 +653,7 @@ public class FlatComboBoxUI
boolean shouldValidate = (c instanceof JPanel);
paddingBorder.install( c );
paddingBorder.install( c, 0 );
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
paddingBorder.uninstall();
@@ -623,6 +668,9 @@ public class FlatComboBoxUI
protected Color getBackground( boolean enabled ) {
if( enabled ) {
if( FlatUIUtils.isAWTPeer( comboBox ) )
return background;
Color background = comboBox.getBackground();
// always use explicitly set color
@@ -635,7 +683,7 @@ public class FlatComboBoxUI
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
} else
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
return disabledBackground;
}
protected Color getForeground( boolean enabled ) {
@@ -682,7 +730,7 @@ public class FlatComboBoxUI
@Override
protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp );
paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall();
return size;
@@ -698,6 +746,10 @@ public class FlatComboBoxUI
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
}
private boolean isMacStyle() {
return "mac".equals( buttonStyle );
}
/** @since 1.3 */
public static boolean isPermanentFocusOwner( JComboBox<?> comboBox ) {
if( comboBox.isEditable() ) {
@@ -732,6 +784,21 @@ public class FlatComboBoxUI
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
protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false);
@@ -749,6 +816,20 @@ public class FlatComboBoxUI
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 -----------------------------------------------
@@ -783,12 +864,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
JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
if( verticalScrollBar != null )
displayWidth += verticalScrollBar.getPreferredSize().width;
// make popup wider if necessary
int pw0 = pw;
if( displayWidth > pw ) {
// limit popup width to screen width
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
@@ -808,6 +896,30 @@ public class FlatComboBoxUI
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 );
}
@@ -815,20 +927,15 @@ public class FlatComboBoxUI
protected void configurePopup() {
super.configurePopup();
// make opaque to avoid that background shines thru border (e.g. at 150% scaling)
// make opaque to avoid that background shines through border (e.g. at 150% scaling)
setOpaque( true );
// set popup border
// use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
// set popup border
// use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
Border border = UIManager.getBorder( "PopupMenu.border" );
if( border != null )
setBorder( FlatUIUtils.nonUIResource( border ) );
}
@Override
protected void configureList() {
super.configureList();
list.setCellRenderer( new PopupListCellRenderer() );
updateStyle();
@@ -836,12 +943,21 @@ public class FlatComboBoxUI
void updateStyle() {
if( popupBackground != null )
list.setBackground( popupBackground );
list.setBackground( popupBackground );
// 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
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
// set popup background because it may shine through when scaled (e.g. at 150%)
// use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview
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
@@ -874,6 +990,29 @@ public class FlatComboBoxUI
}
}
// improve location of selected item in popup if list is large and scrollable
if( list.getHeight() == 0 ) {
// If popup is shown for the first time (or after a laf switch) and is scrollable,
// then BasicComboPopup scrolls the selected item to the top of the visible area.
// But for usability it would be better to have selected item somewhere
// in the middle of the visible area so that the user can see items above
// the selected item, which are usually more "important".
int selectedIndex = list.getSelectedIndex();
if( selectedIndex >= 1 ) {
int maximumRowCount = comboBox.getMaximumRowCount();
if( selectedIndex < maximumRowCount ) {
// selected item is in the first visible items --> scroll to top
list.scrollRectToVisible( new Rectangle() );
} else {
// scroll the selected item to the middle of the visible area
int firstVisibleIndex = Math.max( selectedIndex - (maximumRowCount / 2), 0 );
if( firstVisibleIndex > 0 )
list.ensureIndexIsVisible( firstVisibleIndex );
}
}
}
super.show( invoker, x, y );
}
@@ -883,6 +1022,15 @@ public class FlatComboBoxUI
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 -----
private class PopupListCellRenderer
@@ -900,7 +1048,14 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
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;
}
@@ -916,24 +1071,33 @@ public class FlatComboBoxUI
* which vertically aligns text in popup list with text in combobox.
* <p>
* 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
extends AbstractBorder
{
static final String KEY_MAC_STYLE_HINT = "FlatLaf.internal.FlatComboBoxUI.macStyleHint";
static final int MAC_STYLE_GAP = 4;
private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) {
this.padding = padding;
}
// using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done)
synchronized void install( Component c ) {
// (model, selection, etc.) not on AWT thread (which should be not done)
synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) )
return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder();
if( oldBorder == this )
@@ -966,6 +1130,8 @@ public class FlatComboBoxUI
if( rendererComponent == null )
return;
rendererComponent.putClientProperty( KEY_MAC_STYLE_HINT, null );
if( rendererComponent.getBorder() == this )
rendererComponent.setBorder( rendererBorder );
rendererComponent = null;
@@ -987,6 +1153,24 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom;
insets.right = padding.right;
}
// if used in popup list, add focus width for exact vertical alignment
// of text in popup list with text in combobox
insets.left += focusWidth;
insets.right += focusWidth;
// 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;
}
@@ -994,6 +1178,35 @@ public class FlatComboBoxUI
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( rendererBorder != null )
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 );
}
}
@@ -1023,4 +1236,46 @@ public class FlatComboBoxUI
}
}
}
//---- class FlatKeySelectionManager --------------------------------------
/**
* Key selection manager that delegates to the default manager.
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
* If items contain spaces (e.g. "a b") it is still possible to select them
* by pressing keys 'a', 'Space' and 'b'.
*/
private class FlatKeySelectionManager
implements JComboBox.KeySelectionManager, UIResource
{
private final KeySelectionManager delegate;
private final long timeFactor;
private long lastTime;
FlatKeySelectionManager( JComboBox.KeySelectionManager delegate ) {
this.delegate = delegate;
Long value = (Long) UIManager.get( "ComboBox.timeFactor" );
timeFactor = (value != null) ? value : 1000;
}
@SuppressWarnings( "rawtypes" )
@Override
public int selectionForKey( char aKey, ComboBoxModel aModel ) {
long time = EventQueue.getMostRecentEventTime();
long oldLastTime = lastTime;
lastTime = time;
// SPACE key shows popup if not yet visible
if( aKey == ' ' &&
time - oldLastTime >= timeFactor &&
!comboBox.isPopupVisible() )
{
comboBox.setPopupVisible( true );
return -1;
}
return delegate.selectionForKey( aKey, aModel );
}
}
}

View File

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

View File

@@ -59,7 +59,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatEditorPaneUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault EditorPane.focusedBackground Color optional
*
* @author Karl Tauber
@@ -69,7 +68,6 @@ public class FlatEditorPaneUI
implements StyleableUI
{
@Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color disabledBackground;
@Styleable protected Color inactiveBackground;
@@ -101,7 +99,6 @@ public class FlatEditorPaneUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( prefix + ".background" );
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
@@ -174,7 +171,7 @@ public class FlatEditorPaneUI
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
}
@@ -209,6 +206,12 @@ public class FlatEditorPaneUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private void updateBackground() {
FlatTextFieldUI.updateBackground( getComponent(), background,
disabledBackground, inactiveBackground,
@@ -246,11 +249,11 @@ public class FlatEditorPaneUI
@Override
protected void paintBackground( Graphics g ) {
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
paintBackground( g, getComponent(), focusedBackground );
}
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
g.setColor( FlatTextFieldUI.getBackground( c, focusedBackground ) );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
}

View File

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

View File

@@ -24,13 +24,16 @@ import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.function.Function;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
@@ -45,6 +48,7 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
@@ -53,6 +57,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatFileViewDirectoryIcon;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
@@ -151,9 +156,9 @@ import com.formdev.flatlaf.util.UIScale;
*
* @uiDefault FileChooser.shortcuts.buttonSize Dimension optional; default is 84,64
* @uiDefault FileChooser.shortcuts.iconSize Dimension optional; default is 32,32
* @uiDefault FileChooser.shortcuts.filesFunction Function<File[], File[]>
* @uiDefault FileChooser.shortcuts.displayNameFunction Function<File, String>
* @uiDefault FileChooser.shortcuts.iconFunction Function<File, Icon>
* @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
*/
@@ -162,6 +167,7 @@ public class FlatFileChooserUI
{
private final FlatFileView fileView = new FlatFileView();
private FlatShortcutsPanel shortcutsPanel;
private JScrollPane shortcutsScrollPane;
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
@@ -181,7 +187,10 @@ public class FlatFileChooserUI
FlatShortcutsPanel panel = createShortcutsPanel( fc );
if( panel.getComponentCount() > 0 ) {
shortcutsPanel = panel;
fc.add( shortcutsPanel, BorderLayout.LINE_START );
shortcutsScrollPane = new JScrollPane( shortcutsPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
shortcutsScrollPane.setBorder( BorderFactory.createEmptyBorder() );
fc.add( shortcutsScrollPane, BorderLayout.LINE_START );
fc.addPropertyChangeListener( shortcutsPanel );
}
}
@@ -194,6 +203,7 @@ public class FlatFileChooserUI
if( shortcutsPanel != null ) {
fc.removePropertyChangeListener( shortcutsPanel );
shortcutsPanel = null;
shortcutsScrollPane = null;
}
}
@@ -243,11 +253,13 @@ public class FlatFileChooserUI
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 && center != null && south != null ) {
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 );
@@ -320,7 +332,7 @@ public class FlatFileChooserUI
public Dimension getPreferredSize( JComponent c ) {
Dimension prefSize = super.getPreferredSize( c );
Dimension minSize = getMinimumSize( c );
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
int shortcutsPanelWidth = (shortcutsScrollPane != null) ? shortcutsScrollPane.getPreferredSize().width : 0;
return new Dimension(
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
Math.max( prefSize.height, minSize.height ) );
@@ -344,12 +356,12 @@ public class FlatFileChooserUI
fileView.clearIconCache();
}
private boolean doNotUseSystemIcons() {
private static boolean doNotUseSystemIcons() {
// Java 17 32bit craches on Windows when using system icons
// fixed in Java 18+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
// fixed in Java 18+, fix backported in Java 17.0.3+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
return SystemInfo.isWindows &&
SystemInfo.isX86 &&
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
(SystemInfo.isJava_17_orLater && SystemInfo.javaVersion < SystemInfo.toVersion( 17, 0, 3, 0 ));
}
//---- class FlatFileView -------------------------------------------------
@@ -364,27 +376,68 @@ public class FlatFileChooserUI
if( icon != null )
return icon;
// get system icon
if( f != null ) {
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
// new proxy icon
//
// Note: Since this is a super light weight icon object, we do not add it
// to the icon cache here. This keeps cache small in case of large directories
// with thousands of files when icons of all files are only needed to compute
// the layout of list/table, but never painted because located outside of visible area.
// When an icon needs to be painted, the proxy adds it to the icon cache
// and loads the real icon.
return new FlatFileViewIcon( f );
}
if( icon != null ) {
if( icon instanceof ImageIcon )
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
return icon;
//---- class FlatFileViewIcon -----------------------------------------
/**
* A proxy icon that has a fixed (scaled) width/height (16x16) and
* gets/loads the real (system) icon only for painting.
* Avoids unnecessary getting/loading system icons.
*/
private class FlatFileViewIcon
implements Icon
{
private final File f;
private Icon realIcon;
FlatFileViewIcon( File f ) {
this.f = f;
}
@Override
public int getIconWidth() {
return UIScale.scale( 16 );
}
@Override
public int getIconHeight() {
return UIScale.scale( 16 );
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
// get icon on demand
if( realIcon == null ) {
// get system icon
try {
if( f != null )
realIcon = getFileChooser().getFileSystemView().getSystemIcon( f );
} catch( NullPointerException ex ) {
// Java 21 may throw a NPE for exe files that use default Windows exe icon
}
// get default icon
if( realIcon == null )
realIcon = FlatFileView.super.getIcon( f );
if( realIcon instanceof ImageIcon )
realIcon = new ScaledImageIcon( (ImageIcon) realIcon );
cacheIcon( f, this );
}
realIcon.paintIcon( c, g, x, y );
}
// get default icon
icon = super.getIcon( f );
if( icon instanceof ImageIcon ) {
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
}
return icon;
}
}
@@ -393,7 +446,7 @@ public class FlatFileChooserUI
/** @since 2.3 */
public static class FlatShortcutsPanel
extends JToolBar
implements PropertyChangeListener
implements PropertyChangeListener, Scrollable
{
private final JFileChooser fc;
@@ -405,13 +458,14 @@ public class FlatFileChooserUI
protected final File[] files;
protected final JToggleButton[] buttons;
protected final ButtonGroup buttonGroup;
protected final ButtonGroup buttonGroup = new ButtonGroup();
@SuppressWarnings( "unchecked" )
public FlatShortcutsPanel( JFileChooser fc ) {
super( JToolBar.VERTICAL );
this.fc = fc;
setFloatable( false );
putClientProperty( FlatClientProperties.STYLE, "hoverButtonGroupBackground: null" );
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
@@ -421,22 +475,25 @@ public class FlatFileChooserUI
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
FileSystemView fsv = fc.getFileSystemView();
File[] files = getChooserShortcutPanelFiles( fsv );
File[] files = JavaCompatibility2.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() );
ArrayList<File> filesList = new ArrayList<>();
ArrayList<JToggleButton> buttonsList = new ArrayList<>();
for( File file : files ) {
if( file == null )
continue;
// wrap drive path
if( fsv.isFileSystemRoot( file ) )
file = fsv.createFileObject( file.getAbsolutePath() );
File file = files[i];
String name = getDisplayName( fsv, file );
Icon icon = getIcon( fsv, file );
if( name == null )
continue;
// remove path from name
int lastSepIndex = name.lastIndexOf( File.separatorChar );
@@ -450,16 +507,22 @@ public class FlatFileChooserUI
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
// create button
JToggleButton button = createButton( name, icon );
JToggleButton button = createButton( name, icon, file.toString() );
File f = file;
button.addActionListener( e -> {
fc.setCurrentDirectory( file );
fc.setCurrentDirectory( f );
} );
add( button );
buttonGroup.add( button );
buttons[i] = button;
filesList.add( file );
buttonsList.add( button );
}
this.files = filesList.toArray( new File[filesList.size()] );
this.buttons = buttonsList.toArray( new JToggleButton[buttonsList.size()] );
directoryChanged( fc.getCurrentDirectory() );
}
@@ -470,8 +533,10 @@ public class FlatFileChooserUI
return size;
}
protected JToggleButton createButton( String name, Icon icon ) {
/** @since 3.5 */
protected JToggleButton createButton( String name, Icon icon, String toolTip ) {
JToggleButton button = new JToggleButton( name, icon );
button.setToolTipText( toolTip );
button.setVerticalTextPosition( SwingConstants.BOTTOM );
button.setHorizontalTextPosition( SwingConstants.CENTER );
button.setAlignmentX( Component.CENTER_ALIGNMENT );
@@ -481,32 +546,6 @@ public class FlatFileChooserUI
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 );
@@ -524,29 +563,39 @@ public class FlatFileChooserUI
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 );
}
if( doNotUseSystemIcons() )
return new FlatFileViewDirectoryIcon();
// get system icon in default size 16x16
return fsv.getSystemIcon( file );
try {
// 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( Exception ex ) {
// do not log InaccessibleObjectException because access
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
}
// get system icon in default size 16x16
return fsv.getSystemIcon( file );
} catch( NullPointerException ex ) {
// Java 21 may throw a NPE for exe files that use default Windows exe icon
return new FlatFileViewDirectoryIcon();
}
}
protected void directoryChanged( File file ) {
@@ -565,6 +614,8 @@ public class FlatFileChooserUI
buttonGroup.clearSelection();
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
@@ -573,6 +624,41 @@ public class FlatFileChooserUI
break;
}
}
//---- interface Scrollable ----
@Override
public Dimension getPreferredScrollableViewportSize() {
if( getComponentCount() > 0 ) {
Insets insets = getInsets();
int height = (getComponent( 0 ).getPreferredSize().height * 5) + insets.top + insets.bottom;
return new Dimension( getPreferredSize().width, height );
}
return getPreferredSize();
}
@Override
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
if( orientation == SwingConstants.VERTICAL && getComponentCount() > 0 )
return getComponent( 0 ).getPreferredSize().height;
return getScrollableBlockIncrement( visibleRect, orientation, direction ) / 10;
}
@Override
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
//---- class ShortcutIcon -------------------------------------------------

View File

@@ -40,7 +40,6 @@ import javax.swing.plaf.ComponentUI;
* <!-- FlatTextFieldUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault FormattedTextField.placeholderForeground Color
* @uiDefault FormattedTextField.focusedBackground Color optional
* @uiDefault FormattedTextField.iconTextGap int optional, default is 4

View File

@@ -0,0 +1,290 @@
/*
* Copyright 2024 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.Color;
import java.awt.Font;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JToolTip;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.LabelView;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
/**
* @author Karl Tauber
* @since 3.5
*/
public class FlatHTML
{
private FlatHTML() {}
/**
* Adds CSS rule BASE_SIZE to the style sheet of the HTML view,
* which re-calculates font sizes based on current component font size.
* This is necessary for "absolute-size" keywords (e.g. "x-large")
* for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css).
* See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values">CSS font-size</a>.
* <p>
* This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}.
*/
public static void updateRendererCSSFontBaseSize( JComponent c ) {
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
if( view == null )
return;
// dumpViews( view, 0 );
Document doc = view.getDocument();
if( !(doc instanceof HTMLDocument) )
return;
// add BASE_SIZE rule if necessary
// - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule
// - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
/*debug
for( int i = 1; i <= 7; i++ )
System.out.println( i+": "+ styleSheet.getPointSize( i ) );
debug*/
Font font = c.getFont();
if( styleSheet.getPointSize( 7 ) != 36f ||
font == null || styleSheet.getPointSize( 4 ) == font.getSize() )
return;
// check whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
if( !usesAbsoluteSizeKeywordForFontSize( view ) )
return;
// get HTML text from component
String text;
if( c instanceof JLabel )
text = ((JLabel)c).getText();
else if( c instanceof AbstractButton )
text = ((AbstractButton)c).getText();
else if( c instanceof JToolTip )
text = ((JToolTip)c).getTipText();
else
return;
if( text == null || !BasicHTML.isHTMLString( text ) )
return;
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + font.getSize() + "</style>";
String openTag = "";
String closeTag = "";
int headIndex;
int styleIndex;
int insertIndex;
if( (headIndex = indexOfTag( text, "head", true )) >= 0 ) {
// there is a <head> tag --> insert after <head> tag
insertIndex = headIndex;
} else if( (styleIndex = indexOfTag( text, "style", false )) >= 0 ) {
// there is a <style> tag --> insert before <style> tag
insertIndex = styleIndex;
} else {
// no <head> or <style> tag --> insert <head> tag after <html> tag
insertIndex = "<html>".length();
openTag = "<head>";
closeTag = "</head>";
}
String newText = text.substring( 0, insertIndex )
+ openTag + style + closeTag
+ text.substring( insertIndex );
BasicHTML.updateRenderer( c, newText );
// for unit tests
if( testUpdateRenderer != null )
testUpdateRenderer.accept( c, newText );
}
// for unit tests
static BiConsumer<JComponent, String> testUpdateRenderer;
/**
* Returns start or end index of a HTML tag.
* Checks only for leading '<' character and (case-ignore) tag name.
*/
private static int indexOfTag( String html, String tag, boolean endIndex ) {
int tagLength = tag.length();
int maxLength = html.length() - tagLength - 2;
char lastTagChar = tag.charAt( tagLength - 1 );
for( int i = "<html>".length(); i < maxLength; i++ ) {
// check for leading '<' and last tag name character
if( html.charAt( i ) == '<' && Character.toLowerCase( html.charAt( i + tagLength ) ) == lastTagChar ) {
// compare tag characters from last to first
for( int j = tagLength - 2; j >= 0; j-- ) {
if( Character.toLowerCase( html.charAt( i + 1 + j ) ) != tag.charAt( j ) )
break; // not equal
if( j == 0 ) {
// tag found
return endIndex ? html.indexOf( '>', i + tagLength ) + 1 : i;
}
}
}
}
return -1;
}
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" ) );
/**
* Checks whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
* (see javax/swing/text/html/default.css).
*/
private static boolean usesAbsoluteSizeKeywordForFontSize( View view ) {
AttributeSet attributes = view.getAttributes();
if( attributes != null ) {
Object fontSize = attributes.getAttribute( CSS.Attribute.FONT_SIZE );
if( fontSize != null ) {
if( absoluteSizeKeywordsSet.contains( fontSize.toString() ) )
return true;
}
}
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ ) {
if( usesAbsoluteSizeKeywordForFontSize( view.getView( i ) ) )
return true;
}
return false;
}
/**
* Updates foreground in style sheet of the HTML view.
* Adds "body { color: #&lt;foreground-hex&gt;; }"
*/
public static void updateRendererCSSForeground( View view, Color foreground ) {
Document doc = view.getDocument();
if( !(doc instanceof HTMLDocument) || foreground == null )
return;
// add foreground rule if necessary
// - use tag 'body' because BasicHTML.createHTMLView() also uses this tag
// to set font and color styles to component font/color
// see: SwingUtilities2.displayPropertiesToCSS()
// - this color is not used if component is disabled;
// JTextComponent.getDisabledTextColor() is used for disabled text components;
// UIManager.getColor("textInactiveText") is used for other disabled components
// see: javax.swing.text.GlyphView.paint()
Style bodyStyle = ((HTMLDocument)doc).getStyle( "body" );
if( bodyStyle == null ) {
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
styleSheet.addRule( String.format( "body { color: #%06x; }", foreground.getRGB() & 0xffffff ) );
clearViewCaches( view );
} else if( !foreground.equals( bodyStyle.getAttribute( StyleConstants.Foreground ) ) ) {
bodyStyle.addAttribute( StyleConstants.Foreground, foreground );
clearViewCaches( view );
}
}
/**
* Clears cached values in view so that CSS changes take effect.
*/
private static void clearViewCaches( View view ) {
if( view instanceof LabelView )
((LabelView)view).changedUpdate( null, null, null );
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ )
clearViewCaches( view.getView( i ) );
}
public static PropertyChangeListener createPropertyChangeListener( PropertyChangeListener superListener ) {
return e -> {
if( superListener != null )
superListener.propertyChange( e );
propertyChange( e );
};
}
/**
* Invokes {@link #updateRendererCSSFontBaseSize(JComponent)}
* for {@link BasicHTML#propertyKey} property change events,
* which are fired when {@link BasicHTML#updateRenderer(JComponent, String)}
* updates the HTML view.
*/
public static void propertyChange( PropertyChangeEvent e ) {
if( BasicHTML.propertyKey.equals( e.getPropertyName() ) && e.getNewValue() instanceof View )
updateRendererCSSFontBaseSize( (JComponent) e.getSource() );
}
/*debug
public static void dumpView( JComponent c ) {
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
if( view != null )
dumpViews( view, 0 );
}
public static void dumpViews( View view, int indent ) {
for( int i = 0; i < indent; i++ )
System.out.print( " " );
System.out.printf( "%s @%-8x %3d,%2d",
view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName(),
System.identityHashCode( view ),
(int) view.getPreferredSpan( View.X_AXIS ),
(int) view.getPreferredSpan( View.Y_AXIS ) );
AttributeSet attrs = view.getAttributes();
if( attrs != null ) {
Object fontSize = attrs.getAttribute( CSS.Attribute.FONT_SIZE );
System.out.printf( " %-8s", fontSize );
}
if( view instanceof javax.swing.text.GlyphView ) {
javax.swing.text.GlyphView gview = ((javax.swing.text.GlyphView)view);
java.awt.Font font = gview.getFont();
System.out.printf( " %3d-%-3d %s %2d (@%x) #%06x '%s'",
gview.getStartOffset(), gview.getEndOffset() - 1,
font.getName(), font.getSize(), System.identityHashCode( font ),
gview.getForeground().getRGB() & 0xffffff,
gview.getText( gview.getStartOffset(), gview.getEndOffset() ) );
}
System.out.println();
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ ) {
View child = view.getView( i );
dumpViews( child, indent + 1 );
}
}
debug*/
}

View File

@@ -179,6 +179,12 @@ public class FlatInternalFrameUI
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
public void update( Graphics g, JComponent c ) {
// The internal frame actually should be opaque and fill its background,
@@ -253,6 +259,23 @@ public class FlatInternalFrameUI
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
public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {

View File

@@ -22,10 +22,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
@@ -63,6 +60,9 @@ public class FlatLabelUI
{
@Styleable protected Color disabledForeground;
// only used via styling (not in UI defaults)
/** @since 3.5 */ @Styleable protected int arc = -1;
private final boolean shared;
private boolean defaults_initialized = false;
private Map<String, Object> oldStyleValues;
@@ -109,16 +109,13 @@ public class FlatLabelUI
super.installComponents( c );
// update HTML renderer if necessary
updateHTMLRenderer( c, c.getText(), false );
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
String name = e.getPropertyName();
if( name == "text" || name == "font" || name == "foreground" ) {
JLabel label = (JLabel) e.getSource();
updateHTMLRenderer( label, label.getText(), true );
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
JLabel label = (JLabel) e.getSource();
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
// unshare component UI if necessary
@@ -127,9 +124,11 @@ public class FlatLabelUI
} else
installStyle( label );
label.revalidate();
label.repaint();
} else
super.propertyChange( e );
HiDPIUtils.repaint( label );
}
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
/** @since 2 */
@@ -158,83 +157,16 @@ public class FlatLabelUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
}
/**
* Checks whether text contains HTML tags that use "absolute-size" keywords
* (e.g. "x-large") for font-size in default style sheet
* (see javax/swing/text/html/default.css).
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
* re-calculates font sizes based on current component font size.
*/
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
if( BasicHTML.isHTMLString( text ) &&
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
needsFontBaseSize( text ) )
{
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
String lowerText = text.toLowerCase();
int headIndex;
int styleIndex;
int insertIndex;
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
// there is a <head> tag --> insert after <head> tag
insertIndex = headIndex + "<head>".length();
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
// there is a <style> tag --> insert before <style> tag
insertIndex = styleIndex;
} else {
// no <head> or <style> tag --> insert <head> tag after <html> tag
style = "<head>" + style + "</head>";
insertIndex = "<html>".length();
}
text = text.substring( 0, insertIndex )
+ style
+ text.substring( insertIndex );
} else if( !always )
return; // not necessary to invoke BasicHTML.updateRenderer()
BasicHTML.updateRenderer( c, text );
/** @since 2.5 */
@Override
public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
private static Set<String> tagsUseFontSizeSet;
private static boolean needsFontBaseSize( String text ) {
if( tagsUseFontSizeSet == null ) {
// tags that use font-size in javax/swing/text/html/default.css
tagsUseFontSizeSet = new HashSet<>( Arrays.asList(
"h1", "h2", "h3", "h4", "h5", "h6", "code", "kbd", "big", "small", "samp" ) );
}
// search for tags in HTML text
int textLength = text.length();
for( int i = 6; i < textLength - 1; i++ ) {
if( text.charAt( i ) == '<' ) {
switch( text.charAt( i + 1 ) ) {
// first letters of tags in tagsUseFontSizeSet
case 'b': case 'B':
case 'c': case 'C':
case 'h': case 'H':
case 'k': case 'K':
case 's': case 'S':
int tagBegin = i + 1;
for( i += 2; i < textLength; i++ ) {
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
String tag = text.substring( tagBegin, i ).toLowerCase();
if( tagsUseFontSizeSet.contains( tag ) )
return true;
break;
}
}
break;
}
}
}
return false;
@Override
public void update( Graphics g, JComponent c ) {
FlatPanelUI.fillRoundedBackground( g, c, arc );
paint( g, c );
}
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {

View File

@@ -22,13 +22,21 @@ import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.plaf.ComponentUI;
/**
* Line border for various components.
*
* <p>
* Paints a scaled (usually 1px thick) line around the component.
* The line thickness is not added to the border insets.
* The insets should be at least have line thickness (usually 1,1,1,1).
* <p>
* For {@link javax.swing.JPanel} and {@link javax.swing.JLabel}, this border
* can be used paint rounded background (if line color is {@code null}) or
* paint rounded line border with rounded background.
*
* @author Karl Tauber
*/
@@ -40,7 +48,7 @@ public class FlatLineBorder
/** @since 2 */ private final int arc;
public FlatLineBorder( Insets insets, Color lineColor ) {
this( insets, lineColor, 1f, 0 );
this( insets, lineColor, 1f, -1 );
}
/** @since 2 */
@@ -51,26 +59,62 @@ public class FlatLineBorder
this.arc = arc;
}
/** @since 3.5 */
public FlatLineBorder( Insets insets, int arc ) {
this( insets, null, 0, arc );
}
public Color getLineColor() {
return lineColor;
}
/**
* Returns the (unscaled) line thickness used to paint the border.
* The line thickness does not affect the border insets.
*/
public float getLineThickness() {
return lineThickness;
}
/** @since 2 */
/**
* Returns the (unscaled) arc diameter of the border corners.
*
* @since 2
*/
public int getArc() {
return arc;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
return;
Color lineColor = getLineColor();
float lineThickness = getLineThickness();
if( lineColor == null || lineThickness <= 0 )
return;
int arc = getArc();
if( arc < 0 ) {
// get arc from label or panel
ComponentUI ui = (c instanceof JLabel)
? ((JLabel)c).getUI()
: (c instanceof JPanel ? ((JPanel)c).getUI() : null);
if( ui instanceof FlatLabelUI )
arc = ((FlatLabelUI)ui).arc;
else if( ui instanceof FlatPanelUI )
arc = ((FlatPanelUI)ui).arc;
if( arc < 0 )
arc = 0;
}
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
0, 0, 0, scale( lineThickness ), scale( arc ), null, lineColor, null );
} finally {
g2.dispose();
}

View File

@@ -17,20 +17,35 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.DefaultListCellRenderer;
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.event.ListSelectionListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.basic.BasicListUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
@@ -42,6 +57,7 @@ import com.formdev.flatlaf.util.LoggingFacade;
* @uiDefault List.foreground Color
* @uiDefault List.selectionBackground Color
* @uiDefault List.selectionForeground Color
* @uiDefault List.alternateRowColor Color
* @uiDefault List.dropLineColor Color
* @uiDefault List.border Border
* @uiDefault List.cellRenderer ListCellRenderer
@@ -59,6 +75,8 @@ import com.formdev.flatlaf.util.LoggingFacade;
*
* @uiDefault List.selectionInactiveBackground Color
* @uiDefault List.selectionInactiveForeground Color
* @uiDefault List.selectionInsets Insets
* @uiDefault List.selectionArc int
*
* <!-- FlatListCellBorder -->
*
@@ -76,6 +94,9 @@ public class FlatListUI
@Styleable protected Color selectionForeground;
@Styleable protected Color selectionInactiveBackground;
@Styleable protected Color selectionInactiveForeground;
/** @since 3.6 */ @Styleable protected Color alternateRowColor;
/** @since 3 */ @Styleable protected Insets selectionInsets;
/** @since 3 */ @Styleable protected int selectionArc;
// for FlatListCellBorder
/** @since 2 */ @Styleable protected Insets cellMargins;
@@ -90,6 +111,13 @@ public class FlatListUI
@Override
public void installUI( JComponent c ) {
if( FlatUIUtils.needsLightAWTPeer( c ) )
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
else
installUIImpl( c );
}
private void installUIImpl( JComponent c ) {
super.installUI( c );
installStyle();
@@ -103,6 +131,9 @@ public class FlatListUI
selectionForeground = UIManager.getColor( "List.selectionForeground" );
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
alternateRowColor = UIManager.getColor( "List.alternateRowColor" );
selectionInsets = UIManager.getInsets( "List.selectionInsets" );
selectionArc = UIManager.getInt( "List.selectionArc" );
toggleSelectionColors();
}
@@ -115,6 +146,7 @@ public class FlatListUI
selectionForeground = null;
selectionInactiveBackground = null;
selectionInactiveForeground = null;
alternateRowColor = null;
oldStyleValues = null;
}
@@ -155,12 +187,35 @@ public class FlatListUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
list.revalidate();
list.repaint();
HiDPIUtils.repaint( list );
break;
}
};
}
@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 ) );
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
}
}
};
}
/** @since 2 */
protected void installStyle() {
try {
@@ -209,6 +264,12 @@ public class FlatListUI
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.
*
@@ -234,4 +295,184 @@ public class FlatListUI
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 );
// paint alternating rows
if( alternateRowColor != null && row % 2 != 0 &&
!"ComboBox.list".equals( list.getName() ) ) // combobox does not support alternate row color
{
g.setColor( alternateRowColor );
float arc = UIScale.scale( selectionArc / 2f );
FlatUIUtils.paintSelection( (Graphics2D) g, rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height,
UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 );
}
// get renderer component
@SuppressWarnings( "unchecked" )
Component rendererComponent = cellRenderer.getListCellRendererComponent( list,
dataModel.getElementAt( row ), row, isSelected,
FlatUIUtils.isPermanentFocusOwner( list ) && (row == leadIndex) );
// use smaller cell width if list is used in JFileChooser
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 && !FlatUIUtils.isInsetsEmpty( selectionInsets ))) )
{
// 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 );
}
/**
* Paints (rounded) cell selection.
* Supports {@link #selectionArc} and {@link #selectionInsets}.
* <p>
* <b>Note:</b> This method is only invoked if either selection arc
* is greater than zero or if selection insets are not empty.
*
* @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 fewer 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
* to support {@link #selectionArc} and {@link #selectionInsets}.
*
* @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 );
}
/** @since 2.5 */
@Override
public Object getStyleableValue( String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( !showBottomSeparator( c ) )

View File

@@ -18,15 +18,17 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@@ -36,15 +38,16 @@ import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -70,7 +73,12 @@ public class FlatMenuBarUI
/** @since 2 */ @Styleable protected Insets itemMargins;
// 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.5 */ @Styleable protected Color selectionBackground;
/** @since 2.5 */ @Styleable protected Color selectionForeground;
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
@@ -100,6 +108,10 @@ public class FlatMenuBarUI
super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
}
@Override
@@ -130,12 +142,10 @@ public class FlatMenuBarUI
protected void installKeyboardActions() {
super.installKeyboardActions();
// get shared action map, used for all menu bars
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
if( map == null ) {
map = new ActionMapUIResource();
SwingUtilities.replaceUIActionMap( menuBar, map );
}
map.put( "takeFocus", new TakeFocus() );
if( map != null && !(map.get( "takeFocus" ) instanceof TakeFocusAction) )
map.put( "takeFocus", new TakeFocusAction( "takeFocus" ) );
}
/** @since 2 */
@@ -165,6 +175,12 @@ public class FlatMenuBarUI
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
public void update( Graphics g, JComponent c ) {
// paint background
@@ -221,16 +237,144 @@ public class FlatMenuBarUI
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
}
//---- class TakeFocus ----------------------------------------------------
//---- 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 TakeFocusAction ----------------------------------------------
/**
* Activates the menu bar and shows mnemonics.
* On Windows, the popup of the first menu is not shown.
* On other platforms, the popup of the first menu is shown.
*/
private static class TakeFocus
extends AbstractAction
private static class TakeFocusAction
extends FlatUIAction
{
TakeFocusAction( String name ) {
super( name );
}
@Override
public void actionPerformed( ActionEvent e ) {
JMenuBar menuBar = (JMenuBar) e.getSource();

View File

@@ -25,7 +25,9 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
@@ -35,6 +37,7 @@ import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
@@ -63,6 +66,8 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuItem.acceleratorArrowGap int
* @uiDefault MenuItem.checkBackground Color
* @uiDefault MenuItem.checkMargins Insets
* @uiDefault MenuItem.selectionInsets Insets
* @uiDefault MenuItem.selectionArc int
* @uiDefault MenuItem.selectionType String null (default) or underline
* @uiDefault MenuItem.underlineSelectionBackground Color
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
@@ -91,12 +96,16 @@ public class FlatMenuItemRenderer
@Styleable protected Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
@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 underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
@Styleable protected Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
private boolean iconsShared = true;
private final Font menuFont = UIManager.getFont( "Menu.font" );
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
@@ -169,6 +178,19 @@ public class FlatMenuItemRenderer
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() {
int width = 0;
int height = 0;
@@ -180,7 +202,8 @@ public class FlatMenuItemRenderer
// layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
@@ -202,7 +225,7 @@ public class FlatMenuItemRenderer
}
// arrow size
if( !isTopLevelMenu && arrowIcon != null ) {
if( arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )) ) {
// gap between text and arrow
if( accelText == null )
width += scale( textNoAcceleratorGap );
@@ -234,7 +257,8 @@ public class FlatMenuItemRenderer
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
// layout arrow
if( !isTopLevelMenu && arrowIcon != null ) {
boolean showArrowIcon = (arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )));
if( showArrowIcon ) {
arrowRect.width = arrowIcon.getIconWidth();
arrowRect.height = arrowIcon.getIconHeight();
} else
@@ -268,7 +292,7 @@ public class FlatMenuItemRenderer
int accelArrowWidth = accelRect.width + arrowRect.width;
if( accelText != null )
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
if( !isTopLevelMenu && arrowIcon != null ) {
if( showArrowIcon ) {
if( accelText == null )
accelArrowWidth += scale( textNoAcceleratorGap );
accelArrowWidth += scale( acceleratorArrowGap );
@@ -282,7 +306,8 @@ public class FlatMenuItemRenderer
// layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
@@ -321,32 +346,52 @@ public class FlatMenuItemRenderer
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
debug*/
boolean armedOrSelected = isArmedOrSelected( menuItem );
boolean underlineSelection = isUnderlineSelection();
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) )
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
paintBackground( g );
if( armedOrSelected ) {
if( underlineSelection )
paintUnderlineSelection( g, underlineSelectionBackground, underlineSelectionColor, underlineSelectionHeight );
else
paintSelection( g, selectionBackground, selectionInsets, selectionArc );
}
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) )
if( arrowIcon != null && (!isTopLevelMenu( menuItem ) || isInVerticalMenuBar( menuItem )) )
paintArrowIcon( g, arrowRect, arrowIcon );
}
protected void paintBackground( Graphics g, Color selectionBackground ) {
boolean armedOrSelected = isArmedOrSelected( menuItem );
if( menuItem.isOpaque() || armedOrSelected ) {
// paint background
g.setColor( armedOrSelected
? deriveBackground( selectionBackground )
: menuItem.getBackground() );
/** @since 3 */
protected void paintBackground( Graphics g ) {
if( menuItem.isOpaque() ) {
g.setColor( menuItem.getBackground() );
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 height = menuItem.getHeight();
// paint background
g.setColor( deriveBackground( underlineSelectionBackground ) );
g.fillRect( 0, 0, width, height );
// paint underline
int underlineHeight = scale( underlineSelectionHeight );
g.setColor( underlineSelectionColor );
if( isTopLevelMenu( menuItem ) ) {
@@ -392,9 +437,10 @@ debug*/
}
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 );
}
@@ -414,10 +460,11 @@ debug*/
return;
// center because the real icon may be smaller than dimension in iconRect
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
// paint
icon.paintIcon( menuItem, g, iconRect.x, y );
icon.paintIcon( menuItem, g, x, y );
}
protected static void paintText( Graphics g, JMenuItem menuItem,
@@ -446,6 +493,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
Rectangle textRect, View htmlView, Color selectionForeground )
{
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
// the width of the HTML view may be initially too small (because component
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
//
// If using a too small width for htmlView.paint(), the view would rearrange
// its children and wrap them to two lines. To avoid this, use view preferred X span
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
//
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
textRect = new Rectangle( textRect );
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
@@ -464,10 +524,28 @@ debug*/
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
}
/** @since 3.5 */
public static boolean isInVerticalMenuBar( JMenuItem menuItem ) {
if( !(menuItem instanceof JMenu) || !(menuItem.getParent() instanceof JMenuBar) )
return false;
LayoutManager layout = menuItem.getParent().getLayout();
return layout instanceof GridLayout && ((GridLayout)layout).getRows() != 1;
}
protected boolean isUnderlineSelection() {
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() {
Icon icon = menuItem.getIcon();

View File

@@ -16,16 +16,19 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
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.UnknownStyleException;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -58,9 +61,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
*
* @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
extends BasicMenuItemUI
implements StyleableUI
implements StyleableUI, StyleableLookupProvider
{
private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
@@ -94,13 +103,23 @@ public class FlatMenuItemUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */
@@ -119,42 +138,54 @@ public class FlatMenuItemUI
/** @since 2 */
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 {
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 );
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
}
/** @since 2 */
@Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
return getStyleableInfos( renderer );
return getStyleableInfos( this, renderer );
}
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) {
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
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 );
static Map<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
infos.putAll( renderer.getStyleableInfos() );
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
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();

View File

@@ -17,11 +17,16 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.function.Function;
import javax.swing.ButtonModel;
@@ -30,14 +35,19 @@ import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRootPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.MenuBarUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
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.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -71,16 +81,27 @@ import com.formdev.flatlaf.util.LoggingFacade;
*
* <!-- FlatMenuRenderer -->
*
* @uiDefault MenuBar.selectionInsets Insets
* @uiDefault MenuBar.selectionEmbeddedInsets Insets
* @uiDefault MenuBar.selectionArc int
* @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.underlineSelectionColor Color
* @uiDefault MenuBar.underlineSelectionHeight int
*
* @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
extends BasicMenuUI
implements StyleableUI
implements StyleableUI, StyleableLookupProvider
{
private FlatMenuItemRenderer renderer;
private Map<String, Object> oldStyleValues;
@@ -116,6 +137,14 @@ public class FlatMenuUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@@ -139,7 +168,7 @@ public class FlatMenuUI
JMenu menu = (JMenu) e.getSource();
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
menu.getModel().setRollover( rollover );
menu.repaint();
HiDPIUtils.repaint( menu );
}
}
};
@@ -147,7 +176,9 @@ public class FlatMenuUI
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */
@@ -166,29 +197,27 @@ public class FlatMenuUI
/** @since 2 */
protected Object applyStyleProperty( String key, Object value ) {
try {
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 );
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
}
/** @since 2 */
@Override
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
@@ -215,10 +244,15 @@ public class FlatMenuUI
protected class FlatMenuRenderer
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 menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
/** @since 2.5 */ protected Color menuBarSelectionBackground = UIManager.getColor( "MenuBar.selectionBackground" );
/** @since 2.5 */ protected Color menuBarSelectionForeground = UIManager.getColor( "MenuBar.selectionForeground" );
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,
Font acceleratorFont, String acceleratorDelimiter )
@@ -226,41 +260,90 @@ public class FlatMenuUI
super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
/** @since 3 */
@Override
protected void paintBackground( Graphics g, Color selectionBackground ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
if( isUnderlineSelection() )
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
protected void paintBackground( Graphics g ) {
super.paintBackground( g );
ButtonModel model = menuItem.getModel();
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
g.setColor( deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) ) );
if( ((JMenu)menuItem).isTopLevelMenu() && isHover() ) {
// paint hover background
Color color = deriveBackground( getStyleFromMenuBarUI( ui -> ui.hoverBackground, hoverBackground ) );
if( isUnderlineSelection() ) {
g.setColor( color );
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 );
Container menuBar = 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
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
underlineSelectionColor = getStyleFromMenuBarUI( ui -> ui.underlineSelectionColor, menuBarUnderlineSelectionColor );
underlineSelectionHeight = getStyleFromMenuBarUI( ui -> (ui.underlineSelectionHeight != -1)
? ui.underlineSelectionHeight : null, menuBarUnderlineSelectionHeight );
}
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
if( ((JMenu)menuItem).isTopLevelMenu() && !isUnderlineSelection() )
selectionForeground = getStyleFromMenuBarUI( ui -> ui.selectionForeground, menuBarSelectionForeground, selectionForeground );
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 ) {
MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
if( !(ui instanceof FlatMenuBarUI) )
return defaultValue;
T value = f.apply( (FlatMenuBarUI) ui );
return (value != null) ? value : defaultValue;
Container menuItemParent = menuItem.getParent();
if( menuItemParent instanceof JMenuBar ) {
MenuBarUI ui = ((JMenuBar) menuItemParent).getUI();
if( ui instanceof FlatMenuBarUI ) {
T value = f.apply( (FlatMenuBarUI) ui );
if( value != null ) {
return value;
}
}
}
return defaultValue;
}
}
}

View File

@@ -17,9 +17,12 @@
package com.formdev.flatlaf.ui;
import java.io.File;
import java.net.URL;
import java.security.CodeSource;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -31,69 +34,255 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
class FlatNativeLibrary
{
private static boolean initialized;
private static NativeLibrary nativeLibrary;
private native static int getApiVersion();
/**
* Loads native library (if available) and returns whether loaded successfully.
* Returns {@code false} if no native library is available.
*/
static synchronized boolean isLoaded() {
initialize();
static synchronized boolean isLoaded( int apiVersion ) {
initialize( apiVersion );
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
}
private static void initialize() {
if( nativeLibrary != null )
private static void initialize( int apiVersion ) {
if( initialized )
return;
initialized = true;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_LIBRARY, true ) )
return;
String libraryName;
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
// Windows: requires Windows 10 (x86 or x86_64)
String classifier;
String ext;
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64 || SystemInfo.isAARCH64) ) {
// Windows: requires Windows 10/11 (x86, x86_64 or aarch64)
libraryName = "flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
if( SystemInfo.isAARCH64 )
classifier = "windows-arm64";
else if( SystemInfo.isX86_64 )
classifier = "windows-x86_64";
else
classifier = "windows-x86";
// load jawt native library
ext = "dll";
// Do not load jawt.dll (part of JRE) here explicitly because
// the FlatLaf native library flatlaf.dll may be loaded very early on Windows
// (e.g. from class com.formdev.flatlaf.util.SystemInfo) and before AWT is
// initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll.
// In Java 8, loading jawt.dll before AWT is initialized may load
// a wrong version of awt.dll if a newer Java version (e.g. 19)
// is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll
// are loaded at same time and calling JAWT_GetAWT() crashes the application.
//
// To avoid this, flatlaf.dll is not linked to jawt.dll,
// which avoids loading jawt.dll when flatlaf.dll is loaded.
// Instead, flatlaf.dll dynamically loads jawt.dll when first used,
// which is guaranteed after AWT initialization.
} else if( SystemInfo.isMacOS_10_14_Mojave_orLater && (SystemInfo.isAARCH64 || SystemInfo.isX86_64) ) {
// macOS: requires macOS 10.14 or later (arm64 or x86_64)
classifier = SystemInfo.isAARCH64 ? "macos-arm64" : "macos-x86_64";
ext = "dylib";
} else if( SystemInfo.isLinux && (SystemInfo.isX86_64 || SystemInfo.isAARCH64)) {
// Linux: requires x86_64 or aarch64
classifier = SystemInfo.isAARCH64 ? "linux-arm64" : "linux-x86_64";
ext = "so";
// 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 );
}
NativeLibrary nativeLibrary = createNativeLibrary( classifier, ext );
if( !nativeLibrary.isLoaded() )
return;
private static NativeLibrary createNativeLibrary( String libraryName ) {
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
if( libraryPath != null ) {
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
else
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
// check API version (and check whether library works)
try {
int actualApiVersion = getApiVersion();
if( actualApiVersion != apiVersion ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Wrong API version in native library (expected "
+ apiVersion + ", actual " + actualApiVersion + "). Ignoring native library.", null );
return;
}
} catch( Throwable ex ) {
// could be a UnsatisfiedLinkError in case that loading native library
// from temp directory was blocked by some OS security mechanism
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to get API version of native library. Ignoring native library.", ex );
return;
}
FlatNativeLibrary.nativeLibrary = nativeLibrary;
}
private static NativeLibrary createNativeLibrary( String classifier, String ext ) {
String libraryName = "flatlaf-" + classifier;
// load from "java.library.path" or from path specified in system property "flatlaf.nativeLibraryPath"
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( "FlatLaf: Native library '" + System.mapLibraryName( libraryName )
+ "' not found in java.library.path '" + System.getProperty( "java.library.path" )
+ "'. Using extracted native library instead.", null );
} else {
// try standard library naming scheme
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
// try Maven naming scheme
// (see https://www.formdev.com/flatlaf/native-libraries/)
String libraryName2 = null;
File jarFile = getJarFile();
if( jarFile != null ) {
libraryName2 = buildLibraryName( jarFile, classifier, ext );
File libraryFile2 = new File( libraryPath, libraryName2 );
if( libraryFile2.exists() )
return new NativeLibrary( libraryFile2, true );
}
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Native library '"
+ libraryFile.getName()
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
+ "' not found in '" + libraryFile.getParentFile().getAbsolutePath()
+ "'. Using extracted native library instead.", null );
}
}
// load from beside the FlatLaf jar
// e.g. for flatlaf-3.1.jar, load flatlaf-3.1-windows-x86_64.dll (from same directory)
File libraryFile = findLibraryBesideJar( classifier, ext );
if( libraryFile != null )
return new NativeLibrary( libraryFile, true );
// load from FlatLaf jar (extract native library to temp folder)
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
}
private static void loadJAWT() {
if( SystemInfo.isJava_9_orLater )
return;
/**
* Search for a native library beside the jar that contains this class
* (usually the FlatLaf jar).
* The native library must be in the same directory (or in "../bin" if jar is in "lib")
* as the jar and have the same basename as the jar.
* If FlatLaf jar is repackaged into fat/uber application jar, "-flatlaf" is appended to jar basename.
* The classifier and the extension are appended to the jar basename.
*
* E.g.
* flatlaf-3.1.jar
* flatlaf-3.1-windows-x86_64.dll
* flatlaf-3.1-linux-x86_64.so
*/
private static File findLibraryBesideJar( String classifier, String ext ) {
// get location of FlatLaf jar (or fat/uber application jar)
File jarFile = getJarFile();
if( jarFile == null )
return null;
// 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.
// build library file
String libraryName = buildLibraryName( jarFile, classifier, ext );
File jarDir = jarFile.getParentFile();
// check whether native library exists in same directory as jar
File libraryFile = new File( jarDir, libraryName );
if( libraryFile.isFile() )
return libraryFile;
// if jar is in "lib" directory, then also check whether native library exists
// in "../bin" directory
if( jarDir.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( jarDir.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
// special case: support Gradle cache when running in development environment
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-1>/flatlaf-<version>.jar
// <user-home>/.gradle/caches/modules-2/files-2.1/com.formdev/flatlaf/<version>/<hash-2>/flatlaf-<version>-windows-x86_64.dll
String path = jarDir.getAbsolutePath().replace( '\\', '/' );
if( path.contains( "/.gradle/caches/" ) ) {
File versionDir = jarDir.getParentFile();
if( libraryName.contains( versionDir.getName() ) ) {
File[] dirs = versionDir.listFiles();
if( dirs != null ) {
for( File dir : dirs ) {
libraryFile = new File( dir, libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
}
}
}
// native library not found
return null;
}
private static File getJarFile() {
try {
// get location of FlatLaf jar
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
URL jarUrl = (codeSource != null) ? codeSource.getLocation() : null;
if( jarUrl == null )
return null;
// if url is not a file, then we're running in a special environment (e.g. WebStart)
if( !"file".equals( jarUrl.getProtocol() ) )
return null;
File jarFile = new File( jarUrl.toURI() );
// if jarFile is a directory, then we're in a development environment
if( !jarFile.isFile() )
return null;
return jarFile;
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
return null;
}
}
private static String buildLibraryName( File jarFile, String classifier, String ext ) {
String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
// remove classifier "no-natives" (if used)
jarBasename = StringUtils.removeTrailing( jarBasename, "-no-natives" );
return jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;
}
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( null, ex );
LoggingFacade.INSTANCE.logSevere( message, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.GraphicsConfiguration;
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;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Native methods for Linux.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* @author Karl Tauber
* @since 2.5
*/
class FlatNativeLinuxLibrary
{
private static int API_VERSION_LINUX = 3001;
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
static boolean isLoaded() {
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded( API_VERSION_LINUX );
}
// 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 ) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if( gc == null )
return pt;
AffineTransform transform = gc.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

@@ -0,0 +1,71 @@
/*
* Copyright 2023 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.Rectangle;
import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Native methods for macOS.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* <h2>Methods that use windows as parameter</h2>
*
* For all methods that accept a {@link java.awt.Window} as parameter,
* the underlying macOS window must be already created,
* otherwise the method fails. You can use following to ensure this:
* <pre>{@code
* if( !window.isDisplayable() )
* window.addNotify();
* }</pre>
* or invoke the method after packing the window. E.g.
* <pre>{@code
* window.pack();
* }</pre>
*
* @author Karl Tauber
* @since 3.3
*/
public class FlatNativeMacLibrary
{
private static int API_VERSION_MACOS = 2001;
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded( API_VERSION_MACOS );
}
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
/** @since 3.4 */
public static final int
BUTTONS_SPACING_DEFAULT = 0,
BUTTONS_SPACING_MEDIUM = 1,
BUTTONS_SPACING_LARGE = 2;
/** @since 3.4 */ public native static boolean setWindowButtonsSpacing( Window window, int buttonsSpacing );
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );
}

View File

@@ -17,20 +17,27 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.function.Predicate;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -42,37 +49,27 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatNativeWindowBorder
{
// can use window decorations if:
// - on Windows 10
// - on Windows 10 or later
// - not if system property "sun.java2d.opengl" is true on Windows 10
// - not when running in JetBrains Projector, Webswing or WinPE
// - not disabled via system property
private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater &&
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
!SystemInfo.isProjector &&
!SystemInfo.isWebswing &&
!SystemInfo.isWinPE &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
private static final boolean canUseJBRCustomDecorations =
canUseWindowDecorations &&
SystemInfo.isJetBrainsJVM_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
private static Boolean supported;
private static Provider nativeProvider;
public static boolean isSupported() {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.isSupported();
initialize();
return supported;
}
static Object install( JRootPane rootPane ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.install( rootPane );
if( !isSupported() )
return null;
@@ -161,11 +158,6 @@ public class FlatNativeWindowBorder
}
static void uninstall( JRootPane rootPane, Object data ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.uninstall( rootPane, data );
return;
}
if( !isSupported() )
return;
@@ -213,9 +205,6 @@ public class FlatNativeWindowBorder
}
public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
if( !isSupported() )
return false;
@@ -223,11 +212,6 @@ public class FlatNativeWindowBorder
}
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
return;
}
if( !isSupported() )
return;
@@ -235,23 +219,18 @@ public class FlatNativeWindowBorder
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Predicate<Point> captionHitTestCallback, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return;
}
if( !isSupported() )
return;
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
}
static boolean showWindow( Window window, int cmd ) {
if( canUseJBRCustomDecorations || !isSupported() )
if( !isSupported() )
return false;
return nativeProvider.showWindow( window, cmd );
@@ -292,7 +271,7 @@ public class FlatNativeWindowBorder
{
boolean hasCustomDecoration( Window window );
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds );
@@ -318,20 +297,36 @@ public class FlatNativeWindowBorder
* No longer needed since Windows 11.
*/
static class WindowTopBorder
extends JBRCustomDecorations.JBRWindowTopBorder
extends BorderUIResource.EmptyBorderUIResource
{
private static WindowTopBorder instance;
static JBRWindowTopBorder getInstance() {
if( canUseJBRCustomDecorations )
return JBRWindowTopBorder.getInstance();
private final Color activeLightColor = new Color( 0x707070 );
private final Color activeDarkColor = new Color( 0x2D2E2F );
private final Color inactiveLightColor = new Color( 0xaaaaaa );
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor;
static WindowTopBorder getInstance() {
if( instance == null )
instance = new WindowTopBorder();
return instance;
}
@Override
WindowTopBorder() {
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() {
nativeProvider.addChangeListener( e -> {
update();
@@ -344,19 +339,69 @@ public class FlatNativeWindowBorder
} );
}
@Override
boolean isColorizationColorAffectsBorders() {
return nativeProvider.isColorizationColorAffectsBorders();
}
@Override
Color getColorizationColor() {
return nativeProvider.getColorizationColor();
}
@Override
int getColorizationColorBalance() {
return nativeProvider.getColorizationColorBalance();
}
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.fillRect( x, y, width, 1 );
}
void repaintBorder( Component c ) {
c.repaint( 0, 0, c.getWidth(), 1 );
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.Color;
import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Native methods for Windows.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* @author Karl Tauber
* @since 3.1
*/
public class FlatNativeWindowsLibrary
{
private static int API_VERSION_WINDOWS = 1001;
private static long osBuildNumber = Long.MIN_VALUE;
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded( API_VERSION_WINDOWS );
}
/**
* Gets the Windows operating system build number.
* <p>
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
*/
public static long getOSBuildNumber() {
if( osBuildNumber == Long.MIN_VALUE )
osBuildNumber = getOSBuildNumberImpl();
return osBuildNumber;
}
/**
* Invokes Win32 API method {@code GetVersionEx()} and returns {@code OSVERSIONINFO.dwBuildNumber}.
* See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
*/
private native static long getOSBuildNumberImpl();
/**
* Gets the Windows window handle (HWND) for the given Swing window.
* <p>
* Note that the underlying Windows window must be already created,
* otherwise this method returns zero. Use following to ensure this:
* <pre>{@code
* if( !window.isDisplayable() )
* window.addNotify();
* }</pre>
* or invoke this method after packing the window. E.g.
* <pre>{@code
* window.pack();
* long hwnd = getHWND( window );
* }</pre>
*/
public native static long getHWND( Window window );
/**
* DWM_WINDOW_CORNER_PREFERENCE
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
*/
public static final int
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3;
/**
* Sets the rounded corner preference for the window.
* Allowed values are {@link #DWMWCP_DEFAULT}, {@link #DWMWCP_DONOTROUND},
* {@link #DWMWCP_ROUND} and {@link #DWMWCP_ROUNDSMALL}.
* <p>
* Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_WINDOW_CORNER_PREFERENCE)}.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
* <p>
* Supported since Windows 11 Build 22000.
*/
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
/**
* DWMWINDOWATTRIBUTE
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
*
* @since 3.3
*/
public static final int
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
DWMWA_BORDER_COLOR = 34,
DWMWA_CAPTION_COLOR = 35,
DWMWA_TEXT_COLOR = 36;
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code BOOL} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeBOOL( long hwnd, int attribute, boolean value );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code DWORD} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeDWORD( long hwnd, int attribute, int value );
/** @since 3.3 */
public static final int
// use this constant to reset any window part colors to the system default behavior
DWMWA_COLOR_DEFAULT = 0xFFFFFFFF,
// use this constant to specify that a window part should not be rendered
DWMWA_COLOR_NONE = 0xFFFFFFFE;
/** @since 3.3 */
public static final Color COLOR_NONE = new Color( 0, true );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code COLORREF} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
* <p>
* Supported since Windows 11 Build 22000.
*
* @since 3.3
*/
public static boolean dwmSetWindowAttributeCOLORREF( long hwnd, int attribute, Color color ) {
// convert color to Windows RGB value
int rgb = (color == COLOR_NONE)
? DWMWA_COLOR_NONE
: (color != null
? (color.getRed() | (color.getGreen() << 8) | (color.getBlue() << 16))
: DWMWA_COLOR_DEFAULT);
// DwmSetWindowAttribute() expects COLORREF as attribute value, which is defined as DWORD
return dwmSetWindowAttributeDWORD( hwnd, attribute, rgb );
}
}

View File

@@ -30,9 +30,7 @@ import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
@@ -115,13 +113,6 @@ public class FlatOptionPaneUI
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
}
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -155,6 +146,13 @@ public class FlatOptionPaneUI
protected Container createMessageArea() {
Container messageArea = super.createMessageArea();
// use non-UIResource OptionPane.messageAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( messageArea );
// make known sub-panels non-opaque for OptionPane.background
updateKnownChildPanels( messageArea );
// set icon-message gap
if( iconMessageGap > 0 ) {
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
@@ -169,6 +167,10 @@ public class FlatOptionPaneUI
protected Container createButtonArea() {
Container buttonArea = super.createButtonArea();
// use non-UIResource OptionPane.buttonAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( buttonArea );
// scale button padding and subtract focusWidth
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
@@ -218,22 +220,33 @@ public class FlatOptionPaneUI
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
}
private void updateChildPanels( Container c ) {
private void updateAreaPanel( Container area ) {
if( !(area instanceof JPanel) )
return;
// use non-UIResource border to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
JPanel panel = (JPanel) area;
panel.setBorder( FlatUIUtils.nonUIResource( panel.getBorder() ) );
panel.setOpaque( false );
}
private void updateKnownChildPanels( Container c ) {
for( Component child : c.getComponents() ) {
if( child.getClass() == JPanel.class ) {
JPanel panel = (JPanel)child;
// make sub-panel non-opaque for OptionPane.background
panel.setOpaque( false );
// use non-UIResource borders to avoid that they are replaced when switching LaF
Border border = panel.getBorder();
if( border instanceof UIResource )
panel.setBorder( FlatUIUtils.nonUIResource( border ) );
if( child instanceof JPanel && child.getName() != null ) {
switch( child.getName() ) {
case "OptionPane.realBody":
case "OptionPane.body":
case "OptionPane.separator":
case "OptionPane.break":
// make known sub-panels non-opaque for OptionPane.background
((JPanel)child).setOpaque( false );
break;
}
}
if( child instanceof Container )
updateChildPanels( (Container) child );
updateKnownChildPanels( (Container) child );
}
}

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