Compare commits

...

78 Commits
3.4.1 ... 3.5.4

Author SHA1 Message Date
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
1462636e97 release 3.5.3 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) 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)
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)
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) 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)
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) 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) 2024-11-10 13:28:02 +01:00
Karl Tauber
c29a276188 release 3.5.2 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) 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) 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 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) 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
124 changed files with 4686 additions and 772 deletions

View File

@@ -33,10 +33,11 @@ jobs:
- 11 # LTS
- 17 # LTS
- 21 # LTS
- 23 # latest
toolchain: [""]
include:
- java: 21
toolchain: 22 # latest
# include:
# - java: 21
# toolchain: 22 # latest
steps:
- uses: actions/checkout@v4

View File

@@ -1,6 +1,149 @@
FlatLaf Change Log
==================
## 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
@@ -10,7 +153,7 @@ FlatLaf Change Log
- 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)
(issue #809)
- Extras:
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
@@ -54,8 +197,8 @@ FlatLaf Change Log
- 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.
(issue 652)
- Table: Select all text in cell editor when starting editing using `F2` key on
Windows or Linux. (issue #652)
#### Fixed bugs
@@ -83,7 +226,7 @@ FlatLaf Change Log
#### Fixed bugs
- Button and ToggleButton: Selected buttons did not use explicitly set
foreground color. (issue 756)
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))
@@ -836,7 +979,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.

View File

@@ -33,14 +33,20 @@ FlatLaf can use 3rd party themes created for IntelliJ Platform (see
Sponsors
--------
### Current Sponsors
[![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>
[Become a Sponsor](https://www.formdev.com/flatlaf/sponsor/)
Demo
----

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 3.4
#Version 3.5.2
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -100,6 +100,7 @@ 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"
@@ -223,6 +224,7 @@ meth public static java.util.Map<java.lang.String,java.lang.Class<?>> getStyleab
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>)
@@ -296,6 +298,8 @@ fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flat
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"
@@ -629,16 +633,33 @@ 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 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
meth public abstract void paint(java.awt.Graphics2D,int,int,int,int,double)
@@ -1137,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

@@ -461,7 +461,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}
@@ -481,7 +481,7 @@ 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}
@@ -507,6 +507,8 @@ public interface FlatClientProperties
* 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}
*
@@ -537,7 +539,7 @@ public interface FlatClientProperties
* <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}
@@ -553,6 +555,8 @@ public interface FlatClientProperties
* 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}
*
@@ -567,6 +571,8 @@ public interface FlatClientProperties
* 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}
*
@@ -581,6 +587,8 @@ public interface FlatClientProperties
* 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}
*
@@ -595,6 +603,8 @@ public interface FlatClientProperties
* 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}
*
@@ -605,7 +615,7 @@ public interface FlatClientProperties
/**
* 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}
@@ -617,7 +627,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}
@@ -626,10 +636,24 @@ 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}
*

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;
@@ -119,6 +120,46 @@ public abstract class FlatLaf
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)}.

View File

@@ -23,13 +23,15 @@ 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.
@@ -71,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
@@ -116,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

@@ -82,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
@@ -99,7 +99,7 @@ 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)
@@ -120,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
@@ -135,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 on macOS 14.4+ where it is {@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>
@@ -204,6 +216,16 @@ public interface FlatSystemProperties
*/
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

@@ -111,7 +111,7 @@ class LinuxFontPolicy
if( logicalFamily != null )
family = logicalFamily;
return createFontEx( family, style, size, dsize );
return createFontEx( family, style, size );
}
/**
@@ -121,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;
@@ -135,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;
}
@@ -143,7 +143,7 @@ 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( Locale.ENGLISH );
@@ -155,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() )
@@ -257,7 +248,7 @@ class LinuxFontPolicy
if( size < 1 )
size = 1;
return createFont( family, style, size, dsize );
return FlatLaf.createCompositeFont( family, style, size );
}
@SuppressWarnings( "MixedMutabilityReturnType" ) // Error Prone

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
@@ -64,6 +65,7 @@ class SubMenuUsabilityHelper
// 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;
@@ -83,6 +85,9 @@ class SubMenuUsabilityHelper
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;
@@ -99,7 +104,7 @@ class SubMenuUsabilityHelper
@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
@@ -173,8 +178,29 @@ debug*/
targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to suppress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue();
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 ) )
@@ -247,8 +273,6 @@ debug*/
}
} );
timeoutTimer.setRepeats( false );
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
}
void uninstall() {

View File

@@ -638,14 +638,18 @@ class UIDefaultsLoader
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
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 ) ) : 1f;
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 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 );
};

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

@@ -60,23 +60,24 @@ public abstract class FlatWindowAbstractIcon
@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 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 );
}

View File

@@ -135,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();
}
@@ -277,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

@@ -29,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;
@@ -61,6 +62,7 @@ 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;
@@ -300,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:
@@ -308,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:
@@ -324,7 +330,7 @@ public class FlatButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}
@@ -583,9 +589,16 @@ public class FlatButtonUI
// paint text
if( clippedText != null && !clippedText.isEmpty() ) {
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null )
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
} else
paintText( g, b, textR, clippedText );
}
@@ -631,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;
@@ -906,7 +917,7 @@ public class FlatButtonUI
@Override
public void stateChanged( ChangeEvent e ) {
super.stateChanged( e );
HiDPIUtils.repaint( b );
// if button is in toolbar, repaint button groups
AbstractButton b = (AbstractButton) e.getSource();
@@ -918,5 +929,17 @@ public class FlatButtonUI
((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

@@ -23,6 +23,7 @@ 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;
@@ -102,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 */

View File

@@ -78,6 +78,7 @@ 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;
@@ -220,7 +221,7 @@ public class FlatComboBoxUI
private void repaintArrowButton() {
if( arrowButton != null && !comboBox.isEditable() )
arrowButton.repaint();
HiDPIUtils.repaint( arrowButton );
}
};
comboBox.addMouseListener( hoverListener );
@@ -351,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 );
}
};
}
@@ -386,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:
@@ -402,7 +403,7 @@ public class FlatComboBoxUI
case STYLE_CLASS:
installStyle();
comboBox.revalidate();
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
}
}
@@ -584,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 )
@@ -611,7 +612,7 @@ public class FlatComboBoxUI
}
// 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 );

View File

@@ -171,7 +171,7 @@ public class FlatEditorPaneUI
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
}

View File

@@ -24,6 +24,7 @@ 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;
@@ -32,6 +33,7 @@ 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;
@@ -46,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;
@@ -164,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 );
@@ -183,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 );
}
}
@@ -196,6 +203,7 @@ public class FlatFileChooserUI
if( shortcutsPanel != null ) {
fc.removePropertyChangeListener( shortcutsPanel );
shortcutsPanel = null;
shortcutsScrollPane = null;
}
}
@@ -324,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 ) );
@@ -401,7 +409,7 @@ public class FlatFileChooserUI
/** @since 2.3 */
public static class FlatShortcutsPanel
extends JToolBar
implements PropertyChangeListener
implements PropertyChangeListener, Scrollable
{
private final JFileChooser fc;
@@ -420,6 +428,7 @@ public class FlatFileChooserUI
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 );
@@ -461,7 +470,7 @@ 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( f );
@@ -487,8 +496,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 );
@@ -566,6 +577,8 @@ public class FlatFileChooserUI
buttonGroup.clearSelection();
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
@@ -574,6 +587,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

@@ -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

@@ -22,11 +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.Locale;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
@@ -64,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;
@@ -110,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
@@ -128,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 */
@@ -165,83 +163,10 @@ public class FlatLabelUI
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
/**
* 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( Locale.ENGLISH );
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 );
}
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( Locale.ENGLISH );
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

@@ -23,13 +23,20 @@ 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
*/
@@ -41,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 */
@@ -52,15 +59,28 @@ 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;
}
@@ -70,11 +90,31 @@ public class FlatLineBorder
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

@@ -43,6 +43,7 @@ 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;
@@ -182,7 +183,7 @@ public class FlatListUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
list.revalidate();
list.repaint();
HiDPIUtils.repaint( list );
break;
}
};
@@ -205,7 +206,7 @@ public class FlatListUI
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
if( r != null ) {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
}
}
};
@@ -324,8 +325,7 @@ public class FlatListUI
(rendererComponent instanceof DefaultListCellRenderer ||
rendererComponent instanceof BasicComboBoxRenderer) &&
(selectionArc > 0 ||
(selectionInsets != null &&
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 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.
@@ -374,7 +374,15 @@ public class FlatListUI
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
}
/** @since 3 */
/**
* 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 );
@@ -440,7 +448,8 @@ public class FlatListUI
* 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.
* This method is intended for use in custom cell renderers
* to support {@link #selectionArc} and {@link #selectionInsets}.
*
* @since 3
*/

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;
@@ -222,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 );
@@ -254,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
@@ -288,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 );
@@ -355,7 +359,7 @@ debug*/
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 );
}
@@ -520,6 +524,15 @@ 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" ) );
}

View File

@@ -103,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 */

View File

@@ -47,6 +47,7 @@ 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.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -136,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 );
}
@@ -159,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 );
}
}
};
@@ -167,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 */

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
@@ -96,7 +97,11 @@ class FlatNativeLinuxLibrary
}
private static Point scale( Window window, Point pt ) {
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
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 );

View File

@@ -25,11 +25,13 @@ import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -110,7 +112,7 @@ public class FlatPanelUI
} else
installStyle( c );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
@@ -160,32 +162,38 @@ public class FlatPanelUI
@Override
public void update( Graphics g, JComponent c ) {
// fill background
if( c.isOpaque() ) {
int width = c.getWidth();
int height = c.getHeight();
int arc = (this.arc >= 0)
? this.arc
: ((c.getBorder() instanceof FlatLineBorder)
? ((FlatLineBorder)c.getBorder()).getArc()
: 0);
fillRoundedBackground( g, c, arc );
paint( g, c );
}
// fill background with parent color to avoid garbage in rounded corners
if( arc > 0 )
FlatUIUtils.paintParentBackground( g, c );
g.setColor( c.getBackground() );
if( arc > 0 ) {
// fill rounded rectangle if having rounded corners
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height,
0, UIScale.scale( arc ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} else
g.fillRect( 0, 0, width, height );
/** @since 3.5 */
public static void fillRoundedBackground( Graphics g, JComponent c, int arc ) {
if( arc < 0 ) {
Border border = c.getBorder();
arc = ((border instanceof FlatLineBorder)
? ((FlatLineBorder)border).getArc()
: 0);
}
paint( g, c );
// fill background
if( c.isOpaque() ) {
if( arc > 0 ) {
// fill background with parent color to avoid garbage in rounded corners
FlatUIUtils.paintParentBackground( g, c );
} else {
g.setColor( c.getBackground() );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
}
// fill rounded rectangle if having rounded corners
if( arc > 0 ) {
g.setColor( c.getBackground() );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(),
0, UIScale.scale( arc ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
}
@Override

View File

@@ -43,6 +43,7 @@ import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -163,7 +164,7 @@ public class FlatPasswordFieldUI
}
private void repaint( KeyEvent e ) {
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
e.getComponent().repaint();
HiDPIUtils.repaint( e.getComponent() );
scrollCaretToVisible();
}
}
@@ -326,7 +327,7 @@ public class FlatPasswordFieldUI
if( visible != revealButton.isVisible() ) {
revealButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
if( !visible ) {
revealButton.setSelected( false );

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
@@ -35,12 +36,16 @@ import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowFocusListener;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
@@ -58,6 +63,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicComboPopup;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -76,6 +82,8 @@ public class FlatPopupFactory
private MethodHandle java8getPopupMethod;
private MethodHandle java9getPopupMethod;
private final ArrayList<NonFlashingPopup> stillShownHeavyWeightPopups = new ArrayList<>();
@Override
public Popup getPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException
@@ -88,15 +96,28 @@ public class FlatPopupFactory
fixLinuxWaylandJava21focusIssue( owner );
// reuse a heavy weight popup window, which is still shown on screen,
// to avoid flicker when popup (e.g. tooltip) is moving while mouse is moved
for( NonFlashingPopup popup : stillShownHeavyWeightPopups ) {
if( popup.delegate != null &&
popup.owner == owner &&
(popup.contents == contents ||
(popup.contents instanceof JToolTip && contents instanceof JToolTip)) )
{
stillShownHeavyWeightPopups.remove( popup );
return reuseStillShownHeavyWeightPopups( popup, contents, x, y );
}
}
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null && SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() )
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
if( popup.popupWindow != null && isMacOSBorderSupported() )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
}
@@ -105,7 +126,7 @@ public class FlatPopupFactory
if( isWindows11BorderSupported() &&
getBorderCornerRadius( owner, contents ) > 0 )
{
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
if( popup.popupWindow != null )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
@@ -116,7 +137,11 @@ public class FlatPopupFactory
forceHeavyWeight = true;
// create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight );
GraphicsConfiguration gc = owner.getGraphicsConfiguration();
return (gc != null && gc.isTranslucencyCapable())
? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
: new NonFlashingPopup( popupForScreenOfOwner, owner, contents );
}
/**
@@ -227,6 +252,24 @@ public class FlatPopupFactory
return UIManager.get( uiKey );
}
/**
* Reuse a heavy weight popup window, which is still shown on screen,
* by updating window location and contents.
* This avoid flicker when popup (e.g. a tooltip) is moving while mouse is moved.
* E.g. overridden JComponent.getToolTipLocation(MouseEvent).
* See ToolTipManager.checkForTipChange(MouseEvent).
*/
private static NonFlashingPopup reuseStillShownHeavyWeightPopups(
NonFlashingPopup reusePopup, Component contents, int ownerX, int ownerY )
{
// clone popup because PopupFactory.getPopup() should not return old instance
NonFlashingPopup popup = reusePopup.cloneForReuse();
// update popup location, size and contents
popup.reset( contents, ownerX, ownerY );
return popup;
}
//---- tooltips -----------------------------------------------------------
/**
@@ -316,19 +359,29 @@ public class FlatPopupFactory
//---- native rounded border ----------------------------------------------
private static boolean isWindows11BorderSupported() {
return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded();
return SystemInfo.isWindows_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, true ) &&
FlatNativeWindowsLibrary.isLoaded();
}
private static boolean isMacOSBorderSupported() {
// do not use rounded border on macOS 14.4+ because it may freeze the application
// and crash the macOS WindowServer process (reports vary from Finder restarts to OS restarts)
// https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215
// https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442
boolean isMacOS_14_4_orLater = (SystemInfo.osVersion >= SystemInfo.toVersion( 14, 4, 0, 0 ));
return SystemInfo.isMacOS &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, !isMacOS_14_4_orLater ) &&
FlatNativeMacLibrary.isLoaded();
}
private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) {
// make sure that the native window is created
if( !popupWindow.isDisplayable() )
popupWindow.addNotify();
int borderCornerRadius = getBorderCornerRadius( owner, contents );
float borderWidth = getRoundedBorderWidth( owner, contents );
// get Swing border color
Color borderColor = null; // use system default color
Color borderColor;
if( contents instanceof JComponent ) {
Border border = ((JComponent)contents).getBorder();
border = FlatUIUtils.unwrapNonUIResourceBorder( border );
@@ -340,11 +393,33 @@ public class FlatPopupFactory
borderColor = ((LineBorder)border).getLineColor();
else if( border instanceof EmptyBorder )
borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border
else
borderColor = null; // use system default color
// avoid that FlatLineBorder paints the Swing border
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true );
}
} else
borderColor = null; // use system default color
if( popupWindow.isDisplayable() ) {
// native window already created
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
} else {
// native window not yet created --> add listener to set native border after window creation
AtomicReference<HierarchyListener> l = new AtomicReference<>();
l.set( e -> {
if( e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
(e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 )
{
setupRoundedBorderImpl( popupWindow, borderCornerRadius, borderWidth, borderColor );
popupWindow.removeHierarchyListener( l.get() );
}
} );
popupWindow.addHierarchyListener( l.get() );
}
}
private static void setupRoundedBorderImpl( Window popupWindow, int borderCornerRadius, float borderWidth, Color borderColor ) {
if( SystemInfo.isWindows ) {
// get native window handle
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
@@ -490,18 +565,31 @@ public class FlatPopupFactory
//---- class NonFlashingPopup ---------------------------------------------
private static class NonFlashingPopup
/**
* Fixes popup background flashing effect when using dark theme on light platform theme,
* where the light popup background is shown for a fraction of a second before
* the dark popup content is shown.
* This is fixed by setting popup background to content background.
* <p>
* Defers hiding of heavy weight popup window for an event cycle,
* which allows reusing popup window to avoid flicker when "moving" popup.
*/
private class NonFlashingPopup
extends Popup
{
private Popup delegate;
Component owner;
private Component contents;
// heavy weight
protected Window popupWindow;
Window popupWindow;
private Color oldPopupWindowBackground;
NonFlashingPopup( Popup delegate, Component contents ) {
private boolean disposed;
NonFlashingPopup( Popup delegate, Component owner, Component contents ) {
this.delegate = delegate;
this.owner = owner;
this.contents = contents;
popupWindow = SwingUtilities.windowForComponent( contents );
@@ -515,8 +603,27 @@ public class FlatPopupFactory
}
}
private NonFlashingPopup( NonFlashingPopup reusePopup ) {
delegate = reusePopup.delegate;
owner = reusePopup.owner;
contents = reusePopup.contents;
popupWindow = reusePopup.popupWindow;
oldPopupWindowBackground = reusePopup.oldPopupWindowBackground;
}
NonFlashingPopup cloneForReuse() {
return new NonFlashingPopup( this );
}
@Override
public void show() {
public final void show() {
if( disposed )
return;
showImpl();
}
void showImpl() {
if( delegate != null ) {
showPopupAndFixLocation( delegate, popupWindow );
@@ -540,13 +647,36 @@ public class FlatPopupFactory
}
@Override
public void hide() {
public final void hide() {
if( disposed )
return;
disposed = true;
// immediately hide non-heavy weight popups or combobox popups
if( !(popupWindow instanceof JWindow) || contents instanceof BasicComboPopup ) {
hideImpl();
return;
}
// defer hiding of heavy weight popup window for an event cycle,
// which allows reusing popup window to avoid flicker when "moving" popup
((JWindow)popupWindow).getContentPane().removeAll();
stillShownHeavyWeightPopups.add( this );
EventQueue.invokeLater( () -> {
// hide popup if it was not reused
if( stillShownHeavyWeightPopups.remove( this ) )
hideImpl();
} );
}
void hideImpl() {
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
if( delegate != null ) {
delegate.hide();
delegate = null;
owner = null;
contents = null;
}
@@ -557,6 +687,28 @@ public class FlatPopupFactory
popupWindow = null;
}
}
void reset( Component contents, int ownerX, int ownerY ) {
// update popup window location
popupWindow.setLocation( ownerX, ownerY );
// replace component in content pane
Container contentPane = ((JWindow)popupWindow).getContentPane();
contentPane.removeAll();
contentPane.add( contents, BorderLayout.CENTER );
popupWindow.pack();
// update client property on contents
if( this.contents != contents ) {
Object old = (this.contents instanceof JComponent)
? ((JComponent)this.contents).getClientProperty( KEY_POPUP_USES_NATIVE_BORDER )
: null;
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, old );
this.contents = contents;
}
}
}
//---- class DropShadowPopup ----------------------------------------------
@@ -564,8 +716,6 @@ public class FlatPopupFactory
private class DropShadowPopup
extends NonFlashingPopup
{
private final Component owner;
// light weight
private JComponent lightComp;
private Border oldBorder;
@@ -580,11 +730,11 @@ public class FlatPopupFactory
// heavy weight
private Popup dropShadowDelegate;
private Window dropShadowWindow;
private JPanel dropShadowPanel2;
private Color oldDropShadowWindowBackground;
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
super( delegate, contents );
this.owner = owner;
super( delegate, owner, contents );
Dimension size = contents.getPreferredSize();
if( size.width <= 0 || size.height <= 0 )
@@ -600,24 +750,24 @@ public class FlatPopupFactory
// the drop shadow and is positioned behind the popup window.
// create panel that paints the drop shadow
JPanel dropShadowPanel = new JPanel();
dropShadowPanel.setBorder( createDropShadowBorder() );
dropShadowPanel.setOpaque( false );
dropShadowPanel2 = new JPanel();
dropShadowPanel2.setBorder( createDropShadowBorder() );
dropShadowPanel2.setOpaque( false );
// set preferred size of drop shadow panel
Dimension prefSize = popupWindow.getPreferredSize();
Insets insets = dropShadowPanel.getInsets();
dropShadowPanel.setPreferredSize( new Dimension(
Insets insets = dropShadowPanel2.getInsets();
dropShadowPanel2.setPreferredSize( new Dimension(
prefSize.width + insets.left + insets.right,
prefSize.height + insets.top + insets.bottom ) );
// create heavy weight popup for drop shadow
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel2, x, y, true );
// make drop shadow popup window translucent
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel2 );
if( dropShadowWindow != null ) {
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
dropShadowWindow.setBackground( new Color( 0, true ) );
@@ -654,6 +804,23 @@ public class FlatPopupFactory
}
}
private DropShadowPopup( DropShadowPopup reusePopup ) {
super( reusePopup );
// not necessary to clone fields used for light/medium weight popups
// heavy weight
dropShadowDelegate = reusePopup.dropShadowDelegate;
dropShadowWindow = reusePopup.dropShadowWindow;
dropShadowPanel2 = reusePopup.dropShadowPanel2;
oldDropShadowWindowBackground = reusePopup.oldDropShadowWindowBackground;
}
@Override
NonFlashingPopup cloneForReuse() {
return new DropShadowPopup( this );
}
private Border createDropShadowBorder() {
return new FlatDropShadowBorder(
UIManager.getColor( "Popup.dropShadowColor" ),
@@ -662,14 +829,14 @@ public class FlatPopupFactory
}
@Override
public void show() {
void showImpl() {
if( dropShadowDelegate != null )
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
if( mediumWeightPanel != null )
showMediumWeightDropShadow();
super.show();
super.showImpl();
// fix location of light weight popup in case it has left or top drop shadow
if( lightComp != null ) {
@@ -680,10 +847,11 @@ public class FlatPopupFactory
}
@Override
public void hide() {
void hideImpl() {
if( dropShadowDelegate != null ) {
dropShadowDelegate.hide();
dropShadowDelegate = null;
dropShadowPanel2 = null;
}
if( mediumWeightPanel != null ) {
@@ -692,7 +860,7 @@ public class FlatPopupFactory
mediumWeightPanel = null;
}
super.hide();
super.hideImpl();
if( dropShadowWindow != null ) {
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
@@ -776,5 +944,26 @@ public class FlatPopupFactory
if( dropShadowPanel != null && mediumWeightPanel != null )
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
}
@Override
void reset( Component contents, int ownerX, int ownerY ) {
super.reset( contents, ownerX, ownerY );
if( dropShadowWindow != null ) {
// set preferred size of drop shadow panel
Dimension prefSize = popupWindow.getPreferredSize();
Insets insets = dropShadowPanel2.getInsets();
int w = prefSize.width + insets.left + insets.right;
int h = prefSize.height + insets.top + insets.bottom;
dropShadowPanel2.setPreferredSize( new Dimension( w, h ) );
dropShadowPanel2.invalidate();
dropShadowWindow.pack();
// update drop shadow popup window location
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowWindow.setLocation( x, y );
}
}
}
}

View File

@@ -239,11 +239,13 @@ public class FlatPopupMenuUI
if( gc == null && popupMenu.getInvoker() != null )
gc = popupMenu.getInvoker().getGraphicsConfiguration();
// compute screen height
if( gc == null )
return new Rectangle( Toolkit.getDefaultToolkit().getScreenSize() );
// compute screen bounds
// (always subtract screen insets because there is no API to detect whether
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
Toolkit toolkit = Toolkit.getDefaultToolkit();
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
Rectangle screenBounds = gc.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
return FlatUIUtils.subtractInsets( screenBounds, screenInsets );
}

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -86,6 +87,17 @@ public class FlatProgressBarUI
installStyle();
}
@Override
public void uninstallUI( JComponent c ) {
if( !EventQueue.isDispatchThread() && progressBar.isIndeterminate() ) {
LoggingFacade.INSTANCE.logSevere(
"FlatLaf: Uninstalling indeterminate progress bar UI not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
new IllegalStateException() );
}
super.uninstallUI( c );
}
@Override
protected void installDefaults() {
super.installDefaults();
@@ -110,17 +122,25 @@ public class FlatProgressBarUI
propertyChangeListener = e -> {
switch( e.getPropertyName() ) {
case "indeterminate":
if( !EventQueue.isDispatchThread() && !progressBar.isIndeterminate() ) {
LoggingFacade.INSTANCE.logSevere(
"FlatLaf: Using JProgressBar.setIndeterminate(false) not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
new IllegalStateException() );
}
break;
case PROGRESS_BAR_LARGE_HEIGHT:
case PROGRESS_BAR_SQUARE:
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
case STYLE:
case STYLE_CLASS:
installStyle();
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
}
};
@@ -274,6 +294,6 @@ public class FlatProgressBarUI
// Only solution is to repaint whole progress bar.
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
if( (int) systemScaleFactor != systemScaleFactor )
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
}
}

View File

@@ -23,6 +23,7 @@ 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;
@@ -102,13 +103,23 @@ public class FlatRadioButtonMenuItemUI
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 */

View File

@@ -40,12 +40,14 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicRadioButtonUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
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;
@@ -159,6 +161,10 @@ public class FlatRadioButtonUI
/** @since 2 */
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case BasicHTML.propertyKey:
FlatHTML.updateRendererCSSFontBaseSize( b );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
@@ -168,7 +174,7 @@ public class FlatRadioButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -448,6 +448,11 @@ public class FlatRootPaneUI
titlePane.titleBarColorsChanged();
break;
case FlatClientProperties.TITLE_BAR_HEIGHT:
if( titlePane != null )
titlePane.revalidate();
break;
case FlatClientProperties.FULL_WINDOW_CONTENT:
if( titlePane != null ) {
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );

View File

@@ -43,6 +43,7 @@ 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;
import com.formdev.flatlaf.util.UIScale;
@@ -212,14 +213,14 @@ public class FlatScrollBarUI
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case "componentOrientation":
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
private void repaint() {
if( scrollbar.isEnabled() )
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
}
}

View File

@@ -55,6 +55,7 @@ import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -209,7 +210,7 @@ public class FlatScrollPaneUI
// Use (0, 0) view position to obtain a constant unit increment of first item.
// Unit increment may be different for each item.
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
Rectangle visibleRect = new Rectangle( viewport.getExtentSize() );
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
if( unitIncrement > 0 ) {
@@ -297,11 +298,11 @@ public class FlatScrollPaneUI
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
if( vsb != null ) {
vsb.revalidate();
vsb.repaint();
HiDPIUtils.repaint( vsb );
}
if( hsb != null ) {
hsb.revalidate();
hsb.repaint();
HiDPIUtils.repaint( hsb );
}
break;
@@ -321,14 +322,14 @@ public class FlatScrollPaneUI
break;
case FlatClientProperties.OUTLINE:
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case "border":
@@ -339,7 +340,7 @@ public class FlatScrollPaneUI
borderShared = null;
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
break;
}
@@ -538,14 +539,14 @@ public class FlatScrollPaneUI
public void focusGained( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
}

View File

@@ -32,6 +32,7 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -134,7 +135,7 @@ public class FlatSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -25,6 +25,8 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
@@ -191,6 +193,23 @@ public class FlatSliderUI
return new FlatTrackListener();
}
@Override
protected FocusListener createFocusListener( JSlider slider ) {
return new BasicSliderUI.FocusHandler() {
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( slider );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( slider );
}
};
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
@@ -422,7 +441,7 @@ debug*/
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
{
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( systemScaleFactor != (int) systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
@@ -579,15 +598,15 @@ debug*/
@Override
public void setThumbLocation( int x, int y ) {
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
if( !isRoundThumb() ) {
// the needle of the directional thumb is painted outside of thumbRect
// --> must increase repaint rectangle
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
// increase union rectangle for repaint
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
if( slider.getOrientation() == JSlider.HORIZONTAL )
@@ -597,10 +616,9 @@ debug*/
if( !slider.getComponentOrientation().isLeftToRight() )
r.x -= extra;
}
}
slider.repaint( r );
} else
super.setThumbLocation( x, y );
HiDPIUtils.repaint( slider, r );
}
//---- class FlatTrackListener --------------------------------------------
@@ -688,21 +706,21 @@ debug*/
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
{
calculateThumbLocation();
slider.repaint();
HiDPIUtils.repaint( slider );
}
}
protected void setThumbHover( boolean hover ) {
if( hover != thumbHover ) {
thumbHover = hover;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}
protected void setThumbPressed( boolean pressed ) {
if( pressed != thumbPressed ) {
thumbPressed = pressed;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -586,7 +587,7 @@ public class FlatSpinnerUI
@Override
public void focusGained( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
// if spinner gained focus, transfer it to the editor text field
if( e.getComponent() == spinner ) {
@@ -599,7 +600,7 @@ public class FlatSpinnerUI
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
}
//---- interface PropertyChangeListener ----
@@ -614,7 +615,7 @@ public class FlatSpinnerUI
case FlatClientProperties.COMPONENT_ROUND_RECT:
case FlatClientProperties.OUTLINE:
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
case FlatClientProperties.MINIMUM_WIDTH:
@@ -625,7 +626,7 @@ public class FlatSpinnerUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
spinner.revalidate();
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
}
}

View File

@@ -40,6 +40,7 @@ import javax.swing.UIManager;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -709,7 +710,7 @@ public class FlatStylingSupport
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
};

View File

@@ -98,6 +98,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
@@ -895,7 +896,7 @@ public class FlatTabbedPaneUI
}
}
tabPane.repaint( r );
HiDPIUtils.repaint( tabPane, r );
}
private boolean inCalculateEqual;
@@ -1684,7 +1685,7 @@ debug*/
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
// add gap for selected tab to path
if( getTabType() == TAB_TYPE_CARD ) {
if( getTabType() == TAB_TYPE_CARD && selectedIndex >= 0 ) {
float csh = scale( (float) contentSeparatorHeight );
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
@@ -2305,8 +2306,23 @@ debug*/
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
if( tabForCoordinate( tabPane, x, y ) >= 0 )
return false;
// Note: not using tabForCoordinate() here because this may validate layout and cause dead lock
if( moreTabsButton != null ) {
// convert x,y from JTabbedPane coordinate space to ScrollableTabPanel coordinate space
Point viewPosition = tabViewport.getViewPosition();
x = x - tabViewport.getX() + viewPosition.x;
y = y - tabViewport.getY() + viewPosition.y;
// check whether point is within viewport
if( !tabViewport.getViewRect().contains( x, y ) )
return null; // check children
}
for( int i = 0; i < rects.length; i++ ) {
if( rects[i].contains( x, y ) )
return false;
}
return null; // check children
}
@@ -2581,19 +2597,19 @@ debug*/
@Override
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
popupVisible = true;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuCanceled( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
}
@@ -3102,7 +3118,7 @@ debug*/
case TABBED_PANE_SHOW_TAB_SEPARATORS:
case TABBED_PANE_TAB_TYPE:
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
@@ -3125,14 +3141,14 @@ debug*/
case TABBED_PANE_TAB_ICON_PLACEMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3140,7 +3156,7 @@ debug*/
uninstallTrailingComponent();
installTrailingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3148,7 +3164,7 @@ debug*/
case STYLE_CLASS:
installStyle();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}
@@ -3172,7 +3188,7 @@ debug*/
case TABBED_PANE_TAB_ALIGNMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}

