Compare commits

...

124 Commits
2.0-rc1 ... 2.3

Author SHA1 Message Date
Karl Tauber
69851b7f3a release 2.3 2022-05-28 17:53:54 +02:00
Karl Tauber
92b53bf0df Merge PR #522: File chooser shortcuts panel 2022-05-28 17:46:12 +02:00
Karl Tauber
93e0496fd2 ToggleButton: button style "tab": added missing foreground colors for hover, focused and selected states (issue #535) 2022-05-28 15:09:04 +02:00
Karl Tauber
5151951f46 Button and ToggleButton: added missing foreground colors for hover, pressed, focused and selected states (issue #535) 2022-05-28 14:54:31 +02:00
Karl Tauber
58dbccec2d Table: optionally paint alternating rows below table if table is smaller than scroll pane (issue #504) 2022-05-25 11:18:22 +02:00
Karl Tauber
90de14d013 Native library: refactored loading of Windows native library from FlatWindowsNativeWindowBorder to FlatNativeLibrary to make it easier to add native libraries for other platforms (for issues #204 and #482) 2022-05-17 20:24:28 +02:00
Karl Tauber
5f961618bf Demo and Theme Editor: updated macOS related comments 2022-05-13 13:54:10 +02:00
Karl Tauber
37c375e2fa Theme Editor:
- support "themes" sub-directory
- added "generate Java class" checkbox to "New" dialog
2022-05-10 11:01:45 +02:00
Karl Tauber
1758c175ed FlatLafUIKeys.txt: added some missing UI defaults 2022-05-09 23:30:34 +02:00
Karl Tauber
96f2a02cfa UIDefaultsLoader: added over() color function to convert a translucent color into a solid color based on any background color 2022-05-09 23:28:40 +02:00
Karl Tauber
96d4bda6c8 Demo: hide accent color buttons (instead of disabling them) if not supported by selected theme 2022-05-09 22:51:38 +02:00
Karl Tauber
02cf6050a1 updates for PR #530:
- added @since tags
- changed `FlatToggleButton.setTabUnderlinePlacement()` implementation so that is behaves similar to `FlatTabbedPane.setTabIconPlacement()`
2022-05-09 22:28:34 +02:00
Karl Tauber
38cf32a2e9 Merge PR #530: ToggleButton: made underline placement configurable 2022-05-09 22:08:42 +02:00
Karl Tauber
2ae7589d14 Merge PR #525: Create Bundle_es.properties 2022-05-09 21:28:47 +02:00
Julien Fischer
bcb2e1f0a1 ToggleButton: made underline placement configurable
Supported values: TOP, LEFT, BOTTOM, or RIGHT
2022-05-06 12:07:50 +02:00
Karl Tauber
14932d3f07 Theme Editor: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-05 13:20:23 +02:00
Karl Tauber
c3b9dc397d Demo: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-01 11:45:36 +02:00
Karl Tauber
58b653f55d updated sigtest for FlatLaf 2.2
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-01 11:44:51 +02:00
Jesús Marín
1dcdc42dde Create Bundle_es.properties
Flatlaf Spanish translation
2022-04-29 14:51:11 +01:00
Karl Tauber
58a0a16985 IntelliJ Themes: fixed TitledBorder text color in "Monokai Pro" theme (issue #524) 2022-04-28 15:16:26 +02:00
Karl Tauber
a117243f14 FileChooser: use large system icons in shortcuts panel also in Java 8 to 16 2022-04-26 22:50:34 +02:00
Karl Tauber
22411060be FileChooser: improve layout for shortcuts panel (give it full height) 2022-04-25 23:49:15 +02:00
Karl Tauber
045263ae58 FileChooser: added (optional) shortcuts panel (issue #100) 2022-04-25 23:06:10 +02:00
Karl Tauber
024b6daaf6 release 2.2 2022-04-25 19:36:43 +02:00
Karl Tauber
bd5512c121 SplitPane: allow limiting one-touch expanding to a single side (issue #355) 2022-04-23 17:13:32 +02:00
Karl Tauber
9afce83a02 SplitPane: added missing BasicSplitPaneDivider properties to javadoc 2022-04-23 16:19:04 +02:00
Karl Tauber
07a8bd9486 ComboBox: added missing BasicComboPopup properties to javadoc 2022-04-22 10:55:25 +02:00
Karl Tauber
bcdc0a8fce IntelliJ Themes: added "Monokai Pro" and "Xcode-Dark" themes 2022-04-21 22:03:05 +02:00
Karl Tauber
b295809432 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2022-04-21 22:02:09 +02:00
Karl Tauber
52763ab932 GitHub Actions:
- natives.yml: include core natives in artifacts
- updated versions of used actions
2022-04-21 14:18:27 +02:00
Karl Tauber
99666265c9 gradle:
- use gradle `cpp-library` plugin instead of 3rd party plugin
- build natives only via task `build-natives`
2022-04-21 12:58:16 +02:00
Karl Tauber
af3e280d74 Table: slightly changed grid colors to make grid better recognizable (issue #514) 2022-04-19 23:00:01 +02:00
Karl Tauber
b57e4c0565 TabbedPane: selected tab underline color now changes depending on whether the focus is within the tab content (issue #398) 2022-04-19 22:19:47 +02:00
Karl Tauber
aca9931560 IntelliJ Themes: TabbedPane: use DefaultTabs.underlinedTabBackground and DefaultTabs.underlinedTabForeground from JSON themes for selected tab background/foreground 2022-04-19 16:50:27 +02:00
Karl Tauber
d09e166e4a SplitPane: fixed StackOverflowError caused by layout loop that may occur under special circumstances (issue #513) 2022-04-12 13:47:04 +02:00
Karl Tauber
68a7a60ff2 FileChooser: enabled full row selection for details view to fix alternate row coloring (issue #512) 2022-04-12 13:28:39 +02:00
Karl Tauber
f21261914b gradle: build target flatlaf-natives-windows only on Windows
(to fix build error on macOS)
2022-04-09 18:34:36 +02:00
Karl Tauber
7b11339fdc update to Gradle 7.4.2
./gradlew wrapper --gradle-version=7.4.2
2022-04-09 18:18:45 +02:00
Karl Tauber
081fd43d98 IntelliJ Themes: Component.accentColor UI property now has useful theme specific values (issue #507) 2022-04-07 18:07:09 +02:00
Karl Tauber
ef2eedfc7c Button: fixed icon layout and preferred width of default buttons that use bold font (issue #506) 2022-04-06 23:36:58 +02:00
Karl Tauber
0dba9265be ToolBar: fixed endless loop in focus navigation that may occur under special circumstances (issue #505) 2022-04-06 18:53:45 +02:00
Karl Tauber
301aae9b8e NativeLibrary: use System.mapLibraryName() instead of own implementation 2022-03-19 11:07:46 +01:00
Karl Tauber
c63f4e9662 Window decorations on Linux: limit window resizing/moving to left mouse button (issue #482) 2022-03-18 00:05:15 +01:00
Karl Tauber
47508dc6ac Native window decorations: updated DLLs (issue #502)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/2000978687
2022-03-17 22:48:01 +01:00
Karl Tauber
3a8879608a Native window decorations: fixed wrong window title character encoding used in Windows taskbar (issue #502) 2022-03-17 22:31:18 +01:00
Karl Tauber
b221889549 updated sigtest for FlatLaf 2.1
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-03-17 18:34:44 +01:00
Karl Tauber
c00d99b85f release 2.1 2022-03-17 12:52:49 +01:00
Karl Tauber
0bf87b753d TabbedPane: disable all items in "Show Hidden Tabs" popup menu if tabbed pane is disabled 2022-03-17 12:42:49 +01:00
Karl Tauber
53f2730064 TextArea, TextPane and EditorPane: no longer select all text when component is focused for the first time (issue #498; regression in FlatLaf 2.0) 2022-03-17 12:21:06 +01:00
Karl Tauber
d487c3b005 JIDE: use FlatLaf menu scrolling for JidePopupMenu (issue #225) 2022-03-14 12:23:52 +01:00
Karl Tauber
fef6ae7ff7 Menus: scroll large menus using mouse wheel or up/down arrows (issue #225) 2022-03-14 11:41:05 +01:00
Karl Tauber
f6b42754de Testing: FlatScreenshotsBackground: also make title bar white/black 2022-03-14 11:20:09 +01:00
Karl Tauber
2ae9bb381d Menus: fixed IllegalComponentStateException: component must be showing on the screen to determine its location when submenu is empty (PR #490; issue #247) 2022-03-14 00:23:53 +01:00
Karl Tauber
53bde84710 fixed compiler warning 2022-03-13 19:17:41 +01:00
Karl Tauber
d006ac27ff Merge PR #490: Menus: improved usability of submenus 2022-03-13 19:07:59 +01:00
Karl Tauber
c478d28b71 PasswordField: fixed reveal button appearance in IntelliJ themes (issue #494) 2022-03-13 18:39:12 +01:00
Karl Tauber
99f7b9ad84 ScrollBar:
- added `ScrollBar.minimumButtonSize` to specify minimum scroll arrow button size
- center and scale arrows in scroll up/down buttons

(issue #493)
2022-03-13 10:58:27 +01:00
Karl Tauber
ab58101ce3 SwingX: test JXStatusBar (issue #492) 2022-03-06 11:41:54 +01:00
Karl Tauber
d8f3682dc0 Menus: improved usability of submenus (issue #247) 2022-02-28 14:45:57 +01:00
Karl Tauber
1fec7ba553 Linux: support using custom window decorations (issue #482) 2022-02-26 23:07:16 +01:00
Karl Tauber
418f55f34e Window decorations: fixed window resizing on Linux (issue #482) 2022-02-26 14:00:16 +01:00
Karl Tauber
05d795b2ae Window decorations: use special fix for maximized bounds only on Windows (issue #469) 2022-02-26 13:33:55 +01:00
Karl Tauber
a365b750d9 core: minor code cleanup:
- add final where possible
- removed "public" from interface methods
- simplified conditional expressions
- removed unnecessary unboxing
- removed unused assignements
- removed redundant casts

(used IntelliJ IDEA 2021.3 inspections)
2022-02-25 21:49:15 +01:00
Karl Tauber
0aecfb565f IntelliJ Themes: removed duplicate key and trailing spaces 2022-02-25 21:12:48 +01:00
Karl Tauber
0cf4edd9e5 core: fixed typos/grammar in comments 2022-02-25 20:40:37 +01:00
Karl Tauber
dd7b7c6aef release 2.0.2 2022-02-25 16:58:22 +01:00
Karl Tauber
0bd677c46b FlatSVGIcon: changed logging when icon resource is not found from "severe" to "config" (issue #476) 2022-02-25 16:55:04 +01:00
Karl Tauber
1a131d5206 Merge PR #484: Fix NPE when painting icon on OS X top menu bar 2022-02-25 15:58:41 +01:00
Karl Tauber
016e515ae2 moved TestFlatIconNullComponent to other package and fixed file name (issue #483) 2022-02-25 15:52:40 +01:00
Karl Tauber
456ceb3c58 Merge PR #486: Request to add Ultorg to list of apps using FlatLAF 2022-02-25 15:27:58 +01:00
Eirik Bakke
2169be1b45 In README, add Ultorg to list of apps using FlatLAF. 2022-02-24 19:00:37 -05:00
Karl Tauber
49eb0b0201 Native window decorations: updated DLLs (issue #477)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/1866639721
2022-02-18 22:44:00 +01:00
Karl Tauber
2e222bcdea Native window decorations (Windows 10/11 only): fixed rendering artifacts on HiDPI screens when dragging window partly offscreen and back into screen bounds (issue #477) 2022-02-18 22:24:36 +01:00
Nicolas Roduit
c7fa475128 NPE when painting icon on OS X top menu bar #483 2022-02-18 18:30:24 +01:00
Karl Tauber
4174b065f3 repaint component when setting client property JComponent.outline (issue #480) 2022-02-16 23:53:21 +01:00
Karl Tauber
df6256d989 release 2.0.1 2022-01-25 18:46:29 +01:00
Karl Tauber
c27db56321 moved module-info.class from META-INF\versions\9\ to root folder of JARs (issue #466) 2022-01-25 16:59:31 +01:00
Karl Tauber
97bed8554a FlatSVGIcon: added copy constructor (issue #465) 2022-01-25 00:47:35 +01:00
Karl Tauber
751c0e16e9 ToolTip: fixed wrong tooltip location if component overrides JComponent.getToolTipLocation() and wants place tooltip under mouse location (issue #468) 2022-01-24 23:24:39 +01:00
Karl Tauber
936de60700 fixed memory leak in Panel, Separator and ToolBarSeparator (issue #471) 2022-01-24 18:28:38 +01:00
Karl Tauber
f6b64d48ec Merge PR #463: Updating MegaMek Suite Information 2022-01-19 17:27:12 +01:00
Justin Bowen
b043da7d4c Updating MegaMek Suite Information 2022-01-13 20:22:30 -05:00
Karl Tauber
7e47cc2443 updated sigtest for FlatLaf 2.0
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-01-13 12:14:51 +01:00
Karl Tauber
b8b45f9442 Theme Editor: added to main readme 2022-01-11 16:17:17 +01:00
Karl Tauber
66337f9af6 release 2.0 2022-01-10 12:25:45 +01:00
Karl Tauber
54646706a0 README.md: added more documentation links 2022-01-10 12:13:30 +01:00
Karl Tauber
e8ee037d09 RootPane: uninstall background, foreground and font because not all Lafs set them 2022-01-10 11:58:49 +01:00
Karl Tauber
6d705e568a Extras: FlatInspector: fixed "NOT SET" for component foreground 2022-01-09 19:40:17 +01:00
Karl Tauber
e768791eba Native window decorations: updated hover and pressed colors of iconify/maximize/close buttons for Windows 11 style 2022-01-09 12:59:08 +01:00
Karl Tauber
2aff7c97f9 Demo: changed theme names from "Flat *" to "FlatLaf *" 2022-01-08 22:56:20 +01:00
Karl Tauber
ca6fc7773e README.md: added MooInfo to applications using FlatLaf (issue #460) 2022-01-06 16:05:28 +01:00
Karl Tauber
a1395a5490 TextField: leading/trailing components (PR #386):
- set cursor only on button and toolbar
- do not replace cursor on if already set (issue #461)
- updated client properties javadoc
2022-01-06 15:17:20 +01:00
Karl Tauber
61dd4d71d6 Theme Editor: added text field leading/trailing buttons/toolbar to preview 2022-01-05 19:43:06 +01:00
Karl Tauber
6beda53238 Button: if boolbar button is in leading/trailing component of a text field, increase toolbar button corner arc to match text field corner arc (issue #451) 2022-01-05 18:46:00 +01:00
Karl Tauber
941441d7e1 TextField: clear button has now component name TextField.clearButton
PasswordField: reveals button has now component name `PasswordField.revealButton` and additional style class `revealButton` (issue #173)
SwingUtils: added `getComponentByName()` for easy getting clear or reveal buttons
2022-01-05 18:32:05 +01:00
Karl Tauber
d10ea41b47 GitHub Actions:
- build on Java 17
- run natives.yml when Gradle version changed
2022-01-04 18:13:12 +01:00
Karl Tauber
9458870f70 update to Gradle 7.3.3
./gradlew wrapper --gradle-version=7.3.3
2022-01-04 16:06:37 +01:00
Karl Tauber
095794bbd1 GitHub Actions:
- use actions/setup-java@v2 (provides caching gradle files)
- use pre-installed Java 8 and 11
- no longer build on Java 9 and 14
- not yet build on Java 17 because used Gradle version 6.8.2 does not support it
  can not yet upgrade to Gradle 7.x because nokee plugins (for C++) does not support it
2022-01-04 12:26:13 +01:00
Karl Tauber
c7fc0aa936 Demo: removed unnecessary setting component name 2022-01-03 23:56:26 +01:00
Karl Tauber
a8d98ced61 Theme Editor:
- support multi-line styles in overlay color preview
- minor preview improvements
2022-01-03 21:51:02 +01:00
Karl Tauber
831b3d851a ColorFunctions: javadoc added; do not mix colors if they are equal 2022-01-03 19:04:28 +01:00
Karl Tauber
8c891c7016 UIDefaultsDump: output base color used for resolving derived color 2022-01-03 18:46:56 +01:00
Karl Tauber
5c4706cbc9 Merge remote-tracking branch 'origin/main' into main 2022-01-03 17:05:37 +01:00
Karl Tauber
db66a6c4f0 Unit tests: re-enable testing scaled UI
avoid using UI_SCALE_ENABLED = false because if this test is executed first, class UIScale does not register listeners to UIManager, which prevents updating user scale factor on font changes and testing scaled UI fails
2022-01-03 16:52:43 +01:00
Karl Tauber
0517e4fc02 Native window decorations: updated maximize and restore icons for Windows 11 style
(requires Java 8u321, 11.0.14, 17.0.2 or 18+)
2021-12-31 17:57:02 +01:00
Karl Tauber
dd7fa4a87d Slider: fixed/improved focused indicator color when changing accent color (PR #375) 2021-12-31 15:33:27 +01:00
Karl Tauber
e5956900ea FileChooser: use Windows system icons in Java 18+ 32bit (issue #403)
only Java 17 32bit does not use Windows system icons because of:
https://bugs.openjdk.java.net/browse/JDK-8277299
2021-12-31 12:59:22 +01:00
Karl Tauber
3755593c14 Windows 11: Native window decorations: do not paint top window border because Windows 11 now paints it (issue #431)
(requires Java 8u321, 11.0.14, 17.0.2 or 18+)
2021-12-31 10:35:19 +01:00
Karl Tauber
8ddd3b6d68 Native window decorations: fixed blurry iconify/maximize/close button hover rectangles at 125%, 150% or 175% scaling (issue #431) 2021-12-31 10:28:34 +01:00
Karl Tauber
840083940d Use FlatLaf native window decorations by default when running in
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki)
(instead of using JetBrains custom decorations). System variable
`flatlaf.useJetBrainsCustomDecorations` is now `false` by default (was `true` in FlatLaf 1.x). (issue #454)
2021-12-30 12:04:22 +01:00
Karl Tauber
0cdfd29ecf Extras: fixed concurrent loading of SVG icons on multiple threads (issue #459) 2021-12-30 11:24:48 +01:00
Karl Tauber
bb32c727b6 TextField:
- improved hover/pressed/selected colors of leading/trailing buttons
  (e.g. "reveal" button in password field) (issue #452)
- clear button no longer paints over round border (issue #451)
2021-12-28 20:24:43 +01:00
Karl Tauber
f978c04750 PasswordField: reveal button did not show password if JPasswordField.setEchoChar() was invoked from application (PR #442; issue #173) 2021-12-27 22:53:07 +01:00
Karl Tauber
b6a504e121 Theme Editor: fixed "Pick Color from Screen" on macOS
On macOS Big Sur (and later), to pick colors outside of theme editor window it is necessary to give "Screen Recording" permission to the application (or to the IDE started from).
For the arrow keys, "Accessibility" permission is necessary.
See "System Preferences > Security & Privacy > Privacy".
2021-12-27 21:49:49 +01:00
Karl Tauber
5fae367fab PasswordField: preserve reveal button state when switching theme (issue #173) 2021-12-27 20:07:27 +01:00
Karl Tauber
6e807f44b2 Search/clear icons: fixed colors for some IntelliJ themes (e.g. "Dark Flat") that use translucent color for Actions.GreyInline 2021-12-27 18:22:31 +01:00
Karl Tauber
53ebed7f89 CHANGELOG.md: added changes for #453 and #456 2021-12-26 22:44:55 +01:00
Karl Tauber
1c10c41808 Theme Editor: Preview: added style classes to all preview components (syntax flatlaf-preview-<componenttype>) to allow experimenting with styles in editor 2021-12-26 21:23:42 +01:00
Karl Tauber
01170b669b Theme Editor: Preview:
- reworked disabling to avoid disabling internal components
- do not disable labels
- removed special code for JTextPane and JEditorPane, which seems to be no longer needed
2021-12-26 20:30:36 +01:00
Karl Tauber
b56215e5e3 Demo: moved leading/trailing icons (on text field) code from end of initComponents() to constructor (so that it easier to find) 2021-12-26 19:57:15 +01:00
Karl Tauber
221e801561 support relative path in system property flatlaf.nativeLibraryPath (PR #453) 2021-12-23 22:19:29 +01:00
Karl Tauber
90edbe23d7 Merge PR #453: Add a system property to load pre-extracted native libraries from a directory 2021-12-23 22:10:11 +01:00
Karl Tauber
5b16a814c8 fixed "endless recursion in font" exception in FlatLaf$ActiveFont.createValue() if UIManager.getFont() is invoked from multiple threads (issue #456) 2021-12-23 21:16:07 +01:00
Ingo Kegel
ef01721464 Added the system property flatlaf.nativeLibraryPath to specify a directory where the native libraries have been extracted.
Avoiding extraction at runtime to the temporary directory is useful in order to prevent anti-virus software from blocking the library loading.
2021-12-21 11:13:40 +01:00
202 changed files with 19577 additions and 3893 deletions

View File

@@ -19,46 +19,32 @@ jobs:
strategy: strategy:
matrix: matrix:
# test against # test against
# - Java 1.8 (minimum requirement) # - Java 8 (minimum requirement)
# - Java 9 (first version with JPMS)
# - Java LTS versions (11, 17, ...) # - Java LTS versions (11, 17, ...)
# - lastest Java version(s) # - lastest Java version(s)
java: java:
- 1.8 - 8
- 9
- 11 # LTS - 11 # LTS
- 14 - 17 # LTS
- 15
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
if: matrix.java == '1.8' if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }} - name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
- name: Cache Gradle wrapper cache: gradle
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: matrix.java == '11' if: matrix.java == '11'
with: with:
name: FlatLaf-build-artifacts name: FlatLaf-build-artifacts
@@ -77,25 +63,14 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
- name: Cache Gradle wrapper cache: gradle
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Publish snapshot to oss.sonatype.org - name: Publish snapshot to oss.sonatype.org
run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true run: ./gradlew publish :flatlaf-theme-editor:build -Dorg.gradle.internal.publish.checksums.insecure=true
@@ -124,25 +99,14 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
- name: Cache Gradle wrapper cache: gradle
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Release a new stable version to Maven Central - name: Release a new stable version to Maven Central
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -Drelease=true

View File

@@ -11,48 +11,40 @@ on:
paths: paths:
- 'flatlaf-natives/flatlaf-natives-windows/**' - 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml' - '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
pull_request: pull_request:
branches: branches:
- '*' - '*'
paths: paths:
- 'flatlaf-natives/flatlaf-natives-windows/**' - 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml' - '.github/workflows/natives.yml'
- 'gradle/wrapper/gradle-wrapper.properties'
jobs: jobs:
Windows: Windows:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
- name: Setup Java 1.8 - name: Setup Java 11
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
java-version: 1.8 java-version: 11
distribution: adopt
- name: Cache Gradle wrapper cache: gradle
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
!~/.gradle/caches/modules-2/modules-2.lock
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew :flatlaf-natives-windows:build # --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
run: ./gradlew :flatlaf-natives-windows:build-natives --no-daemon
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: FlatLaf-natives-windows-build-artifacts name: FlatLaf-natives-windows-build-artifacts
path: | path: |
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
flatlaf-natives/flatlaf-natives-windows/build flatlaf-natives/flatlaf-natives-windows/build

View File

@@ -1,6 +1,145 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 2.3
#### New features and improvements
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
is empty/hidden. (issue #100)
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
focused and selected states. (issue #535)
- Table: Optionally paint alternating rows below table if table is smaller than
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
- ToggleButton: Made the underline placement of tab-style toggle buttons
configurable. (PR #530; issue #529)
- Added spanish translation. (PR #525)
#### Fixed bugs
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
(issue #524)
## 2.2
#### New features and improvements
- SplitPane: Allow limiting one-touch expanding to a single side (set client
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
- TabbedPane: Selected tab underline color now changes depending on whether the
focus is within the tab content. (issue #398)
- IntelliJ Themes:
- Added "Monokai Pro" and "Xcode-Dark" themes.
- TabbedPane now use different background color for selected tabs in all "Arc"
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
#### Fixed bugs
- Native window decorations (Windows 10/11 only): Fixed wrong window title
character encoding used in Windows taskbar. (issue #502)
- Button: Fixed icon layout and preferred width of default buttons that use bold
font. (issue #506)
- FileChooser: Enabled full row selection for details view to fix alternate row
coloring. (issue #512)
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
under special circumstances. (issue #513)
- Table: Slightly changed grid colors to make grid better recognizable. (issue
#514)
- ToolBar: Fixed endless loop in focus navigation that may occur under special
circumstances. (issue #505)
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
specific values. (issue #507)
## 2.1
#### New features and improvements
- Menus: Improved usability of submenus. (PR #490; issue #247)
- Menus: Scroll large menus using mouse wheel or up/down arrows. (issue #225)
- Linux: Support using custom window decorations. Enable with
`JFrame.setDefaultLookAndFeelDecorated(true)` and
`JDialog.setDefaultLookAndFeelDecorated(true)` before creating a window.
(issue #482)
- ScrollBar: Added UI value `ScrollBar.minimumButtonSize` to specify minimum
scroll arrow button size (if shown). (issue #493)
#### Fixed bugs
- PasswordField: Fixed reveal button appearance in IntelliJ themes. (issue #494)
- ScrollBar: Center and scale arrows in scroll up/down buttons (if shown).
(issue #493)
- TextArea, TextPane and EditorPane: No longer select all text when component is
focused for the first time. (issue #498; regression in FlatLaf 2.0)
- TabbedPane: Disable all items in "Show Hidden Tabs" popup menu if tabbed pane
is disabled.
#### Incompatibilities
- Method `FlatUIUtils.paintArrow()` (and class `FlatArrowButton`) now paints
arrows one pixel smaller than before. To fix this, increase parameter
`arrowSize` by one.
## 2.0.2
- Native window decorations (Windows 10/11 only): Fixed rendering artifacts on
HiDPI screens when dragging window partly offscreen and back into screen
bounds. (issue #477)
- Repaint component when setting client property `JComponent.outline` (issue
#480).
- macOS: Fixed NPE when using some icons in main menu items. (issue #483)
## 2.0.1
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
regression in FlatLaf 2.0)
- ToolTip: Fixed wrong tooltip location if component overrides
`JComponent.getToolTipLocation()` and wants place tooltip under mouse
location. (issue #468)
- Extras: Added copy constructor to `FlatSVGIcon`. (issue #465)
- Moved `module-info.class` from `META-INF\versions\9\` to root folder of JARs.
(issue #466)
## 2.0
- Added system property `flatlaf.nativeLibraryPath` to load native libraries
from a directory. (PR #453)
- Fixed "endless recursion in font" exception in
`FlatLaf$ActiveFont.createValue()` if `UIManager.getFont()` is invoked from
multiple threads. (issue #456)
- PasswordField: Preserve reveal button state when switching theme. (PR #442;
issue #173)
- PasswordField: Reveal button did not show password if
`JPasswordField.setEchoChar()` was invoked from application. (PR #442; issue
#173)
- Slider: Fixed/improved focused indicator color when changing accent color. (PR
#375)
- TextField:
- Improved hover/pressed/selected colors of leading/trailing buttons (e.g.
"reveal" button in password field). (issue #452)
- Clear button no longer paints over round border. (issue #451)
- Extras: Fixed concurrent loading of SVG icons on multiple threads. (issue
#459)
- Use FlatLaf native window decorations by default when running in
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki)
(instead of using JetBrains custom decorations). System variable
`flatlaf.useJetBrainsCustomDecorations` is now `false` by default (was `true`
in FlatLaf 1.x). (issue #454)
- Native window decorations:
- Fixed blurry iconify/maximize/close button hover rectangles at 125%, 150% or
175% scaling. (issue #431)
- Updated maximize and restore icons for Windows 11 style. (requires Java
8u321, 11.0.14, 17.0.2 or 18+)
- Updated hover and pressed colors of iconify/maximize/close buttons for
Windows 11 style.
## 2.0-rc1 ## 2.0-rc1
#### New features and improvements #### New features and improvements
@@ -24,7 +163,7 @@ FlatLaf Change Log
- Possibility to hide window title bar icon (for single window set client - Possibility to hide window title bar icon (for single window set client
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
value `TitlePane.showIcon` to `false`). value `TitlePane.showIcon` to `false`).
- OptionPane: Hide window title bar icon by default. Can be be made visibly by - OptionPane: Hide window title bar icon by default. Can be made visibly by
setting UI default `OptionPane.showIcon` to `true`. (issue #416) setting UI default `OptionPane.showIcon` to `true`. (issue #416)
- No longer show the Java "duke/cup" icon if no window icon image is set. - No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416) (issue #416)

View File

@@ -11,9 +11,9 @@ scales on **HiDPI** displays and runs on Java 8 or newer.
The look is heavily inspired by **Darcula** and **IntelliJ** themes from The look is heavily inspired by **Darcula** and **IntelliJ** themes from
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons. IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
![Flat Light](images/flat_light.png) ![FlatLaf Light](images/flat_light.png)
![Flat Dark](images/flat_dark.png) ![FlatLaf Dark](images/flat_dark.png)
IntelliJ Platform Themes IntelliJ Platform Themes
@@ -99,10 +99,22 @@ For more information and documentation visit
- [Customizing](https://www.formdev.com/flatlaf/customizing/) - [Customizing](https://www.formdev.com/flatlaf/customizing/)
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/) - [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/) - [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
- [Components UI Properties](https://www.formdev.com/flatlaf/components/)
- [Typography](https://www.formdev.com/flatlaf/typography/)
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/) - [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
- [System Properties](https://www.formdev.com/flatlaf/system-properties/) - [System Properties](https://www.formdev.com/flatlaf/system-properties/)
Theme Editor
------------
The Theme Editor that supports editing FlatLaf theme properties files. See
[Theme Editor documentation](https://www.formdev.com/flatlaf/theme-editor/) for
details and downloads.
![Theme Editor](images/theme-editor@1.5x.png)
Buzz Buzz
---- ----
@@ -114,6 +126,11 @@ Buzz
Applications using FlatLaf Applications using FlatLaf
-------------------------- --------------------------
- ![New](images/new.svg) [Ultorg](https://www.ultorg.com/) (**commercial**) - a
visual query system for relational databases
- ![New](images/new.svg) [MooInfo](https://github.com/rememberber/MooInfo) -
visual implementation of OSHI, to view information about the system and
hardware
- ![New](images/new.svg) [Jailer](https://github.com/Wisser/Jailer) 11.2 - - ![New](images/new.svg) [Jailer](https://github.com/Wisser/Jailer) 11.2 -
database subsetting and relational data browsing tool database subsetting and relational data browsing tool
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML - [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
@@ -149,9 +166,10 @@ Applications using FlatLaf
- [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) - - [Total Validator](https://www.totalvalidator.com/) 15 (**commercial**) -
checks your website checks your website
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware - [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and - [MegaMek](https://github.com/MegaMek/megamek),
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board [MegaMekLab](https://github.com/MegaMek/megameklab) and
game [MekHQ](https://github.com/MegaMek/mekhq) v0.47.5+ - a sci-fi tabletop
BattleTech simulator suite handling battles, unit building, and campaigns
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder) - [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
0.13.b024 - GUI builder for 0.13.b024 - GUI builder for
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI [GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI

View File

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

View File

@@ -28,7 +28,7 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
} }
dependencies { dependencies {
add( "java9Compile", sourceSets.main.get().output ) add( "java9Implementation", sourceSets.main.get().output )
} }
tasks { tasks {

View File

@@ -63,11 +63,9 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
jar { jar {
manifest.attributes( "Multi-Release" to "true" ) manifest.attributes( "Multi-Release" to "true" )
into( "META-INF/versions/9" ) {
from( sourceSets["module-info"].output ) { from( sourceSets["module-info"].output ) {
include( "module-info.class" ) include( "module-info.class" )
} }
} }
} }
} }
}

View File

@@ -43,11 +43,6 @@ tasks {
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) ) options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
} }
processResources {
// build native libraries
dependsOn( ":flatlaf-natives-windows:assemble" )
}
jar { jar {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 1.6 #Version 2.2
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType" fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -30,7 +30,12 @@ fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY = "JTextFiel
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always" fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never" fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once" fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize" fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize"
fld public final static java.lang.String STYLE = "FlatLaf.style"
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
fld public final static java.lang.String TABBED_PANE_ALIGN_CENTER = "center" fld public final static java.lang.String TABBED_PANE_ALIGN_CENTER = "center"
fld public final static java.lang.String TABBED_PANE_ALIGN_FILL = "fill" fld public final static java.lang.String TABBED_PANE_ALIGN_FILL = "fill"
fld public final static java.lang.String TABBED_PANE_ALIGN_LEADING = "leading" fld public final static java.lang.String TABBED_PANE_ALIGN_LEADING = "leading"
@@ -59,6 +64,9 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight" fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement" fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets" fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode" fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode"
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact" fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact"
fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal" fld public final static java.lang.String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal"
@@ -67,12 +75,20 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground" fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor" fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight" fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
fld public final static java.lang.String TEXT_FIELD_PADDING = "JTextField.padding" fld public final static java.lang.String TEXT_FIELD_PADDING = "JTextField.padding"
fld public final static java.lang.String TEXT_FIELD_SHOW_CLEAR_BUTTON = "JTextField.showClearButton"
fld public final static java.lang.String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent"
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_BACKGROUND = "JRootPane.titleBarBackground"
fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground" fld public final static java.lang.String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"
fld public final static java.lang.String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon"
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection" fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection" fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations" fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean) meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object) meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
meth public static int clientPropertyInt(javax.swing.JComponent,java.lang.String,int) meth public static int clientPropertyInt(javax.swing.JComponent,java.lang.String,int)
@@ -165,6 +181,7 @@ meth public boolean isSupportedLookAndFeel()
meth public final boolean equals(java.lang.Object) meth public final boolean equals(java.lang.Object)
meth public final int hashCode() meth public final int hashCode()
meth public java.lang.String getID() meth public java.lang.String getID()
meth public java.util.Map<java.lang.String,java.lang.String> getExtraDefaults()
meth public javax.swing.Icon getDisabledIcon(javax.swing.JComponent,javax.swing.Icon) meth public javax.swing.Icon getDisabledIcon(javax.swing.JComponent,javax.swing.Icon)
meth public javax.swing.UIDefaults getDefaults() meth public javax.swing.UIDefaults getDefaults()
meth public static boolean install(javax.swing.LookAndFeel) meth public static boolean install(javax.swing.LookAndFeel)
@@ -174,6 +191,8 @@ meth public static boolean isShowMnemonics()
meth public static boolean isUseNativeWindowDecorations() meth public static boolean isUseNativeWindowDecorations()
meth public static boolean setup(javax.swing.LookAndFeel) meth public static boolean setup(javax.swing.LookAndFeel)
meth public static boolean supportsNativeWindowDecorations() meth public static boolean supportsNativeWindowDecorations()
meth public static java.lang.Object parseDefaultsValue(java.lang.String,java.lang.String,java.lang.Class<?>)
meth public static java.util.Map<java.lang.String,java.lang.String> getGlobalExtraDefaults()
meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float) meth public static javax.swing.UIDefaults$ActiveValue createActiveFontValue(float)
meth public static void hideMnemonics() meth public static void hideMnemonics()
meth public static void initIconColors(javax.swing.UIDefaults,boolean) meth public static void initIconColors(javax.swing.UIDefaults,boolean)
@@ -181,22 +200,26 @@ meth public static void installLafInfo(java.lang.String,java.lang.Class<? extend
meth public static void registerCustomDefaultsSource(java.io.File) meth public static void registerCustomDefaultsSource(java.io.File)
meth public static void registerCustomDefaultsSource(java.lang.String) meth public static void registerCustomDefaultsSource(java.lang.String)
meth public static void registerCustomDefaultsSource(java.lang.String,java.lang.ClassLoader) meth public static void registerCustomDefaultsSource(java.lang.String,java.lang.ClassLoader)
meth public static void registerCustomDefaultsSource(java.net.URL)
meth public static void repaintAllFramesAndDialogs() meth public static void repaintAllFramesAndDialogs()
meth public static void revalidateAndRepaintAllFramesAndDialogs() meth public static void revalidateAndRepaintAllFramesAndDialogs()
meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable) meth public static void runWithUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>,java.lang.Runnable)
meth public static void setGlobalExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
meth public static void setUseNativeWindowDecorations(boolean) meth public static void setUseNativeWindowDecorations(boolean)
meth public static void showMnemonics(java.awt.Component) meth public static void showMnemonics(java.awt.Component)
meth public static void unregisterCustomDefaultsSource(java.io.File) meth public static void unregisterCustomDefaultsSource(java.io.File)
meth public static void unregisterCustomDefaultsSource(java.lang.String) meth public static void unregisterCustomDefaultsSource(java.lang.String)
meth public static void unregisterCustomDefaultsSource(java.lang.String,java.lang.ClassLoader) meth public static void unregisterCustomDefaultsSource(java.lang.String,java.lang.ClassLoader)
meth public static void unregisterCustomDefaultsSource(java.net.URL)
meth public static void updateUI() meth public static void updateUI()
meth public static void updateUILater() meth public static void updateUILater()
meth public void initialize() meth public void initialize()
meth public void registerUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>) meth public void registerUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.String>)
meth public void uninitialize() meth public void uninitialize()
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>) meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
supr javax.swing.plaf.basic.BasicLookAndFeel supr javax.swing.plaf.basic.BasicLookAndFeel
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,mnemonicHandler,oldPopupFactory,postInitialization,uiDefaultsGetters,updateUIPending hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,subMenuUsabilityHelper,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -231,6 +254,7 @@ hfds baseTheme,dark,name,properties
CLSS public abstract interface com.formdev.flatlaf.FlatSystemProperties CLSS public abstract interface com.formdev.flatlaf.FlatSystemProperties
fld public final static java.lang.String ANIMATION = "flatlaf.animation" fld public final static java.lang.String ANIMATION = "flatlaf.animation"
fld public final static java.lang.String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded" fld public final static java.lang.String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"
fld public final static java.lang.String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath"
fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale" fld public final static java.lang.String UI_SCALE = "flatlaf.uiScale"
fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown" fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown"
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled" fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
@@ -330,7 +354,15 @@ innr public static HSLIncreaseDecrease
innr public static Mix innr public static Mix
meth public !varargs static java.awt.Color applyFunctions(java.awt.Color,com.formdev.flatlaf.util.ColorFunctions$ColorFunction[]) meth public !varargs static java.awt.Color applyFunctions(java.awt.Color,com.formdev.flatlaf.util.ColorFunctions$ColorFunction[])
meth public static float clamp(float) meth public static float clamp(float)
meth public static float luma(java.awt.Color)
meth public static java.awt.Color darken(java.awt.Color,float)
meth public static java.awt.Color desaturate(java.awt.Color,float)
meth public static java.awt.Color lighten(java.awt.Color,float)
meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float) meth public static java.awt.Color mix(java.awt.Color,java.awt.Color,float)
meth public static java.awt.Color saturate(java.awt.Color,float)
meth public static java.awt.Color shade(java.awt.Color,float)
meth public static java.awt.Color spin(java.awt.Color,float)
meth public static java.awt.Color tint(java.awt.Color,float)
supr java.lang.Object supr java.lang.Object
CLSS public abstract interface static com.formdev.flatlaf.util.ColorFunctions$ColorFunction CLSS public abstract interface static com.formdev.flatlaf.util.ColorFunctions$ColorFunction
@@ -566,6 +598,7 @@ meth public static java.util.List<java.awt.Image> getResolutionVariants(java.awt
supr java.lang.Object supr java.lang.Object
CLSS public com.formdev.flatlaf.util.NativeLibrary CLSS public com.formdev.flatlaf.util.NativeLibrary
cons public init(java.io.File,boolean)
cons public init(java.lang.String,java.lang.ClassLoader,boolean) cons public init(java.lang.String,java.lang.ClassLoader,boolean)
meth public boolean isLoaded() meth public boolean isLoaded()
supr java.lang.Object supr java.lang.Object
@@ -589,18 +622,52 @@ meth public void paintIcon(java.awt.Component,java.awt.Graphics,int,int)
supr java.lang.Object supr java.lang.Object
hfds iconHeight,iconWidth,imageIcon,lastImage,lastSystemScaleFactor,lastUserScaleFactor hfds iconHeight,iconWidth,imageIcon,lastImage,lastSystemScaleFactor,lastUserScaleFactor
CLSS public com.formdev.flatlaf.util.SoftCache<%0 extends java.lang.Object, %1 extends java.lang.Object>
cons public init()
cons public init(int)
intf java.util.Map<{com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1}>
meth public boolean containsKey(java.lang.Object)
meth public boolean containsValue(java.lang.Object)
meth public boolean isEmpty()
meth public int size()
meth public java.util.Collection<{com.formdev.flatlaf.util.SoftCache%1}> values()
meth public java.util.Set<java.util.Map$Entry<{com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1}>> entrySet()
meth public java.util.Set<{com.formdev.flatlaf.util.SoftCache%0}> keySet()
meth public void clear()
meth public void forEach(java.util.function.BiConsumer<? super {com.formdev.flatlaf.util.SoftCache%0},? super {com.formdev.flatlaf.util.SoftCache%1}>)
meth public void putAll(java.util.Map<? extends {com.formdev.flatlaf.util.SoftCache%0},? extends {com.formdev.flatlaf.util.SoftCache%1}>)
meth public void replaceAll(java.util.function.BiFunction<? super {com.formdev.flatlaf.util.SoftCache%0},? super {com.formdev.flatlaf.util.SoftCache%1},? extends {com.formdev.flatlaf.util.SoftCache%1}>)
meth public {com.formdev.flatlaf.util.SoftCache%1} get(java.lang.Object)
meth public {com.formdev.flatlaf.util.SoftCache%1} put({com.formdev.flatlaf.util.SoftCache%0},{com.formdev.flatlaf.util.SoftCache%1})
meth public {com.formdev.flatlaf.util.SoftCache%1} remove(java.lang.Object)
supr java.lang.Object
hfds map,queue
hcls CacheReference
CLSS public com.formdev.flatlaf.util.StringUtils CLSS public com.formdev.flatlaf.util.StringUtils
cons public init() cons public init()
meth public static boolean isEmpty(java.lang.String) meth public static boolean isEmpty(java.lang.String)
meth public static boolean isTrimmedEmpty(java.lang.String)
meth public static java.lang.String removeLeading(java.lang.String,java.lang.String) meth public static java.lang.String removeLeading(java.lang.String,java.lang.String)
meth public static java.lang.String removeTrailing(java.lang.String,java.lang.String) meth public static java.lang.String removeTrailing(java.lang.String,java.lang.String)
meth public static java.lang.String substringTrimmed(java.lang.String,int)
meth public static java.lang.String substringTrimmed(java.lang.String,int,int)
meth public static java.util.List<java.lang.String> split(java.lang.String,char) meth public static java.util.List<java.lang.String> split(java.lang.String,char)
meth public static java.util.List<java.lang.String> split(java.lang.String,char,boolean,boolean)
supr java.lang.Object
CLSS public com.formdev.flatlaf.util.SwingUtils
cons public init()
meth public static <%0 extends java.awt.Component> {%%0} getComponentByName(java.awt.Container,java.lang.String)
supr java.lang.Object supr java.lang.Object
CLSS public com.formdev.flatlaf.util.SystemInfo CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init() cons public init()
fld public final static boolean isAARCH64
fld public final static boolean isJava_11_orLater fld public final static boolean isJava_11_orLater
fld public final static boolean isJava_15_orLater fld public final static boolean isJava_15_orLater
fld public final static boolean isJava_17_orLater
fld public final static boolean isJava_18_orLater
fld public final static boolean isJava_9_orLater fld public final static boolean isJava_9_orLater
fld public final static boolean isJetBrainsJVM fld public final static boolean isJetBrainsJVM
fld public final static boolean isJetBrainsJVM_11_orLater fld public final static boolean isJetBrainsJVM_11_orLater
@@ -615,6 +682,8 @@ fld public final static boolean isWebswing
fld public final static boolean isWinPE fld public final static boolean isWinPE
fld public final static boolean isWindows fld public final static boolean isWindows
fld public final static boolean isWindows_10_orLater fld public final static boolean isWindows_10_orLater
fld public final static boolean isWindows_11_orLater
fld public final static boolean isX86
fld public final static boolean isX86_64 fld public final static boolean isX86_64
fld public final static long javaVersion fld public final static long javaVersion
fld public final static long osVersion fld public final static long osVersion
@@ -627,6 +696,7 @@ cons public init()
meth public static boolean isSystemScalingEnabled() meth public static boolean isSystemScalingEnabled()
meth public static double getSystemScaleFactor(java.awt.Graphics2D) meth public static double getSystemScaleFactor(java.awt.Graphics2D)
meth public static double getSystemScaleFactor(java.awt.GraphicsConfiguration) meth public static double getSystemScaleFactor(java.awt.GraphicsConfiguration)
meth public static float computeFontScaleFactor(java.awt.Font)
meth public static float getUserScaleFactor() meth public static float getUserScaleFactor()
meth public static float scale(float) meth public static float scale(float)
meth public static float unscale(float) meth public static float unscale(float)
@@ -933,6 +1003,34 @@ CLSS public abstract interface !annotation java.lang.annotation.Target
intf java.lang.annotation.Annotation intf java.lang.annotation.Annotation
meth public abstract java.lang.annotation.ElementType[] value() meth public abstract java.lang.annotation.ElementType[] value()
CLSS public abstract interface java.util.Map<%0 extends java.lang.Object, %1 extends java.lang.Object>
innr public abstract interface static Entry
meth public abstract boolean containsKey(java.lang.Object)
meth public abstract boolean containsValue(java.lang.Object)
meth public abstract boolean equals(java.lang.Object)
meth public abstract boolean isEmpty()
meth public abstract int hashCode()
meth public abstract int size()
meth public abstract java.util.Collection<{java.util.Map%1}> values()
meth public abstract java.util.Set<java.util.Map$Entry<{java.util.Map%0},{java.util.Map%1}>> entrySet()
meth public abstract java.util.Set<{java.util.Map%0}> keySet()
meth public abstract void clear()
meth public abstract void putAll(java.util.Map<? extends {java.util.Map%0},? extends {java.util.Map%1}>)
meth public abstract {java.util.Map%1} get(java.lang.Object)
meth public abstract {java.util.Map%1} put({java.util.Map%0},{java.util.Map%1})
meth public abstract {java.util.Map%1} remove(java.lang.Object)
meth public boolean remove(java.lang.Object,java.lang.Object)
meth public boolean replace({java.util.Map%0},{java.util.Map%1},{java.util.Map%1})
meth public void forEach(java.util.function.BiConsumer<? super {java.util.Map%0},? super {java.util.Map%1}>)
meth public void replaceAll(java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
meth public {java.util.Map%1} compute({java.util.Map%0},java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
meth public {java.util.Map%1} computeIfAbsent({java.util.Map%0},java.util.function.Function<? super {java.util.Map%0},? extends {java.util.Map%1}>)
meth public {java.util.Map%1} computeIfPresent({java.util.Map%0},java.util.function.BiFunction<? super {java.util.Map%0},? super {java.util.Map%1},? extends {java.util.Map%1}>)
meth public {java.util.Map%1} getOrDefault(java.lang.Object,{java.util.Map%1})
meth public {java.util.Map%1} merge({java.util.Map%0},{java.util.Map%1},java.util.function.BiFunction<? super {java.util.Map%1},? super {java.util.Map%1},? extends {java.util.Map%1}>)
meth public {java.util.Map%1} putIfAbsent({java.util.Map%0},{java.util.Map%1})
meth public {java.util.Map%1} replace({java.util.Map%0},{java.util.Map%1})
CLSS public abstract interface javax.swing.Icon CLSS public abstract interface javax.swing.Icon
meth public abstract int getIconHeight() meth public abstract int getIconHeight()
meth public abstract int getIconWidth() meth public abstract int getIconWidth()

View File

@@ -391,6 +391,40 @@ public interface FlatClientProperties
*/ */
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling"; String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JSplitPane ---------------------------------------------------------
/**
* Specifies what side of the spilt pane is allowed to expand
* via one-touch expanding arrow buttons.
* Requires that one-touch expanding is enabled with
* {@link javax.swing.JSplitPane#setOneTouchExpandable(boolean)}.
* <p>
* <strong>Component</strong> {@link javax.swing.JSplitPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_LEFT} or
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_RIGHT}
*
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide";
/**
* Allow expanding only left/top side of the split pane.
*
* @see #SPLIT_PANE_EXPANDABLE_SIDE
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left";
/**
* Allow expanding only right/bottom side of the split pane.
*
* @see #SPLIT_PANE_EXPANDABLE_SIDE
* @since 2.2
*/
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
//---- JTabbedPane -------------------------------------------------------- //---- JTabbedPane --------------------------------------------------------
/** /**
@@ -874,7 +908,26 @@ public interface FlatClientProperties
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #OUTLINE}. * (see {@link #OUTLINE}).
* <p>
* The component is prepared in the following way:
* <ul>
* <li>Component client property {@link #STYLE_CLASS} is set to {@code inTextField}.
* <li>If component is a button or toggle button, client property {@link #BUTTON_TYPE}
* is set to {@link #BUTTON_TYPE_TOOLBAR_BUTTON}
* and button cursor is set to default cursor (if not set).
* <li>If component is a toolbar, client property {@link #STYLE_CLASS}
* is set to {@code inTextField} on all toolbar children
* and toolbar cursor is set to default cursor (if not set).
* </ul>
* Because text fields use the text cursor by default and the cursor is inherited by child components,
* it may be necessary to explicitly set component cursor if you e.g. need the default arrow cursor.
* E.g. {@code comp.setCursor( Cursor.getDefaultCursor() )}.
* <p>
* Styling is used to modify insets/margins and appearance of buttons and toolbars
* so that they fit nicely into the text field and do not increase text field height.
* See styles {@code [style]Button.inTextField} and {@code [style]ToolBar.inTextField}
* in {@code Flat[Light|Dark]Laf.properties}.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br> * <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link javax.swing.JComponent} * <strong>Value type</strong> {@link javax.swing.JComponent}
@@ -886,15 +939,7 @@ public interface FlatClientProperties
/** /**
* Specifies a component that will be placed at the trailing edge of the text field. * Specifies a component that will be placed at the trailing edge of the text field.
* <p> * <p>
* The component will be positioned inside and aligned to the visible text field border. * See {@link #TEXT_FIELD_LEADING_COMPONENT} for details.
* There is no gap between the visible border and the component.
* The laid out component size will be the preferred component width
* and the inner text field height.
* <p>
* The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #OUTLINE}.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br> * <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link javax.swing.JComponent} * <strong>Value type</strong> {@link javax.swing.JComponent}
@@ -947,7 +992,22 @@ public interface FlatClientProperties
//---- JToggleButton ------------------------------------------------------ //---- JToggleButton ------------------------------------------------------
/** /**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}. * Placement of underline if toggle button type is {@link #BUTTON_TYPE_TAB}
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
* <strong>SupportedValues:</strong>
* {@link SwingConstants#BOTTOM} (default)
* {@link SwingConstants#TOP},
* {@link SwingConstants#LEFT} or
* {@link SwingConstants#RIGHT}
*
* @since 2.3
*/
String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement";
/**
* Thickness of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br> * <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer} * <strong>Value type</strong> {@link java.lang.Integer}

View File

@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
* Allows loading of additional .properties files from addon JARs. * Allows loading of additional .properties files from addon JARs.
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs. * {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
* <p> * <p>
* If you extend this class in a addon JAR, you also have to add a text file named * If you extend this class in an addon JAR, you also have to add a text file named
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon} * {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
* to the addon JAR. The file must contain a single line with the class name. * to the addon JAR. The file must contain a single line with the class name.
* <p> * <p>
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
/** /**
* Returns the priority used to sort addon loading. * Returns the priority used to sort addon loading.
* The order is only important if you want overwrite UI defaults of other addons. * The order is only important if you want to overwrite UI defaults of other addons.
* Lower numbers mean higher priority. * Lower numbers mean higher priority.
* Returns 10000 by default. * Returns 10000 by default.
*/ */

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
/** /**
* Default color palette for action icons and object icons. * Default color palette for action icons and object icons.
* <p> * <p>
* The idea is to use only this well defined set of colors in SVG icons and * The idea is to use only this well-defined set of colors in SVG icons, and
* then they are replaced at runtime to dark variants or to other theme colors. * then they are replaced at runtime to dark variants or to other theme colors.
* Then a single SVG icon (light variant) can be used for dark themes too. * Then a single SVG icon (light variant) can be used for dark themes too.
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons. * IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
* <p> * <p>
* You may use these colors also in your application (outside of SVG icons), but do * You may use these colors also in your application (outside of SVG icons), but do
* not use the RGB values defined in this enum.<br> * not use the RGB values defined in this enum.<br>
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}. * Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */

View File

@@ -596,7 +596,7 @@ class FlatInputMaps
//---- class LazyInputMapEx ----------------------------------------------- //---- class LazyInputMapEx -----------------------------------------------
/** /**
* Lazily creates a input map. * Lazily creates an input map.
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays. * Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
*/ */
private static class LazyInputMapEx private static class LazyInputMapEx

View File

@@ -103,6 +103,7 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory; private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler; private MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper;
private Consumer<UIDefaults> postInitialization; private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters; private List<Function<Object, Object>> uiDefaultsGetters;
@@ -166,18 +167,19 @@ public abstract class FlatLaf
* Returns whether FlatLaf supports custom window decorations. * Returns whether FlatLaf supports custom window decorations.
* This depends on the operating system and on the used Java runtime. * This depends on the operating system and on the used Java runtime.
* <p> * <p>
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise. * This method returns {@code true} on Windows 10/11 (see exception below)
* and on Linux, {@code false} otherwise.
* <p> * <p>
* Returns also {@code false} on Windows 10 if: * Returns also {@code false} on Windows 10/11 if:
* <ul> * <ul>
* <li>FlatLaf native window border support is available (requires Windows 10)</li> * <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
* <li>running in * <li>running in
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a> * <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>) * (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
* and JBR supports custom window decorations * and JBR supports custom window decorations
* </li> * </li>
* </ul> * </ul>
* In this cases, custom decorations are enabled by the root pane. * In these cases, custom decorations are enabled by the root pane.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or * Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary. * {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
*/ */
@@ -190,7 +192,7 @@ public abstract class FlatLaf
FlatNativeWindowBorder.isSupported() ) FlatNativeWindowBorder.isSupported() )
return false; return false;
return SystemInfo.isWindows_10_orLater; return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
} }
@Override @Override
@@ -243,6 +245,10 @@ public abstract class FlatLaf
mnemonicHandler = new MnemonicHandler(); mnemonicHandler = new MnemonicHandler();
mnemonicHandler.install(); mnemonicHandler.install();
// install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper();
subMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes // listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) { if( SystemInfo.isWindows ) {
// Windows 10 allows increasing font size independent of scaling: // Windows 10 allows increasing font size independent of scaling:
@@ -322,6 +328,12 @@ public abstract class FlatLaf
mnemonicHandler = null; mnemonicHandler = null;
} }
// uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) {
subMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null;
}
// restore default link color // restore default link color
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" ); new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
postInitialization = null; postInitialization = null;
@@ -605,7 +617,7 @@ public abstract class FlatLaf
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> { uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) ); return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
} ); } );
}; }
// increase font size if system property "flatlaf.uiScale" is set // increase font size if system property "flatlaf.uiScale" is set
uiFont = UIScale.applyCustomScaleFactor( uiFont ); uiFont = UIScale.applyCustomScaleFactor( uiFont );
@@ -760,7 +772,7 @@ public abstract class FlatLaf
* Invoke this method before setting the look and feel. * Invoke this method before setting the look and feel.
* <p> * <p>
* If using Java modules, the package must be opened in {@code module-info.java}. * If using Java modules, the package must be opened in {@code module-info.java}.
* Otherwise use {@link #registerCustomDefaultsSource(URL)}. * Otherwise, use {@link #registerCustomDefaultsSource(URL)}.
* *
* @param packageName a package name (e.g. "com.myapp.resources") * @param packageName a package name (e.g. "com.myapp.resources")
*/ */
@@ -862,7 +874,7 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p> * <p>
* The global extra defaults are useful for smaller additional defaults that may change. * The global extra defaults are useful for smaller additional defaults that may change.
* E.g. accent color. Otherwise FlatLaf properties files should be used. * E.g. accent color. Otherwise, FlatLaf properties files should be used.
* See {@link #registerCustomDefaultsSource(String)}. * See {@link #registerCustomDefaultsSource(String)}.
* <p> * <p>
* The keys and values are strings in same format as in FlatLaf properties files. * The keys and values are strings in same format as in FlatLaf properties files.
@@ -894,7 +906,7 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p> * <p>
* The extra defaults are useful for smaller additional defaults that may change. * The extra defaults are useful for smaller additional defaults that may change.
* E.g. accent color. Otherwise FlatLaf properties files should be used. * E.g. accent color. Otherwise, FlatLaf properties files should be used.
* See {@link #registerCustomDefaultsSource(String)}. * See {@link #registerCustomDefaultsSource(String)}.
* <p> * <p>
* The keys and values are strings in same format as in FlatLaf properties files. * The keys and values are strings in same format as in FlatLaf properties files.
@@ -951,7 +963,7 @@ public abstract class FlatLaf
// re-set current LaF // re-set current LaF
UIManager.setLookAndFeel( lookAndFeel ); UIManager.setLookAndFeel( lookAndFeel );
// must fire property change events ourself because old and new LaF are the same // must fire property change events ourselves because old and new LaF are the same
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel ); PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() ) for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
l.propertyChange( e ); l.propertyChange( e );
@@ -995,7 +1007,7 @@ public abstract class FlatLaf
/** /**
* Returns whether native window decorations are supported on current platform. * Returns whether native window decorations are supported on current platform.
* <p> * <p>
* This requires Windows 10, but may be disabled if running in special environments * This requires Windows 10/11, but may be disabled if running in special environments
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails. * (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to * If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
* {@code false}, then this method also returns {@code false}. * {@code false}, then this method also returns {@code false}.
@@ -1223,6 +1235,9 @@ public abstract class FlatLaf
} }
private Object getValue( Object key ) { private Object getValue( Object key ) {
// use local variable for getters to avoid potential multi-threading issues
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
if( uiDefaultsGetters == null ) if( uiDefaultsGetters == null )
return null; return null;
@@ -1276,8 +1291,9 @@ public abstract class FlatLaf
this.scaleSize = scaleSize; this.scaleSize = scaleSize;
} }
// using synchronized to avoid exception if invoked at the same time on multiple threads
@Override @Override
public Object createValue( UIDefaults table ) { public synchronized Object createValue( UIDefaults table ) {
if( inCreateValue ) if( inCreateValue )
throw new IllegalStateException( "FlatLaf: endless recursion in font" ); throw new IllegalStateException( "FlatLaf: endless recursion in font" );

View File

@@ -34,7 +34,7 @@ public interface FlatSystemProperties
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale", * To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
* which has the same syntax as this one. * which has the same syntax as this one.
* <p> * <p>
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed. * Since FlatLaf 1.1.2: Scale factors less than 100% are allowed.
* <p> * <p>
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br> * <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
*/ */
@@ -81,7 +81,7 @@ public interface FlatSystemProperties
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and * {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
* UI default {@code TitlePane.useWindowDecorations}. * UI default {@code TitlePane.useWindowDecorations}.
* <p> * <p>
* (requires Window 10) * (requires Window 10/11)
* <p> * <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br> * <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> none * <strong>Default</strong> none
@@ -92,16 +92,16 @@ public interface FlatSystemProperties
* Specifies whether JetBrains Runtime custom window decorations should be used * Specifies whether JetBrains Runtime custom window decorations should be used
* when creating {@code JFrame} or {@code JDialog}. * when creating {@code JFrame} or {@code JDialog}.
* Requires that the application runs in a * Requires that the application runs in a
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a> * <a href="https://github.com/JetBrains/JetBrainsRuntime/wiki">JetBrains Runtime</a>
* (based on OpenJDK). * (based on OpenJDK).
* <p> * <p>
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations. * Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
* Then FlatLaf native window decorations are used. * Then FlatLaf native window decorations are used.
* <p> * <p>
* (requires Window 10) * (requires Window 10/11)
* <p> * <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br> * <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true} * <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
*/ */
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"; String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
@@ -116,7 +116,7 @@ public interface FlatSystemProperties
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and * {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
* UI default {@code TitlePane.menuBarEmbedded}. * UI default {@code TitlePane.menuBarEmbedded}.
* <p> * <p>
* (requires Window 10) * (requires Window 10/11)
* <p> * <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br> * <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> none * <strong>Default</strong> none
@@ -139,6 +139,15 @@ public interface FlatSystemProperties
*/ */
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"; String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
/**
* Specifies a directory in which the native FlatLaf library have been extracted.
* The path can be absolute or relative to current application working directory.
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
*
* @since 2
*/
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
/** /**
* Checks whether a system property is set and returns {@code true} if its value * Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}. * is {@code "true"} (case-insensitive), otherwise it returns {@code false}.

View File

@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.ColorFunctions; import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils; import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* This class supports loading IntelliJ .theme.json files and using them as a Laf. * This class supports loading IntelliJ .theme.json files and using them as a Laf.
@@ -162,8 +163,11 @@ public class IntelliJTheme
applyCheckBoxColors( defaults ); applyCheckBoxColors( defaults );
// copy values // copy values
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
defaults.put( e.getKey(), defaults.get( e.getValue() ) ); Object value = defaults.get( e.getValue() );
if( value != null )
defaults.put( e.getKey(), value );
}
// IDEA does not paint button background if disabled, but FlatLaf does // IDEA does not paint button background if disabled, but FlatLaf does
Object panelBackground = defaults.get( "Panel.background" ); Object panelBackground = defaults.get( "Panel.background" );
@@ -175,7 +179,7 @@ public class IntelliJTheme
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) ); defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) ); defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground // IDEA uses an SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
Object helpButtonBackground = defaults.get( "Button.startBackground" ); Object helpButtonBackground = defaults.get( "Button.startBackground" );
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" ); Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
if( helpButtonBackground == null ) if( helpButtonBackground == null )
@@ -242,7 +246,19 @@ public class IntelliJTheme
defaults.put( "Tree.rowHeight", 22 ); defaults.put( "Tree.rowHeight", 22 );
// apply theme specific UI defaults at the end to allow overwriting // apply theme specific UI defaults at the end to allow overwriting
defaults.putAll( themeSpecificDefaults ); for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
Object key = e.getKey();
Object value = e.getValue();
// append styles to existing styles
if( key instanceof String && ((String)key).startsWith( "[style]" ) ) {
Object oldValue = defaults.get( key );
if( oldValue != null )
value = oldValue + "; " + value;
}
defaults.put( key, value );
}
} }
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) { private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
@@ -299,8 +315,19 @@ public class IntelliJTheme
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) { private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
if( value instanceof Map ) { if( value instanceof Map ) {
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() ) Map<String, Object> map = (Map<String, Object>)value;
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
String osKey = SystemInfo.isWindows ? "os.windows"
: SystemInfo.isMacOS ? "os.mac"
: SystemInfo.isLinux ? "os.linux" : null;
if( osKey != null && map.containsKey( osKey ) )
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
else if( map.containsKey( "os.default" ) )
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
} else {
for( Map.Entry<String, Object> e : map.entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys ); apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
}
} else { } else {
if( "".equals( value ) ) if( "".equals( value ) )
return; // ignore empty value return; // ignore empty value
@@ -534,12 +561,12 @@ public class IntelliJTheme
} }
/** Rename UI default keys (key --> value). */ /** Rename UI default keys (key --> value). */
private static Map<String, String> uiKeyMapping = new HashMap<>(); private static final Map<String, String> uiKeyMapping = new HashMap<>();
/** Copy UI default keys (value --> key). */ /** Copy UI default keys (value --> key). */
private static Map<String, String> uiKeyCopying = new HashMap<>(); private static final Map<String, String> uiKeyCopying = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>(); private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>(); private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
private static Map<String, String> checkboxDuplicateColors = new HashMap<>(); private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
static { static {
// ComboBox // ComboBox
@@ -600,6 +627,11 @@ public class IntelliJTheme
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" ); uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" ); uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
// TabbedPane
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
// TitlePane // TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" ); uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" ); uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );

View File

@@ -170,7 +170,7 @@ class LinuxFontPolicy
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" ); Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
if( value instanceof Integer ) { if( value instanceof Integer ) {
int dpi = ((Integer)value).intValue() / 1024; int dpi = (Integer) value / 1024;
if( dpi == -1 ) if( dpi == -1 )
dpi = 96; dpi = 96;
if( dpi < 50 ) if( dpi < 50 )
@@ -197,7 +197,7 @@ class LinuxFontPolicy
} }
/** /**
* Gets the default font for KDE for KDE configuration files. * Gets the default font for KDE from KDE configuration files.
* *
* The Swing fonts are not updated when the user changes system font size * The Swing fonts are not updated when the user changes system font size
* (System Settings > Fonts > Force Font DPI). A application restart is necessary. * (System Settings > Fonts > Force Font DPI). A application restart is necessary.
@@ -278,7 +278,7 @@ class LinuxFontPolicy
// read config file // read config file
ArrayList<String> lines = new ArrayList<>( 200 ); ArrayList<String> lines = new ArrayList<>( 200 );
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) { try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
String line = null; String line;
while( (line = reader.readLine()) != null ) while( (line = reader.readLine()) != null )
lines.add( line ); lines.add( line );
} catch( IOException ex ) { } catch( IOException ex ) {

View File

@@ -0,0 +1,322 @@
/*
* Copyright 2022 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* Improves usability of submenus by using a
* <a href="https://height.app/blog/guide-to-build-context-menus#safe-triangle">safe triangle</a>
* to avoid that the submenu closes while the user moves the mouse to it.
*
* @author Karl Tauber
*/
class SubMenuUsabilityHelper
implements ChangeListener
{
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter;
private boolean changePending;
// mouse location in screen coordinates
private int mouseX;
private int mouseY;
// target popup bounds in screen coordinates
private int targetX;
private int targetTopY;
private int targetBottomY;
private Rectangle invokerBounds;
void install() {
MenuSelectionManager.defaultManager().addChangeListener( this );
}
void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this );
uninstallEventQueue();
}
@Override
public void stateChanged( ChangeEvent e ) {
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
return;
// handle menu selection change later, but only once in case of temporary changes
// e.g. moving mouse from one menu item to another one, fires two events:
// 1. old menu item is removed from menu selection
// 2. new menu item is added to menu selection
synchronized( this ) {
if( changePending )
return;
changePending = true;
}
EventQueue.invokeLater( () -> {
synchronized( this ) {
changePending = false;
}
menuSelectionChanged();
} );
}
private void menuSelectionChanged() {
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
/*debug
System.out.println( "--- " + path.length );
for( int i = 0; i < path.length; i++ )
System.out.println( " " + i + ": " + path[i].getClass().getName() );
debug*/
// find submenu in menu selection
int subMenuIndex = findSubMenu( path );
// uninstall if there is no submenu in selection
if( subMenuIndex < 0 || subMenuIndex != path.length - 1 ) {
uninstallEventQueue();
return;
}
// get current mouse location
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
Point mouseLocation = (pointerInfo != null) ? pointerInfo.getLocation() : new Point();
mouseX = mouseLocation.x;
mouseY = mouseLocation.y;
// check whether popup is showing, which is e.g. not the case if it is empty
JPopupMenu popup = (JPopupMenu) path[subMenuIndex];
if( !popup.isShowing() ) {
uninstallEventQueue();
return;
}
// get invoker screen bounds
Component invoker = popup.getInvoker();
invokerBounds = (invoker != null)
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
: null;
// check whether mouse location is within invoker
if( invokerBounds != null && !invokerBounds.contains( mouseX, mouseY ) ) {
uninstallEventQueue();
return;
}
// compute top/bottom target locations
Point popupLocation = popup.getLocationOnScreen();
Dimension popupSize = popup.getSize();
targetX = (mouseX < popupLocation.x + (popupSize.width / 2))
? popupLocation.x
: popupLocation.x + popupSize.width;
targetTopY = popupLocation.y;
targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to supress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue();
// create safe triangle painter
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
safeTrianglePainter = new SafeTrianglePainter( popup );
}
private void uninstallEventQueue() {
if( subMenuEventQueue != null ) {
subMenuEventQueue.uninstall();
subMenuEventQueue = null;
}
if( safeTrianglePainter != null ) {
safeTrianglePainter.uninstall();
safeTrianglePainter = null;
}
}
private int findSubMenu( MenuElement[] path ) {
for( int i = path.length - 1; i >= 1; i-- ) {
if( path[i] instanceof JPopupMenu &&
path[i - 1] instanceof JMenu &&
!((JMenu)path[i - 1]).isTopLevelMenu() )
return i;
}
return -1;
}
private Polygon createSafeTriangle() {
return new Polygon(
new int[] { mouseX, targetX, targetX },
new int[] { mouseY, targetTopY, targetBottomY },
3 );
}
//---- class SubMenuEventQueue --------------------------------------------
private class SubMenuEventQueue
extends EventQueue
{
private Timer mouseUpdateTimer;
private Timer timeoutTimer;
private int newMouseX;
private int newMouseY;
private AWTEvent lastMouseEvent;
SubMenuEventQueue() {
// timer used to slightly delay update of mouse location used for safe triangle
mouseUpdateTimer = new Timer( 50, e -> {
mouseX = newMouseX;
mouseY = newMouseY;
if( safeTrianglePainter != null )
safeTrianglePainter.repaint();
} );
mouseUpdateTimer.setRepeats( false );
// timer used to timeout safe triangle when mouse stops moving
timeoutTimer = new Timer( 200, e -> {
if( invokerBounds != null && !invokerBounds.contains( newMouseX, newMouseY ) ) {
// post last mouse event, which selects menu item at mouse location
if( lastMouseEvent != null ) {
postEvent( lastMouseEvent );
lastMouseEvent = null;
}
uninstallEventQueue();
return;
}
} );
timeoutTimer.setRepeats( false );
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
}
void uninstall() {
mouseUpdateTimer.stop();
mouseUpdateTimer = null;
timeoutTimer.stop();
timeoutTimer = null;
lastMouseEvent = null;
super.pop();
}
@Override
protected void dispatchEvent( AWTEvent e ) {
int id = e.getID();
if( e instanceof MouseEvent &&
(id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) )
{
newMouseX = ((MouseEvent)e).getXOnScreen();
newMouseY = ((MouseEvent)e).getYOnScreen();
if( safeTrianglePainter != null )
safeTrianglePainter.repaint();
mouseUpdateTimer.stop();
timeoutTimer.stop();
// check whether mouse moved within safe triangle
if( createSafeTriangle().contains( newMouseX, newMouseY ) ) {
// update mouse location delayed (this changes the safe triangle)
mouseUpdateTimer.start();
timeoutTimer.start();
// remember last mouse event, which will be posted if the mouse stops moving
lastMouseEvent = e;
// ignore mouse event
return;
}
// update mouse location immediately (this changes the safe triangle)
mouseX = newMouseX;
mouseY = newMouseY;
}
super.dispatchEvent( e );
}
}
//---- class SafeTrianglePainter ------------------------------------------
private class SafeTrianglePainter
extends JComponent
{
SafeTrianglePainter( JPopupMenu popup ) {
Window window = SwingUtilities.windowForComponent( popup.getInvoker() );
if( window instanceof RootPaneContainer ) {
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
setSize( layeredPane.getSize() );
layeredPane.add( this, new Integer( JLayeredPane.POPUP_LAYER + 1 ) );
}
}
void uninstall() {
Container parent = getParent();
if( parent != null ) {
parent.remove( this );
parent.repaint();
}
}
@Override
protected void paintComponent( Graphics g ) {
Point locationOnScreen = getLocationOnScreen();
g.translate( -locationOnScreen.x, -locationOnScreen.y );
g.setColor( Color.red );
((Graphics2D)g).draw( createSafeTriangle() );
}
}
}

View File

@@ -350,7 +350,7 @@ class UIDefaultsLoader
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT, enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY } SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
private static ValueType[] tempResultValueType = new ValueType[1]; private static final ValueType[] tempResultValueType = new ValueType[1];
private static Map<Class<?>, ValueType> javaValueTypes; private static Map<Class<?>, ValueType> javaValueTypes;
private static Map<String, ValueType> knownValueTypes; private static Map<String, ValueType> knownValueTypes;
@@ -466,6 +466,10 @@ class UIDefaultsLoader
if( knownValueTypes == null ) { if( knownValueTypes == null ) {
// create lazy // create lazy
knownValueTypes = new HashMap<>(); knownValueTypes = new HashMap<>();
// system colors
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "windowBorder", ValueType.COLOR );
// SplitPane // SplitPane
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER ); knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER ); knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
@@ -780,6 +784,7 @@ class UIDefaultsLoader
case "tint": return parseColorMix( "#fff", params, resolver, reportError ); case "tint": return parseColorMix( "#fff", params, resolver, reportError );
case "shade": return parseColorMix( "#000", params, resolver, reportError ); case "shade": return parseColorMix( "#000", params, resolver, reportError );
case "contrast": return parseColorContrast( params, resolver, reportError ); case "contrast": return parseColorContrast( params, resolver, reportError );
case "over": return parseColorOver( params, resolver, reportError );
} }
} finally { } finally {
parseColorDepth--; parseColorDepth--;
@@ -792,7 +797,7 @@ class UIDefaultsLoader
* Syntax: if(condition,trueValue,falseValue) * Syntax: if(condition,trueValue,falseValue)
* <p> * <p>
* This "if" function is only used if the "if" is passed as parameter to another * This "if" function is only used if the "if" is passed as parameter to another
* color function. Otherwise the general "if" function is used. * color function. Otherwise, the general "if" function is used.
*/ */
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
if( params.size() != 3 ) if( params.size() != 3 )
@@ -847,7 +852,7 @@ class UIDefaultsLoader
int lightness = parsePercentage( params.get( 2 ) ); int lightness = parsePercentage( params.get( 2 ) );
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100; int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
float[] hsl = new float[] { hue, saturation, lightness }; float[] hsl = { hue, saturation, lightness };
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) ); return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
} }
@@ -902,21 +907,32 @@ class UIDefaultsLoader
* Syntax: fade(color,amount[,options]) * Syntax: fade(color,amount[,options])
* - color: a color (e.g. #f00) or a color function * - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100% * - amount: percentage 0-100%
* - options: [derived] * - options: [derived] [lazy]
*/ */
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) { private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) ); int amount = parsePercentage( params.get( 1 ) );
boolean derived = false; boolean derived = false;
boolean lazy = false;
if( params.size() > 2 ) { if( params.size() > 2 ) {
String options = params.get( 2 ); String options = params.get( 2 );
derived = options.contains( "derived" ); derived = options.contains( "derived" );
lazy = options.contains( "lazy" );
} }
// create function // create function
ColorFunction function = new ColorFunctions.Fade( amount ); ColorFunction function = new ColorFunctions.Fade( amount );
if( lazy ) {
return (LazyValue) t -> {
Object color = lazyUIManagerGet( colorStr );
return (color instanceof Color)
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
: null;
};
}
// parse base color, apply function and create derived color // parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError ); return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
} }
@@ -1030,6 +1046,33 @@ class UIDefaultsLoader
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError ); return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
} }
/**
* Syntax: over(foreground,background)
* - foreground: a foreground color (e.g. #f00) or a color function;
* the alpha of this color is used as weight to mix the two colors
* - background: a background color (e.g. #f00) or a color function
*/
private static Object parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
String foregroundStr = params.get( 0 );
String backgroundStr = params.get( 1 );
// parse foreground color
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
if( foreground == null || foreground.getAlpha() == 255 )
return foreground;
Color foreground2 = new Color( foreground.getRGB() );
// parse background color
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
if( background == null )
return foreground2;
// create new color
float weight = foreground.getAlpha() / 255f;
return new ColorUIResource( ColorFunctions.mix( foreground2, background, weight ) );
}
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function, private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError ) boolean derived, Function<String, String> resolver, boolean reportError )
{ {
@@ -1198,7 +1241,7 @@ class UIDefaultsLoader
} }
Integer integer = parseInteger( value, true ); Integer integer = parseInteger( value, true );
if( integer.intValue() < min || integer.intValue() > max ) if( integer < min || integer > max )
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' ); throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
return integer; return integer;
} }
@@ -1239,28 +1282,28 @@ class UIDefaultsLoader
private static ActiveValue parseScaledInteger( String value ) { private static ActiveValue parseScaledInteger( String value ) {
int val = parseInteger( value, true ); int val = parseInteger( value, true );
return (ActiveValue) t -> { return t -> {
return UIScale.scale( val ); return UIScale.scale( val );
}; };
} }
private static ActiveValue parseScaledFloat( String value ) { private static ActiveValue parseScaledFloat( String value ) {
float val = parseFloat( value, true ); float val = parseFloat( value, true );
return (ActiveValue) t -> { return t -> {
return UIScale.scale( val ); return UIScale.scale( val );
}; };
} }
private static ActiveValue parseScaledInsets( String value ) { private static ActiveValue parseScaledInsets( String value ) {
Insets insets = parseInsets( value ); Insets insets = parseInsets( value );
return (ActiveValue) t -> { return t -> {
return UIScale.scale( insets ); return UIScale.scale( insets );
}; };
} }
private static ActiveValue parseScaledDimension( String value ) { private static ActiveValue parseScaledDimension( String value ) {
Dimension dimension = parseDimension( value ); Dimension dimension = parseDimension( value );
return (ActiveValue) t -> { return t -> {
return UIScale.scale( dimension ); return UIScale.scale( dimension );
}; };
} }

View File

@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
import com.formdev.flatlaf.util.AnimatedIcon; import com.formdev.flatlaf.util.AnimatedIcon;
/** /**
* Base class for animated icons that scales width and height, creates and initializes * Base class for animated icons that scale width and height, creates and initializes
* a scaled graphics context for icon painting. * a scaled graphics context for icon painting.
* <p> * <p>
* Subclasses do not need to scale icon painting. * Subclasses do not need to scale icon painting.
* <p> * <p>
* This class does not store any state information (needed for animation) in its instance. * This class does not store any state information (needed for animation) in its instance.
* Instead a client property is set on the painted component. * Instead, a client property is set on the painted component.
* This makes it possible to use a share icon instance for multiple components. * This makes it possible to use a share icon instance for multiple components.
* *
* @author Karl Tauber * @author Karl Tauber

View File

@@ -51,7 +51,7 @@ public class FlatAscendingSortIcon
boolean chevron = this.chevron; boolean chevron = this.chevron;
Color sortIconColor = this.sortIconColor; Color sortIconColor = this.sortIconColor;
// Because this icons are always shared for all table headers, // Because this icon is always shared for all table headers,
// get icon specific style from FlatTableHeaderUI. // get icon specific style from FlatTableHeaderUI.
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c ); JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
if( tableHeader != null ) { if( tableHeader != null ) {

View File

@@ -96,8 +96,8 @@ public class FlatHelpButtonIcon
</svg> </svg>
*/ */
boolean enabled = c.isEnabled(); boolean enabled = c == null || c.isEnabled();
boolean focused = FlatUIUtils.isPermanentFocusOwner( c ); boolean focused = c != null && FlatUIUtils.isPermanentFocusOwner( c );
float xy = 0.5f; float xy = 0.5f;
float wh = iconSize() - 1; float wh = iconSize() - 1;

View File

@@ -63,7 +63,7 @@ public class FlatMenuArrowIcon
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
if( !c.getComponentOrientation().isLeftToRight() ) if( c != null && !c.getComponentOrientation().isLeftToRight() )
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. ); g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
g.setColor( getArrowColor( c ) ); g.setColor( getArrowColor( c ) );
@@ -82,7 +82,7 @@ public class FlatMenuArrowIcon
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() ) if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
return selectionForeground; return selectionForeground;
return c.isEnabled() ? arrowColor : disabledArrowColor; return c == null || c.isEnabled() ? arrowColor : disabledArrowColor;
} }
protected boolean isUnderlineSelection() { protected boolean isUnderlineSelection() {

View File

@@ -76,7 +76,7 @@ public class FlatTreeCollapsedIcon
} }
/** /**
* Because this icons are always shared for all trees, * Because this icon is always shared for all trees,
* get icon specific style from FlatTreeUI. * get icon specific style from FlatTreeUI.
*/ */
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) { static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {

View File

@@ -20,6 +20,7 @@ import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
@@ -65,8 +66,14 @@ public abstract class FlatWindowAbstractIcon
protected void paintBackground( Component c, Graphics2D g ) { protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground ); Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) { if( background != null ) {
// disable antialiasing for background rectangle painting to avoid blury 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 );
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) ); g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
g.fillRect( 0, 0, width, height ); g.fillRect( 0, 0, width, height );
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldHint );
} }
} }

View File

@@ -24,6 +24,7 @@ import java.awt.geom.Line2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* "close" icon for windows (frames and dialogs). * "close" icon for windows (frames and dialogs).
@@ -54,7 +55,7 @@ public class FlatWindowCloseIcon
int iy = y + ((height - iwh) / 2); int iy = y + ((height - iwh) / 2);
int ix2 = ix + iwh - 1; int ix2 = ix + iwh - 1;
int iy2 = iy + iwh - 1; int iy2 = iy + iwh - 1;
int thickness = (int) scaleFactor; float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false ); path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.icons;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* "maximize" icon for windows (frames and dialogs). * "maximize" icon for windows (frames and dialogs).
@@ -35,8 +36,11 @@ public class FlatWindowMaximizeIcon
int iwh = (int) (10 * scaleFactor); int iwh = (int) (10 * scaleFactor);
int ix = x + ((width - iwh) / 2); int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2); int iy = y + ((height - iwh) / 2);
int thickness = (int) scaleFactor; float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
g.fill( FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) ); g.fill( SystemInfo.isWindows_11_orLater
? FlatUIUtils.createRoundRectangle( ix, iy, iwh, iwh, thickness, arc, arc, arc, arc )
: FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
} }
} }

View File

@@ -21,6 +21,7 @@ import java.awt.geom.Area;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* "restore" icon for windows (frames and dialogs). * "restore" icon for windows (frames and dialogs).
@@ -38,18 +39,33 @@ public class FlatWindowRestoreIcon
int iwh = (int) (10 * scaleFactor); int iwh = (int) (10 * scaleFactor);
int ix = x + ((width - iwh) / 2); int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2); int iy = y + ((height - iwh) / 2);
int thickness = (int) scaleFactor; float thickness = SystemInfo.isWindows_11_orLater ? (float) scaleFactor : (int) scaleFactor;
int arc = Math.max( (int) (1.5 * scaleFactor), 2 );
int arcOuter = (int) (arc + (1.5 * scaleFactor));
int rwh = (int) (8 * scaleFactor); int rwh = (int) (8 * scaleFactor);
int ro2 = iwh - rwh; int ro2 = iwh - rwh;
Path2D r1 = FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness ); // upper-right rectangle
Path2D r2 = FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness ); Path2D r1 = SystemInfo.isWindows_11_orLater
? FlatUIUtils.createRoundRectangle( ix + ro2, iy, rwh, rwh, thickness, arc, arcOuter, arc, arc )
: FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
// lower-left rectangle
Path2D r2 = SystemInfo.isWindows_11_orLater
? FlatUIUtils.createRoundRectangle( ix, iy + ro2, rwh, rwh, thickness, arc, arc, arc, arc )
: FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
// paint upper-right rectangle
Area area = new Area( r1 ); Area area = new Area( r1 );
if( SystemInfo.isWindows_11_orLater ) {
area.subtract( new Area( new Rectangle2D.Float( ix, (float) (iy + scaleFactor), rwh, rwh ) ) );
area.subtract( new Area( new Rectangle2D.Float( (float) (ix + scaleFactor), iy + ro2, rwh, rwh ) ) );
} else
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) ); area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
g.fill( area ); g.fill( area );
// paint lower-left rectangle
g.fill( r2 ); g.fill( r2 );
} }
} }

View File

@@ -18,8 +18,8 @@ package com.formdev.flatlaf.resources;
/** /**
* The only purpose of this file is to add a .class file to this package to make it non-empty. * The only purpose of this file is to add a .class file to this package to make it non-empty.
* Otherwise the compiler outputs a warning because this package is opend in module-info.java. * Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages. * Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */

View File

@@ -37,7 +37,7 @@ public class FlatArrowButton
extends BasicArrowButton extends BasicArrowButton
implements UIResource implements UIResource
{ {
public static final int DEFAULT_ARROW_WIDTH = 8; public static final int DEFAULT_ARROW_WIDTH = 9;
protected boolean chevron; protected boolean chevron;
protected Color foreground; protected Color foreground;
@@ -211,6 +211,6 @@ public class FlatArrowButton
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) ) if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 ); x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset ); FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, getArrowWidth(), getXOffset(), getYOffset() );
} }
} }

View File

@@ -159,7 +159,7 @@ public class FlatButtonBorder
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
if( FlatButtonUI.isToolBarButton( c ) ) { if( FlatButtonUI.isToolBarButton( c ) ) {
// In toolbars, use button margin only if explicitly set. // In toolbars, use button margin only if explicitly set.
// Otherwise use toolbar margin specified in UI defaults. // Otherwise, use toolbar margin specified in UI defaults.
Insets margin = (c instanceof AbstractButton) Insets margin = (c instanceof AbstractButton)
? ((AbstractButton)c).getMargin() ? ((AbstractButton)c).getMargin()
: null; : null;

View File

@@ -38,15 +38,19 @@ import javax.swing.ButtonModel;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ButtonUI; import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicButtonUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatHelpButtonIcon; import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -74,20 +78,27 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored * @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used * @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.focusedBackground Color optional * @uiDefault Button.focusedBackground Color optional
* @uiDefault Button.focusedForeground Color optional
* @uiDefault Button.hoverBackground Color optional * @uiDefault Button.hoverBackground Color optional
* @uiDefault Button.hoverForeground Color optional
* @uiDefault Button.pressedBackground Color optional * @uiDefault Button.pressedBackground Color optional
* @uiDefault Button.pressedForeground Color optional
* @uiDefault Button.selectedBackground Color * @uiDefault Button.selectedBackground Color
* @uiDefault Button.selectedForeground Color * @uiDefault Button.selectedForeground Color
* @uiDefault Button.disabledBackground Color optional * @uiDefault Button.disabledBackground Color optional
* @uiDefault Button.disabledText Color * @uiDefault Button.disabledText Color
* @uiDefault Button.disabledSelectedBackground Color * @uiDefault Button.disabledSelectedBackground Color
* @uiDefault Button.disabledSelectedForeground Color optional
* @uiDefault Button.default.background Color * @uiDefault Button.default.background Color
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored * @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used * @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.default.foreground Color * @uiDefault Button.default.foreground Color
* @uiDefault Button.default.focusedBackground Color optional * @uiDefault Button.default.focusedBackground Color optional
* @uiDefault Button.default.focusedForeground Color optional
* @uiDefault Button.default.hoverBackground Color optional * @uiDefault Button.default.hoverBackground Color optional
* @uiDefault Button.default.hoverForeground Color optional
* @uiDefault Button.default.pressedBackground Color optional * @uiDefault Button.default.pressedBackground Color optional
* @uiDefault Button.default.pressedForeground Color optional
* @uiDefault Button.default.boldText boolean * @uiDefault Button.default.boldText boolean
* @uiDefault Button.paintShadow boolean default is false * @uiDefault Button.paintShadow boolean default is false
* @uiDefault Button.shadowWidth int default is 2 * @uiDefault Button.shadowWidth int default is 2
@@ -95,8 +106,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.shadowColor Color optional * @uiDefault Button.default.shadowColor Color optional
* @uiDefault Button.toolbar.spacingInsets Insets * @uiDefault Button.toolbar.spacingInsets Insets
* @uiDefault Button.toolbar.hoverBackground Color * @uiDefault Button.toolbar.hoverBackground Color
* @uiDefault Button.toolbar.hoverForeground Color optional
* @uiDefault Button.toolbar.pressedBackground Color * @uiDefault Button.toolbar.pressedBackground Color
* @uiDefault Button.toolbar.pressedForeground Color optional
* @uiDefault Button.toolbar.selectedBackground Color * @uiDefault Button.toolbar.selectedBackground Color
* @uiDefault Button.toolbar.selectedForeground Color optional
* @uiDefault Button.toolbar.disabledSelectedBackground Color optional
* @uiDefault Button.toolbar.disabledSelectedForeground Color optional
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -113,20 +129,27 @@ public class FlatButtonUI
protected Color startBackground; protected Color startBackground;
protected Color endBackground; protected Color endBackground;
@Styleable protected Color focusedBackground; @Styleable protected Color focusedBackground;
/** @since 2.3 */ @Styleable protected Color focusedForeground;
@Styleable protected Color hoverBackground; @Styleable protected Color hoverBackground;
/** @since 2.3 */ @Styleable protected Color hoverForeground;
@Styleable protected Color pressedBackground; @Styleable protected Color pressedBackground;
/** @since 2.3 */ @Styleable protected Color pressedForeground;
@Styleable protected Color selectedBackground; @Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground; @Styleable protected Color selectedForeground;
@Styleable protected Color disabledBackground; @Styleable protected Color disabledBackground;
@Styleable protected Color disabledText; @Styleable protected Color disabledText;
@Styleable protected Color disabledSelectedBackground; @Styleable protected Color disabledSelectedBackground;
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
@Styleable(dot=true) protected Color defaultBackground; @Styleable(dot=true) protected Color defaultBackground;
protected Color defaultEndBackground; protected Color defaultEndBackground;
@Styleable(dot=true) protected Color defaultForeground; @Styleable(dot=true) protected Color defaultForeground;
@Styleable(dot=true) protected Color defaultFocusedBackground; @Styleable(dot=true) protected Color defaultFocusedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
@Styleable(dot=true) protected Color defaultHoverBackground; @Styleable(dot=true) protected Color defaultHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
@Styleable(dot=true) protected Color defaultPressedBackground; @Styleable(dot=true) protected Color defaultPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
@Styleable(dot=true) protected boolean defaultBoldText; @Styleable(dot=true) protected boolean defaultBoldText;
@Styleable protected boolean paintShadow; @Styleable protected boolean paintShadow;
@@ -135,8 +158,13 @@ public class FlatButtonUI
@Styleable(dot=true) protected Color defaultShadowColor; @Styleable(dot=true) protected Color defaultShadowColor;
@Styleable(dot=true) protected Color toolbarHoverBackground; @Styleable(dot=true) protected Color toolbarHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
@Styleable(dot=true) protected Color toolbarPressedBackground; @Styleable(dot=true) protected Color toolbarPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
@Styleable(dot=true) protected Color toolbarSelectedBackground; @Styleable(dot=true) protected Color toolbarSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarSelectedForeground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedForeground;
// only used via styling (not in UI defaults, but has likewise client properties) // only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected String buttonType; /** @since 2 */ @Styleable protected String buttonType;
@@ -186,20 +214,27 @@ public class FlatButtonUI
startBackground = UIManager.getColor( prefix + "startBackground" ); startBackground = UIManager.getColor( prefix + "startBackground" );
endBackground = UIManager.getColor( prefix + "endBackground" ); endBackground = UIManager.getColor( prefix + "endBackground" );
focusedBackground = UIManager.getColor( prefix + "focusedBackground" ); focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
hoverBackground = UIManager.getColor( prefix + "hoverBackground" ); hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
pressedBackground = UIManager.getColor( prefix + "pressedBackground" ); pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
selectedBackground = UIManager.getColor( prefix + "selectedBackground" ); selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
selectedForeground = UIManager.getColor( prefix + "selectedForeground" ); selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
disabledBackground = UIManager.getColor( prefix + "disabledBackground" ); disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
disabledText = UIManager.getColor( prefix + "disabledText" ); disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" ); disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" ); defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" ); defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" ); defaultForeground = UIManager.getColor( "Button.default.foreground" );
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" ); defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" ); defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" ); defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" ); defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
paintShadow = UIManager.getBoolean( "Button.paintShadow" ); paintShadow = UIManager.getBoolean( "Button.paintShadow" );
@@ -208,8 +243,13 @@ public class FlatButtonUI
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" ); defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" ); toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" ); toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" ); toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
toolbarSelectedForeground = UIManager.getColor( prefix + "toolbar.selectedForeground" );
toolbarDisabledSelectedBackground = UIManager.getColor( prefix + "toolbar.disabledSelectedBackground" );
toolbarDisabledSelectedForeground = UIManager.getColor( prefix + "toolbar.disabledSelectedForeground" );
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" ); helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
defaultMargin = UIManager.getInsets( prefix + "margin" ); defaultMargin = UIManager.getInsets( prefix + "margin" );
@@ -259,6 +299,10 @@ public class FlatButtonUI
b.repaint(); b.repaint();
break; break;
case OUTLINE:
b.repaint();
break;
case STYLE: case STYLE:
case STYLE_CLASS: case STYLE_CLASS:
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) { if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
@@ -336,7 +380,7 @@ public class FlatButtonUI
/** /**
* Returns true if the button has an icon but no text, * Returns true if the button has an icon but no text,
* or it it does not have an icon and the text is either "..." or one character. * or it does not have an icon and the text is either "..." or one character.
*/ */
static boolean isIconOnlyOrSingleCharacterButton( Component c ) { static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
if( !(c instanceof JButton) && !(c instanceof JToggleButton) ) if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
@@ -420,11 +464,21 @@ public class FlatButtonUI
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
boolean def = isDefaultButton( c );
boolean isToolBarButton = isToolBarButton( c ); boolean isToolBarButton = isToolBarButton( c );
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c ); float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
float arc = FlatUIUtils.getBorderArc( c ); float arc = FlatUIUtils.getBorderArc( c );
float textFieldArc = 0;
boolean def = isDefaultButton( c ); // if toolbar button is in leading/trailing component of a text field,
// increase toolbar button arc to match text field arc (if necessary)
if( isToolBarButton &&
FlatClientProperties.clientProperty( c, STYLE_CLASS, "", String.class ).contains( "inTextField" ) )
{
JTextField textField = (JTextField) SwingUtilities.getAncestorOfClass( JTextField.class, c );
if( textField != null )
textFieldArc = FlatUIUtils.getBorderArc( textField );
}
int x = 0; int x = 0;
int y = 0; int y = 0;
@@ -437,8 +491,15 @@ public class FlatButtonUI
y += spacing.top; y += spacing.top;
width -= spacing.left + spacing.right; width -= spacing.left + spacing.right;
height -= spacing.top + spacing.bottom; height -= spacing.top + spacing.bottom;
// reduce text field arc
textFieldArc -= spacing.top + spacing.bottom;
} }
// increase toolbar button arc to match text field arc (if necessary)
if( arc < textFieldArc )
arc = textFieldArc;
// paint shadow // paint shadow
Color shadowColor = def ? defaultShadowColor : this.shadowColor; Color shadowColor = def ? defaultShadowColor : this.shadowColor;
if( paintShadow && if( paintShadow &&
@@ -470,6 +531,23 @@ public class FlatButtonUI
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c ); super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
} }
@Override
protected void paintIcon( Graphics g, JComponent c, Rectangle iconRect ) {
// correct icon location when using bold font for default button
int xOffset = defaultBoldPlainWidthDiff( c ) / 2;
if( xOffset > 0 ) {
boolean ltr = c.getComponentOrientation().isLeftToRight();
switch( ((AbstractButton)c).getHorizontalTextPosition() ) {
case SwingConstants.RIGHT: iconRect.x -= xOffset; break;
case SwingConstants.LEFT: iconRect.x += xOffset; break;
case SwingConstants.TRAILING: iconRect.x -= ltr ? xOffset : -xOffset; break;
case SwingConstants.LEADING: iconRect.x += ltr ? xOffset : -xOffset; break;
}
}
super.paintIcon( g, c, iconRect );
}
@Override @Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) { protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
if( isHelpButton( b ) ) if( isHelpButton( b ) )
@@ -490,6 +568,8 @@ public class FlatButtonUI
} }
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) { public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
if(foreground == null)
foreground=Color.red;
FontMetrics fm = b.getFontMetrics( b.getFont() ); FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1; int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -503,11 +583,14 @@ public class FlatButtonUI
// selected state // selected state
if( ((AbstractButton)c).isSelected() ) { if( ((AbstractButton)c).isSelected() ) {
// in toolbar use same background colors for disabled and enabled because // in toolbar, if toolbarDisabledSelectedBackground is null,
// use same background colors for disabled and enabled because
// we assume that toolbar icon is shown disabled // we assume that toolbar icon is shown disabled
return buttonStateColor( c, return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground, toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground, toolBarButton
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
: disabledSelectedBackground,
null, null,
null, null,
toolBarButton ? toolbarPressedBackground : pressedBackground ); toolBarButton ? toolbarPressedBackground : pressedBackground );
@@ -549,6 +632,9 @@ public class FlatButtonUI
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor, public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
Color focusedColor, Color hoverColor, Color pressedColor ) Color focusedColor, Color hoverColor, Color pressedColor )
{ {
if( c == null )
return enabledColor;
if( !c.isEnabled() ) if( !c.isEnabled() )
return disabledColor; return disabledColor;
@@ -569,18 +655,48 @@ public class FlatButtonUI
} }
protected Color getForeground( JComponent c ) { protected Color getForeground( JComponent c ) {
if( !c.isEnabled() ) boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
return disabledText;
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) ) // selected state
return selectedForeground; if( ((AbstractButton)c).isSelected() ) {
return buttonStateColor( c,
toolBarButton
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
: selectedForeground,
toolBarButton
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
null,
null,
toolBarButton ? toolbarPressedForeground : pressedForeground );
}
// toolbar button
if( toolBarButton ) {
return buttonStateColor( c,
c.getForeground(),
disabledText,
null,
toolbarHoverForeground,
toolbarPressedForeground );
}
boolean def = isDefaultButton( c );
return buttonStateColor( c,
getForegroundBase( c, def ),
disabledText,
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
def ? defaultHoverForeground : hoverForeground,
def ? defaultPressedForeground : pressedForeground );
}
/** @since 2.3 */
protected Color getForegroundBase( JComponent c, boolean def ) {
// use component foreground if explicitly set // use component foreground if explicitly set
Color fg = c.getForeground(); Color fg = c.getForeground();
if( isCustomForeground( fg ) ) if( isCustomForeground( fg ) )
return fg; return fg;
boolean def = isDefaultButton( c );
return def ? defaultForeground : fg; return def ? defaultForeground : fg;
} }
@@ -597,6 +713,9 @@ public class FlatButtonUI
if( prefSize == null ) if( prefSize == null )
return null; return null;
// increase width when using bold font for default button
prefSize.width += defaultBoldPlainWidthDiff( c );
// make square or apply minimum width/height // make square or apply minimum width/height
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c ); boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) { if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
@@ -617,6 +736,23 @@ public class FlatButtonUI
return prefSize; return prefSize;
} }
private int defaultBoldPlainWidthDiff( JComponent c ) {
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
String text = ((AbstractButton)c).getText();
if( text == null || text.isEmpty() )
return 0;
Font font = c.getFont();
Font boldFont = font.deriveFont( Font.BOLD );
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
if( boldWidth > plainWidth )
return boldWidth - plainWidth;
}
return 0;
}
private boolean hasDefaultMargins( JComponent c ) { private boolean hasDefaultMargins( JComponent c ) {
Insets margin = ((AbstractButton)c).getMargin(); Insets margin = ((AbstractButton)c).getMargin();
return margin instanceof UIResource && Objects.equals( margin, defaultMargin ); return margin instanceof UIResource && Objects.equals( margin, defaultMargin );

View File

@@ -96,7 +96,7 @@ public class FlatCaret
// if text component is focused, then caret and selection are visible, // if text component is focused, then caret and selection are visible,
// but when switching theme, the component does not yet have // but when switching theme, the component does not yet have
// an highlighter and the selection is not painted // a highlighter and the selection is not painted
// --> make selection temporary invisible later, then the caret // --> make selection temporary invisible later, then the caret
// adds selection highlights to the text component highlighter // adds selection highlights to the text component highlighter
if( isSelectionVisible() ) { if( isSelectionVisible() ) {
@@ -236,7 +236,7 @@ public class FlatCaret
if( selectAllOnFocusPolicy == null ) if( selectAllOnFocusPolicy == null )
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy; selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) ) if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
return; return;
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) { if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {

View File

@@ -86,6 +86,11 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.padding Insets * @uiDefault ComboBox.padding Insets
* @uiDefault ComboBox.squareButton boolean default is true * @uiDefault ComboBox.squareButton boolean default is true
* *
* <!-- BasicComboPopup -->
*
* @uiDefault ComboBox.selectionBackground Color
* @uiDefault ComboBox.selectionForeground Color
*
* <!-- FlatComboBoxUI --> * <!-- FlatComboBoxUI -->
* *
* @uiDefault ComboBox.minimumWidth int * @uiDefault ComboBox.minimumWidth int
@@ -354,6 +359,7 @@ public class FlatComboBoxUI
break; break;
case COMPONENT_ROUND_RECT: case COMPONENT_ROUND_RECT:
case OUTLINE:
comboBox.repaint(); comboBox.repaint();
break; break;
@@ -699,7 +705,7 @@ public class FlatComboBoxUI
return true; return true;
Component editorComponent = comboBox.getEditor().getEditorComponent(); Component editorComponent = comboBox.getEditor().getEditorComponent();
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false; return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
} else } else
return FlatUIUtils.isPermanentFocusOwner( comboBox ); return FlatUIUtils.isPermanentFocusOwner( comboBox );
} }

View File

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

View File

@@ -72,7 +72,7 @@ public class FlatListCellBorder
} }
/** /**
* Because this borders are always shared for all lists, * Because this border is always shared for all lists,
* get border specific style from FlatListUI. * get border specific style from FlatListUI.
*/ */
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) { static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {

View File

@@ -212,7 +212,7 @@ public class FlatListUI
/** /**
* Toggle selection colors from focused to inactive and vice versa. * Toggle selection colors from focused to inactive and vice versa.
* *
* This is not a optimal solution but much easier than rewriting the whole paint methods. * This is not an optimal solution but much easier than rewriting the whole paint methods.
* *
* Using a LaF specific renderer was avoided because often a custom renderer is * Using a LaF specific renderer was avoided because often a custom renderer is
* already used in applications. Then either the inactive colors are not used, * already used in applications. Then either the inactive colors are not used,

View File

@@ -210,7 +210,7 @@ public class FlatMenuBarUI
// check whether: // check whether:
// - TitlePane.unifiedBackground is true and // - TitlePane.unifiedBackground is true and
// - menu bar is the "main" menu bar and // - menu bar is the "main" menu bar and
// - window has custom decorations enabled // - window root pane has custom decoration style
JRootPane rootPane; JRootPane rootPane;
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime) // (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
@@ -218,7 +218,7 @@ public class FlatMenuBarUI
(rootPane = SwingUtilities.getRootPane( c )) != null && (rootPane = SwingUtilities.getRootPane( c )) != null &&
rootPane.getParent() instanceof Window && rootPane.getParent() instanceof Window &&
rootPane.getJMenuBar() == c && rootPane.getJMenuBar() == c &&
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ); rootPane.getWindowDecorationStyle() != JRootPane.NONE;
} }
//---- class TakeFocus ---------------------------------------------------- //---- class TakeFocus ----------------------------------------------------

View File

@@ -35,7 +35,7 @@ import javax.swing.plaf.MenuBarUI;
public class FlatMenuItemBorder public class FlatMenuItemBorder
extends FlatMarginBorder extends FlatMarginBorder
{ {
// only used if parent menubar is not a instance of FlatMenuBarUI // only used if parent menubar is not an instance of FlatMenuBarUI
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" ); private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
@Override @Override

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2022 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.io.File;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Helper class to load FlatLaf native library (.dll, .so or .dylib),
* if available for current operating system and CPU architecture.
*
* @author Karl Tauber
* @since 2.3
*/
class FlatNativeLibrary
{
private static NativeLibrary nativeLibrary;
/**
* Loads native library (if available) and returns whether loaded successfully.
* Returns {@code false} if no native library is available.
*/
static synchronized boolean isLoaded() {
initialize();
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
}
private static void initialize() {
if( nativeLibrary != null )
return;
String libraryName;
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
// Windows: requires Windows 10 (x86 or x86_64)
libraryName = "flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
// load jawt native library
loadJAWT();
} else
return; // no native library available for current OS or CPU architecture
// load native library
nativeLibrary = createNativeLibrary( libraryName );
}
private static NativeLibrary createNativeLibrary( String libraryName ) {
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
if( libraryPath != null ) {
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true );
else
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
}
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
}
private static void loadJAWT() {
if( SystemInfo.isJava_9_orLater )
return;
// In Java 8, load jawt.dll (part of JRE) explicitly because it
// is not found when running application with <jdk>/bin/java.exe.
// When using <jdk>/jre/bin/java.exe, it is found.
// jawt.dll is located in <jdk>/jre/bin/.
// Java 9 and later do not have this problem.
try {
System.loadLibrary( "jawt" );
} catch( UnsatisfiedLinkError ex ) {
// log error only if native library jawt.dll not already loaded
String message = ex.getMessage();
if( message == null || !message.contains( "already loaded in another classloader" ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
}

View File

@@ -56,7 +56,7 @@ public class FlatNativeWindowBorder
private static final boolean canUseJBRCustomDecorations = private static final boolean canUseJBRCustomDecorations =
canUseWindowDecorations && canUseWindowDecorations &&
SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isJetBrainsJVM_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ); FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
private static Boolean supported; private static Boolean supported;
private static Provider nativeProvider; private static Provider nativeProvider;
@@ -313,6 +313,10 @@ public class FlatNativeWindowBorder
//---- class WindowTopBorder ------------------------------------------- //---- class WindowTopBorder -------------------------------------------
/**
* Window top border used on Windows 10.
* No longer needed since Windows 11.
*/
static class WindowTopBorder static class WindowTopBorder
extends JBRCustomDecorations.JBRWindowTopBorder extends JBRCustomDecorations.JBRWindowTopBorder
{ {

View File

@@ -36,6 +36,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI; import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SwingUtils;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -156,7 +157,7 @@ public class FlatOptionPaneUI
// set icon-message gap // set icon-message gap
if( iconMessageGap > 0 ) { if( iconMessageGap > 0 ) {
Component iconMessageSeparator = findByName( messageArea, "OptionPane.separator" ); Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
if( iconMessageSeparator != null ) if( iconMessageSeparator != null )
iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) ); iconMessageSeparator.setPreferredSize( new Dimension( UIScale.scale( iconMessageGap ), 1 ) );
} }
@@ -236,20 +237,6 @@ public class FlatOptionPaneUI
} }
} }
private Component findByName( Container c, String name ) {
for( Component child : c.getComponents() ) {
if( name.equals( child.getName() ) )
return child;
if( child instanceof Container ) {
Component c2 = findByName( (Container) child, name );
if( c2 != null )
return c2;
}
}
return null;
}
@Override @Override
protected boolean getSizeButtonsToSameWidth() { protected boolean getSizeButtonsToSameWidth() {
return sameSizeButtons; return sameSizeButtons;

View File

@@ -18,12 +18,14 @@ package com.formdev.flatlaf.ui;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI; import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -43,13 +45,12 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatPanelUI public class FlatPanelUI
extends BasicPanelUI extends BasicPanelUI
implements StyleableUI implements StyleableUI, PropertyChangeListener
{ {
// only used via styling (not in UI defaults) // only used via styling (not in UI defaults)
/** @since 2 */ @Styleable protected int arc = -1; /** @since 2 */ @Styleable protected int arc = -1;
private final boolean shared; private final boolean shared;
private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
@@ -67,9 +68,7 @@ public class FlatPanelUI
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
super.installUI( c ); super.installUI( c );
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( c.addPropertyChangeListener( this );
c, () -> stylePropertyChange( (JPanel) c ), null );
c.addPropertyChangeListener( propertyChangeListener );
installStyle( (JPanel) c ); installStyle( (JPanel) c );
} }
@@ -78,13 +77,18 @@ public class FlatPanelUI
public void uninstallUI( JComponent c ) { public void uninstallUI( JComponent c ) {
super.uninstallUI( c ); super.uninstallUI( c );
c.removePropertyChangeListener( propertyChangeListener ); c.removePropertyChangeListener( this );
propertyChangeListener = null;
oldStyleValues = null; oldStyleValues = null;
} }
private void stylePropertyChange( JPanel c ) { /** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
JPanel c = (JPanel) e.getSource();
if( shared && FlatStylingSupport.hasStyleProperty( c ) ) { if( shared && FlatStylingSupport.hasStyleProperty( c ) ) {
// unshare component UI if necessary // unshare component UI if necessary
// updateUI() invokes installStyle() from installUI() // updateUI() invokes installStyle() from installUI()
@@ -93,6 +97,8 @@ public class FlatPanelUI
installStyle( c ); installStyle( c );
c.revalidate(); c.revalidate();
c.repaint(); c.repaint();
break;
}
} }
/** @since 2 */ /** @since 2 */

View File

@@ -28,6 +28,7 @@ import javax.swing.Action;
import javax.swing.ActionMap; import javax.swing.ActionMap;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JPasswordField;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@@ -38,6 +39,7 @@ import javax.swing.text.Element;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import javax.swing.text.PasswordView; import javax.swing.text.PasswordView;
import javax.swing.text.View; import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCapsLockIcon; import com.formdev.flatlaf.icons.FlatCapsLockIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -83,6 +85,9 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatPasswordFieldUI public class FlatPasswordFieldUI
extends FlatTextFieldUI extends FlatTextFieldUI
{ {
// used to preserve reveal button state when switching theme
private static final String KEY_REVEAL_SELECTED = "FlatLaf.internal.FlatPasswordFieldUI.revealSelected";
private Character echoChar; private Character echoChar;
@Styleable protected boolean showCapsLock; @Styleable protected boolean showCapsLock;
@@ -93,6 +98,7 @@ public class FlatPasswordFieldUI
private KeyListener capsLockListener; private KeyListener capsLockListener;
private boolean capsLockIconShared = true; private boolean capsLockIconShared = true;
private JToggleButton revealButton; private JToggleButton revealButton;
private boolean uninstallEchoChar;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatPasswordFieldUI(); return new FlatPasswordFieldUI();
@@ -269,36 +275,65 @@ public class FlatPasswordFieldUI
if( !showCapsLock ) if( !showCapsLock )
return false; return false;
JTextComponent c = getComponent(); return FlatUIUtils.isPermanentFocusOwner( getComponent() ) &&
return FlatUIUtils.isPermanentFocusOwner( c ) &&
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ); Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
} }
/** @since 2 */ /** @since 2 */
protected void installRevealButton() { protected void installRevealButton() {
JTextComponent c = getComponent();
if( showRevealButton ) { if( showRevealButton ) {
revealButton = createRevealButton(); revealButton = createRevealButton();
installLayout(); installLayout();
c.add( revealButton ); getComponent().add( revealButton );
} }
} }
/** @since 2 */ /** @since 2 */
protected JToggleButton createRevealButton() { protected JToggleButton createRevealButton() {
JToggleButton button = new JToggleButton( revealIcon ); JToggleButton button = new JToggleButton( revealIcon );
button.setName( "PasswordField.revealButton" );
prepareLeadingOrTrailingComponent( button ); prepareLeadingOrTrailingComponent( button );
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
if( FlatClientProperties.clientPropertyBoolean( getComponent(), KEY_REVEAL_SELECTED, false ) ) {
button.setSelected( true );
updateEchoChar( true );
}
button.addActionListener( e -> { button.addActionListener( e -> {
LookAndFeel.installProperty( getComponent(), "echoChar", button.isSelected() boolean selected = button.isSelected();
? '\0' updateEchoChar( selected );
: (echoChar != null ? echoChar : '*')); getComponent().putClientProperty( KEY_REVEAL_SELECTED, selected );
} ); } );
return button; return button;
} }
private void updateEchoChar( boolean selected ) {
char newEchoChar = selected
? 0
: (echoChar != null ? echoChar : '*');
JPasswordField c = (JPasswordField) getComponent();
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
// check whether was able to set echo char via LookAndFeel.installProperty()
// if not, then echo char was explicitly changed via JPasswordField.setEchoChar()
char actualEchoChar = c.getEchoChar();
if( actualEchoChar != newEchoChar ) {
if( selected && actualEchoChar != 0 ) {
// use explicitly set echo char
echoChar = actualEchoChar;
uninstallEchoChar = true;
}
c.setEchoChar( newEchoChar );
}
}
/** @since 2 */ /** @since 2 */
protected void uninstallRevealButton() { protected void uninstallRevealButton() {
if( revealButton != null ) { if( revealButton != null ) {
if( uninstallEchoChar && revealButton.isSelected() )
((JPasswordField)getComponent()).setEchoChar( echoChar );
getComponent().remove( revealButton ); getComponent().remove( revealButton );
revealButton = null; revealButton = null;
} }

View File

@@ -16,10 +16,12 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.AWTEvent;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsConfiguration; import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice; import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
@@ -33,6 +35,7 @@ import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -140,7 +143,7 @@ public class FlatPopupFactory
* <p> * <p>
* On a dual screen setup, where screens use different scale factors, it may happen * On a dual screen setup, where screens use different scale factors, it may happen
* that the window location changes when showing a heavy weight popup window. * that the window location changes when showing a heavy weight popup window.
* E.g. when opening an dialog on the secondary screen and making combobox popup visible. * E.g. when opening a dialog on the secondary screen and making combobox popup visible.
* <p> * <p>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608 * This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/ */
@@ -219,7 +222,7 @@ public class FlatPopupFactory
* and corrects the y-location so that the tooltip is placed above the mouse location. * and corrects the y-location so that the tooltip is placed above the mouse location.
*/ */
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) { private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() ) if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() || hasTipLocation( owner ) )
return null; return null;
PointerInfo pointerInfo = MouseInfo.getPointerInfo(); PointerInfo pointerInfo = MouseInfo.getPointerInfo();
@@ -264,6 +267,35 @@ public class FlatPopupFactory
return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 ); return StackUtils.wasInvokedFrom( ToolTipManager.class.getName(), "showTipWindow", 8 );
} }
/**
* Checks whether the owner component returns a tooltip location in
* JComponent.getToolTipLocation(MouseEvent).
*/
private boolean hasTipLocation( Component owner ) {
if( !(owner instanceof JComponent) )
return false;
AWTEvent e = EventQueue.getCurrentEvent();
MouseEvent me;
if( e instanceof MouseEvent )
me = (MouseEvent) e;
else {
// no mouse event available because a timer is used to show the tooltip
// --> create mouse event from current mouse location
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
if( pointerInfo == null )
return false;
Point location = new Point( pointerInfo.getLocation());
SwingUtilities.convertPointFromScreen( location, owner );
me = new MouseEvent( owner, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(),
0, location.x, location.y, 0, false );
}
return me.getSource() == owner &&
((JComponent)owner).getToolTipLocation( me ) != null;
}
//---- class NonFlashingPopup --------------------------------------------- //---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup private class NonFlashingPopup

View File

@@ -16,18 +16,58 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.BasicMenuItemUI;
import javax.swing.plaf.basic.BasicPopupMenuUI; import javax.swing.plaf.basic.BasicPopupMenuUI;
import javax.swing.plaf.basic.DefaultMenuLayout; import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -41,12 +81,22 @@ import com.formdev.flatlaf.util.LoggingFacade;
* @uiDefault PopupMenu.foreground Color * @uiDefault PopupMenu.foreground Color
* @uiDefault PopupMenu.border Border * @uiDefault PopupMenu.border Border
* *
* <!-- FlatPopupMenuUI -->
*
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault PopupMenu.scrollArrowColor Color
* @uiDefault PopupMenu.hoverScrollArrowBackground Color optional
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatPopupMenuUI public class FlatPopupMenuUI
extends BasicPopupMenuUI extends BasicPopupMenuUI
implements StyleableUI implements StyleableUI
{ {
/** @since 2.1 */ @Styleable protected String arrowType;
/** @since 2.1 */ @Styleable protected Color scrollArrowColor;
/** @since 2.1 */ @Styleable protected Color hoverScrollArrowBackground;
private PropertyChangeListener propertyChangeListener; private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
private AtomicBoolean borderShared; private AtomicBoolean borderShared;
@@ -74,11 +124,23 @@ public class FlatPopupMenuUI
public void installDefaults() { public void installDefaults() {
super.installDefaults(); super.installDefaults();
arrowType = UIManager.getString( "Component.arrowType" );
scrollArrowColor = UIManager.getColor( "PopupMenu.scrollArrowColor" );
hoverScrollArrowBackground = UIManager.getColor( "PopupMenu.hoverScrollArrowBackground" );
LayoutManager layout = popupMenu.getLayout(); LayoutManager layout = popupMenu.getLayout();
if( layout == null || layout instanceof UIResource ) if( layout == null || layout instanceof UIResource )
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
} }
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
scrollArrowColor = null;
hoverScrollArrowBackground = null;
}
@Override @Override
protected void installListeners() { protected void installListeners() {
super.installListeners(); super.installListeners();
@@ -122,6 +184,52 @@ public class FlatPopupMenuUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() ); return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
} }
@Override
public Popup getPopup( JPopupMenu popup, int x, int y ) {
// do not add scroller to combobox popups or to popups that already have a scroll pane
if( popup instanceof BasicComboPopup ||
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
return super.getPopup( popup, x, y );
// do not add scroller if popup fits into screen
Dimension prefSize = popup.getPreferredSize();
int screenHeight = getScreenHeightAt( x, y );
if( prefSize.height <= screenHeight )
return super.getPopup( popup, x, y );
// create scroller
FlatPopupScroller scroller = new FlatPopupScroller( popup );
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
// create popup
PopupFactory popupFactory = PopupFactory.getSharedInstance();
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
}
private int getScreenHeightAt( int x, int y ) {
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
GraphicsConfiguration gc = null;
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
if( device.getType() == GraphicsDevice.TYPE_RASTER_SCREEN ) {
GraphicsConfiguration dgc = device.getDefaultConfiguration();
if( dgc.getBounds().contains( x, y ) ) {
gc = dgc;
break;
}
}
}
if( gc == null && popupMenu.getInvoker() != null )
gc = popupMenu.getInvoker().getGraphicsConfiguration();
// compute screen height
// (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() );
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
return screenBounds.height - screenInsets.top - screenInsets.bottom;
}
//---- class FlatMenuLayout ----------------------------------------------- //---- class FlatMenuLayout -----------------------------------------------
protected static class FlatMenuLayout protected static class FlatMenuLayout
@@ -138,4 +246,206 @@ public class FlatPopupMenuUI
return super.preferredLayoutSize( target ); return super.preferredLayoutSize( target );
} }
} }
//---- class FlatPopupScroller --------------------------------------------
private class FlatPopupScroller
extends JPanel
implements MouseWheelListener, PopupMenuListener, MenuKeyListener
{
private final JPopupMenu popup;
private final JScrollPane scrollPane;
private final JButton scrollUpButton;
private final JButton scrollDownButton;
private int unitIncrement;
FlatPopupScroller( JPopupMenu popup ) {
super( new BorderLayout() );
this.popup = popup;
// this panel is required to avoid that JPopupMenu.setLocation() will be invoked
// while scrolling, because this would call JPopupMenu.showPopup()
JPanel view = new JPanel( new BorderLayout() );
view.add( popup, BorderLayout.CENTER );
scrollPane = new JScrollPane( view, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
scrollPane.setBorder( null );
scrollUpButton = new ArrowButton( SwingConstants.NORTH );
scrollDownButton = new ArrowButton( SwingConstants.SOUTH );
add( scrollPane, BorderLayout.CENTER );
add( scrollUpButton, BorderLayout.NORTH );
add( scrollDownButton, BorderLayout.SOUTH );
setBackground( popup.getBackground() );
setBorder( popup.getBorder() );
popup.setBorder( null );
popup.addPopupMenuListener( this );
popup.addMouseWheelListener( this );
popup.addMenuKeyListener( this );
updateArrowButtons();
}
void scroll( int unitsToScroll ) {
if( unitIncrement == 0 )
unitIncrement = new JMenuItem( "X" ).getPreferredSize().height;
JViewport viewport = scrollPane.getViewport();
Point viewPosition = viewport.getViewPosition();
int newY = viewPosition.y + (unitIncrement * unitsToScroll);
if( newY < 0 )
newY = 0;
else
newY = Math.min( newY, viewport.getViewSize().height - viewport.getExtentSize().height );
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
updateArrowButtons();
}
void updateArrowButtons() {
JViewport viewport = scrollPane.getViewport();
Point viewPosition = viewport.getViewPosition();
scrollUpButton.setVisible( viewPosition.y > 0 );
scrollDownButton.setVisible( viewPosition.y < viewport.getViewSize().height - viewport.getExtentSize().height );
}
//---- interface PopupMenuListener ----
@Override
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
// restore popup border
popup.setBorder( getBorder() );
popup.removePopupMenuListener( this );
popup.removeMouseWheelListener( this );
popup.removeMenuKeyListener( this );
}
@Override public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {}
@Override public void popupMenuCanceled( PopupMenuEvent e ) {}
//---- interface MouseWheelListener ----
/**
* Scroll when user rotates mouse wheel.
*/
@Override
public void mouseWheelMoved( MouseWheelEvent e ) {
// convert mouse location before scrolling
Point mouseLocation = SwingUtilities.convertPoint( (Component) e.getSource(), e.getPoint(), this );
// scroll
scroll( e.getUnitsToScroll() );
// select menu item at mouse location
Component c = SwingUtilities.getDeepestComponentAt( this, mouseLocation.x, mouseLocation.y );
if( c instanceof JMenuItem ) {
ButtonUI ui = ((JMenuItem)c).getUI();
if( ui instanceof BasicMenuItemUI )
MenuSelectionManager.defaultManager().setSelectedPath( ((BasicMenuItemUI)ui).getPath() );
}
// this avoids that the popup is closed when running on Java 8
// https://bugs.openjdk.java.net/browse/JDK-8075063
e.consume();
}
//---- interface MenuKeyListener ----
/**
* Scroll when user presses Up or Down keys.
*/
@Override
public void menuKeyPressed( MenuKeyEvent e ) {
// use invokeLater() because menu selection is not yet updated because
// this listener is invoked before another listener that updates the menu selection
EventQueue.invokeLater( () -> {
if( !isDisplayable() )
return;
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
if( path.length == 0 )
return;
// scroll selected menu item to visible area
Component c = path[path.length - 1].getComponent();
JViewport viewport = scrollPane.getViewport();
Point pt = SwingUtilities.convertPoint( c, 0, 0, viewport );
viewport.scrollRectToVisible( new Rectangle( pt, c.getSize() ) );
// update arrow buttons
boolean upVisible = scrollUpButton.isVisible();
updateArrowButtons();
if( !upVisible && scrollUpButton.isVisible() ) {
// if "up" button becomes visible, make sure that bottom menu item stays visible
Point viewPosition = viewport.getViewPosition();
int newY = viewPosition.y + scrollUpButton.getPreferredSize().height;
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
}
} );
}
@Override public void menuKeyTyped( MenuKeyEvent e ) {}
@Override public void menuKeyReleased( MenuKeyEvent e ) {}
//---- class ArrowButton ----------------------------------------------
private class ArrowButton
extends FlatArrowButton
implements MouseListener, ActionListener
{
private Timer timer;
ArrowButton( int direction ) {
super( direction, arrowType, scrollArrowColor, null, null, hoverScrollArrowBackground, null, null );
addMouseListener( this );
}
@Override
public void paint( Graphics g ) {
// always fill background to paint over border on HiDPI screens
g.setColor( popup.getBackground() );
g.fillRect( 0, 0, getWidth(), getHeight() );
super.paint( g );
}
//---- interface MouseListener ----
@Override public void mouseClicked( MouseEvent e ) {}
@Override public void mousePressed( MouseEvent e ) {}
@Override public void mouseReleased( MouseEvent e ) {}
@Override
public void mouseEntered( MouseEvent e ) {
if( timer == null )
timer = new Timer( 50, this );
timer.start();
}
@Override
public void mouseExited( MouseEvent e ) {
if( timer != null )
timer.stop();
}
//---- interface ActionListener ----
@Override
public void actionPerformed( ActionEvent e ) {
if( timer != null && !isDisplayable() ) {
timer.stop();
return;
}
scroll( direction == SwingConstants.NORTH ? -1 : 1 );
}
}
}
} }

View File

@@ -199,7 +199,7 @@ public class FlatRadioButtonUI
return infos; return infos;
} }
private static Insets tempInsets = new Insets( 0, 0, 0, 0 ); private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
@@ -212,7 +212,7 @@ public class FlatRadioButtonUI
if( focusWidth > 0 ) { if( focusWidth > 0 ) {
// Increase preferred width and height if insets were explicitly reduced (e.g. with // Increase preferred width and height if insets were explicitly reduced (e.g. with
// an EmptyBorder) and icon has a focus width, which is not included in icon size. // an EmptyBorder) and icon has a focus width, which is not included in icon size.
// Otherwise the component may be too small and outer focus border may be cut off. // Otherwise, the component may be too small and outer focus border may be cut off.
Insets insets = c.getInsets( tempInsets ); Insets insets = c.getInsets( tempInsets );
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 ); size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 ); size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );

View File

@@ -161,13 +161,26 @@ public class FlatRootPaneUI
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() ); c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
} }
@Override
protected void uninstallDefaults( JRootPane c ) {
super.uninstallDefaults( c );
// uninstall background, foreground and font because not all Lafs set them
if( c.isBackgroundSet() && c.getBackground() instanceof UIResource )
c.setBackground( null );
if( c.isForegroundSet() && c.getForeground() instanceof UIResource )
c.setForeground( null );
if( c.isFontSet() && c.getFont() instanceof UIResource )
c.setFont( null );
}
@Override @Override
protected void installListeners( JRootPane root ) { protected void installListeners( JRootPane root ) {
super.installListeners( root ); super.installListeners( root );
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
// On HiDPI screens, where scaling is used, there may be white lines at the // On HiDPI screens, where scaling is used, there may be white lines on the
// bottom and at the right side of the window when it is initially shown. // bottom and on the right side of the window when it is initially shown.
// This is very disturbing in dark themes, but hard to notice in light themes. // This is very disturbing in dark themes, but hard to notice in light themes.
// Seems to be a rounding issue when Swing adds dirty region of window // Seems to be a rounding issue when Swing adds dirty region of window
// using RepaintManager.nativeAddDirtyRegion(). // using RepaintManager.nativeAddDirtyRegion().
@@ -497,7 +510,7 @@ public class FlatRootPaneUI
return; return;
Container parent = c.getParent(); Container parent = c.getParent();
boolean active = parent instanceof Window ? ((Window)parent).isActive() : false; boolean active = parent instanceof Window && ((Window)parent).isActive();
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) ); g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl ); HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
@@ -509,9 +522,7 @@ public class FlatRootPaneUI
protected boolean isWindowMaximized( Component c ) { protected boolean isWindowMaximized( Component c ) {
Container parent = c.getParent(); Container parent = c.getParent();
return parent instanceof Frame return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
? (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
: false;
} }
} }

View File

@@ -57,6 +57,7 @@ import com.formdev.flatlaf.util.UIScale;
* *
* <!-- FlatScrollBarUI --> * <!-- FlatScrollBarUI -->
* *
* @uiDefault ScrollBar.minimumButtonSize Dimension
* @uiDefault ScrollBar.trackInsets Insets * @uiDefault ScrollBar.trackInsets Insets
* @uiDefault ScrollBar.thumbInsets Insets * @uiDefault ScrollBar.thumbInsets Insets
* @uiDefault ScrollBar.trackArc int * @uiDefault ScrollBar.trackArc int
@@ -83,6 +84,7 @@ public class FlatScrollBarUI
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private) // overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
@Styleable protected boolean allowsAbsolutePositioning; @Styleable protected boolean allowsAbsolutePositioning;
/** @since 2.1 */ @Styleable protected Dimension minimumButtonSize;
@Styleable protected Insets trackInsets; @Styleable protected Insets trackInsets;
@Styleable protected Insets thumbInsets; @Styleable protected Insets thumbInsets;
@Styleable protected int trackArc; @Styleable protected int trackArc;
@@ -142,6 +144,7 @@ public class FlatScrollBarUI
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning(); allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
minimumButtonSize = UIManager.getDimension( "ScrollBar.minimumButtonSize" );
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" ); trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" ); thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
trackArc = UIManager.getInt( "ScrollBar.trackArc" ); trackArc = UIManager.getInt( "ScrollBar.trackArc" );
@@ -171,6 +174,7 @@ public class FlatScrollBarUI
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
minimumButtonSize = null;
trackInsets = null; trackInsets = null;
thumbInsets = null; thumbInsets = null;
hoverTrackColor = null; hoverTrackColor = null;
@@ -451,7 +455,6 @@ public class FlatScrollBarUI
super( direction, type, foreground, disabledForeground, super( direction, type, foreground, disabledForeground,
hoverForeground, hoverBackground, pressedForeground, pressedBackground ); hoverForeground, hoverBackground, pressedForeground, pressedBackground );
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
setFocusable( false ); setFocusable( false );
setRequestFocusEnabled( false ); setRequestFocusEnabled( false );
} }
@@ -461,6 +464,18 @@ public class FlatScrollBarUI
null, hoverButtonBackground, null, pressedButtonBackground ); null, hoverButtonBackground, null, pressedButtonBackground );
} }
@Override
public int getArrowWidth() {
// scale arrow size depending on scroll bar width
// (6 is default arrow width; 10 is base scroll bar width)
int arrowWidth = Math.round( 6 * (scrollBarWidth / 10f) );
// compute arrow size that leaves equal space on both sides (arrow is centered)
arrowWidth = scrollBarWidth - (((scrollBarWidth - arrowWidth) / 2) * 2);
return arrowWidth;
}
@Override @Override
protected Color deriveBackground( Color background ) { protected Color deriveBackground( Color background ) {
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() ); return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
@@ -469,8 +484,9 @@ public class FlatScrollBarUI
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
if( isShowButtons() ) { if( isShowButtons() ) {
int w = UIScale.scale( scrollBarWidth ); int w = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.width : 0 ) );
return new Dimension( w, w ); int h = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.height : 0 ) );
return new Dimension( w, h );
} else } else
return new Dimension(); return new Dimension();
} }

View File

@@ -291,6 +291,10 @@ public class FlatScrollPaneUI
} }
break; break;
case FlatClientProperties.OUTLINE:
scrollpane.repaint();
break;
case FlatClientProperties.STYLE: case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS: case FlatClientProperties.STYLE_CLASS:
installStyle(); installStyle();

View File

@@ -21,6 +21,7 @@ import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -28,6 +29,7 @@ import javax.swing.JSeparator;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSeparatorUI; import javax.swing.plaf.basic.BasicSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -50,7 +52,7 @@ import com.formdev.flatlaf.util.LoggingFacade;
*/ */
public class FlatSeparatorUI public class FlatSeparatorUI
extends BasicSeparatorUI extends BasicSeparatorUI
implements StyleableUI implements StyleableUI, PropertyChangeListener
{ {
@Styleable protected int height; @Styleable protected int height;
@Styleable protected int stripeWidth; @Styleable protected int stripeWidth;
@@ -58,7 +60,6 @@ public class FlatSeparatorUI
private final boolean shared; private final boolean shared;
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
@@ -109,20 +110,23 @@ public class FlatSeparatorUI
protected void installListeners( JSeparator s ) { protected void installListeners( JSeparator s ) {
super.installListeners( s ); super.installListeners( s );
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( s.addPropertyChangeListener( this );
s, () -> stylePropertyChange( s ), null );
s.addPropertyChangeListener( propertyChangeListener );
} }
@Override @Override
protected void uninstallListeners( JSeparator s ) { protected void uninstallListeners( JSeparator s ) {
super.uninstallListeners( s ); super.uninstallListeners( s );
s.removePropertyChangeListener( propertyChangeListener ); s.removePropertyChangeListener( this );
propertyChangeListener = null;
} }
private void stylePropertyChange( JSeparator s ) { /** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
JSeparator s = (JSeparator) e.getSource();
if( shared && FlatStylingSupport.hasStyleProperty( s ) ) { if( shared && FlatStylingSupport.hasStyleProperty( s ) ) {
// unshare component UI if necessary // unshare component UI if necessary
// updateUI() invokes installStyle() from installUI() // updateUI() invokes installStyle() from installUI()
@@ -131,6 +135,8 @@ public class FlatSeparatorUI
installStyle( s ); installStyle( s );
s.revalidate(); s.revalidate();
s.repaint(); s.repaint();
break;
}
} }
/** @since 2 */ /** @since 2 */

View File

@@ -234,7 +234,7 @@ public class FlatSliderUI
return -1; return -1;
// use default font (instead of slider font) because the slider font size // use default font (instead of slider font) because the slider font size
// may be different to label font size, but we want align the track/thumb with labels // may be different to label font size, but we want to align the track/thumb with labels
Font font = UIManager.getFont( "defaultFont" ); Font font = UIManager.getFont( "defaultFont" );
if( font == null ) if( font == null )
font = slider.getFont(); font = slider.getFont();

View File

@@ -293,9 +293,7 @@ public class FlatSpinnerUI
return true; return true;
JTextField textField = getEditorTextField( spinner.getEditor() ); JTextField textField = getEditorTextField( spinner.getEditor() );
return (textField != null) return textField != null && FlatUIUtils.isPermanentFocusOwner( textField );
? FlatUIUtils.isPermanentFocusOwner( textField )
: false;
} }
protected Color getBackground( boolean enabled ) { protected Color getBackground( boolean enabled ) {
@@ -447,7 +445,7 @@ public class FlatSpinnerUI
Insets padding = scale( FlatSpinnerUI.this.padding ); Insets padding = scale( FlatSpinnerUI.this.padding );
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 ); Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
// the arrows width is the same as the inner height so that the arrows area is square // the arrow buttons width is the same as the inner height so that the arrow buttons area is square
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth ); int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
int innerHeight = editorSize.height + padding.top + padding.bottom; int innerHeight = editorSize.height + padding.top + padding.bottom;
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner ); float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
@@ -538,6 +536,7 @@ public class FlatSpinnerUI
break; break;
case FlatClientProperties.COMPONENT_ROUND_RECT: case FlatClientProperties.COMPONENT_ROUND_RECT:
case FlatClientProperties.OUTLINE:
spinner.repaint(); spinner.repaint();
break; break;

View File

@@ -34,6 +34,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException; import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
@@ -52,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault SplitPaneDivider.border Border * @uiDefault SplitPaneDivider.border Border
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false * @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
* *
* <!-- BasicSplitPaneDivider -->
*
* @uiDefault SplitPane.oneTouchButtonSize int
* @uiDefault SplitPane.oneTouchButtonOffset int
* @uiDefault SplitPane.centerOneTouchButtons boolean
* @uiDefault SplitPane.supportsOneTouchButtons boolean optional; default is true
*
* <!-- JSplitPane --> * <!-- JSplitPane -->
* *
* @uiDefault SplitPane.continuousLayout boolean * @uiDefault SplitPane.continuousLayout boolean
@@ -235,7 +243,7 @@ public class FlatSplitPaneUI
switch( e.getPropertyName() ) { switch( e.getPropertyName() ) {
case JSplitPane.DIVIDER_LOCATION_PROPERTY: case JSplitPane.DIVIDER_LOCATION_PROPERTY:
// necessary to show/hide one-touch buttons on expand/collapse // necessary to show/hide one-touch buttons on expand/collapse
revalidate(); doLayout();
break; break;
} }
} }
@@ -345,7 +353,7 @@ public class FlatSplitPaneUI
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() ) if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
return; return;
// increase side of buttons, which makes them easier to hit by the user // increase size of buttons, which makes them easier to hit by the user
// and avoids cut arrows at small divider sizes // and avoids cut arrows at small divider sizes
int extraSize = UIScale.scale( 4 ); int extraSize = UIScale.scale( 4 );
if( orientation == JSplitPane.VERTICAL_SPLIT ) { if( orientation == JSplitPane.VERTICAL_SPLIT ) {
@@ -360,10 +368,19 @@ public class FlatSplitPaneUI
// hide buttons if not applicable // hide buttons if not applicable
boolean leftCollapsed = isLeftCollapsed(); boolean leftCollapsed = isLeftCollapsed();
if( leftCollapsed ) boolean rightCollapsed = isRightCollapsed();
rightButton.setLocation( leftButton.getLocation() ); if( leftCollapsed || rightCollapsed ) {
leftButton.setVisible( !leftCollapsed ); leftButton.setVisible( !leftCollapsed );
rightButton.setVisible( !isRightCollapsed() ); rightButton.setVisible( !rightCollapsed );
} else {
Object expandableSide = splitPane.getClientProperty( FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE );
leftButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_LEFT.equals( expandableSide ) );
rightButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_RIGHT.equals( expandableSide ) );
}
// move right button if left button is hidden
if( !leftButton.isVisible() )
rightButton.setLocation( leftButton.getLocation() );
} }
} }
} }

View File

@@ -359,7 +359,7 @@ public class FlatStylingSupport
* @param key the name of the field * @param key the name of the field
* @param value the new value * @param value the new value
* @return the old value of the field * @return the old value of the field
* @throws UnknownStyleException if object does not have a annotated field with given name * @throws UnknownStyleException if object does not have an annotated field with given name
* @throws IllegalArgumentException if value type does not fit to expected type * @throws IllegalArgumentException if value type does not fit to expected type
*/ */
public static Object applyToAnnotatedObject( Object obj, String key, Object value ) public static Object applyToAnnotatedObject( Object obj, String key, Object value )
@@ -517,7 +517,7 @@ public class FlatStylingSupport
* @param key the name of the field * @param key the name of the field
* @param value the new value * @param value the new value
* @return the old value of the field * @return the old value of the field
* @throws UnknownStyleException if object does not have a annotated field with given name * @throws UnknownStyleException if object does not have an annotated field with given name
* @throws IllegalArgumentException if value type does not fit to expected type * @throws IllegalArgumentException if value type does not fit to expected type
*/ */
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value ) public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )

View File

@@ -59,6 +59,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
import java.util.function.Predicate;
import javax.accessibility.Accessible; import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleContext;
import javax.swing.Action; import javax.swing.Action;
@@ -82,10 +83,12 @@ import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTabbedPaneUI; import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import javax.swing.text.View; import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon; import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -127,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.selectedBackground Color optional * @uiDefault TabbedPane.selectedBackground Color optional
* @uiDefault TabbedPane.selectedForeground Color * @uiDefault TabbedPane.selectedForeground Color
* @uiDefault TabbedPane.underlineColor Color * @uiDefault TabbedPane.underlineColor Color
* @uiDefault TabbedPane.inactiveUnderlineColor Color
* @uiDefault TabbedPane.disabledUnderlineColor Color * @uiDefault TabbedPane.disabledUnderlineColor Color
* @uiDefault TabbedPane.hoverColor Color * @uiDefault TabbedPane.hoverColor Color
* @uiDefault TabbedPane.focusColor Color * @uiDefault TabbedPane.focusColor Color
@@ -198,6 +202,7 @@ public class FlatTabbedPaneUI
@Styleable protected Color selectedBackground; @Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground; @Styleable protected Color selectedForeground;
@Styleable protected Color underlineColor; @Styleable protected Color underlineColor;
/** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor;
@Styleable protected Color disabledUnderlineColor; @Styleable protected Color disabledUnderlineColor;
@Styleable protected Color hoverColor; @Styleable protected Color hoverColor;
@Styleable protected Color focusColor; @Styleable protected Color focusColor;
@@ -288,6 +293,7 @@ public class FlatTabbedPaneUI
super.installUI( c ); super.installUI( c );
FlatSelectedTabRepainter.install();
installStyle(); installStyle();
} }
@@ -318,6 +324,7 @@ public class FlatTabbedPaneUI
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" ); selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" ); selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" ); underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor );
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" ); disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" ); hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
focusColor = UIManager.getColor( "TabbedPane.focusColor" ); focusColor = UIManager.getColor( "TabbedPane.focusColor" );
@@ -385,6 +392,7 @@ public class FlatTabbedPaneUI
selectedBackground = null; selectedBackground = null;
selectedForeground = null; selectedForeground = null;
underlineColor = null; underlineColor = null;
inactiveUnderlineColor = null;
disabledUnderlineColor = null; disabledUnderlineColor = null;
hoverColor = null; hoverColor = null;
focusColor = null; focusColor = null;
@@ -650,7 +658,7 @@ public class FlatTabbedPaneUI
case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break; case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break;
} }
} else { } else {
Object oldValue = null; Object oldValue;
switch( key ) { switch( key ) {
// BasicTabbedPaneUI // BasicTabbedPaneUI
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue; case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
@@ -733,7 +741,6 @@ public class FlatTabbedPaneUI
// increase size of repaint region to include part of content border // increase size of repaint region to include part of content border
if( contentSeparatorHeight > 0 && if( contentSeparatorHeight > 0 &&
getTabType() == TAB_TYPE_CARD &&
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) ) clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
{ {
int sh = scale( contentSeparatorHeight ); int sh = scale( contentSeparatorHeight );
@@ -1205,7 +1212,9 @@ public class FlatTabbedPaneUI
} }
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) { protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor ); g.setColor( tabPane.isEnabled()
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
: disabledUnderlineColor );
// paint underline selection // paint underline selection
boolean atBottom = (getTabType() != TAB_TYPE_CARD); boolean atBottom = (getTabType() != TAB_TYPE_CARD);
@@ -1236,6 +1245,23 @@ public class FlatTabbedPaneUI
} }
} }
/** @since 2.2 */
@SuppressWarnings( "unchecked" )
protected boolean isTabbedPaneOrChildFocused() {
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Object value = tabPane.getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
if( value instanceof Predicate ) {
return ((Predicate<JComponent>)value).test( tabPane ) &&
FlatUIUtils.isInActiveWindow( tabPane, keyboardFocusManager.getActiveWindow() );
}
Component focusOwner = keyboardFocusManager.getPermanentFocusOwner();
return focusOwner != null &&
SwingUtilities.isDescendingFrom( focusOwner, tabPane ) &&
FlatUIUtils.isInActiveWindow( focusOwner, keyboardFocusManager.getActiveWindow() );
}
/** /**
* Actually does nearly the same as super.paintContentBorder() but * Actually does nearly the same as super.paintContentBorder() but
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly * - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
@@ -1841,7 +1867,7 @@ public class FlatTabbedPaneUI
super( direction, arrowType, super( direction, arrowType,
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground, FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
null, buttonHoverBackground, null, buttonPressedBackground ); null, buttonHoverBackground, null, buttonPressedBackground );
setArrowWidth( 10 ); setArrowWidth( 11 );
} }
protected void updateStyle() { protected void updateStyle() {
@@ -1983,7 +2009,7 @@ public class FlatTabbedPaneUI
} }
protected JMenuItem createTabMenuItem( int tabIndex ) { protected JMenuItem createTabMenuItem( int tabIndex ) {
// search for tab name in this places // search for tab name in these places
// 1. tab title // 1. tab title
// 2. text of label or text component in custom tab component (including children) // 2. text of label or text component in custom tab component (including children)
// 3. accessible name of tab // 3. accessible name of tab
@@ -2016,7 +2042,7 @@ public class FlatTabbedPaneUI
menuItem.setOpaque( true ); menuItem.setOpaque( true );
} }
if( !tabPane.isEnabledAt( tabIndex ) ) if( !tabPane.isEnabled() || !tabPane.isEnabledAt( tabIndex ) )
menuItem.setEnabled( false ); menuItem.setEnabled( false );
menuItem.addActionListener( e -> selectTab( tabIndex ) ); menuItem.addActionListener( e -> selectTab( tabIndex ) );
@@ -2412,7 +2438,7 @@ public class FlatTabbedPaneUI
if( tabPane == null || tabViewport == null ) if( tabPane == null || tabViewport == null )
return; return;
if( !scrolled || tabViewport == null ) if( !scrolled )
return; return;
scrolled = false; scrolled = false;
@@ -2525,9 +2551,7 @@ public class FlatTabbedPaneUI
setRolloverTab( tabIndex ); setRolloverTab( tabIndex );
// check whether mouse hit tab close area // check whether mouse hit tab close area
boolean hitClose = isTabClosable( tabIndex ) boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y );
? getTabCloseHitArea( tabIndex ).contains( x, y )
: false;
if( e.getID() == MouseEvent.MOUSE_PRESSED ) if( e.getID() == MouseEvent.MOUSE_PRESSED )
pressedTabIndex = hitClose ? tabIndex : -1; pressedTabIndex = hitClose ? tabIndex : -1;
setRolloverTabClose( hitClose ); setRolloverTabClose( hitClose );
@@ -2550,7 +2574,6 @@ public class FlatTabbedPaneUI
if( tabIndex == lastTipTabIndex ) if( tabIndex == lastTipTabIndex )
return; // closeTip already set return; // closeTip already set
if( tabIndex != lastTipTabIndex )
restoreTabToolTip(); restoreTabToolTip();
lastTipTabIndex = tabIndex; lastTipTabIndex = tabIndex;
@@ -3344,4 +3367,77 @@ public class FlatTabbedPaneUI
delegate.actionPerformed( e ); delegate.actionPerformed( e );
} }
} }
//---- class FlatSelectedTabRepainter -------------------------------------
private static class FlatSelectedTabRepainter
implements PropertyChangeListener//, Runnable
{
private static FlatSelectedTabRepainter instance;
private KeyboardFocusManager keyboardFocusManager;
static void install() {
synchronized( FlatSelectedTabRepainter.class ) {
if( instance != null )
return;
instance = new FlatSelectedTabRepainter();
}
}
FlatSelectedTabRepainter() {
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyboardFocusManager.addPropertyChangeListener( this );
}
private void uninstall() {
synchronized( FlatSelectedTabRepainter.class ) {
if( instance == null )
return;
keyboardFocusManager.removePropertyChangeListener( this );
keyboardFocusManager = null;
instance = null;
}
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
// uninstall if no longer using FlatLaf
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) {
uninstall();
return;
}
switch( e.getPropertyName() ) {
case "permanentFocusOwner":
Object oldValue = e.getOldValue();
Object newValue = e.getNewValue();
if( oldValue instanceof Component )
repaintSelectedTabs( (Component) oldValue );
if( newValue instanceof Component )
repaintSelectedTabs( (Component) newValue );
break;
case "activeWindow":
repaintSelectedTabs( keyboardFocusManager.getPermanentFocusOwner() );
break;
}
}
private void repaintSelectedTabs( Component c ) {
if( c instanceof JTabbedPane )
repaintSelectedTab( (JTabbedPane) c );
while( (c = SwingUtilities.getAncestorOfClass( JTabbedPane.class, c )) != null )
repaintSelectedTab( (JTabbedPane) c );
}
private void repaintSelectedTab( JTabbedPane tabbedPane ) {
TabbedPaneUI ui = tabbedPane.getUI();
if( ui instanceof FlatTabbedPaneUI )
((FlatTabbedPaneUI) ui).repaintTab( tabbedPane.getSelectedIndex() );
}
}
} }

View File

@@ -72,7 +72,7 @@ public class FlatTableCellBorder
} }
/** /**
* Because this borders are always shared for all tables, * Because this border is always shared for all tables,
* get border specific style from FlatTableUI. * get border specific style from FlatTableUI.
*/ */
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) { static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {

View File

@@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension * @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color * @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color * @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.paintOutsideAlternateRows boolean
* *
* <!-- FlatTableCellBorder --> * <!-- FlatTableCellBorder -->
* *
@@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatTableUI public class FlatTableUI
extends BasicTableUI extends BasicTableUI
implements StyleableUI implements StyleableUI, FlatViewportUI.ViewportPainter
{ {
protected boolean showHorizontalLines; protected boolean showHorizontalLines;
protected boolean showVerticalLines; protected boolean showVerticalLines;
@@ -288,7 +289,7 @@ public class FlatTableUI
/** /**
* Toggle selection colors from focused to inactive and vice versa. * Toggle selection colors from focused to inactive and vice versa.
* *
* This is not a optimal solution but much easier than rewriting the whole paint methods. * This is not an optimal solution but much easier than rewriting the whole paint methods.
* *
* Using a LaF specific renderer was avoided because often a custom renderer is * Using a LaF specific renderer was avoided because often a custom renderer is
* already used in applications. Then either the inactive colors are not used, * already used in applications. Then either the inactive colors are not used,
@@ -421,4 +422,38 @@ public class FlatTableUI
? (viewport != rowHeader) ? (viewport != rowHeader)
: (viewport == rowHeader || rowHeader == null); : (viewport == rowHeader || rowHeader == null);
} }
/** @since 2.3 */
@Override
public void paintViewport( Graphics g, JComponent c, JViewport viewport ) {
int viewportWidth = viewport.getWidth();
int viewportHeight = viewport.getHeight();
// fill viewport background in same color as table background
if( viewport.isOpaque() ) {
g.setColor( table.getBackground() );
g.fillRect( 0, 0, viewportWidth, viewportHeight );
}
// paint alternating empty rows
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
Color alternateColor;
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
g.setColor( alternateColor );
int rowCount = table.getRowCount();
// paint alternating empty rows below the table
int tableHeight = table.getHeight();
if( tableHeight < viewportHeight ) {
int tableWidth = table.getWidth();
int rowHeight = table.getRowHeight();
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
if( row % 2 != 0 )
g.fillRect( 0, y, tableWidth, rowHeight );
}
}
}
}
} }

View File

@@ -232,6 +232,7 @@ public class FlatTextFieldUI
switch( e.getPropertyName() ) { switch( e.getPropertyName() ) {
case PLACEHOLDER_TEXT: case PLACEHOLDER_TEXT:
case COMPONENT_ROUND_RECT: case COMPONENT_ROUND_RECT:
case OUTLINE:
case TEXT_FIELD_PADDING: case TEXT_FIELD_PADDING:
c.repaint(); c.repaint();
break; break;
@@ -368,7 +369,7 @@ public class FlatTextFieldUI
if( !(oldBackground instanceof UIResource) ) if( !(oldBackground instanceof UIResource) )
return; return;
// do not update background if it currently has a unknown color (assigned from outside) // do not update background if it currently has an unknown color (assigned from outside)
if( oldBackground != background && if( oldBackground != background &&
oldBackground != disabledBackground && oldBackground != disabledBackground &&
oldBackground != inactiveBackground && oldBackground != inactiveBackground &&
@@ -609,7 +610,7 @@ debug*/
* Returns the rectangle used to paint leading and trailing icons. * Returns the rectangle used to paint leading and trailing icons.
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or * It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
* right margin if the text field has leading or trailing icons or components. * right margin if the text field has leading or trailing icons or components.
* Also the preferred widths of leading and trailing components are removed. * Also, the preferred widths of leading and trailing components are removed.
* *
* @since 2 * @since 2
*/ */
@@ -764,6 +765,7 @@ debug*/
/** @since 2 */ /** @since 2 */
protected JComponent createClearButton() { protected JComponent createClearButton() {
JButton button = new JButton(); JButton button = new JButton();
button.setName( "TextField.clearButton" );
button.putClientProperty( STYLE_CLASS, "clearButton" ); button.putClientProperty( STYLE_CLASS, "clearButton" );
button.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ); button.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
button.setCursor( Cursor.getDefaultCursor() ); button.setCursor( Cursor.getDefaultCursor() );
@@ -825,15 +827,20 @@ debug*/
/** @since 2 */ /** @since 2 */
protected void prepareLeadingOrTrailingComponent( JComponent c ) { protected void prepareLeadingOrTrailingComponent( JComponent c ) {
c.putClientProperty( STYLE_CLASS, "inTextField" ); c.putClientProperty( STYLE_CLASS, "inTextField" );
c.setCursor( Cursor.getDefaultCursor() );
if( c instanceof JButton || c instanceof JToggleButton ) if( c instanceof JButton || c instanceof JToggleButton ) {
c.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ); c.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
else if( c instanceof JToolBar ) {
if( !c.isCursorSet() )
c.setCursor( Cursor.getDefaultCursor() );
} else if( c instanceof JToolBar ) {
for( Component child : c.getComponents() ) { for( Component child : c.getComponents() ) {
if( child instanceof JComponent ) if( child instanceof JComponent )
((JComponent)child).putClientProperty( STYLE_CLASS, "inTextField" ); ((JComponent)child).putClientProperty( STYLE_CLASS, "inTextField" );
} }
if( !c.isCursorSet() )
c.setCursor( Cursor.getDefaultCursor() );
} }
} }
@@ -937,6 +944,7 @@ debug*/
((LayoutManager2)delegate).invalidateLayout( target ); ((LayoutManager2)delegate).invalidateLayout( target );
} }
} }
//---- class FlatDocumentListener ----------------------------------------- //---- class FlatDocumentListener -----------------------------------------
private class FlatDocumentListener private class FlatDocumentListener

View File

@@ -248,7 +248,7 @@ public class FlatTitlePane
if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) { if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) {
// JRootPane.FRAME works only for frames (and not for dialogs) // JRootPane.FRAME works only for frames (and not for dialogs)
// but at this time the owner window type is unknown (not yet added) // but at this time the owner window type is unknown (not yet added)
// so we add the iconify/maximize/restore buttons and they are shown // so we add the iconify/maximize/restore buttons, and they are shown
// later in frameStateChanged(), which is invoked from addNotify() // later in frameStateChanged(), which is invoked from addNotify()
buttonPanel.add( iconifyButton ); buttonPanel.add( iconifyButton );
@@ -420,7 +420,7 @@ public class FlatTitlePane
} }
/** /**
* Returns whether this title pane currently has an visible and embedded menubar. * Returns whether this title pane currently has a visible and embedded menubar.
*/ */
protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) { protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) {
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded(); return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
@@ -615,7 +615,7 @@ debug*/
int maximizedWidth = screenBounds.width; int maximizedWidth = screenBounds.width;
int maximizedHeight = screenBounds.height; int maximizedHeight = screenBounds.height;
if( !isMaximizedBoundsFixed() ) { if( SystemInfo.isWindows && !isMaximizedBoundsFixed() ) {
// on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment // on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment
maximizedX = 0; maximizedX = 0;
maximizedY = 0; maximizedY = 0;
@@ -772,7 +772,7 @@ debug*/
if( horizontalGlue != null ) { if( horizontalGlue != null ) {
// If menu bar is embedded and contains a horizontal glue component, // If menu bar is embedded and contains a horizontal glue component,
// then split the hit test spot into two spots so that // then split the hit test spot into two spots so that
// the glue component area can used to move the window. // the glue component area can be used to move the window.
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window ); Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
int x2 = glueLocation.x + horizontalGlue.getWidth(); int x2 = glueLocation.x + horizontalGlue.getWidth();
@@ -854,7 +854,7 @@ debug*/
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 ); insets.bottom += UIScale.scale( 1 );
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() ); insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets; return insets;
@@ -873,7 +873,7 @@ debug*/
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight ); FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
} }
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
} }
@@ -883,9 +883,7 @@ debug*/
} }
protected boolean isWindowMaximized( Component c ) { protected boolean isWindowMaximized( Component c ) {
return window instanceof Frame return window instanceof Frame && (((Frame) window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
? (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
: false;
} }
} }
@@ -911,7 +909,7 @@ debug*/
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle; boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) { if( center ) {
// If window is wide enough, center title within window bounds. // If window is wide enough, center title within window bounds.
// Otherwise leave it centered within free space (label bounds). // Otherwise, leave it centered within free space (label bounds).
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX(); int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap ) if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
textX = centeredTextX; textX = centeredTextX;
@@ -966,7 +964,7 @@ debug*/
activeChanged( true ); activeChanged( true );
updateNativeTitleBarHeightAndHitTestSpots(); updateNativeTitleBarHeightAndHitTestSpots();
if( hasNativeCustomDecoration() ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this ); WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder(); repaintWindowBorder();
@@ -977,7 +975,7 @@ debug*/
activeChanged( false ); activeChanged( false );
updateNativeTitleBarHeightAndHitTestSpots(); updateNativeTitleBarHeightAndHitTestSpots();
if( hasNativeCustomDecoration() ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this ); WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder(); repaintWindowBorder();
@@ -1018,6 +1016,9 @@ debug*/
if( window == null ) if( window == null )
return; // should newer occur return; // should newer occur
if( !SwingUtilities.isLeftMouseButton( e ) )
return;
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window ); dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
} }
@@ -1032,6 +1033,9 @@ debug*/
if( window == null ) if( window == null )
return; // should newer occur return; // should newer occur
if( !SwingUtilities.isLeftMouseButton( e ) )
return;
if( hasNativeCustomDecoration() ) if( hasNativeCustomDecoration() )
return; // do nothing if having native window border return; // do nothing if having native window border

View File

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

View File

@@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -31,6 +32,7 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolBarSeparatorUI; import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -47,7 +49,7 @@ import com.formdev.flatlaf.util.LoggingFacade;
*/ */
public class FlatToolBarSeparatorUI public class FlatToolBarSeparatorUI
extends BasicToolBarSeparatorUI extends BasicToolBarSeparatorUI
implements StyleableUI implements StyleableUI, PropertyChangeListener
{ {
private static final int LINE_WIDTH = 1; private static final int LINE_WIDTH = 1;
@@ -56,7 +58,6 @@ public class FlatToolBarSeparatorUI
private final boolean shared; private final boolean shared;
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
@@ -105,20 +106,23 @@ public class FlatToolBarSeparatorUI
protected void installListeners( JSeparator s ) { protected void installListeners( JSeparator s ) {
super.installListeners( s ); super.installListeners( s );
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( s.addPropertyChangeListener( this );
s, () -> stylePropertyChange( s ), null );
s.addPropertyChangeListener( propertyChangeListener );
} }
@Override @Override
protected void uninstallListeners( JSeparator s ) { protected void uninstallListeners( JSeparator s ) {
super.uninstallListeners( s ); super.uninstallListeners( s );
s.removePropertyChangeListener( propertyChangeListener ); s.removePropertyChangeListener( this );
propertyChangeListener = null;
} }
private void stylePropertyChange( JSeparator s ) { /** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
JSeparator s = (JSeparator) e.getSource();
if( shared && FlatStylingSupport.hasStyleProperty( s ) ) { if( shared && FlatStylingSupport.hasStyleProperty( s ) ) {
// unshare component UI if necessary // unshare component UI if necessary
// updateUI() invokes installStyle() from installUI() // updateUI() invokes installStyle() from installUI()
@@ -127,6 +131,8 @@ public class FlatToolBarSeparatorUI
installStyle( s ); installStyle( s );
s.revalidate(); s.revalidate();
s.repaint(); s.repaint();
break;
}
} }
/** @since 2 */ /** @since 2 */

View File

@@ -256,11 +256,15 @@ public class FlatToolBarUI
default: return; default: return;
} }
for( int i = focusedCompIndex + add; i != focusedCompIndex; i += add ) { int i = focusedCompIndex;
for(;;) {
i += add;
if( i < 0 ) if( i < 0 )
i = count - 1; i = count - 1;
else if( i >= count ) else if( i >= count )
i = 0; i = 0;
if( i == focusedCompIndex )
break;
Component c = toolBar.getComponentAtIndex( i ); Component c = toolBar.getComponentAtIndex( i );
if( canBeFocusOwner( c ) ) { if( canBeFocusOwner( c ) ) {
@@ -282,7 +286,7 @@ public class FlatToolBarUI
return comboBox.getUI().isFocusTraversable( comboBox ); return comboBox.getUI().isFocusTraversable( comboBox );
} }
// check whether component has a empty input map to skip components that // check whether component has an empty input map to skip components that
// are focusable, but do nothing when focused (e.g. JLabel) // are focusable, but do nothing when focused (e.g. JLabel)
// see LayoutFocusTraversalPolicy.accept() // see LayoutFocusTraversalPolicy.accept()
if( c instanceof JComponent ) { if( c instanceof JComponent ) {

View File

@@ -21,6 +21,7 @@ import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.List; import java.util.List;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -49,9 +50,8 @@ import com.formdev.flatlaf.util.StringUtils;
*/ */
public class FlatToolTipUI public class FlatToolTipUI
extends BasicToolTipUI extends BasicToolTipUI
implements PropertyChangeListener
{ {
private static PropertyChangeListener sharedPropertyChangedListener;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return FlatUIUtils.createSharedUI( FlatToolTipUI.class, FlatToolTipUI::new ); return FlatUIUtils.createSharedUI( FlatToolTipUI.class, FlatToolTipUI::new );
} }
@@ -68,24 +68,24 @@ public class FlatToolTipUI
protected void installListeners( JComponent c ) { protected void installListeners( JComponent c ) {
super.installListeners( c ); super.installListeners( c );
if( sharedPropertyChangedListener == null ) { c.addPropertyChangeListener( this );
sharedPropertyChangedListener = e -> {
String name = e.getPropertyName();
if( name == "tiptext" || name == "font" || name == "foreground" ) {
JToolTip toolTip = (JToolTip) e.getSource();
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
}
};
}
c.addPropertyChangeListener( sharedPropertyChangedListener );
} }
@Override @Override
protected void uninstallListeners( JComponent c ) { protected void uninstallListeners( JComponent c ) {
super.uninstallListeners( c ); super.uninstallListeners( c );
c.removePropertyChangeListener( sharedPropertyChangedListener ); c.removePropertyChangeListener( this );
}
/** @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 );
}
} }
@Override @Override

View File

@@ -71,7 +71,7 @@ public class FlatUIUtils
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" ); public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
private static boolean useSharedUIs = true; private static boolean useSharedUIs = true;
private static WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>(); private static final WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
public static Rectangle addInsets( Rectangle r, Insets insets ) { public static Rectangle addInsets( Rectangle r, Insets insets ) {
return new Rectangle( return new Rectangle(
@@ -245,7 +245,7 @@ public class FlatUIUtils
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() ); isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
} }
private static boolean isInActiveWindow( Component c, Window activeWindow ) { static boolean isInActiveWindow( Component c, Window activeWindow ) {
Window window = SwingUtilities.windowForComponent( c ); Window window = SwingUtilities.windowForComponent( c );
return window == activeWindow || return window == activeWindow ||
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow); (window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
@@ -777,8 +777,8 @@ public class FlatUIUtils
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST}) * {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow * @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
* @param arrowSize the width of the painted arrow (for vertical direction) (will be scaled) * @param arrowSize the width of the painted arrow (for vertical direction) (will be scaled)
* @param xOffset a offset added to the x coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled) * @param xOffset an offset added to the x coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
* @param yOffset a offset added to the y coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled) * @param yOffset an offset added to the y coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
* *
* @since 1.1 * @since 1.1
*/ */
@@ -786,13 +786,15 @@ public class FlatUIUtils
int direction, boolean chevron, int arrowSize, float xOffset, float yOffset ) int direction, boolean chevron, int arrowSize, float xOffset, float yOffset )
{ {
// compute arrow width/height // compute arrow width/height
int aw = UIScale.scale( arrowSize + (chevron ? 0 : 1) ); // - make chevron arrows one pixel smaller because coordinates are based on center of pixels (0.5/0.5)
int ah = UIScale.scale( (arrowSize / 2) + (chevron ? 0 : 1) ); // - make triangle arrows one pixel taller (and round height up) to make them look stronger
float aw = UIScale.scale( arrowSize + (chevron ? -1 : 0) );
float ah = chevron ? (aw / 2) : UIScale.scale( (arrowSize / 2) + 1 );
// rotate arrow width/height for horizontal directions // rotate arrow width/height for horizontal directions
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH); boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
if( !vert ) { if( !vert ) {
int temp = aw; float temp = aw;
aw = ah; aw = ah;
ah = temp; ah = temp;
} }
@@ -804,19 +806,19 @@ public class FlatUIUtils
// compute arrow location // compute arrow location
float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset ); float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset );
float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset ); float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset );
int ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -ox ) : Math.round( ox )); float ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -(ox + aw) ) - aw : Math.round( ox ));
int ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -oy ) : Math.round( oy )); float ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -(oy + ah) ) - ah : Math.round( oy ));
// paint arrow // paint arrow
g.translate( ax, ay ); g.translate( ax, ay );
/*debug /*debug
debugPaintArrow( g, Color.red, vert, aw + extra, ah + extra ); debugPaintArrow( g, Color.red, vert, Math.round( aw + extra ), Math.round( ah + extra ) );
debug*/ debug*/
Shape arrowShape = createArrowShape( direction, chevron, aw, ah ); Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
if( chevron ) { if( chevron ) {
Stroke oldStroke = g.getStroke(); Stroke oldStroke = g.getStroke();
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) ); g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
g.draw( arrowShape ); drawShapePure( g, arrowShape );
g.setStroke( oldStroke ); g.setStroke( oldStroke );
} else { } else {
// triangle // triangle
@@ -828,7 +830,7 @@ debug*/
/** /**
* Creates a chevron or triangle arrow shape for the given direction and size. * Creates a chevron or triangle arrow shape for the given direction and size.
* <p> * <p>
* The chevron shape is a open path that can be painted with {@link Graphics2D#draw(Shape)}. * The chevron shape is an open path that can be painted with {@link Graphics2D#draw(Shape)}.
* The triangle shape is a close path that can be painted with {@link Graphics2D#fill(Shape)}. * The triangle shape is a close path that can be painted with {@link Graphics2D#fill(Shape)}.
* *
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH} * @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
@@ -880,7 +882,7 @@ debug*/
} }
/** /**
* Creates a open or closed path for the given points. * Creates an open or closed path for the given points.
*/ */
public static Path2D createPath( boolean close, double... points ) { public static Path2D createPath( boolean close, double... points ) {
Path2D path = new Path2D.Float(); Path2D path = new Path2D.Float();
@@ -892,6 +894,23 @@ debug*/
return path; return path;
} }
/**
* Draws the given shape with disabled stroke normalization.
* The x/y coordinates of the shape are translated by a half pixel.
*
* @since 2.1
*/
public static void drawShapePure( Graphics2D g, Shape shape ) {
Object oldStrokeControl = g.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
g.translate( 0.5, 0.5 );
g.draw( shape );
g.translate( -0.5, -0.5 );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldStrokeControl );
}
/** /**
* Draws the given string at the specified location. * Draws the given string at the specified location.
* The provided component is used to query text properties and anti-aliasing hints. * The provided component is used to query text properties and anti-aliasing hints.

View File

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

View File

@@ -23,6 +23,7 @@ import java.awt.Dialog;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
@@ -42,7 +43,9 @@ import javax.swing.JComponent;
import javax.swing.JInternalFrame; import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -231,8 +234,15 @@ public abstract class FlatWindowResizer
{ {
protected Window window; protected Window window;
private final boolean limitResizeToScreenBounds;
public WindowResizer( JRootPane rootPane ) { public WindowResizer( JRootPane rootPane ) {
super( rootPane ); super( rootPane );
// 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 @Override
@@ -289,11 +299,19 @@ public abstract class FlatWindowResizer
@Override @Override
protected boolean limitToParentBounds() { protected boolean limitToParentBounds() {
return false; return limitResizeToScreenBounds && window != null;
} }
@Override @Override
protected Rectangle getParentBounds() { 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; return null;
} }
@@ -385,7 +403,7 @@ public abstract class FlatWindowResizer
@Override @Override
protected Rectangle getParentBounds() { protected Rectangle getParentBounds() {
return getFrame().getParent().getBounds(); return new Rectangle( getFrame().getParent().getSize() );
} }
@Override @Override
@@ -504,7 +522,7 @@ debug*/
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
if( !isWindowResizable() ) if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return; return;
int xOnScreen = e.getXOnScreen(); int xOnScreen = e.getXOnScreen();
@@ -533,7 +551,7 @@ debug*/
@Override @Override
public void mouseReleased( MouseEvent e ) { public void mouseReleased( MouseEvent e ) {
if( !isWindowResizable() ) if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return; return;
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0; dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
@@ -559,7 +577,7 @@ debug*/
@Override @Override
public void mouseDragged( MouseEvent e ) { public void mouseDragged( MouseEvent e ) {
if( !isWindowResizable() ) if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return; return;
int xOnScreen = e.getXOnScreen(); int xOnScreen = e.getXOnScreen();
@@ -579,8 +597,8 @@ debug*/
// top // top
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) { if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
newBounds.y = yOnScreen - dragTopOffset; newBounds.y = yOnScreen - dragTopOffset;
if( limitToParentBounds() && newBounds.y < 0 ) if( limitToParentBounds() )
newBounds.y = 0; newBounds.y = Math.max( newBounds.y, getParentBounds().y );
newBounds.height += (oldBounds.y - newBounds.y); newBounds.height += (oldBounds.y - newBounds.y);
} }
@@ -597,8 +615,8 @@ debug*/
// left // left
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) { if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
newBounds.x = xOnScreen - dragLeftOffset; newBounds.x = xOnScreen - dragLeftOffset;
if( limitToParentBounds() && newBounds.x < 0 ) if( limitToParentBounds() )
newBounds.x = 0; newBounds.x = Math.max( newBounds.x, getParentBounds().x );
newBounds.width += (oldBounds.x - newBounds.x); newBounds.width += (oldBounds.x - newBounds.x);
} }

View File

@@ -38,7 +38,6 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList; import javax.swing.event.EventListenerList;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
// //
@@ -82,7 +81,6 @@ class FlatWindowsNativeWindowBorder
private Color colorizationColor; private Color colorizationColor;
private int colorizationColorBalance; private int colorizationColorBalance;
private static NativeLibrary nativeLibrary;
private static FlatWindowsNativeWindowBorder instance; private static FlatWindowsNativeWindowBorder instance;
static FlatNativeWindowBorder.Provider getInstance() { static FlatNativeWindowBorder.Provider getInstance() {
@@ -94,35 +92,8 @@ class FlatWindowsNativeWindowBorder
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 ) if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
return null; return null;
// load native library
if( nativeLibrary == null ) {
if( !SystemInfo.isJava_9_orLater ) {
// In Java 8, load jawt.dll (part of JRE) explicitly because it
// is not found when running application with <jdk>/bin/java.exe.
// When using <jdk>/jre/bin/java.exe, it is found.
// jawt.dll is located in <jdk>/jre/bin/.
// Java 9 and later does not have this problem.
try {
System.loadLibrary( "jawt" );
} catch( UnsatisfiedLinkError ex ) {
// log error only if native library jawt.dll not already loaded
String message = ex.getMessage();
if( message == null || !message.contains( "already loaded in another classloader" ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
String libraryName = "com/formdev/flatlaf/natives/flatlaf-windows-x86";
if( SystemInfo.isX86_64 )
libraryName += "_64";
nativeLibrary = new NativeLibrary( libraryName, null, true );
}
// check whether native library was successfully loaded // check whether native library was successfully loaded
if( !nativeLibrary.isLoaded() ) if( !FlatNativeLibrary.isLoaded() )
return null; return null;
// create new instance // create new instance
@@ -140,7 +111,7 @@ class FlatWindowsNativeWindowBorder
} }
/** /**
* Tell the window whether the application wants use custom decorations. * Tell the window whether the application wants to use custom decorations.
* If {@code true}, the Windows 10 title bar is hidden (including minimize, * If {@code true}, the Windows 10 title bar is hidden (including minimize,
* maximize and close buttons), but not the resize borders (including drop shadow). * maximize and close buttons), but not the resize borders (including drop shadow).
*/ */

View File

@@ -284,7 +284,7 @@ public class JBRCustomDecorations
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c ); Window window = SwingUtilities.windowForComponent( c );
boolean active = (window != null) ? window.isActive() : false; boolean active = window != null && window.isActive();
// paint top border // paint top border
// - in light themes // - in light themes
@@ -298,7 +298,7 @@ public class JBRCustomDecorations
} }
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) { private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.drawRect( x, y, width - 1, 0 ); g.fillRect( x, y, width, 1 );
} }
void repaintBorder( Component c ) { void repaintBorder( Component c ) {

View File

@@ -59,7 +59,7 @@ import com.formdev.flatlaf.util.Animator.Interpolator;
* </pre> * </pre>
* *
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)} * Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
* is a instance of {@link JComponent}. * is an instance of {@link JComponent}.
* A client property is set on the component to store the animation state. * A client property is set on the component to store the animation state.
* *
* @author Karl Tauber * @author Karl Tauber
@@ -68,7 +68,7 @@ public interface AnimatedIcon
extends Icon extends Icon
{ {
@Override @Override
public default void paintIcon( Component c, Graphics g, int x, int y ) { default void paintIcon( Component c, Graphics g, int x, int y ) {
AnimationSupport.paintIcon( this, c, g, x, y ); AnimationSupport.paintIcon( this, c, g, x, y );
} }

View File

@@ -122,6 +122,8 @@ public class ColorFunctions
return color1; return color1;
if( weight <= 0 ) if( weight <= 0 )
return color2; return color2;
if( color1.equals( color2 ) )
return color1;
int r1 = color1.getRed(); int r1 = color1.getRed();
int g1 = color1.getGreen(); int g1 = color1.getGreen();
@@ -196,6 +198,9 @@ public class ColorFunctions
: (float) Math.pow( (value + 0.055) / 1.055, 2.4 ); : (float) Math.pow( (value + 0.055) / 1.055, 2.4 );
} }
/**
* Applies the given color functions to the given color and returns the new color.
*/
public static Color applyFunctions( Color color, ColorFunction... functions ) { public static Color applyFunctions( Color color, ColorFunction... functions ) {
// if having only a single function of type Mix, then avoid four unnecessary conversions: // if having only a single function of type Mix, then avoid four unnecessary conversions:
// 1. RGB to HSL in this method // 1. RGB to HSL in this method
@@ -221,6 +226,9 @@ public class ColorFunctions
return HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 ); return HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 );
} }
/**
* Clamps the given value between 0 and 100.
*/
public static float clamp( float value ) { public static float clamp( float value ) {
return (value < 0) return (value < 0)
? 0 ? 0

View File

@@ -47,7 +47,7 @@ public class DerivedColor
Color result = ColorFunctions.applyFunctions( baseColor, functions ); Color result = ColorFunctions.applyFunctions( baseColor, functions );
// if the result is equal to the default color, then the original base color // if the result is equal to the default color, then the original base color
// was passed and we can cache this to avoid color calculations // was passed, and we can cache this to avoid color calculations
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) { if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
hasBaseOfDefaultColor = true; hasBaseOfDefaultColor = true;
baseOfDefaultColorRGB = baseColor.getRGB(); baseOfDefaultColorRGB = baseColor.getRGB();

View File

@@ -76,7 +76,7 @@ public class HSLColor
} }
/** /**
* Create a HSLColor object using an an array containing the * Create a HSLColor object using an array containing the
* individual HSL values and with a default alpha value of 1. * individual HSL values and with a default alpha value of 1.
* *
* @param hsl array containing HSL values * @param hsl array containing HSL values
@@ -87,7 +87,7 @@ public class HSLColor
} }
/** /**
* Create a HSLColor object using an an array containing the * Create a HSLColor object using an array containing the
* individual HSL values. * individual HSL values.
* *
* @param hsl array containing HSL values * @param hsl array containing HSL values
@@ -291,7 +291,7 @@ public class HSLColor
// Calculate the Saturation // Calculate the Saturation
float s = 0; float s;
if (max == min) if (max == min)
s = 0; s = 0;
@@ -386,7 +386,7 @@ public class HSLColor
s /= 100f; s /= 100f;
l /= 100f; l /= 100f;
float q = 0; float q;
if (l < 0.5) if (l < 0.5)
q = l * (1 + s); q = l * (1 + s);

View File

@@ -31,7 +31,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
public class HiDPIUtils public class HiDPIUtils
{ {
public interface Painter { public interface Painter {
public void paint( Graphics2D g, int x, int y, int width, int height, double scaleFactor ); void paint( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
} }
public static void paintAtScale1x( Graphics2D g, JComponent c, Painter painter ) { public static void paintAtScale1x( Graphics2D g, JComponent c, Painter painter ) {
@@ -114,7 +114,7 @@ public class HiDPIUtils
* painted too far down on some operating systems. * painted too far down on some operating systems.
* The higher the system scale factor is, the more. * The higher the system scale factor is, the more.
* <p> * <p>
* This methods computes a correction value for the Y position. * This method computes a correction value for the Y position.
*/ */
public static float computeTextYCorrection( Graphics2D g ) { public static float computeTextYCorrection( Graphics2D g ) {
if( !useTextYCorrection() || !SystemInfo.isWindows ) if( !useTextYCorrection() || !SystemInfo.isWindows )

View File

@@ -89,7 +89,7 @@ public class JavaCompatibility
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
? "getClippedString" ? "getClippedString"
: "clipStringIfNecessary", : "clipStringIfNecessary",
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } ); JComponent.class, FontMetrics.class, String.class, int.class );
} catch( Exception ex ) { } catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex ); LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex ); throw new RuntimeException( ex );

View File

@@ -72,7 +72,7 @@ public class MultiResolutionImageSupport
* <p> * <p>
* The given dimensions array is only used for {@link #getResolutionVariants(Image)}. * The given dimensions array is only used for {@link #getResolutionVariants(Image)}.
* The producer function may be invoked with any dimension (that is not contained in * The producer function may be invoked with any dimension (that is not contained in
* dimensions array) and is expected to produce a image for the passed in dimension. * dimensions array) and is expected to produce an image for the passed in dimension.
* *
* @param baseImageIndex index of the base image in the dimensions array * @param baseImageIndex index of the base image in the dimensions array
* @param dimensions dimensions of resolution variants (sorted by size; smallest first) * @param dimensions dimensions of resolution variants (sorted by size; smallest first)
@@ -92,7 +92,7 @@ public class MultiResolutionImageSupport
* for "disabled" state. * for "disabled" state.
* *
* @param image a multi-resolution image that is mapped using the given mapper function * @param image a multi-resolution image that is mapped using the given mapper function
* @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying an filter) * @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying a filter)
* @return a multi-resolution image on Java 9 or later; a mapped image on Java 8 * @return a multi-resolution image on Java 9 or later; a mapped image on Java 8
*/ */
public static Image map( Image image, Function<Image, Image> mapper ) { public static Image map( Image image, Function<Image, Image> mapper ) {
@@ -104,7 +104,7 @@ public class MultiResolutionImageSupport
* <p> * <p>
* If the given image is a multi-resolution image then invokes * If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariant(destImageWidth, destImageHeight)}. * {@code java.awt.image.MultiResolutionImage.getResolutionVariant(destImageWidth, destImageHeight)}.
* Otherwise returns the given image. * Otherwise, returns the given image.
*/ */
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) { public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return image; return image;
@@ -115,7 +115,7 @@ public class MultiResolutionImageSupport
* <p> * <p>
* If the given image is a multi-resolution image then invokes * If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariants()}. * {@code java.awt.image.MultiResolutionImage.getResolutionVariants()}.
* Otherwise returns a list containing only the given image. * Otherwise, returns a list containing only the given image.
*/ */
public static List<Image> getResolutionVariants( Image image ) { public static List<Image> getResolutionVariants( Image image ) {
return Collections.singletonList( image ); return Collections.singletonList( image );

View File

@@ -60,6 +60,19 @@ public class NativeLibrary
: false; : false;
} }
/**
* Load native library from given file.
*
* @param libraryFile the file of the native library
* @param supported whether the native library is supported on the current platform
* @since 2
*/
public NativeLibrary( File libraryFile, boolean supported ) {
this.loaded = supported
? loadLibraryFromFile( libraryFile )
: false;
}
/** /**
* Returns whether the native library is loaded. * Returns whether the native library is loaded.
* <p> * <p>
@@ -120,16 +133,29 @@ public class NativeLibrary
} }
} }
private boolean loadLibraryFromFile( File libraryFile ) {
try {
System.load( libraryFile.getAbsolutePath() );
return true;
} catch( Throwable ex ) {
log( null, ex );
return false;
}
}
/**
* Add prefix and suffix to library name.
* <ul>
* <li>Windows: libraryName + ".dll"
* <li>macOS: "lib" + libraryName + ".dylib"
* <li>Linux: "lib" + libraryName + ".so"
* </ul>
*/
private static String decorateLibraryName( String libraryName ) { private static String decorateLibraryName( String libraryName ) {
if( SystemInfo.isWindows )
return libraryName.concat( ".dll" );
String suffix = SystemInfo.isMacOS ? ".dylib" : ".so";
int sep = libraryName.lastIndexOf( '/' ); int sep = libraryName.lastIndexOf( '/' );
return (sep >= 0) return (sep >= 0)
? libraryName.substring( 0, sep + 1 ) + "lib" + libraryName.substring( sep + 1 ) + suffix ? libraryName.substring( 0, sep + 1 ) + System.mapLibraryName( libraryName.substring( sep + 1 ) )
: "lib" + libraryName + suffix; : System.mapLibraryName( libraryName );
} }
private static void log( String msg, Throwable thrown ) { private static void log( String msg, Throwable thrown ) {

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2022 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Container;
/**
* Utility methods for Swing.
*
* @author Karl Tauber
* @since 2
*/
public class SwingUtils
{
/**
* Search for a (grand) child component with the given name.
*
* @return a component; or {@code null}
*/
@SuppressWarnings( "unchecked" )
public static <T extends Component> T getComponentByName( Container parent, String name ) {
for( Component child : parent.getComponents() ) {
if( name.equals( child.getName() ) )
return (T) child;
if( child instanceof Container ) {
T c = getComponentByName( (Container) child, name );
if( c != null )
return c;
}
}
return null;
}
}

View File

@@ -34,6 +34,9 @@ public class SystemInfo
// OS versions // OS versions
public static final long osVersion; public static final long osVersion;
public static final boolean isWindows_10_orLater; public static final boolean isWindows_10_orLater;
/** <strong>Note</strong>: This requires Java 8u321, 11.0.14, 17.0.2 or 18 (or later).
* (see https://bugs.openjdk.java.net/browse/JDK-8274840)
* @since 2 */ public static final boolean isWindows_11_orLater;
public static final boolean isMacOS_10_11_ElCapitan_orLater; public static final boolean isMacOS_10_11_ElCapitan_orLater;
public static final boolean isMacOS_10_14_Mojave_orLater; public static final boolean isMacOS_10_14_Mojave_orLater;
public static final boolean isMacOS_10_15_Catalina_orLater; public static final boolean isMacOS_10_15_Catalina_orLater;
@@ -47,8 +50,10 @@ public class SystemInfo
public static final long javaVersion; public static final long javaVersion;
public static final boolean isJava_9_orLater; public static final boolean isJava_9_orLater;
public static final boolean isJava_11_orLater; public static final boolean isJava_11_orLater;
/** @since 2.3 */ public static final boolean isJava_12_orLater;
public static final boolean isJava_15_orLater; public static final boolean isJava_15_orLater;
/** @since 2 */ public static final boolean isJava_17_orLater; /** @since 2 */ public static final boolean isJava_17_orLater;
/** @since 2 */ public static final boolean isJava_18_orLater;
// Java VMs // Java VMs
public static final boolean isJetBrainsJVM; public static final boolean isJetBrainsJVM;
@@ -62,6 +67,9 @@ public class SystemInfo
/** @since 1.1.2 */ public static final boolean isWebswing; /** @since 1.1.2 */ public static final boolean isWebswing;
/** @since 1.1.1 */ public static final boolean isWinPE; /** @since 1.1.1 */ public static final boolean isWinPE;
// features
/** @since 2.3 */ public static final boolean isMacFullWindowContentSupported;
static { static {
// platforms // platforms
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
@@ -72,6 +80,8 @@ public class SystemInfo
// OS versions // OS versions
osVersion = scanVersion( System.getProperty( "os.version" ) ); osVersion = scanVersion( System.getProperty( "os.version" ) );
isWindows_10_orLater = (isWindows && osVersion >= toVersion( 10, 0, 0, 0 )); isWindows_10_orLater = (isWindows && osVersion >= toVersion( 10, 0, 0, 0 ));
isWindows_11_orLater = (isWindows_10_orLater && osName.length() > "windows ".length() &&
scanVersion( osName.substring( "windows ".length() ) ) >= toVersion( 11, 0, 0, 0 ));
isMacOS_10_11_ElCapitan_orLater = (isMacOS && osVersion >= toVersion( 10, 11, 0, 0 )); isMacOS_10_11_ElCapitan_orLater = (isMacOS && osVersion >= toVersion( 10, 11, 0, 0 ));
isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 )); isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 )); isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
@@ -86,8 +96,10 @@ public class SystemInfo
javaVersion = scanVersion( System.getProperty( "java.version" ) ); javaVersion = scanVersion( System.getProperty( "java.version" ) );
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 )); isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 )); isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 ));
isJava_12_orLater = (javaVersion >= toVersion( 12, 0, 0, 0 ));
isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 )); isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 ));
isJava_17_orLater = (javaVersion >= toVersion( 17, 0, 0, 0 )); isJava_17_orLater = (javaVersion >= toVersion( 17, 0, 0, 0 ));
isJava_18_orLater = (javaVersion >= toVersion( 18, 0, 0, 0 ));
// Java VMs // Java VMs
isJetBrainsJVM = System.getProperty( "java.vm.vendor", "Unknown" ) isJetBrainsJVM = System.getProperty( "java.vm.vendor", "Unknown" )
@@ -101,6 +113,12 @@ public class SystemInfo
isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" ); isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" );
isWebswing = (System.getProperty( "webswing.rootDir" ) != null); isWebswing = (System.getProperty( "webswing.rootDir" ) != null);
isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) ); isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) );
// features
// available since Java 12; backported to Java 11.0.8 and 8u292
isMacFullWindowContentSupported =
javaVersion >= toVersion( 11, 0, 8, 0 ) ||
(javaVersion >= toVersion( 1, 8, 0, 292 ) && !isJava_9_orLater);
} }
public static long scanVersion( String version ) { public static long scanVersion( String version ) {

View File

@@ -103,7 +103,7 @@ public class UIScale
// Java 9 and later supports per-monitor scaling // Java 9 and later supports per-monitor scaling
jreHiDPI = true; jreHiDPI = true;
} else if( SystemInfo.isJetBrainsJVM ) { } else if( SystemInfo.isJetBrainsJVM ) {
// IntelliJ IDEA ships its own JetBrains Java 8 JRE that may supports per-monitor scaling // IntelliJ IDEA ships its own JetBrains Java 8 JRE that may support per-monitor scaling
// see com.intellij.ui.JreHiDpiUtil.isJreHiDPIEnabled() // see com.intellij.ui.JreHiDpiUtil.isJreHiDPIEnabled()
try { try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
@@ -411,7 +411,7 @@ public class UIScale
* Scales the given dimension with the user scale factor. * Scales the given dimension with the user scale factor.
* <p> * <p>
* If user scale factor is 1, then the given dimension is simply returned. * If user scale factor is 1, then the given dimension is simply returned.
* Otherwise a new instance of {@link Dimension} or {@link DimensionUIResource} * Otherwise, a new instance of {@link Dimension} or {@link DimensionUIResource}
* is returned, depending on whether the passed dimension implements {@link UIResource}. * is returned, depending on whether the passed dimension implements {@link UIResource}.
*/ */
public static Dimension scale( Dimension dimension ) { public static Dimension scale( Dimension dimension ) {
@@ -427,7 +427,7 @@ public class UIScale
* Scales the given insets with the user scale factor. * Scales the given insets with the user scale factor.
* <p> * <p>
* If user scale factor is 1, then the given insets is simply returned. * If user scale factor is 1, then the given insets is simply returned.
* Otherwise a new instance of {@link Insets} or {@link InsetsUIResource} * Otherwise, a new instance of {@link Insets} or {@link InsetsUIResource}
* is returned, depending on whether the passed dimension implements {@link UIResource}. * is returned, depending on whether the passed dimension implements {@link UIResource}.
*/ */
public static Insets scale( Insets insets ) { public static Insets scale( Insets insets ) {

View File

@@ -24,7 +24,7 @@
# #
# NOTE: Avoid copying the whole content of this file to own properties files. # NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone. # This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter. # Instead, copy and modify only those properties that you need to alter.
# #
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition, # Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,

View File

@@ -23,7 +23,7 @@
# #
# NOTE: Avoid copying the whole content of this file to own properties files. # NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone. # This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter. # Instead, copy and modify only those properties that you need to alter.
# #
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition, # Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
@@ -240,6 +240,7 @@ MenuBar.borderColor = $Separator.foreground
#---- PasswordField ---- #---- PasswordField ----
PasswordField.capsLockIconColor = #ffffff64 PasswordField.capsLockIconColor = #ffffff64
PasswordField.revealIconColor = @foreground
#---- Popup ---- #---- Popup ----
@@ -251,6 +252,7 @@ Popup.dropShadowOpacity = 0.25
#---- PopupMenu ---- #---- PopupMenu ----
PopupMenu.borderColor = tint(@background,17%) PopupMenu.borderColor = tint(@background,17%)
PopupMenu.hoverScrollArrowBackground = lighten(@background,5%)
#---- ProgressBar ---- #---- ProgressBar ----
@@ -289,7 +291,7 @@ Slider.trackValueColor = @accentSliderColor
Slider.trackColor = lighten(@background,15%) Slider.trackColor = lighten(@background,15%)
Slider.thumbColor = $Slider.trackValueColor Slider.thumbColor = $Slider.trackValueColor
Slider.tickColor = @disabledForeground Slider.tickColor = @disabledForeground
Slider.focusedColor = fade($Component.focusColor,70%,derived) Slider.focusedColor = fade(changeLightness($Component.focusColor,60%,derived),30%,derived)
Slider.hoverThumbColor = lighten($Slider.thumbColor,5%,derived) Slider.hoverThumbColor = lighten($Slider.thumbColor,5%,derived)
Slider.pressedThumbColor = lighten($Slider.thumbColor,8%,derived) Slider.pressedThumbColor = lighten($Slider.thumbColor,8%,derived)
Slider.disabledTrackColor = lighten(@background,10%) Slider.disabledTrackColor = lighten(@background,10%)
@@ -304,6 +306,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.underlineColor = @accentUnderlineColor TabbedPane.underlineColor = @accentUnderlineColor
TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,60%)
TabbedPane.disabledUnderlineColor = lighten(@background,23%) TabbedPane.disabledUnderlineColor = lighten(@background,23%)
TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse) TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,25%) TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,25%)
@@ -322,7 +325,7 @@ TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----
Table.gridColor = lighten($Table.background,5%) Table.gridColor = lighten($Table.background,8%)
#---- TableHeader ---- #---- TableHeader ----
@@ -334,8 +337,8 @@ TableHeader.bottomSeparatorColor = $TableHeader.separatorColor
#---- TitlePane ---- #---- TitlePane ----
TitlePane.embeddedForeground = darken($TitlePane.foreground,15%) TitlePane.embeddedForeground = darken($TitlePane.foreground,15%)
TitlePane.buttonHoverBackground = lighten($TitlePane.background,10%,derived) TitlePane.buttonHoverBackground = lighten($TitlePane.background,15%,derived)
TitlePane.buttonPressedBackground = lighten($TitlePane.background,20%,derived) TitlePane.buttonPressedBackground = lighten($TitlePane.background,10%,derived)
#---- ToggleButton ---- #---- ToggleButton ----
@@ -367,7 +370,6 @@ Tree.hash = lighten($Tree.background,5%)
focusable: false; \ focusable: false; \
toolbar.margin: 1,1,1,1; \ toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \ toolbar.spacingInsets: 1,1,1,1; \
background: $TextField.background; \ toolbar.hoverBackground: lighten($TextField.background,5%); \
toolbar.hoverBackground: lighten($TextField.background,4%,derived); \ toolbar.pressedBackground: lighten($TextField.background,10%); \
toolbar.pressedBackground: lighten($TextField.background,6%,derived); \ toolbar.selectedBackground: lighten($TextField.background,15%)
toolbar.selectedBackground: lighten($TextField.background,12%,derived)

View File

@@ -24,7 +24,7 @@
# #
# NOTE: Avoid copying the whole content of this file to own properties files. # NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone. # This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter. # Instead, copy and modify only those properties that you need to alter.
# #
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition, # Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,

View File

@@ -23,7 +23,7 @@
# #
# NOTE: Avoid copying the whole content of this file to own properties files. # NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone. # This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter. # Instead, copy and modify only those properties that you need to alter.
# #
#---- typography / fonts ---- #---- typography / fonts ----
@@ -332,6 +332,7 @@ FileView.fileIcon = com.formdev.flatlaf.icons.FlatFileViewFileIcon
FileView.computerIcon = com.formdev.flatlaf.icons.FlatFileViewComputerIcon FileView.computerIcon = com.formdev.flatlaf.icons.FlatFileViewComputerIcon
FileView.hardDriveIcon = com.formdev.flatlaf.icons.FlatFileViewHardDriveIcon FileView.hardDriveIcon = com.formdev.flatlaf.icons.FlatFileViewHardDriveIcon
FileView.floppyDriveIcon = com.formdev.flatlaf.icons.FlatFileViewFloppyDriveIcon FileView.floppyDriveIcon = com.formdev.flatlaf.icons.FlatFileViewFloppyDriveIcon
FileView.fullRowSelection = true
#---- FormattedTextField ---- #---- FormattedTextField ----
@@ -487,7 +488,6 @@ PasswordField.showCapsLock = true
PasswordField.showRevealButton = false PasswordField.showRevealButton = false
PasswordField.capsLockIcon = com.formdev.flatlaf.icons.FlatCapsLockIcon PasswordField.capsLockIcon = com.formdev.flatlaf.icons.FlatCapsLockIcon
PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
PasswordField.revealIconColor = lazy(Actions.Grey)
#---- Popup ---- #---- Popup ----
@@ -501,6 +501,7 @@ Popup.dropShadowInsets = -4,-4,4,4
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
PopupMenu.borderInsets = 4,1,4,1 PopupMenu.borderInsets = 4,1,4,1
PopupMenu.background = @menuBackground PopupMenu.background = @menuBackground
PopupMenu.scrollArrowColor = @buttonArrowColor
#---- PopupMenuSeparator ---- #---- PopupMenuSeparator ----
@@ -555,6 +556,7 @@ RootPane.honorDialogMinimumSizeOnResize = true
#---- ScrollBar ---- #---- ScrollBar ----
ScrollBar.width = 10 ScrollBar.width = 10
ScrollBar.minimumButtonSize = 12,12
ScrollBar.minimumThumbSize = 10,10 ScrollBar.minimumThumbSize = 10,10
ScrollBar.maximumThumbSize = 100000,100000 ScrollBar.maximumThumbSize = 100000,100000
ScrollBar.trackInsets = 0,0,0,0 ScrollBar.trackInsets = 0,0,0,0
@@ -589,13 +591,13 @@ ScrollPane.smoothScrolling = true
#---- SearchField ---- #---- SearchField ----
SearchField.searchIconColor = fadeout(Actions.GreyInline,10%,lazy) SearchField.searchIconColor = fade(Actions.GreyInline,90%,lazy)
SearchField.searchIconHoverColor = fadeout(Actions.GreyInline,30%,lazy) SearchField.searchIconHoverColor = fade(Actions.GreyInline,70%,lazy)
SearchField.searchIconPressedColor = fadeout(Actions.GreyInline,50%,lazy) SearchField.searchIconPressedColor = fade(Actions.GreyInline,50%,lazy)
SearchField.clearIconColor = fadeout(Actions.GreyInline,50%,lazy) SearchField.clearIconColor = fade(Actions.GreyInline,50%,lazy)
SearchField.clearIconHoverColor = $SearchField.clearIconColor SearchField.clearIconHoverColor = $SearchField.clearIconColor
SearchField.clearIconPressedColor = fadeout(Actions.GreyInline,20%,lazy) SearchField.clearIconPressedColor = fade(Actions.GreyInline,80%,lazy)
#---- Separator ---- #---- Separator ----
@@ -802,8 +804,8 @@ TitlePane.inactiveBackground = $TitlePane.background
TitlePane.foreground = @foreground TitlePane.foreground = @foreground
TitlePane.inactiveForeground = @disabledForeground TitlePane.inactiveForeground = @disabledForeground
TitlePane.closeHoverBackground = #e81123 TitlePane.closeHoverBackground = #c42b1c
TitlePane.closePressedBackground = fade($TitlePane.closeHoverBackground,60%) TitlePane.closePressedBackground = fade($TitlePane.closeHoverBackground,90%)
TitlePane.closeHoverForeground = #fff TitlePane.closeHoverForeground = #fff
TitlePane.closePressedForeground = #fff TitlePane.closePressedForeground = #fff
@@ -827,6 +829,7 @@ ToggleButton.tab.underlineHeight = 2
ToggleButton.tab.underlineColor = $TabbedPane.underlineColor ToggleButton.tab.underlineColor = $TabbedPane.underlineColor
ToggleButton.tab.disabledUnderlineColor = $TabbedPane.disabledUnderlineColor ToggleButton.tab.disabledUnderlineColor = $TabbedPane.disabledUnderlineColor
ToggleButton.tab.selectedBackground = $?TabbedPane.selectedBackground ToggleButton.tab.selectedBackground = $?TabbedPane.selectedBackground
ToggleButton.tab.selectedForeground = $?TabbedPane.selectedForeground
ToggleButton.tab.hoverBackground = $TabbedPane.hoverColor ToggleButton.tab.hoverBackground = $TabbedPane.hoverColor
ToggleButton.tab.focusBackground = $TabbedPane.focusColor ToggleButton.tab.focusBackground = $TabbedPane.focusColor
@@ -920,6 +923,5 @@ Tree.icon.openColor = @icon
focusable: false; \ focusable: false; \
toolbar.margin: 1,1,1,1; \ toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \ toolbar.spacingInsets: 1,1,1,1; \
background: $TextField.background; \ toolbar.hoverBackground: null; \
toolbar.hoverBackground: $TextField.background; \ toolbar.pressedBackground: null
toolbar.pressedBackground: $TextField.background

View File

@@ -23,7 +23,7 @@
# #
# NOTE: Avoid copying the whole content of this file to own properties files. # NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone. # This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter. # Instead, copy and modify only those properties that you need to alter.
# #
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition, # Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,
@@ -247,6 +247,7 @@ MenuBar.borderColor = $Separator.foreground
#---- PasswordField ---- #---- PasswordField ----
PasswordField.capsLockIconColor = #00000064 PasswordField.capsLockIconColor = #00000064
PasswordField.revealIconColor = tint(@foreground,40%)
#---- Popup ---- #---- Popup ----
@@ -258,6 +259,7 @@ Popup.dropShadowOpacity = 0.15
#---- PopupMenu ---- #---- PopupMenu ----
PopupMenu.borderColor = shade(@background,28%) PopupMenu.borderColor = shade(@background,28%)
PopupMenu.hoverScrollArrowBackground = darken(@background,5%)
#---- ProgressBar ---- #---- ProgressBar ----
@@ -296,7 +298,7 @@ Slider.trackValueColor = @accentSliderColor
Slider.trackColor = darken(@background,18%) Slider.trackColor = darken(@background,18%)
Slider.thumbColor = $Slider.trackValueColor Slider.thumbColor = $Slider.trackValueColor
Slider.tickColor = @disabledForeground Slider.tickColor = @disabledForeground
Slider.focusedColor = fade($Component.focusColor,50%,derived) Slider.focusedColor = fade(changeLightness($Component.focusColor,75%,derived),50%,derived)
Slider.hoverThumbColor = darken($Slider.thumbColor,5%,derived) Slider.hoverThumbColor = darken($Slider.thumbColor,5%,derived)
Slider.pressedThumbColor = darken($Slider.thumbColor,8%,derived) Slider.pressedThumbColor = darken($Slider.thumbColor,8%,derived)
Slider.disabledTrackColor = darken(@background,13%) Slider.disabledTrackColor = darken(@background,13%)
@@ -311,6 +313,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.underlineColor = @accentUnderlineColor TabbedPane.underlineColor = @accentUnderlineColor
TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,50%)
TabbedPane.disabledUnderlineColor = darken(@background,28%) TabbedPane.disabledUnderlineColor = darken(@background,28%)
TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived) TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived)
TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,10%) TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,10%)
@@ -329,7 +332,7 @@ TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----
Table.gridColor = darken($Table.background,5%) Table.gridColor = darken($Table.background,8%)
#---- TableHeader ---- #---- TableHeader ----
@@ -342,7 +345,7 @@ TableHeader.bottomSeparatorColor = $TableHeader.separatorColor
TitlePane.embeddedForeground = lighten($TitlePane.foreground,35%) TitlePane.embeddedForeground = lighten($TitlePane.foreground,35%)
TitlePane.buttonHoverBackground = darken($TitlePane.background,10%,derived) TitlePane.buttonHoverBackground = darken($TitlePane.background,10%,derived)
TitlePane.buttonPressedBackground = darken($TitlePane.background,20%,derived) TitlePane.buttonPressedBackground = darken($TitlePane.background,8%,derived)
#---- ToggleButton ---- #---- ToggleButton ----
@@ -374,7 +377,6 @@ Tree.hash = darken($Tree.background,10%)
focusable: false; \ focusable: false; \
toolbar.margin: 1,1,1,1; \ toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \ toolbar.spacingInsets: 1,1,1,1; \
background: $TextField.background; \ toolbar.hoverBackground: darken($TextField.background,4%); \
toolbar.hoverBackground: darken($TextField.background,4%,derived); \ toolbar.pressedBackground: darken($TextField.background,8%); \
toolbar.pressedBackground: darken($TextField.background,8%,derived); \ toolbar.selectedBackground: darken($TextField.background,12%)
toolbar.selectedBackground: darken($TextField.background,12%,derived)

View File

@@ -67,6 +67,11 @@ Button.default.hoverBorderColor = null
[dark]CheckBoxMenuItem.icon.checkmarkColor=#fff9 [dark]CheckBoxMenuItem.icon.checkmarkColor=#fff9
#---- Component ----
Component.accentColor = lazy(ProgressBar.foreground)
#---- HelpButton ---- #---- HelpButton ----
HelpButton.hoverBorderColor = null HelpButton.hoverBorderColor = null
@@ -77,6 +82,13 @@ HelpButton.hoverBorderColor = null
Slider.focusedColor = fade($Component.focusColor,40%,derived) Slider.focusedColor = fade($Component.focusColor,40%,derived)
#---- TabbedPane ----
# colors from JBUI.CurrentTheme.DefaultTabs.inactiveUnderlineColor()
[light]TabbedPane.inactiveUnderlineColor = #9ca7b8
[dark]TabbedPane.inactiveUnderlineColor = #747a80
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.startBackground = $ToggleButton.background ToggleButton.startBackground = $ToggleButton.background
@@ -121,6 +133,7 @@ ToggleButton.endBackground = $ToggleButton.background
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionBackground = #ddd [Arc_Theme_Dark_-_Orange]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionForeground = #fff [Arc_Theme_Dark_-_Orange]ProgressBar.selectionForeground = #fff
[Cobalt_2]Component.accentColor = lazy(Component.focusColor)
[Cobalt_2]CheckBox.icon.background = #002946 [Cobalt_2]CheckBox.icon.background = #002946
[Cobalt_2]CheckBox.icon.checkmarkColor = #002946 [Cobalt_2]CheckBox.icon.checkmarkColor = #002946
[Cobalt_2]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [Cobalt_2]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
@@ -129,37 +142,50 @@ ToggleButton.endBackground = $ToggleButton.background
[Cyan_light]MenuItem.checkBackground = @ijMenuCheckBackgroundL20 [Cyan_light]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[Cyan_light]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20 [Cyan_light]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[Dark_Flat_Theme]Component.accentColor = lazy(List.selectionBackground)
[Dark_Flat_Theme]TableHeader.background = #3B3B3B [Dark_Flat_Theme]TableHeader.background = #3B3B3B
[Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived) [Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived)
[Dracula---Zihan_Ma]Component.accentColor = lazy(Component.focusColor)
[Dracula---Zihan_Ma]ProgressBar.selectionBackground = #fff [Dracula---Zihan_Ma]ProgressBar.selectionBackground = #fff
[Dracula---Zihan_Ma]ProgressBar.selectionForeground = #fff [Dracula---Zihan_Ma]ProgressBar.selectionForeground = #fff
[Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10 [Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Hard]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Hard]ToggleButton.selectedBackground = $ToggleButton.selectedBackground [Gruvbox_Dark_Hard]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground [Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Medium]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Medium]ToggleButton.selectedBackground = $ToggleButton.selectedBackground [Gruvbox_Dark_Medium]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground [Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Soft]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Soft]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [Gruvbox_Dark_Soft]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10 [Gruvbox_Dark_Soft]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]ToggleButton.selectedBackground = $ToggleButton.selectedBackground [Gruvbox_Dark_Soft]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Soft]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground [Gruvbox_Dark_Soft]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Hiberbee_Dark]TabbedPane.focusColor = #5A5A5A [Hiberbee_Dark]TabbedPane.focusColor = #5A5A5A
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground [Hiberbee_Dark]TabbedPane.selectedBackground = #434241
[Hiberbee_Dark]TabbedPane.selectedForeground = #70D7FF
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground [Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground [Hiberbee_Dark]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[High_contrast]Component.accentColor = lazy(Component.focusColor)
[High_contrast]ToggleButton.selectedBackground = #fff [High_contrast]ToggleButton.selectedBackground = #fff
[High_contrast]ToggleButton.selectedForeground = #000 [High_contrast]ToggleButton.selectedForeground = #000
[High_contrast]ToggleButton.disabledSelectedBackground = #444 [High_contrast]ToggleButton.disabledSelectedBackground = #444
[High_contrast]ToggleButton.toolbar.selectedBackground = #fff [High_contrast]ToggleButton.toolbar.selectedBackground = #fff
[High_contrast][style]Button.inTextField = \
toolbar.hoverBackground: #444; \
toolbar.pressedBackground: #666; \
toolbar.selectedBackground: #fff
[High_contrast][style]ToggleButton.inTextField = $[High_contrast][style]Button.inTextField
[Light_Flat]Component.accentColor = lazy(TabbedPane.underlineColor)
[Light_Flat]TableHeader.background = #E5E5E9 [Light_Flat]TableHeader.background = #E5E5E9
[Monocai]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [Monocai]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
@@ -175,6 +201,8 @@ ToggleButton.endBackground = $ToggleButton.background
[Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground [Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground [Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monokai_Pro]TitledBorder.titleColor = @foreground
[Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10 [Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
@@ -182,8 +210,12 @@ ToggleButton.endBackground = $ToggleButton.background
[One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10 [One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[One_Dark]Slider.focusedColor = fade(#568af2,40%) [One_Dark]Slider.focusedColor = fade(#568af2,40%)
[Solarized_Dark---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[Solarized_Dark---4lex4]Slider.focusedColor = fade($Component.focusColor,80%,derived) [Solarized_Dark---4lex4]Slider.focusedColor = fade($Component.focusColor,80%,derived)
[Solarized_Light---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[vuesion-theme]Component.accentColor = lazy(Button.default.endBackground)
[vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10 [vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10 [vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]Slider.trackValueColor = #ececee [vuesion-theme]Slider.trackValueColor = #ececee

View File

@@ -0,0 +1,65 @@
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Buscar &en:
FileChooser.saveInLabelText = Guardar en:
FileChooser.fileNameLabel.textAndMnemonic = &Nombre de fichero:
FileChooser.folderNameLabel.textAndMnemonic = &Nombre de carpeta:
FileChooser.filesOfTypeLabel.textAndMnemonic = Ficheros de &Tipo:
# toolbar
FileChooser.upFolderToolTipText = Subir un nivel
FileChooser.upFolderAccessibleName = Subir
FileChooser.homeFolderToolTipText = Inicio
FileChooser.homeFolderAccessibleName = Inicio
FileChooser.newFolderToolTipText = Crear nueva carpeta
FileChooser.newFolderAccessibleName = Nueva carpeta
FileChooser.listViewButtonToolTipText = Lista
FileChooser.listViewButtonAccessibleName = Lista
FileChooser.detailsViewButtonToolTipText = Detalles
FileChooser.detailsViewButtonAccessibleName = Detalles
# details table header
FileChooser.fileNameHeaderText = Nombre
FileChooser.fileSizeHeaderText = Tama\u00F1o
FileChooser.fileTypeHeaderText = Tipo
FileChooser.fileDateHeaderText = Modificado
FileChooser.fileAttrHeaderText = Atributos
# popup menu
FileChooser.viewMenuLabelText = Ver
FileChooser.refreshActionLabelText = Refrescar
FileChooser.newFolderActionLabelText = Nueva carpeta
FileChooser.listViewActionLabelText = Lista
FileChooser.detailsViewActionLabelText = Detalles
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Contraer Panel Izquierdo
SplitPaneDivider.collapseRightToolTipText = Contraer panel Derecho
SplitPaneDivider.collapseTopToolTipText = Contraer panel Superior
SplitPaneDivider.collapseBottomToolTipText = Contraer Panel Inferior
SplitPaneDivider.expandLeftToolTipText = Expandir Panel Izquierdo
SplitPaneDivider.expandRightToolTipText = Expandir Panel Derecho
SplitPaneDivider.expandTopToolTipText = Expandir Panel Superior
SplitPaneDivider.expandBottomToolTipText = Expandir Panel Inferior
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText = Mostrar Pesta\u00F1as Ocultas

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2022 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import com.formdev.flatlaf.ui.TestUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
class TestFlatIconPaintingNullComponent
{
static Graphics graphics;
@BeforeAll
static void setup() {
TestUtils.setup( false );
graphics = new BufferedImage( 32, 32, BufferedImage.TYPE_INT_ARGB ).getGraphics();
graphics.setColor( Color.white );
}
@AfterAll
static void cleanup() {
TestUtils.cleanup();
graphics = null;
}
@Test
void flatHelpButtonIcon() {
paintWithoutException( new FlatHelpButtonIcon() );
}
@Test
void flatMenuArrowIcon() {
paintWithoutException( new FlatMenuArrowIcon() );
}
@Test
void flatSearchIcon() {
paintWithoutException( new FlatSearchIcon() );
}
private void paintWithoutException( Icon icon ) {
graphics.clearRect( 0, 0, 32, 32 );
assertDoesNotThrow( () -> icon.paintIcon( null, graphics, 0, 0 ) );
}
}

View File

@@ -40,7 +40,7 @@ public class TestFlatStyleClasses
@BeforeAll @BeforeAll
static void setup() { static void setup() {
System.setProperty( FlatSystemProperties.UI_SCALE_ENABLED, "false" ); System.setProperty( FlatSystemProperties.UI_SCALE, "1x" );
TestUtils.setup( false ); TestUtils.setup( false );
UIManager.put( "[style]Button.primary", BUTTON_PRIMARY ); UIManager.put( "[style]Button.primary", BUTTON_PRIMARY );
@@ -100,7 +100,7 @@ public class TestFlatStyleClasses
@AfterAll @AfterAll
static void cleanup() { static void cleanup() {
TestUtils.cleanup(); TestUtils.cleanup();
System.clearProperty( FlatSystemProperties.UI_SCALE_ENABLED ); System.clearProperty( FlatSystemProperties.UI_SCALE );
} }
@Test @Test

View File

@@ -96,19 +96,26 @@ public class TestFlatStyleableInfo
"minimumWidth", int.class, "minimumWidth", int.class,
"focusedBackground", Color.class, "focusedBackground", Color.class,
"focusedForeground", Color.class,
"hoverBackground", Color.class, "hoverBackground", Color.class,
"hoverForeground", Color.class,
"pressedBackground", Color.class, "pressedBackground", Color.class,
"pressedForeground", Color.class,
"selectedBackground", Color.class, "selectedBackground", Color.class,
"selectedForeground", Color.class, "selectedForeground", Color.class,
"disabledBackground", Color.class, "disabledBackground", Color.class,
"disabledText", Color.class, "disabledText", Color.class,
"disabledSelectedBackground", Color.class, "disabledSelectedBackground", Color.class,
"disabledSelectedForeground", Color.class,
"default.background", Color.class, "default.background", Color.class,
"default.foreground", Color.class, "default.foreground", Color.class,
"default.focusedBackground", Color.class, "default.focusedBackground", Color.class,
"default.focusedForeground", Color.class,
"default.hoverBackground", Color.class, "default.hoverBackground", Color.class,
"default.hoverForeground", Color.class,
"default.pressedBackground", Color.class, "default.pressedBackground", Color.class,
"default.pressedForeground", Color.class,
"default.boldText", boolean.class, "default.boldText", boolean.class,
"paintShadow", boolean.class, "paintShadow", boolean.class,
@@ -118,8 +125,13 @@ public class TestFlatStyleableInfo
"toolbar.spacingInsets", Insets.class, "toolbar.spacingInsets", Insets.class,
"toolbar.hoverBackground", Color.class, "toolbar.hoverBackground", Color.class,
"toolbar.hoverForeground", Color.class,
"toolbar.pressedBackground", Color.class, "toolbar.pressedBackground", Color.class,
"toolbar.pressedForeground", Color.class,
"toolbar.selectedBackground", Color.class, "toolbar.selectedBackground", Color.class,
"toolbar.selectedForeground", Color.class,
"toolbar.disabledSelectedBackground", Color.class,
"toolbar.disabledSelectedForeground", Color.class,
"buttonType", String.class, "buttonType", String.class,
"squareSize", boolean.class, "squareSize", boolean.class,
@@ -422,6 +434,10 @@ public class TestFlatStyleableInfo
FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI(); FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
Map<String, Class<?>> expected = expectedMap( Map<String, Class<?>> expected = expectedMap(
"arrowType", String.class,
"scrollArrowColor", Color.class,
"hoverScrollArrowBackground", Color.class,
"borderInsets", Insets.class, "borderInsets", Insets.class,
"borderColor", Color.class "borderColor", Color.class
); );
@@ -539,6 +555,7 @@ public class TestFlatStyleableInfo
"maximumThumbSize", Dimension.class, "maximumThumbSize", Dimension.class,
"allowsAbsolutePositioning", boolean.class, "allowsAbsolutePositioning", boolean.class,
"minimumButtonSize", Dimension.class,
"trackInsets", Insets.class, "trackInsets", Insets.class,
"thumbInsets", Insets.class, "thumbInsets", Insets.class,
"trackArc", int.class, "trackArc", int.class,
@@ -688,6 +705,7 @@ public class TestFlatStyleableInfo
"selectedBackground", Color.class, "selectedBackground", Color.class,
"selectedForeground", Color.class, "selectedForeground", Color.class,
"underlineColor", Color.class, "underlineColor", Color.class,
"inactiveUnderlineColor", Color.class,
"disabledUnderlineColor", Color.class, "disabledUnderlineColor", Color.class,
"hoverColor", Color.class, "hoverColor", Color.class,
"focusColor", Color.class, "focusColor", Color.class,
@@ -857,8 +875,11 @@ public class TestFlatStyleableInfo
"tab.underlineColor", Color.class, "tab.underlineColor", Color.class,
"tab.disabledUnderlineColor", Color.class, "tab.disabledUnderlineColor", Color.class,
"tab.selectedBackground", Color.class, "tab.selectedBackground", Color.class,
"tab.selectedForeground", Color.class,
"tab.hoverBackground", Color.class, "tab.hoverBackground", Color.class,
"tab.focusBackground", Color.class "tab.hoverForeground", Color.class,
"tab.focusBackground", Color.class,
"tab.focusForeground", Color.class
); );
// FlatToggleButtonUI extends FlatButtonUI // FlatToggleButtonUI extends FlatButtonUI

View File

@@ -221,19 +221,26 @@ public class TestFlatStyling
ui.applyStyle( b, "minimumWidth: 100" ); ui.applyStyle( b, "minimumWidth: 100" );
ui.applyStyle( b, "focusedBackground: #fff" ); ui.applyStyle( b, "focusedBackground: #fff" );
ui.applyStyle( b, "focusedForeground: #fff" );
ui.applyStyle( b, "hoverBackground: #fff" ); ui.applyStyle( b, "hoverBackground: #fff" );
ui.applyStyle( b, "hoverForeground: #fff" );
ui.applyStyle( b, "pressedBackground: #fff" ); ui.applyStyle( b, "pressedBackground: #fff" );
ui.applyStyle( b, "pressedForeground: #fff" );
ui.applyStyle( b, "selectedBackground: #fff" ); ui.applyStyle( b, "selectedBackground: #fff" );
ui.applyStyle( b, "selectedForeground: #fff" ); ui.applyStyle( b, "selectedForeground: #fff" );
ui.applyStyle( b, "disabledBackground: #fff" ); ui.applyStyle( b, "disabledBackground: #fff" );
ui.applyStyle( b, "disabledText: #fff" ); ui.applyStyle( b, "disabledText: #fff" );
ui.applyStyle( b, "disabledSelectedBackground: #fff" ); ui.applyStyle( b, "disabledSelectedBackground: #fff" );
ui.applyStyle( b, "disabledSelectedForeground: #fff" );
ui.applyStyle( b, "default.background: #fff" ); ui.applyStyle( b, "default.background: #fff" );
ui.applyStyle( b, "default.foreground: #fff" ); ui.applyStyle( b, "default.foreground: #fff" );
ui.applyStyle( b, "default.focusedBackground: #fff" ); ui.applyStyle( b, "default.focusedBackground: #fff" );
ui.applyStyle( b, "default.focusedForeground: #fff" );
ui.applyStyle( b, "default.hoverBackground: #fff" ); ui.applyStyle( b, "default.hoverBackground: #fff" );
ui.applyStyle( b, "default.hoverForeground: #fff" );
ui.applyStyle( b, "default.pressedBackground: #fff" ); ui.applyStyle( b, "default.pressedBackground: #fff" );
ui.applyStyle( b, "default.pressedForeground: #fff" );
ui.applyStyle( b, "default.boldText: true" ); ui.applyStyle( b, "default.boldText: true" );
ui.applyStyle( b, "paintShadow: true" ); ui.applyStyle( b, "paintShadow: true" );
@@ -243,8 +250,13 @@ public class TestFlatStyling
ui.applyStyle( b, "toolbar.spacingInsets: 1,2,3,4" ); ui.applyStyle( b, "toolbar.spacingInsets: 1,2,3,4" );
ui.applyStyle( b, "toolbar.hoverBackground: #fff" ); ui.applyStyle( b, "toolbar.hoverBackground: #fff" );
ui.applyStyle( b, "toolbar.hoverForeground: #fff" );
ui.applyStyle( b, "toolbar.pressedBackground: #fff" ); ui.applyStyle( b, "toolbar.pressedBackground: #fff" );
ui.applyStyle( b, "toolbar.pressedForeground: #fff" );
ui.applyStyle( b, "toolbar.selectedBackground: #fff" ); ui.applyStyle( b, "toolbar.selectedBackground: #fff" );
ui.applyStyle( b, "toolbar.selectedForeground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedBackground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedForeground: #fff" );
ui.applyStyle( b, "buttonType: help" ); ui.applyStyle( b, "buttonType: help" );
ui.applyStyle( b, "squareSize: true" ); ui.applyStyle( b, "squareSize: true" );
@@ -565,6 +577,10 @@ public class TestFlatStyling
JPopupMenu c = new JPopupMenu(); JPopupMenu c = new JPopupMenu();
FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI(); FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "scrollArrowColor: #fff" );
ui.applyStyle( "hoverScrollArrowBackground: #fff" );
ui.applyStyle( "borderInsets: 1,2,3,4" ); ui.applyStyle( "borderInsets: 1,2,3,4" );
ui.applyStyle( "borderColor: #fff" ); ui.applyStyle( "borderColor: #fff" );
@@ -684,6 +700,7 @@ public class TestFlatStyling
ui.applyStyle( "maximumThumbSize: 1,2" ); ui.applyStyle( "maximumThumbSize: 1,2" );
ui.applyStyle( "allowsAbsolutePositioning: true" ); ui.applyStyle( "allowsAbsolutePositioning: true" );
ui.applyStyle( "minimumButtonSize: 1,2" );
ui.applyStyle( "trackInsets: 1,2,3,4" ); ui.applyStyle( "trackInsets: 1,2,3,4" );
ui.applyStyle( "thumbInsets: 1,2,3,4" ); ui.applyStyle( "thumbInsets: 1,2,3,4" );
ui.applyStyle( "trackArc: 5" ); ui.applyStyle( "trackArc: 5" );
@@ -857,6 +874,7 @@ public class TestFlatStyling
ui.applyStyle( "selectedBackground: #fff" ); ui.applyStyle( "selectedBackground: #fff" );
ui.applyStyle( "selectedForeground: #fff" ); ui.applyStyle( "selectedForeground: #fff" );
ui.applyStyle( "underlineColor: #fff" ); ui.applyStyle( "underlineColor: #fff" );
ui.applyStyle( "inactiveUnderlineColor: #fff" );
ui.applyStyle( "disabledUnderlineColor: #fff" ); ui.applyStyle( "disabledUnderlineColor: #fff" );
ui.applyStyle( "hoverColor: #fff" ); ui.applyStyle( "hoverColor: #fff" );
ui.applyStyle( "focusColor: #fff" ); ui.applyStyle( "focusColor: #fff" );
@@ -1067,8 +1085,11 @@ public class TestFlatStyling
ui.applyStyle( b, "tab.underlineColor: #fff" ); ui.applyStyle( b, "tab.underlineColor: #fff" );
ui.applyStyle( b, "tab.disabledUnderlineColor: #fff" ); ui.applyStyle( b, "tab.disabledUnderlineColor: #fff" );
ui.applyStyle( b, "tab.selectedBackground: #fff" ); ui.applyStyle( b, "tab.selectedBackground: #fff" );
ui.applyStyle( b, "tab.selectedForeground: #fff" );
ui.applyStyle( b, "tab.hoverBackground: #fff" ); ui.applyStyle( b, "tab.hoverBackground: #fff" );
ui.applyStyle( b, "tab.hoverForeground: #fff" );
ui.applyStyle( b, "tab.focusBackground: #fff" ); ui.applyStyle( b, "tab.focusBackground: #fff" );
ui.applyStyle( b, "tab.focusForeground: #fff" );
} }
@Test @Test

View File

@@ -30,7 +30,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
*/ */
public class TestUtils public class TestUtils
{ {
public static final float[] FACTORS = new float[] { 1f }; //, 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 2.75f, 3f, 3.25f, 3.5f, 3.75f, 4f, 5f, 6f }; public static final float[] FACTORS = new float[] { 1f, 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 2.75f, 3f, 3.25f, 3.5f, 3.75f, 4f, 5f, 6f };
public static void setup( boolean withFocus ) { public static void setup( boolean withFocus ) {
System.setProperty( FlatSystemProperties.UI_SCALE, "1x" ); System.setProperty( FlatSystemProperties.UI_SCALE, "1x" );

View File

@@ -43,6 +43,17 @@ class BasicComponentsPanel
// UIManager.put( "PasswordField.showRevealButton", true ); // UIManager.put( "PasswordField.showRevealButton", true );
passwordField1.putClientProperty( FlatClientProperties.STYLE, "showRevealButton: true" ); passwordField1.putClientProperty( FlatClientProperties.STYLE, "showRevealButton: true" );
// add leading/trailing icons to text fields
leadingIconTextField.putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, "Search" );
leadingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON,
new FlatSearchIcon() );
trailingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/DataTables.svg" ) );
iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/user.svg" ) );
iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/bookmarkGroup.svg" ) );
// search history button // search history button
JButton searchHistoryButton = new JButton( new FlatSearchWithHistoryIcon( true ) ); JButton searchHistoryButton = new JButton( new FlatSearchWithHistoryIcon( true ) );
searchHistoryButton.setToolTipText( "Search History" ); searchHistoryButton.setToolTipText( "Search History" );
@@ -177,9 +188,9 @@ class BasicComponentsPanel
JComboBox<String> warningHintsComboBox = new JComboBox<>(); JComboBox<String> warningHintsComboBox = new JComboBox<>();
JSpinner warningHintsSpinner = new JSpinner(); JSpinner warningHintsSpinner = new JSpinner();
JLabel iconsLabel = new JLabel(); JLabel iconsLabel = new JLabel();
JTextField leadingIconTextField = new JTextField(); leadingIconTextField = new JTextField();
JTextField trailingIconTextField = new JTextField(); trailingIconTextField = new JTextField();
JTextField iconsTextField = new JTextField(); iconsTextField = new JTextField();
JLabel compsLabel = new JLabel(); JLabel compsLabel = new JLabel();
compsTextField = new JTextField(); compsTextField = new JTextField();
clearTextField = new JTextField(); clearTextField = new JTextField();
@@ -851,17 +862,6 @@ class BasicComponentsPanel
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() ); pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
// add leading/trailing icons to text fields
leadingIconTextField.putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, "Search" );
leadingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON,
new FlatSearchIcon() );
trailingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/DataTables.svg" ) );
iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/user.svg" ) );
iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON,
new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/bookmarkGroup.svg" ) );
if( FlatLafDemo.screenshotsMode ) { if( FlatLafDemo.screenshotsMode ) {
// hide some components // hide some components
Component[] hiddenComponents = { Component[] hiddenComponents = {
@@ -919,6 +919,9 @@ class BasicComponentsPanel
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPasswordField passwordField1; private JPasswordField passwordField1;
private JTextField leadingIconTextField;
private JTextField trailingIconTextField;
private JTextField iconsTextField;
private JTextField compsTextField; private JTextField compsTextField;
private JTextField clearTextField; private JTextField clearTextField;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -659,18 +659,27 @@ new FormModel {
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "leadingIconTextField" name: "leadingIconTextField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 14,growx" "value": "cell 1 14,growx"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "trailingIconTextField" name: "trailingIconTextField"
"text": "text" "text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 14,growx" "value": "cell 2 14,growx"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "iconsTextField" name: "iconsTextField"
"text": "text" "text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 14,growx" "value": "cell 3 14,growx"
} ) } )

View File

@@ -62,10 +62,10 @@ class ControlBar
// initialize look and feels combo box // initialize look and feels combo box
DefaultComboBoxModel<LookAndFeelInfo> lafModel = new DefaultComboBoxModel<>(); DefaultComboBoxModel<LookAndFeelInfo> lafModel = new DefaultComboBoxModel<>();
lafModel.addElement( new LookAndFeelInfo( "Flat Light (F1)", FlatLightLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Light (F1)", FlatLightLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat Dark (F2)", FlatDarkLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat Darcula (F4)", FlatDarculaLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) );
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels(); UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) { for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {

View File

@@ -38,8 +38,8 @@ import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.demo.HintManager.Hint; import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*; import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*; import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector; import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.components.FlatButton; import com.formdev.flatlaf.extras.components.FlatButton;
@@ -82,13 +82,33 @@ class DemoFrame
if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() ) if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() )
tabbedPane.setSelectedIndex( tabIndex ); tabbedPane.setSelectedIndex( tabIndex );
// hide some menu items on macOS // macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) { if( SystemInfo.isMacOS ) {
// hide menu items that are in macOS application menu
exitMenuItem.setVisible( false ); exitMenuItem.setVisible( false );
aboutMenuItem.setVisible( false ); aboutMenuItem.setVisible( false );
// do not use HTML text on macOS // do not use HTML text in menu items because this is not supported in macOS screen menu
htmlMenuItem.setText( "some text" ); htmlMenuItem.setText( "some text" );
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
} }
// integrate into macOS screen menu // integrate into macOS screen menu
@@ -433,9 +453,9 @@ class DemoFrame
lafClass == FlatIntelliJLaf.class || lafClass == FlatIntelliJLaf.class ||
lafClass == FlatDarculaLaf.class; lafClass == FlatDarculaLaf.class;
accentColorLabel.setEnabled( isAccentColorSupported ); accentColorLabel.setVisible( isAccentColorSupported );
for( int i = 0; i < accentColorButtons.length; i++ ) for( int i = 0; i < accentColorButtons.length; i++ )
accentColorButtons[i].setEnabled( isAccentColorSupported ); accentColorButtons[i].setVisible( isAccentColorSupported );
} }
private void initComponents() { private void initComponents() {
@@ -464,6 +484,7 @@ class DemoFrame
JMenuItem projectViewMenuItem = new JMenuItem(); JMenuItem projectViewMenuItem = new JMenuItem();
JMenuItem structureViewMenuItem = new JMenuItem(); JMenuItem structureViewMenuItem = new JMenuItem();
JMenuItem propertiesViewMenuItem = new JMenuItem(); JMenuItem propertiesViewMenuItem = new JMenuItem();
scrollingPopupMenu = new JMenu();
JMenuItem menuItem2 = new JMenuItem(); JMenuItem menuItem2 = new JMenuItem();
htmlMenuItem = new JMenuItem(); htmlMenuItem = new JMenuItem();
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem(); JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
@@ -668,6 +689,12 @@ class DemoFrame
} }
viewMenu.add(menu1); viewMenu.add(menu1);
//======== scrollingPopupMenu ========
{
scrollingPopupMenu.setText("Scrolling Popup Menu");
}
viewMenu.add(scrollingPopupMenu);
//---- menuItem2 ---- //---- menuItem2 ----
menuItem2.setText("Disabled Item"); menuItem2.setText("Disabled Item");
menuItem2.setEnabled(false); menuItem2.setEnabled(false);
@@ -889,7 +916,16 @@ class DemoFrame
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() ); pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
if( FlatLaf.supportsNativeWindowDecorations() ) { scrollingPopupMenu.add( "Large menus are scrollable" );
scrollingPopupMenu.add( "Use mouse wheel to scroll" );
scrollingPopupMenu.add( "Or use up/down arrows at top/bottom" );
for( int i = 1; i <= 100; i++ )
scrollingPopupMenu.add( "Item " + i );
if( FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated()) ) {
if( SystemInfo.isLinux )
unsupported( windowDecorationsCheckBoxMenuItem );
else
windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() ); windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() );
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) ); menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) ); unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
@@ -931,6 +967,7 @@ class DemoFrame
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenuItem exitMenuItem; private JMenuItem exitMenuItem;
private JMenu scrollingPopupMenu;
private JMenuItem htmlMenuItem; private JMenuItem htmlMenuItem;
private JMenu fontMenu; private JMenu fontMenu;
private JMenu optionsMenu; private JMenu optionsMenu;

View File

@@ -282,6 +282,13 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) )
} ) } )
} ) } )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "scrollingPopupMenu"
"text": "Scrolling Popup Menu"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
} )
add( new FormComponent( "javax.swing.JMenuItem" ) { add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem2" name: "menuItem2"
"text": "Disabled Item" "text": "Disabled Item"

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf.demo; package com.formdev.flatlaf.demo;
import java.awt.Dimension; import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.FlatInspector; import com.formdev.flatlaf.extras.FlatInspector;
@@ -34,7 +36,7 @@ public class FlatLafDemo
static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) ); static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) );
public static void main( String[] args ) { public static void main( String[] args ) {
// macOS // macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) { if( SystemInfo.isMacOS ) {
// enable screen menu bar // enable screen menu bar
// (moves menu bar from JFrame window to top of screen) // (moves menu bar from JFrame window to top of screen)
@@ -49,9 +51,17 @@ public class FlatLafDemo
// - "system": use current macOS appearance (light or dark) // - "system": use current macOS appearance (light or dark)
// - "NSAppearanceNameAqua": use light appearance // - "NSAppearanceNameAqua": use light appearance
// - "NSAppearanceNameDarkAqua": use dark appearance // - "NSAppearanceNameDarkAqua": use dark appearance
// (needs to be set on main thread; setting it on AWT thread does not work)
System.setProperty( "apple.awt.application.appearance", "system" ); System.setProperty( "apple.awt.application.appearance", "system" );
} }
// Linux
if( SystemInfo.isLinux ) {
// enable custom window decorations
JFrame.setDefaultLookAndFeelDecorated( true );
JDialog.setDefaultLookAndFeelDecorated( true );
}
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null ) if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
System.setProperty( "flatlaf.uiScale", "2x" ); System.setProperty( "flatlaf.uiScale", "2x" );

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