View File

@@ -65,8 +65,28 @@ public class FlatTableCellBorder
return super.getLineColor();
}
@Override
public int getArc() {
if( c != null ) {
Integer selectionArc = getStyleFromTableUI( c, ui -> ui.selectionArc );
if( selectionArc != null )
return selectionArc;
}
return super.getArc();
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( c != null ) {
Insets selectionInsets = getStyleFromTableUI( c, ui -> ui.selectionInsets );
if( selectionInsets != null ) {
x += selectionInsets.left;
y += selectionInsets.top;
width -= selectionInsets.left + selectionInsets.right;
height -= selectionInsets.top + selectionInsets.bottom;
}
}
this.c = c;
super.paintBorder( c, g, x, y, width, height );
this.c = null;

View File

@@ -45,6 +45,7 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -234,8 +235,8 @@ public class FlatTableHeaderUI
@Override
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
header.repaint( header.getHeaderRect( oldColumn ) );
header.repaint( header.getHeaderRect( newColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( oldColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( newColumn ) );
}
@Override

View File

@@ -24,7 +24,13 @@ 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.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
@@ -38,21 +44,29 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
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.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -92,6 +106,8 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.selectionInsets Insets
* @uiDefault Table.selectionArc int
* @uiDefault Table.paintOutsideAlternateRows boolean
* @uiDefault Table.editorSelectAllOnStartEditing boolean
*
@@ -120,6 +136,8 @@ public class FlatTableUI
@Styleable protected Color selectionForeground;
@Styleable protected Color selectionInactiveBackground;
@Styleable protected Color selectionInactiveForeground;
/** @since 3.5 */ @Styleable protected Insets selectionInsets;
/** @since 3.5 */ @Styleable protected int selectionArc;
// for FlatTableCellBorder
/** @since 2 */ @Styleable protected Insets cellMargins;
@@ -132,6 +150,9 @@ public class FlatTableUI
private TableCellRenderer oldBooleanRenderer;
private PropertyChangeListener propertyChangeListener;
private ComponentListener outsideAlternateRowsListener;
private ListSelectionListener rowSelectionListener;
private TableColumnModelListener columnSelectionListener;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
@@ -158,6 +179,8 @@ public class FlatTableUI
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
selectionInsets = UIManager.getInsets( "Table.selectionInsets" );
selectionArc = UIManager.getInt( "Table.selectionArc" );
toggleSelectionColors();
@@ -245,6 +268,28 @@ public class FlatTableUI
propertyChangeListener = e -> {
switch( e.getPropertyName() ) {
case "selectionModel":
if( rowSelectionListener != null ) {
Object oldModel = e.getOldValue();
Object newModel = e.getNewValue();
if( oldModel != null )
((ListSelectionModel)oldModel).removeListSelectionListener( rowSelectionListener );
if( newModel != null )
((ListSelectionModel)newModel).addListSelectionListener( rowSelectionListener );
}
break;
case "columnModel":
if( columnSelectionListener != null ) {
Object oldModel = e.getOldValue();
Object newModel = e.getNewValue();
if( oldModel != null )
((TableColumnModel)oldModel).removeColumnModelListener( columnSelectionListener );
if( newModel != null )
((TableColumnModel)newModel).addColumnModelListener( columnSelectionListener );
}
break;
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
toggleSelectionColors();
break;
@@ -253,11 +298,14 @@ public class FlatTableUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
table.revalidate();
table.repaint();
HiDPIUtils.repaint( table );
break;
}
};
table.addPropertyChangeListener( propertyChangeListener );
if( selectionArc > 0 )
installRepaintRoundedSelectionListeners();
}
@Override
@@ -266,6 +314,19 @@ public class FlatTableUI
table.removePropertyChangeListener( propertyChangeListener );
propertyChangeListener = null;
if( outsideAlternateRowsListener != null ) {
table.removeComponentListener( outsideAlternateRowsListener );
outsideAlternateRowsListener = null;
}
if( rowSelectionListener != null ) {
table.getSelectionModel().removeListSelectionListener( rowSelectionListener );
rowSelectionListener = null;
}
if( columnSelectionListener != null ) {
table.getColumnModel().removeColumnModelListener( columnSelectionListener );
columnSelectionListener = null;
}
}
@Override
@@ -342,6 +403,8 @@ public class FlatTableUI
protected Object applyStyleProperty( String key, Object value ) {
if( "rowHeight".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
else if( "selectionArc".equals( key ) && value instanceof Integer && (Integer) value > 0 )
installRepaintRoundedSelectionListeners();
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
}
@@ -404,6 +467,7 @@ public class FlatTableUI
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
double lineOffset = (1. - lineThickness) + 0.05; // adding 0.05 to fix line location in some cases
// Java 8 uses drawLine() to paint grid lines
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
@@ -446,11 +510,11 @@ public class FlatTableUI
// reduce line thickness to avoid unstable painted line thickness
if( lineThickness != 1 ) {
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
super.fill( new Rectangle2D.Double( x, y + lineOffset, width, lineThickness ) );
return;
}
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
super.fill( new Rectangle2D.Double( x + lineOffset, y, lineThickness, height ) );
return;
}
}
@@ -468,6 +532,10 @@ public class FlatTableUI
};
}
// rounded selection or selection insets
if( selectionArc > 0 || (selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets )) )
g = new RoundedSelectionGraphics( g, UIManager.getColor( "Table.alternateRowColor" ) );
super.paint( g, c );
}
@@ -513,8 +581,6 @@ public class FlatTableUI
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
Color alternateColor;
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
g.setColor( alternateColor );
int rowCount = table.getRowCount();
// paint alternating empty rows below the table
@@ -523,10 +589,350 @@ public class FlatTableUI
int tableWidth = table.getWidth();
int rowHeight = table.getRowHeight();
g.setColor( alternateColor );
int x = viewport.getComponentOrientation().isLeftToRight() ? 0 : viewportWidth - tableWidth;
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
if( row % 2 != 0 )
g.fillRect( 0, y, tableWidth, rowHeight );
paintAlternateRowBackground( g, -1, -1, x, y, tableWidth, rowHeight );
}
// add listener on demand
if( outsideAlternateRowsListener == null && table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF ) {
outsideAlternateRowsListener = new FlatOutsideAlternateRowsListener();
table.addComponentListener( outsideAlternateRowsListener );
}
}
}
}
/**
* Paints (rounded) alternate row background.
* 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.5
*/
protected void paintAlternateRowBackground( Graphics g, int row, int column, int x, int y, int width, int height ) {
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( column >= 0 ) {
// selection insets
// selection arc
if( column > 0 ) {
if( insets != null )
insets.left = 0;
if( table.getComponentOrientation().isLeftToRight() )
arcTopLeft = arcBottomLeft = 0;
else
arcTopRight = arcBottomRight = 0;
}
if( column < table.getColumnCount() - 1 ) {
if( insets != null )
insets.right = 0;
if( table.getComponentOrientation().isLeftToRight() )
arcTopRight = arcBottomRight = 0;
else
arcTopLeft = arcBottomLeft = 0;
}
}
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
}
/**
* 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.5
*/
protected void paintCellSelection( Graphics g, int row, int column, int x, int y, int width, int height ) {
boolean rowSelAllowed = table.getRowSelectionAllowed();
boolean colSelAllowed = table.getColumnSelectionAllowed();
boolean rowSelOnly = rowSelAllowed && !colSelAllowed;
boolean colSelOnly = colSelAllowed && !rowSelAllowed;
boolean cellOnlySel = rowSelAllowed && colSelAllowed;
// get selection state of surrounding cells
boolean leftSelected = (column > 0 && (rowSelOnly || table.isCellSelected( row, column - 1 )));
boolean topSelected = (row > 0 && (colSelOnly || table.isCellSelected( row - 1, column )));
boolean rightSelected = (column < table.getColumnCount() - 1 && (rowSelOnly || table.isCellSelected( row, column + 1 )));
boolean bottomSelected = (row < table.getRowCount() - 1 && (colSelOnly || table.isCellSelected( row + 1, column )));
if( !table.getComponentOrientation().isLeftToRight() ) {
boolean temp = leftSelected;
leftSelected = rightSelected;
rightSelected = temp;
}
// selection insets
// (insets are applied to whole row if row-only selection is used,
// or to whole column if column-only selection is used,
// or to cell if cell selection is used)
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
if( insets != null ) {
if( rowSelOnly && leftSelected )
insets.left = 0;
if( rowSelOnly && rightSelected )
insets.right = 0;
if( colSelOnly && topSelected )
insets.top = 0;
if( colSelOnly && bottomSelected )
insets.bottom = 0;
}
// selection arc
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( selectionArc > 0 ) {
// note that intercellSpacing is not considered as a gap because
// grid lines are usually painted to intercell space
boolean hasRowGap = (rowSelOnly || cellOnlySel) && insets != null && (insets.top != 0 || insets.bottom != 0);
boolean hasColGap = (colSelOnly || cellOnlySel) && insets != null && (insets.left != 0 || insets.right != 0);
if( leftSelected && !hasColGap )
arcTopLeft = arcBottomLeft = 0;
if( rightSelected && !hasColGap )
arcTopRight = arcBottomRight = 0;
if( topSelected && !hasRowGap )
arcTopLeft = arcTopRight = 0;
if( bottomSelected && !hasRowGap )
arcBottomLeft = arcBottomRight = 0;
}
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 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.5
*/
public static void paintCellSelection( JTable table, Graphics g, int row, int column, int x, int y, int width, int height ) {
if( !(table.getUI() instanceof FlatTableUI) )
return;
FlatTableUI ui = (FlatTableUI) table.getUI();
ui.paintCellSelection( g, row, column, x, y, width, height );
}
private void installRepaintRoundedSelectionListeners() {
if( rowSelectionListener == null ) {
rowSelectionListener = this::repaintRoundedRowSelection;
table.getSelectionModel().addListSelectionListener( rowSelectionListener );
}
if( columnSelectionListener == null ) {
columnSelectionListener = new TableColumnModelListener() {
@Override
public void columnSelectionChanged( ListSelectionEvent e ) {
repaintRoundedColumnSelection( e );
}
@Override public void columnRemoved( TableColumnModelEvent e ) {}
@Override public void columnMoved( TableColumnModelEvent e ) {}
@Override public void columnMarginChanged( ChangeEvent e ) {}
@Override public void columnAdded( TableColumnModelEvent e ) {}
};
table.getColumnModel().addColumnModelListener( columnSelectionListener );
}
}
private void repaintRoundedRowSelection( ListSelectionEvent e ) {
if( selectionArc <= 0 || !table.getRowSelectionAllowed() )
return;
int rowCount = table.getRowCount();
int columnCount = table.getColumnCount();
if( rowCount <= 0 || columnCount <= 0 )
return;
// repaint including rows before and after changed selection
int firstRow = Math.max( 0, Math.min( e.getFirstIndex() - 1, rowCount - 1 ) );
int lastRow = Math.max( 0, Math.min( e.getLastIndex() + 1, rowCount - 1 ) );
Rectangle firstRect = table.getCellRect( firstRow, 0, false );
Rectangle lastRect = table.getCellRect( lastRow, columnCount - 1, false );
table.repaint( firstRect.union( lastRect ) );
}
private void repaintRoundedColumnSelection( ListSelectionEvent e ) {
if( selectionArc <= 0 || !table.getColumnSelectionAllowed() )
return;
int rowCount = table.getRowCount();
int columnCount = table.getColumnCount();
if( rowCount <= 0 || columnCount <= 0 )
return;
// limit to selected rows for cell selection
int firstRow = 0;
int lastRow = rowCount - 1;
if( table.getRowSelectionAllowed() ) {
firstRow = table.getSelectionModel().getMinSelectionIndex();
lastRow = table.getSelectionModel().getMaxSelectionIndex();
}
// repaint including columns before and after changed selection
int firstColumn = Math.max( 0, Math.min( e.getFirstIndex() - 1, columnCount - 1 ) );
int lastColumn = Math.max( 0, Math.min( e.getLastIndex() + 1, columnCount - 1 ) );
Rectangle firstRect = table.getCellRect( firstRow, firstColumn, false );
Rectangle lastRect = table.getCellRect( lastRow, lastColumn, false );
table.repaint( firstRect.union( lastRect ) );
}
//---- class RoundedSelectionGraphics -------------------------------------
/**
* 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.
* <p>
* To solve this, a graphics proxy is used that paints rounded selection
* if row/column/cell is selected and the renderer wants to fill the background.
*/
private class RoundedSelectionGraphics
extends Graphics2DProxy
{
private final Color alternateRowColor;
// 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, Color alternateRowColor ) {
super( (Graphics2D) delegate );
this.alternateRowColor = alternateRowColor;
}
@Override
public Graphics create() {
return new RoundedSelectionGraphics( super.create(), alternateRowColor );
}
@Override
public Graphics create( int x, int y, int width, int height ) {
return new RoundedSelectionGraphics( super.create( x, y, width, height ), alternateRowColor );
}
@Override
public void fillRect( int x, int y, int width, int height ) {
if( fillCellSelection( x, y, width, height ) )
return;
super.fillRect( x, y, width, height );
}
@Override
public void fill( Shape shape ) {
if( shape instanceof Rectangle2D ) {
Rectangle2D r = (Rectangle2D) shape;
double x = r.getX();
double y = r.getY();
double width = r.getWidth();
double height = r.getHeight();
if( x == (int) x && y == (int) y && width == (int) width && height == (int) height ) {
if( fillCellSelection( (int) x, (int) y, (int) width, (int) height ) )
return;
}
}
super.fill( shape );
}
private boolean fillCellSelection( int x, int y, int width, int height ) {
if( inPaintSelection )
return false;
Color color;
Component rendererComponent;
if( x == 0 && y == 0 &&
((color = getColor()) == table.getSelectionBackground() ||
(alternateRowColor != null && color == alternateRowColor)) &&
(rendererComponent = findActiveRendererComponent()) != null &&
width == rendererComponent.getWidth() &&
height == rendererComponent.getHeight() )
{
Point location = rendererComponent.getLocation();
int row = table.rowAtPoint( location );
int column = table.columnAtPoint( location );
if( row >= 0 && column >= 0 ) {
inPaintSelection = true;
if( color == table.getSelectionBackground() )
paintCellSelection( this, row, column, x, y, width, height );
else
paintAlternateRowBackground( this, row, column, x, y, width, height );
inPaintSelection = false;
return true;
}
}
return false;
}
/**
* A CellRendererPane may contain multiple components, if multiple renderers
* are used. Inactive renderer components have size {@code 0x0}.
*/
private Component findActiveRendererComponent() {
int count = rendererPane.getComponentCount();
for( int i = 0; i < count; i++ ) {
Component c = rendererPane.getComponent( i );
if( c.getWidth() > 0 && c.getHeight() > 0 )
return c;
}
return null;
}
}
//---- class OutsideAlternateRowsListener ---------------------------------
/**
* Used if table auto-resize-mode is off to repaint outside alternate rows
* when table width changed (column resized) or component orientation changed.
*/
private class FlatOutsideAlternateRowsListener
extends ComponentAdapter
{
@Override
public void componentHidden( ComponentEvent e ) {
Container viewport = SwingUtilities.getUnwrappedParent( table );
if( viewport instanceof JViewport )
HiDPIUtils.repaint( viewport );
}
@Override
public void componentMoved( ComponentEvent e ) {
repaintAreaBelowTable();
}
@Override
public void componentResized( ComponentEvent e ) {
repaintAreaBelowTable();
}
private void repaintAreaBelowTable() {
Container viewport = SwingUtilities.getUnwrappedParent( table );
if( viewport instanceof JViewport ) {
int viewportHeight = viewport.getHeight();
int tableHeight = table.getHeight();
if( tableHeight < viewportHeight )
HiDPIUtils.repaint( viewport, 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
}
}
}

View File

@@ -239,7 +239,7 @@ public class FlatTextFieldUI
case COMPONENT_ROUND_RECT:
case OUTLINE:
case TEXT_FIELD_PADDING:
c.repaint();
HiDPIUtils.repaint( c );
break;
case MINIMUM_WIDTH:
@@ -250,38 +250,38 @@ public class FlatTextFieldUI
case STYLE_CLASS:
installStyle();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_ICON:
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_ICON:
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_COMPONENT:
uninstallTrailingComponent();
installTrailingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
uninstallClearButton();
installClearButton();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case "enabled":
@@ -815,7 +815,7 @@ debug*/
if( visible != clearButton.isVisible() ) {
clearButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
}
}

View File

@@ -222,7 +222,7 @@ public class FlatTitlePane
windowTopBorderLayer = new JPanel();
windowTopBorderLayer.setVisible( false );
windowTopBorderLayer.setOpaque( false );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( FlatNativeWindowBorder.WindowTopBorder.getInstance() ) );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( WindowTopBorder.getInstance() ) );
} else
windowTopBorderLayer = null;
@@ -359,6 +359,10 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
int titleBarHeight = clientPropertyInt( rootPane, TITLE_BAR_HEIGHT, -1 );
if( titleBarHeight >= 0 )
return new Dimension( size.width, UIScale.scale( titleBarHeight ) );
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
// make title pane height smaller when frame is maximized
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
@@ -395,6 +399,12 @@ public class FlatTitlePane
// allow the button to shrink if space is rare
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
}
@Override
public Dimension getMaximumSize() {
// allow the button to fill whole button area height
// see also BasicMenuUI.getMaximumSize()
return new Dimension( super.getMaximumSize().width, Short.MAX_VALUE );
}
};
button.setFocusable( false );
button.setContentAreaFilled( false );
@@ -745,16 +755,6 @@ public class FlatTitlePane
g.fillRect( 0, 0, getWidth(), getHeight() );
}
protected void repaintWindowBorder() {
int width = rootPane.getWidth();
int height = rootPane.getHeight();
Insets insets = rootPane.getInsets();
rootPane.repaint( 0, 0, width, insets.top ); // top
rootPane.repaint( 0, 0, insets.left, height ); // left
rootPane.repaint( 0, height - insets.bottom, width, insets.bottom ); // bottom
rootPane.repaint( width - insets.right, 0, insets.right, height ); // right
}
/**
* Iconifies the window.
*/
@@ -824,7 +824,8 @@ public class FlatTitlePane
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
if( !hasNativeCustomDecoration() &&
(oldMaximizedBounds == null ||
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) &&
window.getGraphicsConfiguration() != null )
{
GraphicsConfiguration gc = window.getGraphicsConfiguration();
@@ -1058,10 +1059,11 @@ public class FlatTitlePane
* <p>
* Note:
* <ul>
* <li>This method is invoked often when mouse is moved over title bar
* <li>This method is invoked often when mouse is moved over window title bar area
* and should therefore return quickly.
* <li>This method 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>
*/
private boolean captionHitTest( Point pt ) {
@@ -1089,7 +1091,7 @@ public class FlatTitlePane
}
private boolean isTitleBarCaptionAt( Component c, int x, int y ) {
if( !c.isDisplayable() || !c.isVisible() || !c.contains( x, y ) || c == mouseLayer )
if( !c.isDisplayable() || !c.isVisible() || !contains( c, x, y ) || c == mouseLayer )
return true; // continue checking with next component
if( c.isEnabled() &&
@@ -1107,8 +1109,18 @@ public class FlatTitlePane
// if component is not fully layouted, do not invoke function
// because it is too dangerous that the function tries to layout the component,
// which could cause a dead lock
if( !c.isValid() )
if( !c.isValid() ) {
// revalidate if necessary so that it is valid when invoked again later
EventQueue.invokeLater( () -> {
Window w = SwingUtilities.windowForComponent( c );
if( w != null )
w.revalidate();
else
c.revalidate();
} );
return false; // assume that this is not a caption because the component has mouse listeners
}
if( caption instanceof Function ) {
// check client property function value
@@ -1141,6 +1153,16 @@ public class FlatTitlePane
return true;
}
/**
* Same as {@link Component#contains(int, int)}, but not using that method
* because it may be overridden by custom components and invoke code that
* tries to request AWT tree lock on 'AWT-Windows' thread.
* This could freeze the application if AWT tree is already locked on 'AWT-EventQueue' thread.
*/
private boolean contains( Component c, int x, int y ) {
return x >= 0 && y >= 0 && x < c.getWidth() && y < c.getHeight();
}
private int lastCaptionHitTestX;
private int lastCaptionHitTestY;
private long lastCaptionHitTestTime;
@@ -1352,10 +1374,7 @@ public class FlatTitlePane
activeChanged( true );
updateNativeTitleBarHeightAndHitTestSpots();
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
repaintBorder();
}
@Override
@@ -1363,10 +1382,22 @@ public class FlatTitlePane
activeChanged( false );
updateNativeTitleBarHeightAndHitTestSpots();
if( isWindowTopBorderNeeded() )
repaintBorder();
}
private void repaintBorder() {
// Windows 10 top border
if( windowTopBorderLayer != null && windowTopBorderLayer.isShowing())
WindowTopBorder.getInstance().repaintBorder( windowTopBorderLayer );
else if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
// Window border used for non-native window decorations
if( rootPane.getBorder() instanceof FlatRootPaneUI.FlatWindowBorder ) {
// not repainting four areas on the four sides because RepaintManager
// unions dirty regions, which also results in repaint of whole rootpane
rootPane.repaint();
}
}
@Override
@@ -1559,6 +1590,15 @@ debug*/
* Useful for components that do not use mouse input on whole component bounds.
* E.g. a tabbed pane with a few tabs has some empty space beside the tabs
* that can be used to move the window.
* <p>
* Note:
* <ul>
* <li>This method is invoked often when mouse is moved over window title bar area
* and should therefore return quickly.
* <li>This method 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>
*
* @return {@code true} if the component is not interested in mouse input at the given location
* {@code false} if the component wants process mouse input at the given location

View File

@@ -26,6 +26,7 @@ import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -159,14 +160,14 @@ public class FlatToggleButtonUI
b.revalidate();
}
b.repaint();
HiDPIUtils.repaint( b );
break;
case TAB_BUTTON_UNDERLINE_PLACEMENT:
case TAB_BUTTON_UNDERLINE_HEIGHT:
case TAB_BUTTON_UNDERLINE_COLOR:
case TAB_BUTTON_SELECTED_BACKGROUND:
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -36,6 +36,7 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -131,7 +132,7 @@ public class FlatToolBarSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -443,7 +444,7 @@ public class FlatToolBarUI
// repaint button group
if( gr != null )
toolBar.repaint( gr );
HiDPIUtils.repaint(toolBar, gr );
}
private ButtonGroup getButtonGroup( AbstractButton b ) {
@@ -530,8 +531,11 @@ public class FlatToolBarUI
private Component getRecentComponent( Container aContainer, boolean first ) {
// if moving focus into the toolbar, focus recently focused toolbar button
if( focusedCompIndex >= 0 && focusedCompIndex < toolBar.getComponentCount() )
return toolBar.getComponent( focusedCompIndex );
if( focusedCompIndex >= 0 && focusedCompIndex < toolBar.getComponentCount() ) {
Component c = toolBar.getComponent( focusedCompIndex );
if( accept( c ) )
return c;
}
return first
? super.getFirstComponent( aContainer )

View File

@@ -61,7 +61,7 @@ public class FlatToolTipUI
super.installUI( c );
// update HTML renderer if necessary
FlatLabelUI.updateHTMLRenderer( c, ((JToolTip)c).getTipText(), false );
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
@@ -81,11 +81,7 @@ public class FlatToolTipUI
/** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
String name = e.getPropertyName();
if( name == "tiptext" || name == "font" || name == "foreground" ) {
JToolTip toolTip = (JToolTip) e.getSource();
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
}
FlatHTML.propertyChange( e );
}
@Override

View File

@@ -47,6 +47,7 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -310,7 +311,7 @@ public class FlatTreeUI
switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION:
case TREE_PAINT_SELECTION:
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "dropLocation":
@@ -325,7 +326,7 @@ public class FlatTreeUI
case STYLE_CLASS:
installStyle();
tree.revalidate();
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "enabled":
@@ -353,7 +354,7 @@ public class FlatTreeUI
Rectangle r = tree.getPathBounds( loc.getPath() );
if( r != null )
tree.repaint( 0, r.y, tree.getWidth(), r.height );
HiDPIUtils.repaint( tree, 0, r.y, tree.getWidth(), r.height );
}
@Override
@@ -370,14 +371,14 @@ public class FlatTreeUI
{
if( changedPaths.length > 4 ) {
// same is done in BasicTreeUI.Handler.valueChanged()
tree.repaint();
HiDPIUtils.repaint( tree );
} else {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
for( TreePath path : changedPaths ) {
Rectangle r = getPathBounds( tree, path );
if( r != null )
tree.repaint( r.x, r.y - arc, r.width, r.height + (arc * 2) );
HiDPIUtils.repaint( tree, r.x, r.y - arc, r.width, r.height + (arc * 2) );
}
}
}

View File

@@ -124,6 +124,11 @@ public class FlatUIUtils
dest.right = src.right;
}
/** @since 3.5 */
public static boolean isInsetsEmpty( Insets insets ) {
return insets.top == 0 && insets.left == 0 && insets.bottom == 0 && insets.right == 0;
}
public static Color getUIColor( String key, int defaultColorRGB ) {
Color color = UIManager.getColor( key );
return (color != null) ? color : new Color( defaultColorRGB );
@@ -601,28 +606,55 @@ public class FlatUIUtils
public static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
{
paintOutlinedComponent( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
borderWidth, arc, focusColor, borderColor, background, false );
}
static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane )
{
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( (int) systemScaleFactor != systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintOutlinedComponentImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), focusWidthFraction, (float) (focusInnerWidth * scaleFactor),
(float) (borderWidth * scaleFactor), (float) (arc * scaleFactor),
focusColor, borderColor, background );
focusColor, borderColor, background, scrollPane, scaleFactor );
} );
return;
}
paintOutlinedComponentImpl( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
borderWidth, arc, focusColor, borderColor, background );
borderWidth, arc, focusColor, borderColor, background, scrollPane, systemScaleFactor );
}
@SuppressWarnings( "SelfAssignment" ) // Error Prone
private static void paintOutlinedComponentImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane, double scaleFactor )
{
// Special handling for scrollpane and fractional scale factors (e.g. 1.25 - 1.75),
// where Swing scales one "logical" pixel (border insets) to either one or two physical pixels.
// Antialiasing is used to paint the border, which usually needs two physical pixels
// at small scale factors. 1px for the solid border and another 1px for antialiasing.
// But scrollpane view is painted over the border, which results in a painted border
// that is 1px thick at some sides and 2px thick at other sides.
if( scrollPane && scaleFactor != (int) scaleFactor ) {
if( focusWidth > 0 ) {
// reduce outer border thickness (focusWidth) so that inner side of
// component border (focusWidth + borderWidth) is at a full pixel
int totalWidth = (int) (focusWidth + borderWidth);
focusWidth = totalWidth - borderWidth;
} else {// if( scaleFactor > 1 && scaleFactor < 2 ) {
// reduce component border thickness (borderWidth) to full pixels
borderWidth = (int) borderWidth;
}
}
// outside bounds of the border and the background
float x1 = x + focusWidth;
float y1 = y + focusWidth;
@@ -780,7 +812,7 @@ public class FlatUIUtils
if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( systemScaleFactor != (int) systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
@@ -1315,13 +1347,13 @@ debug*/
@Override
public void focusGained( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
@Override
public void focusLost( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
}

View File

@@ -42,6 +42,7 @@ import javax.swing.DesktopManager;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@@ -191,7 +192,7 @@ public abstract class FlatWindowResizer
protected abstract Dimension getWindowMinimumSize();
protected abstract Dimension getWindowMaximumSize();
protected void beginResizing( int direction ) {}
protected void beginResizing( int resizeDir ) {}
protected void endResizing() {}
//---- interface PropertyChangeListener ----
@@ -234,17 +235,47 @@ public abstract class FlatWindowResizer
{
protected Window window;
private final JComponent centerComp;
private final boolean limitResizeToScreenBounds;
public WindowResizer( JRootPane rootPane ) {
super( rootPane );
// Transparent "center" component that is made visible only while resizing window.
// It uses same cursor as the area where resize dragging started.
// This ensures that the cursor shape stays stable while dragging mouse
// into the window to make window smaller. Otherwise it would toggling between
// resize and standard cursor because the component layout is not updated
// fast enough and the mouse cursor is always updated from the component
// at the mouse location.
centerComp = new JPanel();
centerComp.setOpaque( false );
centerComp.setVisible( false );
Container cont = rootPane.getLayeredPane();
cont.add( centerComp, WINDOW_RESIZER_LAYER, 4 );
// On Linux, limit window resizing to screen bounds because otherwise
// there would be a strange effect when the mouse is moved over a sidebar
// while resizing and the opposite window side is also resized.
limitResizeToScreenBounds = SystemInfo.isLinux;
}
@Override
public void uninstall() {
Container cont = topDragComp.getParent();
cont.remove( centerComp );
super.uninstall();
}
@Override
public void doLayout() {
super.doLayout();
if( centerComp != null && centerComp.isVisible() )
centerComp.setBounds( 0, 0, resizeComp.getWidth(), resizeComp.getHeight() );
}
@Override
protected void addNotify() {
Container parent = resizeComp.getParent();
@@ -299,20 +330,17 @@ public abstract class FlatWindowResizer
@Override
protected boolean limitToParentBounds() {
return limitResizeToScreenBounds && window != null;
return limitResizeToScreenBounds && window != null && window.getGraphicsConfiguration() != null;
}
@Override
protected Rectangle getParentBounds() {
if( limitResizeToScreenBounds && window != null ) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
Rectangle bounds = gc.getBounds();
Insets insets = window.getToolkit().getScreenInsets( gc );
return new Rectangle( bounds.x + insets.left, bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom );
}
return null;
GraphicsConfiguration gc = window.getGraphicsConfiguration();
Rectangle bounds = gc.getBounds();
Insets insets = window.getToolkit().getScreenInsets( gc );
return new Rectangle( bounds.x + insets.left, bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom );
}
@Override
@@ -346,6 +374,19 @@ public abstract class FlatWindowResizer
public void windowStateChanged( WindowEvent e ) {
updateVisibility();
}
@Override
protected void beginResizing( int resizeDir ) {
centerComp.setBounds( 0, 0, resizeComp.getWidth(), resizeComp.getHeight() );
centerComp.setCursor( getPredefinedCursor( resizeDir ) );
centerComp.setVisible( true );
}
@Override
protected void endResizing() {
centerComp.setVisible( false );
centerComp.setCursor( null );
}
}
//---- class InternalFrameResizer -----------------------------------------
@@ -427,7 +468,18 @@ public abstract class FlatWindowResizer
}
@Override
protected void beginResizing( int direction ) {
protected void beginResizing( int resizeDir ) {
int direction = 0;
switch( resizeDir ) {
case N_RESIZE_CURSOR: direction = NORTH; break;
case S_RESIZE_CURSOR: direction = SOUTH; break;
case W_RESIZE_CURSOR: direction = WEST; break;
case E_RESIZE_CURSOR: direction = EAST; break;
case NW_RESIZE_CURSOR: direction = NORTH_WEST; break;
case NE_RESIZE_CURSOR: direction = NORTH_EAST; break;
case SW_RESIZE_CURSOR: direction = SOUTH_WEST; break;
case SE_RESIZE_CURSOR: direction = SOUTH_EAST; break;
}
desktopManager.get().beginResizingFrame( getFrame(), direction );
}
@@ -535,18 +587,7 @@ debug*/
dragRightOffset = windowBounds.x + windowBounds.width - xOnScreen;
dragBottomOffset = windowBounds.y + windowBounds.height - yOnScreen;
int direction = 0;
switch( resizeDir ) {
case N_RESIZE_CURSOR: direction = NORTH; break;
case S_RESIZE_CURSOR: direction = SOUTH; break;
case W_RESIZE_CURSOR: direction = WEST; break;
case E_RESIZE_CURSOR: direction = EAST; break;
case NW_RESIZE_CURSOR: direction = NORTH_WEST; break;
case NE_RESIZE_CURSOR: direction = NORTH_EAST; break;
case SW_RESIZE_CURSOR: direction = SOUTH_WEST; break;
case SE_RESIZE_CURSOR: direction = SOUTH_EAST; break;
}
beginResizing( direction );
beginResizing( resizeDir );
}
@Override

View File

@@ -16,14 +16,17 @@
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import javax.swing.JComponent;
import javax.swing.RepaintManager;
import com.formdev.flatlaf.FlatSystemProperties;
/**
@@ -322,4 +325,243 @@ public class HiDPIUtils
}
};
}
/**
* Repaints the given component.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c ) {
repaint( c, 0, 0, c.getWidth(), c.getHeight() );
}
/**
* Repaints the given component area.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c, Rectangle r ) {
repaint( c, r.x, r.y, r.width, r.height );
}
/**
* Repaints the given component area.
* <p>
* Invokes {@link Component#repaint(int, int, int, int)} on the given component,
* <p>
* Use this method, instead of {@code Component.repaint(...)},
* to fix a problem in Swing when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The problem may occur under following conditions:
* <ul>
* <li>using Java 9 or later
* <li>system scale factor is 125%, 175%, 225%, ...
* (Windows only; Java on macOS and Linux does not support fractional scale factors)
* <li>repaint whole component or right/bottom area of component
* <li>component is opaque; or component is contained in a opaque container
* that has same right/bottom bounds as component
* <li>component has bounds that Java/Swing scales different when repainting components
* </ul>
*
* @since 3.5
*/
public static void repaint( Component c, int x, int y, int width, int height ) {
// repaint given component area
// Always invoke repaint() on given component, even if also invoked (below)
// on one of its ancestors, for the case that component overrides that method.
// Also RepaintManager "merges" the two repaints into one.
c.repaint( x, y, width, height );
if( RepaintManager.currentManager( c ) instanceof HiDPIRepaintManager )
return;
// if necessary, also repaint given area in first ancestor that is larger than component
// to avoid clipping issue (see needsSpecialRepaint())
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() ) {
p.repaint( x2, y2, width, height );
break;
}
}
}
}
/**
* There is a problem in Swing, when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The component is first painted to an in-memory image,
* and then that image is copied to the screen.
* See {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* There are two clipping rectangles involved when copying the image to the screen:
* {@code sun.java2d.SunGraphics2D#devClip} and
* {@code sun.java2d.SunGraphics2D#usrClip}.
* <p>
* {@code devClip} is the device clipping in physical pixels.
* It gets the bounds of the painting component, which is either the passed component,
* or if it is non-opaque, then the first opaque ancestor of the passed component.
* It is calculated in {@code sun.java2d.SunGraphics2D#constrain()} while
* getting a graphics context via {@link JComponent#getGraphics()}.
* <p>
* {@code usrClip} is the user clipping, which is set via {@link Graphics} clipping methods.
* This is done in {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* The intersection of {@code devClip} and {@code usrClip}
* (computed in {@code sun.java2d.SunGraphics2D#validateCompClip()})
* is used to copy the image to the screen.
* <p>
* Unfortunately different scaling/rounding strategies are used to calculate
* the two clipping rectangles, which is the reason of the issue.
* <p>
* {@code devClip} (see {@code sun.java2d.SunGraphics2D#constrain()}):
* <pre>{@code
* int devX = (int) (x * scale);
* int devWidth = Math.round( width * scale )
* }</pre>
* {@code usrClip} (see {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}):
* <pre>{@code
* int usrX = (int) Math.ceil( (x * scale) - 0.5 );
* int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;
* }</pre>
* X/Y coordinates are always rounded down for {@code devClip}, but rounded up for {@code usrClip}.
* Width/height calculation is also different.
*/
private static boolean needsSpecialRepaint( Component c, int x, int y, int width, int height ) {
// no special repaint necessary for Java 8 or for macOS and Linux
// (Java on those platforms does not support fractional scale factors)
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return false;
// check whether repaint area is empty or no component given
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
if( width <= 0 || height <= 0 || c == null )
return false;
// check whether component has zero size
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
int compWidth = c.getWidth();
int compHeight = c.getHeight();
if( compWidth <= 0 || compHeight <= 0 )
return false;
// check whether repaint area does span to right or bottom component edges
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x + width < compWidth && y + height < compHeight )
return false;
// if component is not opaque, Swing uses the first opaque ancestor for painting
if( !c.isOpaque() ) {
int x2 = x;
int y2 = y;
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( p.isOpaque() ) {
// check whether repaint area does span to right or bottom edges
// of the opaque ancestor component
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() )
return false;
break;
}
}
}
// check whether Special repaint is necessary for current scale factor
// (doing this check late because it temporary allocates some memory)
double scaleFactor = UIScale.getSystemScaleFactor( c.getGraphicsConfiguration() );
double fraction = scaleFactor - (int) scaleFactor;
if( fraction == 0 || fraction == 0.5 )
return false;
return true;
}
/**
* Installs a {@link HiDPIRepaintManager} on Windows when running in Java 9+,
* but only if default repaint manager is currently installed.
* <p>
* Invoke once on application startup.
* Compatible with all/other LaFs.
*
* @since 3.5
*/
public static void installHiDPIRepaintManager() {
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return;
RepaintManager manager = RepaintManager.currentManager( (Component) null );
if( manager.getClass() == RepaintManager.class )
RepaintManager.setCurrentManager( new HiDPIRepaintManager() );
}
/**
* Similar to {@link #repaint(Component, int, int, int, int)},
* but invokes callback instead of invoking {@link Component#repaint(int, int, int, int)}.
* <p>
* For use in custom repaint managers.
*
* @since 3.5
*/
public static void addDirtyRegion( JComponent c, int x, int y, int width, int height, DirtyRegionCallback callback ) {
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() && p instanceof JComponent ) {
callback.addDirtyRegion( (JComponent) p, x2, y2, width, height );
return;
}
x2 += p.getX();
y2 += p.getY();
}
}
callback.addDirtyRegion( c, x, y, width, height );
}
//---- interface DirtyRegionCallback --------------------------------------
/**
* For {@link HiDPIUtils#addDirtyRegion(JComponent, int, int, int, int, DirtyRegionCallback)}.
*
* @since 3.5
*/
public interface DirtyRegionCallback {
void addDirtyRegion( JComponent c, int x, int y, int w, int h );
}
//---- class HiDPIRepaintManager ------------------------------------------
/**
* A repaint manager that fixes a problem in Swing when repainting components
* at some scale factors (e.g. 125%, 175%, etc) on Windows.
* <p>
* Use {@link HiDPIUtils#installHiDPIRepaintManager()} to install it.
* <p>
* See {@link HiDPIUtils#repaint(Component, int, int, int, int)} for details.
*
* @since 3.5
*/
public static class HiDPIRepaintManager
extends RepaintManager
{
@Override
public void addDirtyRegion( JComponent c, int x, int y, int w, int h ) {
HiDPIUtils.addDirtyRegion( c, x, y, w, h, super::addDirtyRegion );
}
}
}

View File

@@ -33,6 +33,7 @@ import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
/**
@@ -188,7 +189,9 @@ public class UIScale
// because even if we are on a HiDPI display it is not sure
// that a larger font size is set by the current LaF
// (e.g. can avoid large icons with small text)
Font font = UIManager.getFont( "defaultFont" );
Font font = null;
if( UIManager.getLookAndFeel() instanceof FlatLaf )
font = UIManager.getFont( "defaultFont" );
if( font == null )
font = UIManager.getFont( "Label.font" );
@@ -244,6 +247,16 @@ public class UIScale
}
private static float computeScaleFactor( Font font ) {
String customFontSizeDivider = System.getProperty( "flatlaf.uiScale.fontSizeDivider" );
if( customFontSizeDivider != null ) {
try {
float fontSizeDivider = Math.max( Integer.parseInt( customFontSizeDivider ), 10 );
return font.getSize() / fontSizeDivider;
} catch( NumberFormatException ex ) {
// ignore
}
}
// default font size
float fontSizeDivider = 12f;

View File

@@ -84,10 +84,12 @@ public class TestUIDefaultsLoader
void parseBorders() {
Insets insets = new Insets( 1,2,3,4 );
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4" );
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4,,," );
assertBorderEquals( new FlatLineBorder( insets, Color.red ), "1,2,3,4,#f00" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 0 ), "1,2,3,4,#f00,2.5" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, -1 ), "1,2,3,4,#f00,2.5" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 6 ), "1,2,3,4,#f00,2.5,6" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 1, 6 ), "1,2,3,4,#f00,,6" );
assertBorderEquals( new FlatLineBorder( insets, null, 1, 6 ), "1,2,3,4,,,6" );
}
private void assertBorderEquals( Border expected, String actualStyle ) {

View File

@@ -0,0 +1,123 @@
/*
* 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 static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Locale;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.View;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author Karl Tauber
*/
public class TestFlatHTML
{
private final String body = "some <small>small</small> text";
private final String bodyInBody = "<body>" + body + "</body>";
private final String bodyPlain = "some small text";
@BeforeAll
static void setup() {
TestUtils.setup( false );
TestUtils.scaleFont( 2 );
}
@AfterAll
static void cleanup() {
TestUtils.cleanup();
}
@Test
void simple() {
testHtmlBaseSize( "<html>${BASE_SIZE_IN_HEAD}" + body + "</html>", bodyPlain );
testHtmlBaseSize( "<html>${BASE_SIZE_IN_HEAD}" + bodyInBody + "</html>", bodyPlain );
}
@Test
void htmlWithHeadTag() {
testHtmlBaseSize( "<html><head>${BASE_SIZE}<title>test</title><head>" + body + "</html>", bodyPlain );
testHtmlBaseSize( "<html><head>${BASE_SIZE}<title>test</title><head>" + bodyInBody + "</html>", bodyPlain );
testHtmlBaseSize( "<html><head id=\"abc\">${BASE_SIZE}<title>test</title><head>" + body + "</html>", bodyPlain );
testHtmlBaseSize( "<html><head id=\"abc\">${BASE_SIZE}<title>test</title><head>" + bodyInBody + "</html>", bodyPlain );
}
@Test
void htmlWithStyleTag() {
testHtmlBaseSize( "<html>${BASE_SIZE}<style>body { color: #f00; }</style>" + bodyInBody + "</html>", bodyPlain );
testHtmlBaseSize( "<html>${BASE_SIZE}<style>body { color: #f00; }</style><h1>header1</h1>" + body + "</html>", "header1\n" + bodyPlain );
testHtmlBaseSize( "<html>${BASE_SIZE}<style type='text/css'>body { color: #f00; }</style>" + bodyInBody + "</html>", bodyPlain );
testHtmlBaseSize( "<html>${BASE_SIZE}<style type='text/css'>body { color: #f00; }</style><h1>header1</h1>" + body + "</html>", "header1\n" + bodyPlain );
}
@Test
void htmlOnComponentWithNullFont() {
assertDoesNotThrow( () -> {
JLabel label = new JLabel();
label.setFont( null );
label.setText( "<html>foo<br>bar</html>" );
} );
}
private void testHtmlBaseSize( String html, String expectedPlain ) {
testHtmlBaseSizeImpl( html, expectedPlain );
testHtmlBaseSizeImpl( html.toUpperCase( Locale.ENGLISH ), expectedPlain.toUpperCase( Locale.ENGLISH ) );
}
private void testHtmlBaseSizeImpl( String html, String expectedPlain ) {
String baseSize = "<style>BASE_SIZE " + UIManager.getFont( "Label.font" ).getSize() + "</style>";
String baseSizeInHead = "<head>" + baseSize + "</head>";
String expectedHtml = html.replace( "${BASE_SIZE}", baseSize ).replace( "${BASE_SIZE_IN_HEAD}", baseSizeInHead );
html = html.replace( "${BASE_SIZE}", "" ).replace( "${BASE_SIZE_IN_HEAD}", "" );
testHtml( html, expectedHtml, expectedPlain );
}
private void testHtml( String html, String expectedHtml, String expectedPlain ) {
FlatHTML.testUpdateRenderer = (c, newHtml) -> {
assertEquals( expectedHtml, newHtml );
assertEquals( expectedPlain, getPlainText( c ) );
};
new JLabel( html );
FlatHTML.testUpdateRenderer = null;
}
private String getPlainText( JComponent c ) {
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
if( view == null )
return null;
Document doc = view.getDocument();
try {
return doc.getText( 0, doc.getLength() ).trim();
} catch( BadLocationException ex ) {
ex.printStackTrace();
return null;
}
}
}

View File

@@ -253,7 +253,8 @@ public class TestFlatStyleableInfo
FlatLabelUI ui = (FlatLabelUI) c.getUI();
Map<String, Class<?>> expected = expectedMap(
"disabledForeground", Color.class
"disabledForeground", Color.class,
"arc", int.class
);
assertMapEquals( expected, ui.getStyleableInfos( c ) );
@@ -799,6 +800,8 @@ public class TestFlatStyleableInfo
"selectionForeground", Color.class,
"selectionInactiveBackground", Color.class,
"selectionInactiveForeground", Color.class,
"selectionInsets", Insets.class,
"selectionArc", int.class,
// FlatTableCellBorder
"cellMargins", Insets.class,
@@ -993,12 +996,20 @@ public class TestFlatStyleableInfo
"disabledBorderColor", Color.class,
"focusedBorderColor", Color.class,
"hoverBorderColor", Color.class,
"pressedBorderColor", Color.class,
"selectedBorderColor", Color.class,
"disabledSelectedBorderColor", Color.class,
"focusedSelectedBorderColor", Color.class,
"hoverSelectedBorderColor", Color.class,
"pressedSelectedBorderColor", Color.class,
"default.borderWidth", float.class,
"default.borderColor", Color.class,
"default.focusedBorderColor", Color.class,
"default.focusColor", Color.class,
"default.hoverBorderColor", Color.class,
"default.pressedBorderColor", Color.class,
"toolbar.focusWidth", float.class,
"toolbar.focusColor", Color.class,

View File

@@ -305,6 +305,9 @@ public class TestFlatStyleableValue
testColor( c, ui, "buttonPressedArrowColor", 0x123456 );
testColor( c, ui, "popupBackground", 0x123456 );
testInsets( c, ui, "popupInsets", 1,2,3,4 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// border
flatRoundBorder( c, ui );
@@ -355,6 +358,7 @@ public class TestFlatStyleableValue
FlatLabelUI ui = (FlatLabelUI) c.getUI();
testColor( c, ui, "disabledForeground", 0x123456 );
testInteger( c, ui, "arc", 123 );
}
@Test
@@ -366,6 +370,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// FlatListCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -801,6 +807,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x901324 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// FlatTableCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -930,6 +938,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
testColor( c, ui, "selectionBorderColor", 0x123456 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
testBoolean( c, ui, "wideSelection", true );
testBoolean( c, ui, "showCellFocusIndicator", true );
@@ -955,12 +965,20 @@ public class TestFlatStyleableValue
testColor( c, ui, "disabledBorderColor", 0x123456 );
testColor( c, ui, "focusedBorderColor", 0x123456 );
testColor( c, ui, "hoverBorderColor", 0x123456 );
testColor( c, ui, "pressedBorderColor", 0x123456 );
testColor( c, ui, "selectedBorderColor", 0x123456 );
testColor( c, ui, "disabledSelectedBorderColor", 0x123456 );
testColor( c, ui, "focusedSelectedBorderColor", 0x123456 );
testColor( c, ui, "hoverSelectedBorderColor", 0x123456 );
testColor( c, ui, "pressedSelectedBorderColor", 0x123456 );
testFloat( c, ui, "default.borderWidth", 1.23f );
testColor( c, ui, "default.borderColor", 0x123456 );
testColor( c, ui, "default.focusedBorderColor", 0x123456 );
testColor( c, ui, "default.focusColor", 0x123456 );
testColor( c, ui, "default.hoverBorderColor", 0x123456 );
testColor( c, ui, "default.pressedBorderColor", 0x123456 );
testFloat( c, ui, "toolbar.focusWidth", 1.23f );
testColor( c, ui, "toolbar.focusColor", 0x123456 );
@@ -1025,12 +1043,20 @@ public class TestFlatStyleableValue
testValue( border, "disabledBorderColor", Color.WHITE );
testValue( border, "focusedBorderColor", Color.WHITE );
testValue( border, "hoverBorderColor", Color.WHITE );
testValue( border, "pressedBorderColor", Color.WHITE );
testValue( border, "selectedBorderColor", Color.WHITE );
testValue( border, "disabledSelectedBorderColor", Color.WHITE );
testValue( border, "focusedSelectedBorderColor", Color.WHITE );
testValue( border, "hoverSelectedBorderColor", Color.WHITE );
testValue( border, "pressedSelectedBorderColor", Color.WHITE );
testValue( border, "default.borderWidth", 2f );
testValue( border, "default.borderColor", Color.WHITE );
testValue( border, "default.focusedBorderColor", Color.WHITE );
testValue( border, "default.focusColor", Color.WHITE );
testValue( border, "default.hoverBorderColor", Color.WHITE );
testValue( border, "default.pressedBorderColor", Color.WHITE );
testValue( border, "toolbar.focusWidth", 1.5f );
testValue( border, "toolbar.focusColor", Color.WHITE );

View File

@@ -412,6 +412,7 @@ public class TestFlatStyling
FlatLabelUI ui = (FlatLabelUI) c.getUI();
ui.applyStyle( c, "disabledForeground: #fff" );
ui.applyStyle( c, "arc: 8" );
// JComponent properties
ui.applyStyle( c, "background: #fff" );
@@ -986,6 +987,8 @@ public class TestFlatStyling
ui.applyStyle( "selectionForeground: #fff" );
ui.applyStyle( "selectionInactiveBackground: #fff" );
ui.applyStyle( "selectionInactiveForeground: #fff" );
ui.applyStyle( "selectionInsets: 1,2,3,4" );
ui.applyStyle( "selectionArc: 8" );
// FlatTableCellBorder
ui.applyStyle( "cellMargins: 1,2,3,4" );
@@ -1294,12 +1297,20 @@ public class TestFlatStyling
border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
border.applyStyleProperty( "hoverBorderColor", Color.WHITE );
border.applyStyleProperty( "pressedBorderColor", Color.WHITE );
border.applyStyleProperty( "selectedBorderColor", Color.WHITE );
border.applyStyleProperty( "disabledSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "focusedSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "hoverSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "pressedSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "default.borderWidth", 2 );
border.applyStyleProperty( "default.borderColor", Color.WHITE );
border.applyStyleProperty( "default.focusedBorderColor", Color.WHITE );
border.applyStyleProperty( "default.focusColor", Color.WHITE );
border.applyStyleProperty( "default.hoverBorderColor", Color.WHITE );
border.applyStyleProperty( "default.pressedBorderColor", Color.WHITE );
border.applyStyleProperty( "toolbar.focusWidth", 1.5f );
border.applyStyleProperty( "toolbar.focusColor", Color.WHITE );

View File

@@ -6,7 +6,7 @@
# when the Demo window is activated.
# base theme (light, dark, intellij or darcula)
# base theme (light, dark, intellij, darcula, maclight or macdark)
@baseTheme = light
# add you theme defaults here

View File

@@ -140,8 +140,8 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
// register Alt+UP and Alt+DOWN to switch to previous/next theme
((JComponent)frame.getContentPane()).registerKeyboardAction(
@@ -153,6 +153,9 @@ class ControlBar
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
// register Alt+Shift+F1, F2, ... keys to change system scale factor
DemoPrefs.registerSystemScaleFactors( frame );
// register ESC key to close frame
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> {

View File

@@ -25,6 +25,8 @@ import javax.swing.*;
import javax.swing.table.*;
import javax.swing.tree.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.ColorFunctions;
import net.miginfocom.swing.*;
/**
@@ -93,10 +95,12 @@ class DataComponentsPanel
private void rowSelectionChanged() {
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
roundedSelectionChanged();
}
private void columnSelectionChanged() {
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
roundedSelectionChanged();
}
private void showHorizontalLinesChanged() {
@@ -127,6 +131,28 @@ class DataComponentsPanel
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
}
private void roundedSelectionChanged() {
String style = null;
if( roundedSelectionCheckBox.isSelected() ) {
style = rowSelectionCheckBox.isSelected()
? "selectionArc: 6; selectionInsets: 0,1,0,1"
: "selectionArc: 6";
}
table1.putClientProperty( FlatClientProperties.STYLE, style );
}
private void alternatingRowsChanged() {
Color alternateRowColor = null;
if( alternatingRowsCheckBox.isSelected() ) {
Color background = table1.getBackground();
alternateRowColor = FlatLaf.isLafDark()
? ColorFunctions.lighten( background, 0.05f )
: ColorFunctions.darken( background, 0.05f );
}
UIManager.put( "Table.alternateRowColor", alternateRowColor );
table1.repaint();
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
@@ -151,12 +177,14 @@ class DataComponentsPanel
JScrollPane scrollPane5 = new JScrollPane();
table1 = new JTable();
JPanel tableOptionsPanel = new JPanel();
roundedSelectionCheckBox = new JCheckBox();
showHorizontalLinesCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
intercellSpacingCheckBox = new JCheckBox();
redGridColorCheckBox = new JCheckBox();
rowSelectionCheckBox = new JCheckBox();
columnSelectionCheckBox = new JCheckBox();
alternatingRowsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPopupMenu popupMenu2 = new JPopupMenu();
JMenuItem menuItem3 = new JMenuItem();
@@ -403,44 +431,56 @@ class DataComponentsPanel
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0"));
//---- roundedSelectionCheckBox ----
roundedSelectionCheckBox.setText("rounded selection");
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
tableOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
//---- showHorizontalLinesCheckBox ----
showHorizontalLinesCheckBox.setText("show horizontal lines");
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 0");
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1");
//---- showVerticalLinesCheckBox ----
showVerticalLinesCheckBox.setText("show vertical lines");
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 1");
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 2");
//---- intercellSpacingCheckBox ----
intercellSpacingCheckBox.setText("intercell spacing");
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 2");
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 3");
//---- redGridColorCheckBox ----
redGridColorCheckBox.setText("red grid color");
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 3");
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 4");
//---- rowSelectionCheckBox ----
rowSelectionCheckBox.setText("row selection");
rowSelectionCheckBox.setSelected(true);
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 4");
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 5");
//---- columnSelectionCheckBox ----
columnSelectionCheckBox.setText("column selection");
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 5");
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 6");
//---- alternatingRowsCheckBox ----
alternatingRowsCheckBox.setText("alternating rows");
alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged());
tableOptionsPanel.add(alternatingRowsCheckBox, "cell 0 7");
//---- dndCheckBox ----
dndCheckBox.setText("enable drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
tableOptionsPanel.add(dndCheckBox, "cell 0 6");
tableOptionsPanel.add(dndCheckBox, "cell 0 8");
}
add(tableOptionsPanel, "cell 4 3");
@@ -477,12 +517,14 @@ class DataComponentsPanel
private JTree tree3;
private JTree tree2;
private JTable table1;
private JCheckBox roundedSelectionCheckBox;
private JCheckBox showHorizontalLinesCheckBox;
private JCheckBox showVerticalLinesCheckBox;
private JCheckBox intercellSpacingCheckBox;
private JCheckBox redGridColorCheckBox;
private JCheckBox rowSelectionCheckBox;
private JCheckBox columnSelectionCheckBox;
private JCheckBox alternatingRowsCheckBox;
private JCheckBox dndCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -343,9 +343,19 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[]"
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0"
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "roundedSelectionCheckBox"
"text": "rounded selection"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showHorizontalLinesCheckBox"
"text": "show horizontal lines"
@@ -354,7 +364,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showVerticalLinesCheckBox"
@@ -364,7 +374,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "intercellSpacingCheckBox"
@@ -374,7 +384,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "redGridColorCheckBox"
@@ -384,7 +394,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rowSelectionCheckBox"
@@ -395,7 +405,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "columnSelectionCheckBox"
@@ -405,7 +415,17 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "alternatingRowsCheckBox"
"text": "alternating rows"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alternatingRowsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
@@ -416,7 +436,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
"value": "cell 0 8"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3"

View File

@@ -19,6 +19,10 @@ package com.formdev.flatlaf.demo;
import java.io.File;
import java.io.FileInputStream;
import java.util.prefs.Preferences;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
@@ -27,6 +31,7 @@ import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
@@ -35,6 +40,7 @@ public class DemoPrefs
{
public static final String KEY_LAF = "laf";
public static final String KEY_LAF_THEME = "lafTheme";
public static final String KEY_SYSTEM_SCALE_FACTOR = "systemScaleFactor";
public static final String RESOURCE_PREFIX = "res:";
public static final String FILE_PREFIX = "file:";
@@ -96,4 +102,66 @@ public class DemoPrefs
state.put( KEY_LAF, UIManager.getLookAndFeel().getClass().getName() );
} );
}
public static void initSystemScale() {
if( System.getProperty( "sun.java2d.uiScale" ) == null ) {
String scaleFactor = getState().get( KEY_SYSTEM_SCALE_FACTOR, null );
if( scaleFactor != null )
System.setProperty( "sun.java2d.uiScale", scaleFactor );
}
}
/**
* register Alt+Shift+F1, F2, ... F12 keys to change system scale factor
*/
public static void registerSystemScaleFactors( JFrame frame ) {
registerSystemScaleFactor( frame, "alt shift F1", null );
registerSystemScaleFactor( frame, "alt shift F2", "1" );
if( SystemInfo.isWindows ) {
registerSystemScaleFactor( frame, "alt shift F3", "1.25" );
registerSystemScaleFactor( frame, "alt shift F4", "1.5" );
registerSystemScaleFactor( frame, "alt shift F5", "1.75" );
registerSystemScaleFactor( frame, "alt shift F6", "2" );
registerSystemScaleFactor( frame, "alt shift F7", "2.25" );
registerSystemScaleFactor( frame, "alt shift F8", "2.5" );
registerSystemScaleFactor( frame, "alt shift F9", "2.75" );
registerSystemScaleFactor( frame, "alt shift F10", "3" );
registerSystemScaleFactor( frame, "alt shift F11", "3.5" );
registerSystemScaleFactor( frame, "alt shift F12", "4" );
} else {
// Java on macOS and Linux supports only integer scale factors
registerSystemScaleFactor( frame, "alt shift F3", "2" );
registerSystemScaleFactor( frame, "alt shift F4", "3" );
registerSystemScaleFactor( frame, "alt shift F5", "4" );
}
}
private static void registerSystemScaleFactor( JFrame frame, String keyStrokeStr, String scaleFactor ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
if( keyStroke == null )
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> applySystemScaleFactor( frame, scaleFactor ),
keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
}
private static void applySystemScaleFactor( JFrame frame, String scaleFactor ) {
if( JOptionPane.showConfirmDialog( frame,
"Change system scale factor to "
+ (scaleFactor != null ? scaleFactor : "default")
+ " and exit?",
frame.getTitle(), JOptionPane.YES_NO_OPTION ) != JOptionPane.YES_OPTION )
return;
if( scaleFactor != null )
DemoPrefs.getState().put( KEY_SYSTEM_SCALE_FACTOR, scaleFactor );
else
DemoPrefs.getState().remove( KEY_SYSTEM_SCALE_FACTOR );
System.exit( 0 );
}
}

View File

@@ -70,9 +70,10 @@ public class FlatLafDemo
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
System.setProperty( "flatlaf.uiScale", "2x" );
SwingUtilities.invokeLater( () -> {
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.initSystemScale();
SwingUtilities.invokeLater( () -> {
// install fonts for lazy loading
FlatInterFont.installLazy();
FlatJetBrainsMonoFont.installLazy();
@@ -95,6 +96,9 @@ public class FlatLafDemo
// use Roboto Mono font
// FlatLaf.setPreferredMonospacedFontFamily( FlatRobotoMonoFont.FAMILY );
// install own repaint manager to fix repaint issues at 125%, 175%, 225%, ... on Windows
// HiDPIUtils.installHiDPIRepaintManager();
// application specific UI defaults
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );

View File

@@ -118,6 +118,14 @@ class MoreComponentsPanel
JLabel label5 = new JLabel();
JPanel panel13 = new JPanel();
JLabel label6 = new JLabel();
JLabel panelLabel = new JLabel();
JPanel panel5 = new JPanel();
JLabel label9 = new JLabel();
JPanel panel4 = new JPanel();
JLabel label8 = new JLabel();
JLabel labelLabel = new JLabel();
JLabel label13 = new JLabel();
JLabel label10 = new JLabel();
//======== this ========
setLayout(new MigLayout(
@@ -140,7 +148,9 @@ class MoreComponentsPanel
"[]" +
"[]" +
"[]" +
"[100,top]"));
"[100,top]" +
"[50,top]" +
"[]"));
//---- scrollPaneLabel ----
scrollPaneLabel.setText("JScrollPane:");
@@ -441,7 +451,7 @@ class MoreComponentsPanel
//======== panel10 ========
{
panel10.setBackground(new Color(217, 163, 67));
panel10.setBackground(new Color(0xd9a343));
panel10.setLayout(new BorderLayout());
//---- label1 ----
@@ -454,7 +464,7 @@ class MoreComponentsPanel
//======== panel11 ========
{
panel11.setBackground(new Color(98, 181, 67));
panel11.setBackground(new Color(0x62b543));
panel11.setLayout(new BorderLayout());
//---- label2 ----
@@ -474,7 +484,7 @@ class MoreComponentsPanel
//======== panel12 ========
{
panel12.setBackground(new Color(242, 101, 34));
panel12.setBackground(new Color(0xf26522));
panel12.setLayout(new BorderLayout());
//---- label5 ----
@@ -487,7 +497,7 @@ class MoreComponentsPanel
//======== panel13 ========
{
panel13.setBackground(new Color(64, 182, 224));
panel13.setBackground(new Color(0x40b6e0));
panel13.setLayout(new BorderLayout());
//---- label6 ----
@@ -502,6 +512,52 @@ class MoreComponentsPanel
}
add(splitPane3, "cell 1 11 4 1,grow");
//---- panelLabel ----
panelLabel.setText("JPanel:");
add(panelLabel, "cell 0 12");
//======== panel5 ========
{
panel5.putClientProperty("FlatLaf.style", "arc: 16; background: darken($Panel.background,5%)");
panel5.setLayout(new BorderLayout());
//---- label9 ----
label9.setText("rounded background");
label9.setHorizontalAlignment(SwingConstants.CENTER);
panel5.add(label9, BorderLayout.CENTER);
}
add(panel5, "cell 1 12 4 1,growy,width 150");
//======== panel4 ========
{
panel4.putClientProperty("FlatLaf.style", "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)");
panel4.setLayout(new BorderLayout());
//---- label8 ----
label8.setText("rounded border");
label8.setHorizontalAlignment(SwingConstants.CENTER);
panel4.add(label8, BorderLayout.CENTER);
}
add(panel4, "cell 1 12 4 1,growy,width 150");
//---- labelLabel ----
labelLabel.setText("JLabel:");
add(labelLabel, "cell 0 13");
//---- label13 ----
label13.setText("rounded background");
label13.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10");
label13.setBackground(new Color(0xb8e4f3));
label13.setForeground(new Color(0x135b76));
add(label13, "cell 1 13 4 1");
//---- label10 ----
label10.setText("rounded border");
label10.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10,#135b76");
label10.setBackground(new Color(0xb8e4f3));
label10.setForeground(new Color(0x135b76));
add(label10, "cell 1 13 4 1");
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
buttonGroup1.add(toggleButton1);

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[][][][][]"
"$rowConstraints": "[][][][][][][][][][][][100,top]"
"$rowConstraints": "[][][][][][][][][][][][100,top][50,top][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -467,6 +467,62 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11 4 1,grow"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "panelLabel"
"text": "JPanel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel5"
"$client.FlatLaf.style": "arc: 16; background: darken($Panel.background,5%)"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"text": "rounded background"
"horizontalAlignment": 0
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12 4 1,growy,width 150"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel4"
"$client.FlatLaf.style": "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"text": "rounded border"
"horizontalAlignment": 0
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12 4 1,growy,width 150"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "labelLabel"
"text": "JLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label13"
"text": "rounded background"
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10"
"background": new java.awt.Color( 184, 228, 243, 255 )
"foreground": new java.awt.Color( 19, 91, 118, 255 )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13 4 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"text": "rounded border"
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10,#135b76"
"background": new java.awt.Color( 184, 228, 243, 255 )
"foreground": new java.awt.Color( 19, 91, 118, 255 )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13 4 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 700, 550 )
@@ -474,7 +530,7 @@ new FormModel {
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonGroup1"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 560 )
"location": new java.awt.Point( 0, 600 )
} )
}
}

View File

@@ -24,6 +24,7 @@ import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
@@ -450,15 +451,18 @@ public class FlatInspector
Dimension size = tip.getPreferredSize();
// position the tip in the visible area
Rectangle visibleRect = rootPane.getGraphicsConfiguration().getBounds();
if( tx + size.width > visibleRect.x + visibleRect.width )
tx -= size.width + UIScale.scale( 16 );
if( ty + size.height > visibleRect.y + visibleRect.height )
ty -= size.height + UIScale.scale( 32 );
if( tx < visibleRect.x )
tx = visibleRect.x;
if( ty < visibleRect.y )
ty = visibleRect.y;
GraphicsConfiguration gc = rootPane.getGraphicsConfiguration();
if( gc != null ) {
Rectangle visibleRect = gc.getBounds();
if( tx + size.width > visibleRect.x + visibleRect.width )
tx -= size.width + UIScale.scale( 16 );
if( ty + size.height > visibleRect.y + visibleRect.height )
ty -= size.height + UIScale.scale( 32 );
if( tx < visibleRect.x )
tx = visibleRect.x;
if( ty < visibleRect.y )
ty = visibleRect.y;
}
PopupFactory popupFactory = PopupFactory.getSharedInstance();
popup = popupFactory.getPopup( c, tip, tx, ty );
@@ -547,6 +551,12 @@ public class FlatInspector
appendRow( buf, "Left-to-right", String.valueOf( c.getComponentOrientation().isLeftToRight() ) );
appendRow( buf, "Parent", (c.getParent() != null ? toString( c.getParent().getClass(), classHierarchy ) : "null") );
if( c instanceof JComponent ) {
Object style = ((JComponent)c).getClientProperty( FlatClientProperties.STYLE );
if( style != null )
appendRow( buf, "FlatLaf Style", style.toString() );
}
// append parent level
buf.append( "<tr><td colspan=\"2\">" );
if( parentLevel > 0 )

View File

@@ -28,10 +28,10 @@ Otherwise, download `flatlaf-intellij-themes-<version>.jar` here:
How to use?
-----------
Choose a theme (see list below) and invoke its `install` method. E.g.:
Choose a theme (see list below) and invoke its `setup` method. E.g.:
~~~java
FlatArcOrangeIJTheme.install();
FlatArcOrangeIJTheme.setup();
~~~

View File

@@ -16,9 +16,14 @@
package com.formdev.flatlaf.jideoss.ui;
import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import com.formdev.flatlaf.ui.FlatHTML;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideButtonListener;
import com.jidesoft.plaf.basic.BasicJideButtonUI;
/**
@@ -37,4 +42,26 @@ public class FlatJideButtonUI
return new FlatJideButtonUI();
}
@Override
protected BasicButtonListener createButtonListener( AbstractButton b ) {
return new FlatJideButtonListener( b );
}
//---- class FlatJideButtonListener ---------------------------------------
/** @since 3.5 */
protected static class FlatJideButtonListener
extends BasicJideButtonListener
{
protected FlatJideButtonListener( AbstractButton b ) {
super( b );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
}
}

View File

@@ -18,11 +18,13 @@ package com.formdev.flatlaf.jideoss.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideLabelUI;
@@ -65,6 +67,20 @@ public class FlatJideLabelUI
defaults_initialized = false;
}
@Override
protected void installComponents( JLabel c ) {
super.installComponents( c );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -60,8 +60,11 @@ import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinUser.HMONITOR;
import com.sun.jna.platform.win32.WinUser.WindowProc;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
//
@@ -306,6 +309,8 @@ public class FlatWindowsNativeWindowBorder
WM_ENTERSIZEMOVE = 0x0231,
WM_EXITSIZEMOVE = 0x0232,
WM_DPICHANGED = 0x02E0,
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
// WM_SIZE wParam
@@ -498,6 +503,22 @@ public class FlatWindowsNativeWindowBorder
isMoving = true;
break;
case WM_DPICHANGED:
LRESULT lResult = User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
// if window is maximized and DPI/scaling changed, then Windows
// does not send a subsequent WM_SIZE message and Java window bounds,
// which depend on scale factor, are not updated
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
if( isMaximized ) {
MyRECT r = new MyRECT( new Pointer( lParam.longValue() ) );
int width = r.right - r.left;
int height = r.bottom - r.top;
User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, WM_SIZE, new WPARAM( SIZE_MAXIMIZED ), MAKELPARAM( width, height ) );
}
return lResult;
case WM_ERASEBKGND:
// do not erase background while the user is moving the window,
// otherwise there may be rendering artifacts on HiDPI screens with Java 9+
@@ -615,6 +636,14 @@ public class FlatWindowsNativeWindowBorder
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params.rgrc[0].right--;
}
} else if( SystemInfo.isWindows_11_orLater ) {
// For Windows 11, add border thickness to top, which is necessary to make the whole Java area visible.
// This also avoids that a black line is sometimes painted on top window border.
// Note: Do not increase top on Windows 10 because this would not hide Windows title bar.
IntByReference borderThickness = new IntByReference();
if( DWMApi.INSTANCE.DwmGetWindowAttribute( hwnd, DWMApi.DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
borderThickness.getPointer(), 4 ) == WinError.S_OK.intValue() )
params.rgrc[0].top += borderThickness.getValue();
}
// write changed params back to native memory
@@ -807,6 +836,13 @@ public class FlatWindowsNativeWindowBorder
return (low & 0xffff) | ((high & 0xffff) << 16);
}
/**
* Same implementation as MAKELPARAM(l, h) macro in winuser.h.
*/
private LPARAM MAKELPARAM( int low, int high ) {
return new LPARAM( MAKELONG( low, high ) );
}
/**
* Same implementation as RGB(r,g,b) macro in wingdi.h.
*/
@@ -898,6 +934,18 @@ public class FlatWindowsNativeWindowBorder
HBRUSH CreateSolidBrush( DWORD color );
}
//---- interface DWMApi ---------------------------------------------------
private interface DWMApi
extends StdCallLibrary
{
DWMApi INSTANCE = Native.load( "dwmapi", DWMApi.class, W32APIOptions.DEFAULT_OPTIONS );
int DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;
int DwmGetWindowAttribute( HWND hwnd, int dwAttribute, Pointer pvAttribute, int cbAttribute );
}
//---- class NCCALCSIZE_PARAMS --------------------------------------------
@FieldOrder( { "rgrc" } )
@@ -914,6 +962,23 @@ public class FlatWindowsNativeWindowBorder
}
}
//---- class MyRECT -------------------------------------------------------
@FieldOrder( { "left", "top", "right", "bottom" } )
public static class MyRECT
extends Structure
{
public int left;
public int top;
public int right;
public int bottom;
public MyRECT( Pointer pointer ) {
super( pointer );
read();
}
}
//---- class MENUITEMINFO -------------------------------------------------
@FieldOrder( { "cbSize", "fMask", "fType", "fState", "wID", "hSubMenu",

View File

@@ -19,3 +19,32 @@ The DLLs were built on a GitHub server with the help of GitHub Actions. See:
[Native Libraries](https://github.com/JFormDesigner/FlatLaf/actions/workflows/natives.yml)
workflow. Then the produced Artifacts ZIP was downloaded, signed DLLs with
FormDev Software code signing certificate and committed the DLLs to Git.
## Development
To build the library on Windows using Gradle, (parts of)
[Visual Studio Community
2022](https://visualstudio.microsoft.com/downloads/)
needs to be installed. After downloading and running `VisualStudioSetup.exe` the
**Visual Studio Installer** is installed and started. Once running, it shows the
**Workloads** tab that allows you to install additional packages. Either choose
**Desktop development with C++**, or to save some disk space switch to the
**Single Components** tab and choose following components (newest versions):
- MSVC v143 - VS 2022 C++ x64/x86 Buildtools
- MSVC v143 - VS 2022 C++ ARM64/ARM64EC Buildtools
- Windows 11 SDK
Note that the Visual Studio Installer shows many similar named components for
MSVC. Make sure to choose exactly those components listed above.
Using
[Build Tools for Visual Studio 2022](https://visualstudio.microsoft.com/downloads/#remote-tools-for-visual-studio-2022)
(installs only compiler and SDKs) instead of
[Visual Studio Community
2022](https://visualstudio.microsoft.com/downloads/)
does not work with Gradle.
[Visual Studio Code](https://code.visualstudio.com/) with **C/C++** extension
can be used for C++ code editing.

View File

@@ -20,6 +20,7 @@
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <dwmapi.h>
#include <jawt.h>
#include <jawt_md.h>
#include "FlatWndProc.h"
@@ -76,6 +77,7 @@ jmethodID FlatWndProc::isFullscreenMID;
jmethodID FlatWndProc::fireStateChangedLaterOnceMID;
HWNDMap* FlatWndProc::hwndMap;
DWORD FlatWndProc::osBuildNumber = 0;
#define java_awt_Frame_ICONIFIED 1
#define java_awt_Frame_MAXIMIZED_BOTH (4 | 2)
@@ -107,6 +109,14 @@ HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
return 0;
}
// get OS build number
if( osBuildNumber == 0 ) {
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof( info );
if( ::GetVersionEx( &info ) )
osBuildNumber = info.dwBuildNumber;
}
// get window handle
HWND hwnd = getWindowHandle( env, window );
if( hwnd == NULL || hwndMap->get( hwnd ) != NULL )
@@ -278,6 +288,23 @@ LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, L
isMoving = true;
break;
case WM_DPICHANGED: {
LRESULT lResult = ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
// if window is maximized and DPI/scaling changed, then Windows
// does not send a subsequent WM_SIZE message and Java window bounds,
// which depend on scale factor, are not updated
bool isMaximized = ::IsZoomed( hwnd );
if( isMaximized ) {
RECT* r = reinterpret_cast<RECT*>( lParam );
int width = r->right - r->left;
int height = r->bottom - r->top;
::CallWindowProc( defaultWndProc, hwnd, WM_SIZE, SIZE_MAXIMIZED, MAKELPARAM( width, height ) );
}
return lResult;
}
case WM_ERASEBKGND:
// do not erase background while the user is moving the window,
// otherwise there may be rendering artifacts on HiDPI screens with Java 9+
@@ -391,6 +418,13 @@ LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lP
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params->rgrc[0].right--;
}
} else if( osBuildNumber >= 22000 ) {
// For Windows 11, add border thickness to top, which is necessary to make the whole Java area visible.
// This also avoids that a black line is sometimes painted on top window border.
// Note: Do not increase top on Windows 10 because this would not hide Windows title bar.
UINT borderThickness = 0;
if( ::DwmGetWindowAttribute( hwnd, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &borderThickness, sizeof( borderThickness ) ) == S_OK )
params->rgrc[0].top += borderThickness;
}
return lResult;

View File

@@ -35,6 +35,7 @@ private:
static jmethodID fireStateChangedLaterOnceMID;
static HWNDMap* hwndMap;
static DWORD osBuildNumber;
JavaVM* jvm;
JNIEnv* env; // attached to AWT-Windows/Win32 thread

View File

@@ -30,12 +30,26 @@ HWND getWindowHandle( JNIEnv* env, jobject window );
//---- Utility ----------------------------------------------------------------
typedef LONG (WINAPI *RtlGetVersion_Type)( OSVERSIONINFO* );
extern "C"
JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibrary_getOSBuildNumberImpl
( JNIEnv* env, jclass cls )
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof( info );
info.dwBuildNumber = 0;
// use RtlGetVersion for the case that app manifest does not specify Windows 10+ compatibility
// https://www.codeproject.com/Articles/5336372/Windows-Version-Detection
HMODULE ntdllModule = ::GetModuleHandleA( "ntdll.dll" );
if( ntdllModule != NULL ) {
RtlGetVersion_Type pRtlGetVersion = (RtlGetVersion_Type) ::GetProcAddress( ntdllModule, "RtlGetVersion" );
if( pRtlGetVersion != NULL && pRtlGetVersion( &info ) == 0 )
return info.dwBuildNumber;
}
// fallback
if( !::GetVersionEx( &info ) )
return 0;
return info.dwBuildNumber;

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.swingx.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
@@ -25,6 +26,7 @@ import javax.swing.plaf.ComponentUI;
import org.jdesktop.swingx.JXBusyLabel;
import org.jdesktop.swingx.plaf.basic.BasicBusyLabelUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
//TODO scale busy spinner
@@ -70,6 +72,20 @@ public class FlatBusyLabelUI
disabledForeground = null;
}
@Override
protected void installComponents( JLabel c ) {
super.installComponents( c );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
@Override
protected void paintDisabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -21,12 +21,15 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import org.jdesktop.swingx.plaf.basic.BasicHyperlinkUI;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -60,6 +63,11 @@ public class FlatHyperlinkUI
disabledText = null;
}
@Override
protected BasicButtonListener createButtonListener( AbstractButton b ) {
return new FlatHyperlinkListener( b );
}
@Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
@@ -79,4 +87,21 @@ public class FlatHyperlinkUI
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
//---- class FlatHyperlinkListener ----------------------------------------
/** @since 3.5 */
protected static class FlatHyperlinkListener
extends BasicHyperlinkListener
{
protected FlatHyperlinkListener( AbstractButton b ) {
super( b );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
}
}

View File

@@ -79,6 +79,7 @@ Button.default.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.Colo
Button.default.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedBorderColor #8888ff HSL 240 100 77 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
@@ -87,6 +88,7 @@ Button.disabledBackground #e0e0e0 HSL 0 0 88 javax.swing.plaf.Colo
Button.disabledBorderColor #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBackground #112233 HSL 210 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBorderColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedForeground #ffcccc HSL 0 100 90 javax.swing.plaf.ColorUIResource [UI]
Button.disabledText #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.endBackground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
@@ -94,21 +96,26 @@ Button.endBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.Colo
Button.focusedBackground #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedBorderColor #466d94 HSL 210 36 43 javax.swing.plaf.ColorUIResource [UI]
Button.focusedForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedSelectedBorderColor #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.font [active] $defaultFont [UI]
Button.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverSelectedBorderColor #aaaaaa HSL 0 0 67 javax.swing.plaf.ColorUIResource [UI]
Button.iconTextGap 4
Button.innerFocusWidth 1
Button.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI]
Button.margin 2,14,2,14 javax.swing.plaf.InsetsUIResource [UI]
Button.minimumWidth 72
Button.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedBorderColor #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedSelectedBorderColor #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI]
Button.rollover true
Button.selectedBackground #ffbbbb HSL 0 100 87 javax.swing.plaf.ColorUIResource [UI]
Button.selectedBorderColor #ff00ff HSL 300 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.selectedForeground #332211 HSL 30 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI]
Button.startBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]

View File

@@ -47,6 +47,7 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatListUI;
import com.formdev.flatlaf.ui.FlatTableUI;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
import com.jidesoft.swing.CheckBoxTreeCellRenderer;
@@ -131,6 +132,14 @@ public class FlatComponents2Test
initTableEditors( table1 );
initTableEditors( xTable1 );
// table selection listeners
table1.getSelectionModel().addListSelectionListener( e -> {
System.out.printf( "row sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
} );
table1.getColumnModel().getSelectionModel().addListSelectionListener( e -> {
System.out.printf( "column sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
} );
// JXTable
Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
PatternPredicate patternPredicate = new PatternPredicate( "^J", 2 );
@@ -164,6 +173,8 @@ public class FlatComponents2Test
JComboBox<String> editableComboBox = new JComboBox<>( months );
editableComboBox.setEditable( true );
cm.getColumn(3).setCellEditor( new DefaultCellEditor( editableComboBox ) );
// table.setDefaultRenderer( Object.class, new TestLabelRoundedTableCellRenderer() );
}
private void expandTree( JTree tree ) {
@@ -229,6 +240,65 @@ public class FlatComponents2Test
FlatLaf.updateUILater();
}
private void roundedSelectionChanged() {
String style = roundedSelectionCheckBox.isSelected() ? "selectionArc: 12; " : "";
int left = leftSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int right = rightSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int top = topSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int bottom = bottomSelectionInsetsCheckBox.isSelected() ? 2 : 0;
if( left > 0 || right > 0 || top > 0 || bottom > 0 )
style += "selectionInsets: " + top + ',' + left + ',' + bottom + ',' + right;
if( style.isEmpty() )
style = null;
list1.putClientProperty( FlatClientProperties.STYLE, style );
list2.putClientProperty( FlatClientProperties.STYLE, style );
tree1.putClientProperty( FlatClientProperties.STYLE, style );
tree2.putClientProperty( FlatClientProperties.STYLE, style );
xTree1.putClientProperty( FlatClientProperties.STYLE, style );
checkBoxTree1.putClientProperty( FlatClientProperties.STYLE, style );
table1.putClientProperty( FlatClientProperties.STYLE, style );
xTable1.putClientProperty( FlatClientProperties.STYLE, style );
xTreeTable1.putClientProperty( FlatClientProperties.STYLE, style );
// initial selection
if( style != null ) {
initSelection( list1 );
initSelection( list2 );
initSelection( tree1 );
initSelection( tree2 );
initSelection( xTree1 );
initSelection( checkBoxTree1 );
initSelection( table1 );
initSelection( xTable1 );
initSelection( xTreeTable1 );
}
if( paintOutsideAlternateRowsCheckBox.isSelected() )
table1ScrollPane.repaint();
}
private static void initSelection( JList<?> list ) {
if( list.isSelectionEmpty() ) {
list.addSelectionInterval( 1, 2 );
list.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTree tree ) {
if( tree.isSelectionEmpty() ) {
tree.addSelectionInterval( 1, 2 );
tree.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTable table ) {
if( table.getSelectedRowCount() == 0 ) {
table.addRowSelectionInterval( 1, 2 );
table.addRowSelectionInterval( 5, 5 );
}
}
private void dndChanged() {
boolean dnd = dndCheckBox.isSelected();
list1.setDragEnabled( dnd );
@@ -535,6 +605,12 @@ public class FlatComponents2Test
public void applyComponentOrientation( ComponentOrientation o ) {
super.applyComponentOrientation( o );
// always use left-to-right for options panels
generalOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
listOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
treeOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
tableOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
// swap upper right and left corners (other corners are not used in this app)
Component leftCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_LEFT_CORNER );
Component rightCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_RIGHT_CORNER );
@@ -596,16 +672,22 @@ public class FlatComponents2Test
JLabel label2 = new JLabel();
xTreeTable1ScrollPane = new JScrollPane();
xTreeTable1 = new JXTreeTable();
JPanel panel5 = new JPanel();
generalOptionsPanel = new JPanel();
roundedSelectionCheckBox = new JCheckBox();
JLabel label6 = new JLabel();
topSelectionInsetsCheckBox = new JCheckBox();
bottomSelectionInsetsCheckBox = new JCheckBox();
leftSelectionInsetsCheckBox = new JCheckBox();
rightSelectionInsetsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPanel panel6 = new JPanel();
listOptionsPanel = new JPanel();
JLabel listRendererLabel = new JLabel();
listRendererComboBox = new JComboBox<>();
JLabel listLayoutOrientationLabel = new JLabel();
listLayoutOrientationField = new JComboBox<>();
JLabel listVisibleRowCountLabel = new JLabel();
listVisibleRowCountSpinner = new JSpinner();
JPanel treeOptionsPanel = new JPanel();
treeOptionsPanel = new JPanel();
JLabel treeRendererLabel = new JLabel();
treeRendererComboBox = new JComboBox<>();
treeWideSelectionCheckBox = new JCheckBox();
@@ -614,7 +696,7 @@ public class FlatComponents2Test
treeRedLinesCheckBox = new JCheckBox();
treeEditableCheckBox = new JCheckBox();
treeShowDefaultIconsCheckBox = new JCheckBox();
JPanel tableOptionsPanel = new JPanel();
tableOptionsPanel = new JPanel();
JLabel autoResizeModeLabel = new JLabel();
autoResizeModeField = new JComboBox<>();
JLabel sortIconPositionLabel = new JLabel();
@@ -872,30 +954,68 @@ public class FlatComponents2Test
}
add(xTreeTable1ScrollPane, "cell 4 3 2 1");
//======== panel5 ========
//======== generalOptionsPanel ========
{
panel5.setBorder(new TitledBorder("General Control"));
panel5.putClientProperty("FlatLaf.internal.testing.ignore", true);
panel5.setLayout(new MigLayout(
"hidemode 3",
generalOptionsPanel.setBorder(new TitledBorder("General Control"));
generalOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
generalOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]",
"[left]",
// rows
"[]" +
"[]0" +
"[]0" +
"[]rel" +
"[]"));
//---- roundedSelectionCheckBox ----
roundedSelectionCheckBox.setText("rounded selection");
roundedSelectionCheckBox.setMnemonic('D');
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
//---- label6 ----
label6.setText("Selection insets:");
generalOptionsPanel.add(label6, "cell 0 1");
//---- topSelectionInsetsCheckBox ----
topSelectionInsetsCheckBox.setText("top");
topSelectionInsetsCheckBox.setMnemonic('D');
topSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(topSelectionInsetsCheckBox, "cell 0 2,gapx ind");
//---- bottomSelectionInsetsCheckBox ----
bottomSelectionInsetsCheckBox.setText("bottom");
bottomSelectionInsetsCheckBox.setMnemonic('D');
bottomSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(bottomSelectionInsetsCheckBox, "cell 0 2");
//---- leftSelectionInsetsCheckBox ----
leftSelectionInsetsCheckBox.setText("left");
leftSelectionInsetsCheckBox.setMnemonic('D');
leftSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(leftSelectionInsetsCheckBox, "cell 0 3,gapx ind");
//---- rightSelectionInsetsCheckBox ----
rightSelectionInsetsCheckBox.setText("right");
rightSelectionInsetsCheckBox.setMnemonic('D');
rightSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(rightSelectionInsetsCheckBox, "cell 0 3");
//---- dndCheckBox ----
dndCheckBox.setText("drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
panel5.add(dndCheckBox, "cell 0 0");
generalOptionsPanel.add(dndCheckBox, "cell 0 4");
}
add(panel5, "cell 0 4 4 1");
add(generalOptionsPanel, "cell 0 4 4 1");
//======== panel6 ========
//======== listOptionsPanel ========
{
panel6.setBorder(new TitledBorder("JList Control"));
panel6.setLayout(new MigLayout(
"hidemode 3",
listOptionsPanel.setBorder(new TitledBorder("JList Control"));
listOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]" +
"[fill]",
@@ -906,7 +1026,7 @@ public class FlatComponents2Test
//---- listRendererLabel ----
listRendererLabel.setText("Renderer:");
panel6.add(listRendererLabel, "cell 0 0");
listOptionsPanel.add(listRendererLabel, "cell 0 0");
//---- listRendererComboBox ----
listRendererComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -916,11 +1036,11 @@ public class FlatComponents2Test
"labelRounded"
}));
listRendererComboBox.addActionListener(e -> listRendererChanged());
panel6.add(listRendererComboBox, "cell 1 0");
listOptionsPanel.add(listRendererComboBox, "cell 1 0");
//---- listLayoutOrientationLabel ----
listLayoutOrientationLabel.setText("Orientation:");
panel6.add(listLayoutOrientationLabel, "cell 0 1");
listOptionsPanel.add(listLayoutOrientationLabel, "cell 0 1");
//---- listLayoutOrientationField ----
listLayoutOrientationField.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -929,25 +1049,25 @@ public class FlatComponents2Test
"horzontal wrap"
}));
listLayoutOrientationField.addActionListener(e -> listLayoutOrientationChanged());
panel6.add(listLayoutOrientationField, "cell 1 1");
listOptionsPanel.add(listLayoutOrientationField, "cell 1 1");
//---- listVisibleRowCountLabel ----
listVisibleRowCountLabel.setText("Visible row count:");
panel6.add(listVisibleRowCountLabel, "cell 0 2");
listOptionsPanel.add(listVisibleRowCountLabel, "cell 0 2");
//---- listVisibleRowCountSpinner ----
listVisibleRowCountSpinner.setModel(new SpinnerNumberModel(8, 0, null, 1));
listVisibleRowCountSpinner.addChangeListener(e -> listVisibleRowCountChanged());
panel6.add(listVisibleRowCountSpinner, "cell 1 2");
listOptionsPanel.add(listVisibleRowCountSpinner, "cell 1 2");
}
add(panel6, "cell 0 4 4 1");
add(listOptionsPanel, "cell 0 4 4 1");
//======== treeOptionsPanel ========
{
treeOptionsPanel.setBorder(new TitledBorder("JTree Control"));
treeOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
treeOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[left]",
// rows
@@ -1014,7 +1134,7 @@ public class FlatComponents2Test
tableOptionsPanel.setBorder(new TitledBorder("JTable Control"));
tableOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
tableOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[]" +
"[fill]" +
@@ -1132,10 +1252,18 @@ public class FlatComponents2Test
private CheckBoxTree checkBoxTree1;
private JScrollPane xTreeTable1ScrollPane;
private JXTreeTable xTreeTable1;
private JPanel generalOptionsPanel;
private JCheckBox roundedSelectionCheckBox;
private JCheckBox topSelectionInsetsCheckBox;
private JCheckBox bottomSelectionInsetsCheckBox;
private JCheckBox leftSelectionInsetsCheckBox;
private JCheckBox rightSelectionInsetsCheckBox;
private JCheckBox dndCheckBox;
private JPanel listOptionsPanel;
private JComboBox<String> listRendererComboBox;
private JComboBox<String> listLayoutOrientationField;
private JSpinner listVisibleRowCountSpinner;
private JPanel treeOptionsPanel;
private JComboBox<String> treeRendererComboBox;
private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treePaintSelectionCheckBox;
@@ -1143,6 +1271,7 @@ public class FlatComponents2Test
private JCheckBox treeRedLinesCheckBox;
private JCheckBox treeEditableCheckBox;
private JCheckBox treeShowDefaultIconsCheckBox;
private JPanel tableOptionsPanel;
private JComboBox<String> autoResizeModeField;
private JComboBox<String> sortIconPositionComboBox;
private JCheckBox showHorizontalLinesCheckBox;
@@ -1683,4 +1812,46 @@ public class FlatComponents2Test
return this;
}
}
//---- class TestLabelRoundedTableCellRenderer ----------------------------
@SuppressWarnings( "unused" )
private static class TestLabelRoundedTableCellRenderer
extends JLabel
implements TableCellRenderer
{
private JTable table;
private int row;
private int column;
private boolean isSelected;
TestLabelRoundedTableCellRenderer() {
setBorder( new FlatEmptyBorder( 1, 6, 1, 6 ) );
}
@Override
public Component getTableCellRendererComponent( JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column )
{
this.table = table;
this.row = row;
this.column = column;
this.isSelected = isSelected;
setText( String.valueOf( value ) );
setBackground( isSelected ? Color.green : table.getBackground() );
setForeground( isSelected ? Color.blue : table.getForeground() );
return this;
}
@Override
protected void paintComponent( Graphics g ) {
if( isSelected ) {
g.setColor( getBackground() );
FlatTableUI.paintCellSelection( table, g, row, column, 0, 0, getWidth(), getHeight() );
}
super.paintComponent( g );
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -297,13 +297,77 @@ new FormModel {
"value": "cell 4 3 2 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]rel[]"
} ) {
name: "panel5"
name: "generalOptionsPanel"
"border": new javax.swing.border.TitledBorder( "General Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "roundedSelectionCheckBox"
"text": "rounded selection"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "Selection insets:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "topSelectionInsetsCheckBox"
"text": "top"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "bottomSelectionInsetsCheckBox"
"text": "bottom"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "leftSelectionInsetsCheckBox"
"text": "left"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rightSelectionInsetsCheckBox"
"text": "right"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
"text": "drag and drop"
@@ -313,18 +377,21 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
"value": "cell 0 4"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[fill][fill]"
"$rowConstraints": "[][][]"
} ) {
name: "panel6"
name: "listOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JList Control" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "listRendererLabel"
"text": "Renderer:"
@@ -392,13 +459,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]0[]"
} ) {
name: "treeOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTree Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "treeRendererLabel"
"text": "Renderer:"
@@ -491,13 +561,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[][fill][fill]"
"$rowConstraints": "[][]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTable Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "autoResizeModeLabel"
"text": "Auto resize mode:"

View File

@@ -59,6 +59,11 @@ public class FlatComponentsTest
};
for( JSlider slider : allSliders )
slider.addChangeListener( sliderChanged );
UIManager.addPropertyChangeListener( e -> {
if( "lookAndFeel".equals( e.getPropertyName() ) && hideArrowButtonCheckBox.isSelected() )
SwingUtilities.invokeLater( () -> hideArrowButton() );
} );
}
private void changeProgress() {
@@ -127,6 +132,18 @@ public class FlatComponentsTest
}
}
private void hideArrowButton() {
boolean hideArrowButton = hideArrowButtonCheckBox.isSelected();
for( Component c : getComponents() ) {
if( c instanceof JComboBox ) {
Component b = ((JComboBox<?>)c).getComponent( 0 );
if( b instanceof AbstractButton )
b.setVisible( !hideArrowButton );
}
}
}
private void roundRectChanged() {
Boolean roundRect = roundRectCheckBox.isSelected() ? true : null;
@@ -380,6 +397,7 @@ public class FlatComponentsTest
magentaOutlineRadioButton = new JRadioButton();
magentaCyanOutlineRadioButton = new JRadioButton();
focusPaintedCheckBox = new JCheckBox();
hideArrowButtonCheckBox = new JCheckBox();
JLabel scrollBarLabel = new JLabel();
JScrollBar scrollBar1 = new JScrollBar();
JScrollBar scrollBar4 = new JScrollBar();
@@ -1234,9 +1252,10 @@ public class FlatComponentsTest
"[]" +
"[]",
// rows
"[]" +
"[]" +
"[]" +
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]"));
//---- buttonTypeComboBox ----
@@ -1290,13 +1309,18 @@ public class FlatComponentsTest
magentaCyanOutlineRadioButton.addActionListener(e -> outlineChanged());
panel4.add(magentaCyanOutlineRadioButton);
}
panel5.add(panel4, "cell 0 2 1 2");
panel5.add(panel4, "cell 0 2 1 3");
//---- focusPaintedCheckBox ----
focusPaintedCheckBox.setText("focusPainted");
focusPaintedCheckBox.setSelected(true);
focusPaintedCheckBox.addActionListener(e -> focusPaintedChanged());
panel5.add(focusPaintedCheckBox, "cell 1 2");
//---- hideArrowButtonCheckBox ----
hideArrowButtonCheckBox.setText("hide arrow button");
hideArrowButtonCheckBox.addActionListener(e -> hideArrowButton());
panel5.add(hideArrowButtonCheckBox, "cell 1 3");
}
add(panel5, "cell 5 13 2 10,grow");
@@ -1703,6 +1727,7 @@ public class FlatComponentsTest
private JRadioButton magentaOutlineRadioButton;
private JRadioButton magentaCyanOutlineRadioButton;
private JCheckBox focusPaintedCheckBox;
private JCheckBox hideArrowButtonCheckBox;
private JSlider slider1;
private JSlider slider6;
private JCheckBox sliderPaintTrackCheckBox;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -993,7 +993,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$columnConstraints": "[][]"
"$rowConstraints": "[][][][]"
"$rowConstraints": "[]0[]0[]0[]0[]"
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
} ) {
name: "panel5"
@@ -1092,7 +1092,7 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "outlineChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 1 2"
"value": "cell 0 2 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "focusPaintedCheckBox"
@@ -1105,6 +1105,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hideArrowButtonCheckBox"
"text": "hide arrow button"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideArrowButton", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 13 2 10,grow"
} )

View File

@@ -40,6 +40,8 @@ import net.miginfocom.swing.*;
public class FlatFileChooserTest
extends FlatTestPanel
{
private static ShortcutsCount shortcutsCount = ShortcutsCount.home;
public static void main( String[] args ) {
// Locale.setDefault( Locale.FRENCH );
// Locale.setDefault( Locale.GERMAN );
@@ -52,8 +54,21 @@ public class FlatFileChooserTest
FlatTestFrame frame = FlatTestFrame.create( args, "FlatFileChooserTest" );
UIManager.put( "FileChooser.shortcuts.filesFunction", (Function<File[], File[]>) files -> {
if( shortcutsCount == null )
return files;
if( shortcutsCount == ShortcutsCount.empty )
return new File[0];
ArrayList<File> list = new ArrayList<>( Arrays.asList( files ) );
list.add( 0, new File( System.getProperty( "user.home" ) ) );
if( shortcutsCount == ShortcutsCount.home )
list.add( 0, new File( System.getProperty( "user.home" ) ) );
else {
File home = new File( System.getProperty( "user.home" ) );
File[] homeFiles = home.listFiles();
int count = shortcutsCount.value;
for( int i = 0; i < count; i++ )
list.add( i < homeFiles.length ? homeFiles[i] : new File( "Dummy " + i ) );
}
return list.toArray( new File[list.size()] );
} );
@@ -78,6 +93,8 @@ public class FlatFileChooserTest
dialogTypeField.init( DialogType.class, false );
fileSelectionModeField.init( FileSelectionMode.class, false );
localesField.init( Locales.class, false );
shortcutsCountField.init( ShortcutsCount.class, true );
shortcutsCountField.setSelectedValue( shortcutsCount );
showControlButtonsCheckBox.setSelected( fileChooser1.getControlButtonsAreShown() );
multiSelectionCheckBox.setSelected( fileChooser1.isMultiSelectionEnabled() );
@@ -197,6 +214,11 @@ public class FlatFileChooserTest
} );
}
private void shortcutsCountChanged() {
shortcutsCount = shortcutsCountField.getSelectedValue();
fileChooser1.updateUI();
}
private void printShortcutFiles() {
printFiles( JavaCompatibility2.getChooserShortcutPanelFiles( fileChooser1.getFileSystemView() ) );
}
@@ -263,6 +285,8 @@ public class FlatFileChooserTest
printRootsButton = new JButton();
JLabel localesLabel = new JLabel();
localesField = new FlatTestEnumSelector<>();
JLabel label12 = new JLabel();
shortcutsCountField = new FlatTestEnumSelector<>();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JLabel label3 = new JLabel();
@@ -291,6 +315,7 @@ public class FlatFileChooserTest
"[]" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]"));
@@ -432,49 +457,57 @@ public class FlatFileChooserTest
localesField.addActionListener(e -> localesChanged());
add(localesField, "cell 1 8 2 1");
//---- label12 ----
label12.setText("Shortcuts:");
add(label12, "cell 0 9");
//---- shortcutsCountField ----
shortcutsCountField.addActionListener(e -> shortcutsCountChanged());
add(shortcutsCountField, "cell 1 9 2 1");
//---- label1 ----
label1.setText("icons:");
add(label1, "cell 0 9");
add(label1, "cell 0 10");
//---- label2 ----
label2.setIcon(UIManager.getIcon("FileView.directoryIcon"));
add(label2, "cell 1 9 2 1");
add(label2, "cell 1 10 2 1");
//---- label3 ----
label3.setIcon(UIManager.getIcon("FileView.fileIcon"));
add(label3, "cell 1 9 2 1");
add(label3, "cell 1 10 2 1");
//---- label4 ----
label4.setIcon(UIManager.getIcon("FileView.computerIcon"));
add(label4, "cell 1 9 2 1");
add(label4, "cell 1 10 2 1");
//---- label5 ----
label5.setIcon(UIManager.getIcon("FileView.hardDriveIcon"));
add(label5, "cell 1 9 2 1");
add(label5, "cell 1 10 2 1");
//---- label6 ----
label6.setIcon(UIManager.getIcon("FileView.floppyDriveIcon"));
add(label6, "cell 1 9 2 1");
add(label6, "cell 1 10 2 1");
//---- label7 ----
label7.setIcon(UIManager.getIcon("FileChooser.newFolderIcon"));
add(label7, "cell 1 9 2 1");
add(label7, "cell 1 10 2 1");
//---- label8 ----
label8.setIcon(UIManager.getIcon("FileChooser.upFolderIcon"));
add(label8, "cell 1 9 2 1");
add(label8, "cell 1 10 2 1");
//---- label9 ----
label9.setIcon(UIManager.getIcon("FileChooser.homeFolderIcon"));
add(label9, "cell 1 9 2 1");
add(label9, "cell 1 10 2 1");
//---- label10 ----
label10.setIcon(UIManager.getIcon("FileChooser.detailsViewIcon"));
add(label10, "cell 1 9 2 1");
add(label10, "cell 1 10 2 1");
//---- label11 ----
label11.setIcon(UIManager.getIcon("FileChooser.listViewIcon"));
add(label11, "cell 1 9 2 1");
add(label11, "cell 1 10 2 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
@@ -500,6 +533,7 @@ public class FlatFileChooserTest
private JButton printComboBoxFilesButton;
private JButton printRootsButton;
private FlatTestEnumSelector<Locales> localesField;
private FlatTestEnumSelector<ShortcutsCount> shortcutsCountField;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- enum DialogType ----------------------------------------------------
@@ -553,4 +587,26 @@ public class FlatFileChooserTest
this.value = value;
}
}
//---- enum ShortcutsCount ------------------------------------------------
enum ShortcutsCount {
empty( -1 ),
home( -2 ),
zero( 0 ),
one( 1 ),
two( 2 ),
three( 3 ),
four( 4 ),
five( 5 ),
ten( 10 ),
twenty( 20 ),
thirty( 30 );
public final int value;
ShortcutsCount( int value ) {
this.value = value;
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][][grow]"
"$rowConstraints": "[grow,fill][][][][][][][][]para[]"
"$rowConstraints": "[grow,fill][][][][][][][][][]para[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -273,71 +273,87 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label12"
"text": "Shortcuts:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumSelector" ) {
name: "shortcutsCountField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "ShortcutsCount"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "shortcutsCountChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "icons:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
"value": "cell 0 10"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.directoryIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.fileIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.computerIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.hardDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.floppyDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.newFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.upFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.homeFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.detailsViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.listViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )

View File

@@ -0,0 +1,390 @@
/*
* 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.testing;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.function.Supplier;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
*/
public class FlatHiDPITest
{
private static final double scale = 1.25;
private final JFrame frame;
private final JPanel testPanel;
private final Insets frameInsets;
public static void main( String[] args ) {
System.setProperty( FlatSystemProperties.USE_WINDOW_DECORATIONS, "false" );
System.setProperty( "sun.java2d.uiScale", Double.toString( scale ) );
System.out.println( "Scale factor: " + scale );
for( int x = 0; x <= 100; x++ ) {
int devX = devScaleXY( x, scale );
int usrX = usrScaleXY( x, scale );
if( usrX != devX )
System.out.printf( "%d: %d != %d\n", x, devX, usrX );
/*
for( int w = 0; w <= 10; w++ ) {
int devW = devScaleWH( w, scale );
int usrW = usrScaleWH( x, w, scale );
if( usrW != devW )
System.out.printf( " %d %d: %d != %d\n", x, w, devW, usrW );
}
*/
}
SwingUtilities.invokeLater( () -> {
if( !SystemInfo.isJava_9_orLater ) {
JOptionPane.showMessageDialog( null, "Use Java 9+" );
return;
}
// HiDPIUtils.installHiDPIRepaintManager();
FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
FlatLightLaf.setup();
UIManager.put( "Button.pressedBorderColor", Color.blue );
UIManager.put( "TextField.caretBlinkRate", 0 );
UIManager.put( "FormattedTextField.caretBlinkRate", 0 );
new FlatHiDPITest();
} );
}
FlatHiDPITest() {
frame = new JFrame( "FlatHiDPITest " + scale ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "JFrame" );
}
};
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// get frame insets
frame.addNotify();
frameInsets = frame.getInsets();
testPanel = new JPanel( null ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "JPanel" );
}
};
int y = 0;
addAtProblematicXY( 0, y, 40, 16, 48, "TestComp", TestComp::new );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JButton", () -> new JButton( "B" ) );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JTextField", () -> new JTextField( "Text" ) );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JComboBox", JComboBox<String>::new );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JComboBox editable", () -> {
JComboBox<String> c = new JComboBox<>();
c.setEditable( true );
return c;
} );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JSpinner", JSpinner::new );
y += 20;
addAtProblematicXY( 0, y, 80, 16, 88, "JSlider", JSlider::new );
y += 20;
addAtProblematicXY( 0, y, 80, 16, 88, "JScrollBar", () -> new JScrollBar( JScrollBar.HORIZONTAL ) );
y += 20;
addAtProblematicXY( 0, y, 16, 40, 20, "JScrollBar", () -> new JScrollBar( JScrollBar.VERTICAL ) );
y += 60;
addAtProblematicXY( 0, y, 82, 60, 88, "JScrollPane", () -> new JScrollPane( new JTree() ) );
y += 80;
addAtProblematicXY( 0, y, 80, 16, 88, "JProgressBar", () -> {
JProgressBar c = new JProgressBar();
c.setValue( 60 );
c.addMouseListener( new MouseAdapter() {
@Override
public void mousePressed( MouseEvent e ) {
int value = c.getValue();
c.setValue( (value >= 20) ? value - 20 : 100 );
}
} );
return c;
} );
frame.getContentPane().add( testPanel );
frame.setSize( 400, 400 );
frame.setVisible( true );
}
private void addAtProblematicXY( int x, int y, int w, int h, int offset, String text, Supplier<Component> generator ) {
// plain component
addAtProblematicXY( x, y, w, h, generator.get() );
// component in (opaque) panel which has same bounds as component
addAtProblematicXY( x + offset, y, w, h, wrapInPanel( generator.get(), false ) );
// component in (opaque) panel which is 1px larger than component
addAtProblematicXY( x + (offset * 2), y, w + 1, h + 1, wrapInPanel( generator.get(), true ) );
JLabel l = new JLabel( text );
testPanel.add( l );
l.setLocation( x + (offset * 3) + 20, y );
l.setSize( l.getPreferredSize() );
}
private void addAtProblematicXY( int x, int y, int w, int h, Component c ) {
int px = nextProblematicXY( x + frameInsets.left ) - frameInsets.left;
int py = nextProblematicXY( y + frameInsets.top ) - frameInsets.top;
testPanel.add( c );
c.setBounds( px, py, w, h );
}
private Component wrapInPanel( Component c, boolean emptyBorder ) {
JPanel p = new JPanel( new BorderLayout() ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "wrapping JPanel" );
}
};
if( emptyBorder )
p.setBorder( new EmptyBorder( 0, 0, 1, 1 ) );
p.add( c, BorderLayout.CENTER );
return p;
}
private static int nextProblematicXY( int xy ) {
for( int i = xy; i < xy + 20; i++ ) {
if( devScaleXY( i, scale ) != usrScaleXY( i, scale ) )
return i;
}
throw new IllegalArgumentException();
}
private static int devScaleXY( int xy, double scale ) {
return (int) (xy * scale);
}
private static int usrScaleXY( int xy, double scale ) {
// see sun.java2d.pipe.Region.clipRound(double);
return (int) Math.ceil( (xy * scale) - 0.5 );
}
@SuppressWarnings( "unused" )
private static int devScaleWH( int wh, double scale ) {
return (int) Math.round( wh * scale );
}
@SuppressWarnings( "unused" )
private static int usrScaleWH( int xy, int wh, double scale ) {
int usrXY = usrScaleXY( xy, scale );
return ((int) Math.ceil( ((xy + wh) * scale) - 0.5 )) - usrXY;
}
//---- class TestComp -----------------------------------------------------
private static class TestComp
extends JComponent
implements FocusListener
{
// used to avoid repainting when window is deactivated and activated (for easier debugging)
private boolean permanentFocused;
TestComp() {
setOpaque( true );
setFocusable( true );
addFocusListener( this );
addMouseListener( new MouseAdapter() {
@Override
public void mouseClicked( MouseEvent e ) {
requestFocusInWindow();
}
} );
}
@Override
protected void paintComponent( Graphics g ) {
g.setColor( isFocusOwner() ? Color.green : Color.red );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
@Override
public void focusGained( FocusEvent e ) {
if( permanentFocused )
return;
if( !e.isTemporary() ) {
repaint();
permanentFocused = true;
}
}
@Override
public void focusLost( FocusEvent e ) {
if( !e.isTemporary() ) {
repaint();
permanentFocused = false;
}
}
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "TestComp" );
}
}
//---- TestGraphics2D -----------------------------------------------------
private static class TestGraphics2D
extends Graphics2DProxy
{
private final Graphics2D delegate;
private final String id;
static Graphics install( Graphics g, String id ) {
return wasInvokedFrom_safelyGetGraphics()
? new TestGraphics2D( (Graphics2D) g, id )
: g;
}
private static boolean wasInvokedFrom_safelyGetGraphics() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[4];
return "javax.swing.JComponent".equals( stackTraceElement.getClassName() ) &&
"safelyGetGraphics".equals( stackTraceElement.getMethodName() );
}
private TestGraphics2D( Graphics2D delegate, String id ) {
super( delegate );
this.delegate = delegate;
this.id = id;
System.out.println();
System.out.println( "---------------------------------------- " );
System.out.println( id + ": construct" );
printClipRects();
}
private void printClipRects() {
try {
Class<?> sunGraphics2DClass = Class.forName( "sun.java2d.SunGraphics2D" );
if( !sunGraphics2DClass.isInstance( delegate ) ) {
System.out.println( " not a SunGraphics2D: " + delegate.getClass().getName() );
return;
}
Rectangle devClip = region2rect( getFieldValue( sunGraphics2DClass, delegate, "devClip" ) );
Shape usrClip = (Shape) getFieldValue( sunGraphics2DClass, delegate, "usrClip" );
Rectangle clipRegion = region2rect( getFieldValue( sunGraphics2DClass, delegate, "clipRegion" ) );
printField( devClip, "devClip" );
printField( usrClip, "usrClip" );
printField( clipRegion, "clipRegion" );
if( (usrClip instanceof Rectangle && !devClip.contains( (Rectangle) usrClip )) ||
(usrClip instanceof Rectangle2D && !devClip.contains( (Rectangle2D) usrClip )) )
{
System.out.flush();
System.err.println( "WARNING: devClip smaller than usrClip" );
System.err.flush();
}
} catch( Exception ex ) {
ex.printStackTrace();
}
}
private void printField( Object value, String name ) throws Exception {
System.out.printf( " %-16s", name );
if( value instanceof Rectangle ) {
Rectangle r = (Rectangle) value;
System.out.printf( "xy %3d %3d -> %3d %3d wh %3d %3d\n",
r.x, r.y, r.x + r.width, r.y + r.height, r.width, r.height );
} else if( value instanceof Rectangle2D ) {
Rectangle2D r = (Rectangle2D) value;
System.out.printf( "xy %.2f %.2f -> %.2f %.2f wh %.2f %.2f\n",
r.getX(), r.getY(), r.getX() + r.getWidth(), r.getY() + r.getHeight(), r.getWidth(), r.getHeight() );
} else
System.out.println( value );
}
private static Rectangle region2rect( Object region ) throws Exception {
Class<?> regionClass = Class.forName( "sun.java2d.pipe.Region" );
int loX = (int) getMethodValue( regionClass, region, "getLoX" );
int loY = (int) getMethodValue( regionClass, region, "getLoY" );
int hiX = (int) getMethodValue( regionClass, region, "getHiX" );
int hiY = (int) getMethodValue( regionClass, region, "getHiY" );
return new Rectangle( loX, loY, hiX - loX, hiY - loY );
}
private static Object getFieldValue( Class<?> cls, Object object, String name ) throws Exception {
Field f = cls.getDeclaredField( name );
f.setAccessible( true );
return f.get( object );
}
private static Object getMethodValue( Class<?> cls, Object object, String name ) throws Exception {
Method m = cls.getDeclaredMethod( name );
m.setAccessible( true );
return m.invoke( object );
}
@Override
public void clipRect( int x, int y, int width, int height ) {
System.out.printf( "\n%s: clipRect( %d, %d, %d, %d )\n", id, x, y, width, height );
super.clipRect( x, y, width, height );
printClipRects();
}
@Override
public void setClip( int x, int y, int width, int height ) {
System.out.printf( "\n%s: setClip( %d, %d, %d, %d )\n", id, x, y, width, height );
super.setClip( x, y, width, height );
printClipRects();
}
@Override
public void setClip( Shape clip ) {
System.out.printf( "\n%s: setClip( %s )\n", id, clip );
super.setClip( clip );
printClipRects();
}
@Override
public void clip( Shape s ) {
System.out.printf( "\n%s: clip( %s )\n", id, s );
super.clip( s );
printClipRects();
}
}
}

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.testing;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
import net.miginfocom.swing.*;
import org.jdesktop.swingx.*;
@@ -53,6 +55,58 @@ public class FlatHtmlTest
increaseFontSize();
}
private void changeHtmlText() {
changeHtmlText( this );
}
private void changeHtmlText( Component c ) {
if( c instanceof AbstractButton )
((AbstractButton)c).setText( changeHtmlText( ((AbstractButton)c).getText() ) );
else if( c instanceof JLabel )
((JLabel)c).setText( changeHtmlText( ((JLabel)c).getText() ) );
else if( c instanceof JTextComponent )
((JTextComponent)c).setText( changeHtmlText( ((JTextComponent)c).getText() ) );
else if( c instanceof JToolTip )
((JToolTip)c).setTipText( changeHtmlText( ((JToolTip)c).getTipText() ) );
else if( c instanceof JComboBox ) {
@SuppressWarnings( "unchecked" )
JComboBox<String> cb = (JComboBox<String>) c;
DefaultComboBoxModel<String> model = (DefaultComboBoxModel<String>) cb.getModel();
String text = model.getElementAt( 0 );
String newText = changeHtmlText( text );
if( newText != text ) {
model.insertElementAt( newText, 1 );
model.removeElementAt( 0 );
}
}
if( c instanceof Container ) {
for( Component child : ((Container)c).getComponents() )
changeHtmlText( child );
}
}
private String changeHtmlText( String text ) {
String htmlTag = "<html>";
if( !text.startsWith( htmlTag ) )
return text;
String bodyTag = "<body>";
int bodyIndex = text.indexOf( bodyTag );
if( bodyIndex < 0 )
bodyIndex = htmlTag.length();
else
bodyIndex += bodyTag.length();
int insertIndex = text.indexOf( '>', bodyIndex );
if( insertIndex < 0 )
insertIndex = bodyIndex;
else
insertIndex++;
return text.substring( 0, insertIndex ) + "X" + text.substring( insertIndex );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel labelLabel = new JLabel();
@@ -88,14 +142,28 @@ public class FlatHtmlTest
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem2 = new JRadioButtonMenuItem();
JLabel label14 = new JLabel();
JLabel label15 = new JLabel();
JLabel label16 = new JLabel();
JToolTip toolTip3 = new JToolTip();
JToolTip toolTip4 = new JToolTip();
JLabel label17 = new JLabel();
JComboBox<String> comboBox1 = new JComboBox<>();
JComboBox<String> comboBox2 = new JComboBox<>();
JLabel label56 = new JLabel();
JXBusyLabel xBusyLabel1 = new JXBusyLabel();
JXBusyLabel xBusyLabel2 = new JXBusyLabel();
JLabel label18 = new JLabel();
JXHyperlink xHyperlink1 = new JXHyperlink();
JXHyperlink xHyperlink2 = new JXHyperlink();
JLabel label33 = new JLabel();
JideLabel jideLabel1 = new JideLabel();
JideLabel jideLabel2 = new JideLabel();
JLabel label16 = new JLabel();
JideButton jideButton1 = new JideButton();
JideButton jideButton2 = new JideButton();
JLabel label54 = new JLabel();
JideToggleButton jideToggleButton1 = new JideToggleButton();
JideToggleButton jideToggleButton2 = new JideToggleButton();
JButton changeHtmlTextButton = new JButton();
JLabel label15 = new JLabel();
label1 = new JLabel();
JScrollPane scrollPane15 = new JScrollPane();
editorPane1 = new JEditorPane();
@@ -143,13 +211,10 @@ public class FlatHtmlTest
JLabel label47 = new JLabel();
JLabel label53 = new JLabel();
JLabel label48 = new JLabel();
JLabel label54 = new JLabel();
JLabel label56 = new JLabel();
JLabel label57 = new JLabel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
"flowy,ltr,insets dialog,hidemode 3",
// columns
"[grow,sizegroup 1,fill]" +
"[grow,sizegroup 1,fill]" +
@@ -196,6 +261,12 @@ public class FlatHtmlTest
"[]" +
"[]" +
"[]" +
"[]unrel" +
"[]" +
"[]unrel" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]"));
@@ -204,7 +275,7 @@ public class FlatHtmlTest
panel1.add(label5, "cell 0 0");
//---- label6 ----
label6.setText("<html>Some <b>Bold</b> Text");
label6.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(label6, "cell 1 0");
//---- label7 ----
@@ -212,11 +283,11 @@ public class FlatHtmlTest
panel1.add(label7, "cell 2 0");
//---- label3 ----
label3.setText("JButon:");
label3.setText("JButton:");
panel1.add(label3, "cell 0 1");
//---- button1 ----
button1.setText("<html>Some <b>Bold</b> Text");
button1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(button1, "cell 1 1");
//---- button2 ----
@@ -228,7 +299,7 @@ public class FlatHtmlTest
panel1.add(label11, "cell 0 2");
//---- toggleButton1 ----
toggleButton1.setText("<html>Some <b>Bold</b> Text");
toggleButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
toggleButton1.setSelected(true);
panel1.add(toggleButton1, "cell 1 2");
@@ -242,7 +313,7 @@ public class FlatHtmlTest
panel1.add(label12, "cell 0 3");
//---- checkBox1 ----
checkBox1.setText("<html>Some <b>Bold</b> Text");
checkBox1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(checkBox1, "cell 1 3");
//---- checkBox2 ----
@@ -254,7 +325,7 @@ public class FlatHtmlTest
panel1.add(label13, "cell 0 4");
//---- radioButton1 ----
radioButton1.setText("<html>Some <b>Bold</b> Text");
radioButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(radioButton1, "cell 1 4");
//---- radioButton2 ----
@@ -267,7 +338,7 @@ public class FlatHtmlTest
//======== menu1 ========
{
menu1.setText("<html>Some <b>Bold</b> Text");
menu1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
}
panel1.add(menu1, "cell 1 5");
@@ -282,7 +353,7 @@ public class FlatHtmlTest
panel1.add(label4, "cell 0 6");
//---- menuItem1 ----
menuItem1.setText("<html>Some <b>Bold</b> Text");
menuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(menuItem1, "cell 1 6");
//---- menuItem2 ----
@@ -294,7 +365,7 @@ public class FlatHtmlTest
panel1.add(label9, "cell 0 7");
//---- checkBoxMenuItem1 ----
checkBoxMenuItem1.setText("<html>Some <b>Bold</b> Text");
checkBoxMenuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
checkBoxMenuItem1.setSelected(true);
panel1.add(checkBoxMenuItem1, "cell 1 7");
@@ -308,7 +379,7 @@ public class FlatHtmlTest
panel1.add(label10, "cell 0 8");
//---- radioButtonMenuItem1 ----
radioButtonMenuItem1.setText("<html>Some <b>Bold</b> Text");
radioButtonMenuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
radioButtonMenuItem1.setSelected(true);
panel1.add(radioButtonMenuItem1, "cell 1 8");
@@ -321,15 +392,13 @@ public class FlatHtmlTest
label14.setText("JToolTip:");
panel1.add(label14, "cell 0 9");
//---- label15 ----
label15.setText("(move mouse here)");
label15.setToolTipText("<html>Some <b>Bold</b> Text");
panel1.add(label15, "cell 1 9");
//---- toolTip3 ----
toolTip3.setTipText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(toolTip3, "cell 1 9");
//---- label16 ----
label16.setText("(move mouse here)");
label16.setToolTipText("Some text");
panel1.add(label16, "cell 2 9");
//---- toolTip4 ----
toolTip4.setTipText("Some text");
panel1.add(toolTip4, "cell 2 9");
//---- label17 ----
label17.setText("JComboBox:");
@@ -337,7 +406,7 @@ public class FlatHtmlTest
//---- comboBox1 ----
comboBox1.setModel(new DefaultComboBoxModel<>(new String[] {
"<html>Some <b>Bold</b> Text",
"<html>Some <b>Bold</b> Text <kbd>kbd</kbd>",
"abc",
"def"
}));
@@ -351,19 +420,76 @@ public class FlatHtmlTest
}));
panel1.add(comboBox2, "cell 2 10");
//---- label56 ----
label56.setText("JXBusyLabel:");
panel1.add(label56, "cell 0 11");
//---- xBusyLabel1 ----
xBusyLabel1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(xBusyLabel1, "cell 1 11");
//---- xBusyLabel2 ----
xBusyLabel2.setText("Some text");
panel1.add(xBusyLabel2, "cell 2 11");
//---- label18 ----
label18.setText("JXHyperlink:");
panel1.add(label18, "cell 0 11");
panel1.add(label18, "cell 0 12");
//---- xHyperlink1 ----
xHyperlink1.setText("<html>Some <b>Bold</b> Text");
panel1.add(xHyperlink1, "cell 1 11");
xHyperlink1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(xHyperlink1, "cell 1 12");
//---- xHyperlink2 ----
xHyperlink2.setText("Some text");
panel1.add(xHyperlink2, "cell 2 11");
panel1.add(xHyperlink2, "cell 2 12");
//---- label33 ----
label33.setText("JideLabel:");
panel1.add(label33, "cell 0 13");
//---- jideLabel1 ----
jideLabel1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideLabel1, "cell 1 13");
//---- jideLabel2 ----
jideLabel2.setText("Some text");
panel1.add(jideLabel2, "cell 2 13");
//---- label16 ----
label16.setText("JideButton:");
panel1.add(label16, "cell 0 14");
//---- jideButton1 ----
jideButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideButton1, "cell 1 14");
//---- jideButton2 ----
jideButton2.setText("Some text");
panel1.add(jideButton2, "cell 2 14");
//---- label54 ----
label54.setText("JideToggleButton:");
panel1.add(label54, "cell 0 15");
//---- jideToggleButton1 ----
jideToggleButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideToggleButton1, "cell 1 15");
//---- jideToggleButton2 ----
jideToggleButton2.setText("Some text");
panel1.add(jideToggleButton2, "cell 2 15");
//---- changeHtmlTextButton ----
changeHtmlTextButton.setText("Change HTML Text");
changeHtmlTextButton.addActionListener(e -> changeHtmlText());
panel1.add(changeHtmlTextButton, "cell 0 16");
//---- label15 ----
label15.setText("(use to check whether CSS is updated on text changes)");
panel1.add(label15, "cell 0 17 3 1");
}
add(panel1, "cell 4 0 1 2,aligny top,growy 0");
add(panel1, "cell 4 0 1 3,aligny top,growy 0");
//---- label1 ----
label1.setText("<html>HTML<br>Sample <b>content</b><br> <u>text</u> with <a href=\"#\">link</a><h1>Header 1</h1><h2>Header 2</h2><h3>Header 3</h3><h4>Header 4</h4><h5>Header 5</h5><h6>Header 6</h6><p>Paragraph</p><address>Address</address><hr><table border=\"1\"><tr><th>Col 1</th><th>Col 2</th></tr><tr><td>abc</td><td>def</td></tr></table><ul><li>item 1</li><li>item 2</li></ul></html>");
@@ -441,10 +567,6 @@ public class FlatHtmlTest
"[]" +
"[]" +
"[]" +
"[]para" +
"[]para" +
"[]" +
"[]" +
"[]"));
//---- label22 ----
@@ -582,20 +704,8 @@ public class FlatHtmlTest
//---- label48 ----
label48.setText("<html><address>address</address></html>");
panel2.add(label48, "cell 1 5");
//---- label54 ----
label54.setText("Test whether inserted rule affects display:");
panel2.add(label54, "cell 0 7 7 1");
//---- label56 ----
label56.setText("<html><head><style>body { color: red }</style></head>leading <big>red</big> trailing</html>");
panel2.add(label56, "cell 0 8 7 1");
//---- label57 ----
label57.setText("<html><style>body { color: red }</style><p>leading <big>red</big> trailing</p></html>");
panel2.add(label57, "cell 0 9 7 1");
}
add(panel2, "cell 4 2");
add(panel2, "cell 4 0 1 3");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -7,7 +7,7 @@ new FormModel {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$layoutConstraints": "flowy,ltr,insets dialog,hidemode 3"
"$columnConstraints": "[grow,sizegroup 1,fill][grow,sizegroup 1,fill][grow,sizegroup 1,fill][grow,sizegroup 1,fill][fill]"
"$rowConstraints": "[][fill][grow,fill]"
} ) {
@@ -39,7 +39,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[][][][][][][][][][][][]"
"$rowConstraints": "[][][][][][][][][][][]unrel[][]unrel[][][]para[][]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -50,7 +50,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
@@ -62,13 +62,13 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "JButon:"
"text": "JButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
@@ -86,7 +86,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "toggleButton1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
@@ -106,7 +106,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "checkBox1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
@@ -124,7 +124,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "radioButton1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
@@ -142,7 +142,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
@@ -160,7 +160,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
@@ -178,7 +178,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "checkBoxMenuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7"
@@ -198,7 +198,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JRadioButtonMenuItem" ) {
name: "radioButtonMenuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8"
@@ -216,17 +216,15 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label15"
"text": "(move mouse here)"
"toolTipText": "<html>Some <b>Bold</b> Text"
add( new FormComponent( "javax.swing.JToolTip" ) {
name: "toolTip3"
"tipText": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label16"
"text": "(move mouse here)"
"toolTipText": "Some text"
add( new FormComponent( "javax.swing.JToolTip" ) {
name: "toolTip4"
"tipText": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 9"
} )
@@ -239,8 +237,8 @@ new FormModel {
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox1"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "<html>Some <b>Bold</b> Text"
addElement( "<html>Some <b>Bold</b> Text" )
selectedItem: "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
addElement( "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>" )
addElement( "abc" )
addElement( "def" )
}
@@ -259,25 +257,110 @@ new FormModel {
"value": "cell 2 10"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label18"
"text": "JXHyperlink:"
name: "label56"
"text": "JXBusyLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink1"
"text": "<html>Some <b>Bold</b> Text"
add( new FormComponent( "org.jdesktop.swingx.JXBusyLabel" ) {
name: "xBusyLabel1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXBusyLabel" ) {
name: "xBusyLabel2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label18"
"text": "JXHyperlink:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11"
"value": "cell 2 12"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label33"
"text": "JideLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13"
} )
add( new FormComponent( "com.jidesoft.swing.JideLabel" ) {
name: "jideLabel1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13"
} )
add( new FormComponent( "com.jidesoft.swing.JideLabel" ) {
name: "jideLabel2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 13"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label16"
"text": "JideButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 14"
} )
add( new FormComponent( "com.jidesoft.swing.JideButton" ) {
name: "jideButton1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 14"
} )
add( new FormComponent( "com.jidesoft.swing.JideButton" ) {
name: "jideButton2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 14"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label54"
"text": "JideToggleButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 15"
} )
add( new FormComponent( "com.jidesoft.swing.JideToggleButton" ) {
name: "jideToggleButton1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15"
} )
add( new FormComponent( "com.jidesoft.swing.JideToggleButton" ) {
name: "jideToggleButton2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 15"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "changeHtmlTextButton"
"text": "Change HTML Text"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeHtmlText", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 16"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label15"
"text": "(use to check whether CSS is updated on text changes)"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 17 3 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0 1 2,aligny top,growy 0"
"value": "cell 4 0 1 3,aligny top,growy 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
@@ -372,7 +455,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill]para[fill][fill][fill][fill][fill][fill]"
"$rowConstraints": "[][][][][][]para[]para[][][]"
"$rowConstraints": "[][][][][][]"
} ) {
name: "panel2"
auxiliary() {
@@ -582,30 +665,12 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label54"
"text": "Test whether inserted rule affects display:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7 7 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label56"
"text": "<html><head><style>body { color: red }</style></head>leading <big>red</big> trailing</html>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8 7 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label57"
"text": "<html><style>body { color: red }</style><p>leading <big>red</big> trailing</p></html>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9 7 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 2"
"value": "cell 4 0 1 3"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 905, 815 )
"size": new java.awt.Dimension( 905, 880 )
} )
}
}

View File

@@ -22,6 +22,7 @@ import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.*;
import java.util.function.Supplier;
import javax.swing.*;
@@ -49,6 +50,7 @@ public class FlatMenusTest
initComponents();
largerCheckBox.setSelected( LargerMenuItem.useLargerSize );
verticalMenuBar.setLayout( new GridLayout( 0, 1 ) );
}
private void armedChanged() {
@@ -246,7 +248,15 @@ public class FlatMenusTest
JLabel popupMenuLabel = new JLabel();
JButton showPopupMenuButton = new JButton();
showScrollingPopupMenuButton = new JButton();
JLabel label1 = new JLabel();
armedCheckBox = new JCheckBox();
verticalMenuBar = new JMenuBar();
JMenu menu14 = new JMenu();
JMenuItem menuItem53 = new JMenuItem();
JMenu menu15 = new JMenu();
JMenuItem menuItem54 = new JMenuItem();
JMenu menu16 = new JMenu();
JMenuItem menuItem55 = new JMenuItem();
underlineCheckBox = new JCheckBox();
popupMenubackgroundCheckBox = new JCheckBox();
@@ -884,6 +894,10 @@ public class FlatMenusTest
showScrollingPopupMenuButton.addActionListener(e -> showScrollingPopupMenu(e));
add(showScrollingPopupMenuButton, "cell 2 2");
//---- label1 ----
label1.setText("Vertical JMenuBar:");
add(label1, "cell 4 2");
//---- armedCheckBox ----
armedCheckBox.setText("armed");
armedCheckBox.setMnemonic('A');
@@ -891,6 +905,42 @@ public class FlatMenusTest
armedCheckBox.addActionListener(e -> armedChanged());
add(armedCheckBox, "cell 0 3");
//======== verticalMenuBar ========
{
//======== menu14 ========
{
menu14.setText("menu");
//---- menuItem53 ----
menuItem53.setText("text");
menu14.add(menuItem53);
}
verticalMenuBar.add(menu14);
//======== menu15 ========
{
menu15.setText("another menu");
//---- menuItem54 ----
menuItem54.setText("text");
menu15.add(menuItem54);
}
verticalMenuBar.add(menu15);
//======== menu16 ========
{
menu16.setText("menu 3");
menu16.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test16.png")));
//---- menuItem55 ----
menuItem55.setText("text");
menu16.add(menuItem55);
}
verticalMenuBar.add(menu16);
}
add(verticalMenuBar, "cell 4 3 1 3");
//---- underlineCheckBox ----
underlineCheckBox.setText("underline menu selection");
underlineCheckBox.putClientProperty("FlatLaf.internal.testing.ignore", true);
@@ -931,6 +981,7 @@ public class FlatMenusTest
private JCheckBox accelCheckBox;
private JButton showScrollingPopupMenuButton;
private JCheckBox armedCheckBox;
private JMenuBar verticalMenuBar;
private JCheckBox underlineCheckBox;
private JCheckBox popupMenubackgroundCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "16" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -662,6 +662,12 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "Vertical JMenuBar:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "armedCheckBox"
"text": "armed"
@@ -674,6 +680,39 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "verticalMenuBar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu14"
"text": "menu"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem53"
"text": "text"
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu15"
"text": "another menu"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem54"
"text": "text"
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu16"
"text": "menu 3"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test16.png" )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem55"
"text": "text"
} )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "underlineCheckBox"
"text": "underline menu selection"

View File

@@ -36,6 +36,8 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.util.SystemInfo;
import net.miginfocom.swing.*;
@@ -161,6 +163,8 @@ public class FlatNativeWindowBorderTest
registerSwitchToLookAndFeel( "F2", FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F3", FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( "F4", FlatDarculaLaf.class.getName() );
registerSwitchToLookAndFeel( "F5", FlatMacLightLaf.class.getName() );
registerSwitchToLookAndFeel( "F6", FlatMacDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F8", FlatTestLaf.class.getName() );
@@ -170,8 +174,8 @@ public class FlatNativeWindowBorderTest
registerSwitchToLookAndFeel( "F9", "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( "F9", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F11", NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
}
private void updateInfo() {

View File

@@ -21,6 +21,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.demo.ScrollablePanel;
import net.miginfocom.swing.*;
@@ -63,6 +64,15 @@ public class FlatOptionPaneTest
UIManager.put( "OptionPane.showIcon", showTitleBarIconCheckBox.isSelected() );
}
private void showWithCustomIcon() {
JOptionPane optionPane = new JOptionPane( "Hello world." );
JDialog dialog = optionPane.createDialog( "With Custom Icon" );
dialog.getRootPane().putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, true );
dialog.setIconImage( new ImageIcon( FlatOptionPaneTest.class.getResource( "/com/formdev/flatlaf/testing/test32.png" ) ).getImage() );
dialog.setVisible( true );
dialog.dispose();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
ScrollablePanel panel9 = new ScrollablePanel();
@@ -75,6 +85,7 @@ public class FlatOptionPaneTest
JPanel panel2 = new JPanel();
JOptionPane errorOptionPane = new JOptionPane();
errorShowDialogLabel = new FlatOptionPaneTest.ShowDialogLinkLabel();
JButton showWithCustomIconButton = new JButton();
JLabel informationLabel = new JLabel();
JPanel panel3 = new JPanel();
JOptionPane informationOptionPane = new JOptionPane();
@@ -173,6 +184,11 @@ public class FlatOptionPaneTest
errorShowDialogLabel.setOptionPane(errorOptionPane);
panel9.add(errorShowDialogLabel, "cell 1 1");
//---- showWithCustomIconButton ----
showWithCustomIconButton.setText("Show with custom icon");
showWithCustomIconButton.addActionListener(e -> showWithCustomIcon());
panel9.add(showWithCustomIconButton, "cell 2 1");
//---- informationLabel ----
informationLabel.setText("Information");
panel9.add(informationLabel, "cell 0 2");

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -83,6 +83,13 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "showWithCustomIconButton"
"text": "Show with custom icon"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showWithCustomIcon", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "informationLabel"
"text": "Information"

View File

@@ -40,10 +40,15 @@ public class FlatPaintingHiDPITest
FlatPaintingHiDPITest() {
initComponents();
reset();
sliderChanged();
}
@Override
public void addNotify() {
super.addNotify();
reset();
}
private void sliderChanged() {
painter.originX = originXSlider.getValue();
painter.originY = originYSlider.getValue();
@@ -212,7 +217,7 @@ public class FlatPaintingHiDPITest
scaleXSlider.setPaintTicks(true);
scaleXSlider.setMajorTickSpacing(50);
scaleXSlider.setSnapToTicks(true);
scaleXSlider.setMinorTickSpacing(10);
scaleXSlider.setMinorTickSpacing(5);
scaleXSlider.setMinimum(-100);
scaleXSlider.addChangeListener(e -> sliderChanged());
add(scaleXSlider, "cell 1 4");
@@ -228,7 +233,7 @@ public class FlatPaintingHiDPITest
scaleYSlider.setPaintLabels(true);
scaleYSlider.setMajorTickSpacing(50);
scaleYSlider.setSnapToTicks(true);
scaleYSlider.setMinorTickSpacing(10);
scaleYSlider.setMinorTickSpacing(5);
scaleYSlider.setMinimum(-100);
scaleYSlider.addChangeListener(e -> sliderChanged());
add(scaleYSlider, "cell 1 5");

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