Compare commits

...

116 Commits

Author SHA1 Message Date
Karl Tauber
5729c20386 release 1.1.2 2021-04-07 11:53:18 +02:00
Karl Tauber
a4d70d8095 FlatTextComponentsTest: fixed compiler warnings (for previous commit) 2021-04-07 10:34:11 +02:00
Karl Tauber
8fcce349d5 ComboBox and Spinner: fixed too wide arrow button if component is higher than preferred (issue #302) 2021-04-07 01:39:29 +02:00
Karl Tauber
5a94676a3a Merge pull request #269 from SchiopuMatei/main
Added option for downscaling
2021-04-07 00:24:18 +02:00
Karl Tauber
f32d72ee62 UIScale:
- allow scale factors less than 100% for system property `flatlaf.uiScale`
- no longer round scale factor of system property `flatlaf.uiScale` to 1/4
- renamed system property `flatlaf.uiDowncale.enabled` to `flatlaf.uiScale.allowScaleDown`
- round smaller scale factors to 1/10
- absolute minimum user scale factor is now 0.1
2021-04-07 00:21:15 +02:00
Karl Tauber
e35fc8620c JIDE: fixed null font in other Lafs if (wrongly) using LookAndFeelFactory.addUIDefaultsInitializer() or LookAndFeelFactory.addUIDefaultsCustomizer() (issue #288) 2021-04-06 18:35:48 +02:00
Karl Tauber
277c288952 IntelliJ Themes: fixed system colors 2021-04-06 11:29:55 +02:00
Karl Tauber
240b08e55c IntelliJ Themes: fixed window title bar background if unified background is enabled 2021-04-06 11:04:52 +02:00
Karl Tauber
fe7f345661 Native window decorations: support changing title bar background and foreground colors per window (via client property) also if unified window title bar is enabled 2021-04-06 10:46:28 +02:00
Karl Tauber
c8db01c958 SplitPane: fixed JSplitPane.setContinuousLayout(false) (issue #301) 2021-04-05 14:24:49 +02:00
Karl Tauber
f456185f7d Native window decorations: support changing title bar background and foreground colors per window (via client property) 2021-04-05 14:19:41 +02:00
Karl Tauber
801b555835 Window decorations: fixed random window title bar background for unified backgrounds in cases were background is not filled by custom window/rootpane components (issue #254) 2021-04-04 11:47:15 +02:00
Karl Tauber
eee177e64b Window decorations: enabling/disabling menu bar embedding via system and client properties now works the same way as for window decorations
(previously it was only possible to disable menu bar embedding)
2021-04-03 16:19:11 +02:00
Karl Tauber
63639f8e96 Native window decorations: cleaned-up/simplified JetBrains Runtime custom window decorations "enabled" checking:
- `FlatSystemProperties.USE_WINDOW_DECORATIONS` is now also used for JBR custom window decorations
- `FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS` is now only used to disable JBR custom window decorations; then FlatLaf native window decorations are used
- JBR custom window decorations are now disabled when running in JetBrains Projector, Webswing or WinPE
2021-04-03 13:32:46 +02:00
Karl Tauber
de1b0b1bb6 MenuBar: do not use TitlePane.unifiedBackground if window decorations are disabled for the window 2021-04-03 11:51:45 +02:00
Karl Tauber
bbdd7fc2b4 Demo:
- keep "Options > Window decorations" selected for JetBrains Runtime
- disable "Options > Use underline menu selection" on macOS
- added font size `11`
2021-04-03 11:49:57 +02:00
Karl Tauber
6addb5c4b4 Native window decorations:
- API to check whether current platform supports window decorations `FlatLaf.supportsNativeWindowDecorations()`
- API to toggle window decorations of all windows `FlatLaf.setUseNativeWindowDecorations(boolean)`
- `FlatClientProperties.USE_WINDOW_DECORATIONS` can now used to toggle window decorations for single window
- cleaned-up/fixed/simplified window decorations "enabled" checking:
  1. if `FlatSystemProperties.USE_WINDOW_DECORATIONS` is set, its value is used
  2. if `FlatClientProperties.USE_WINDOW_DECORATIONS` is set, its value is used
  3. use value of UI default `TitlePane.useWindowDecorations`
2021-04-03 11:13:57 +02:00
Karl Tauber
b47e0c88d6 Merge pull request #298 from Bios-Marcel/fix-demo-menu-item-states
Fix selected states for native window border related menu items
2021-04-02 16:13:36 +02:00
Marcel Schramm
d06993d940 Add comment explaining why the use of JBR results in not having custom decorations 2021-04-01 22:14:39 +02:00
Karl Tauber
d31f167b9e TabbedPane: fixed NPE when creating/modifying in another thread (issue #299) 2021-04-01 12:35:50 +02:00
Karl Tauber
f12ee6c167 added dummy class to empty opend module packages 2021-04-01 09:40:22 +02:00
Karl Tauber
983b341f33 Native window decorations: fixed loading of native library when using JPMS for application (issue #289) 2021-04-01 01:07:35 +02:00
Karl Tauber
f3e6642f05 Button and ToggleButton: simplified/unified code of FlatButtonUI.getBackground() (issue #292) 2021-03-31 23:14:45 +02:00
Karl Tauber
0a63990d21 Button and ToggleButton: do not paint background of disabled (and unselected) toolBar buttons (issue #292; regression since fixing #112) 2021-03-31 22:28:43 +02:00
Karl Tauber
6909bb4b03 Native window decorations: removed superfluous pixel-line at top of screen when window is maximized (issue #296) 2021-03-31 20:56:17 +02:00
Marcel Schramm
620aa8bcee Fix selected states for native window border related menu items
The menu items for custom window decorations and embeded menu bar aren't selected anymore if the feature isn't supported.
On top of that, there's now a tooltip indicating that these aren't supported.
2021-03-31 19:59:29 +02:00
Karl Tauber
d13ddeb944 use larger font when running on WinPE (issue #279) 2021-03-30 11:00:27 +02:00
Karl Tauber
1b5da0e1d1 Window decorations: support enabling/disabling unified title bar backgrounds at runtime without FlatLaf.updateUI() 2021-03-30 01:34:34 +02:00
Karl Tauber
7a2d0e7fcb fixed crash when running in Webswing (issue #290) 2021-03-30 01:06:30 +02:00
Karl Tauber
477c3b6b1e README.md: added link to FlatLaf 1.0 announcement on Reddit 2021-03-28 18:44:21 +02:00
Karl Tauber
95312c3650 release 1.1.1 2021-03-28 16:04:14 +02:00
Karl Tauber
9006e835c6 natives.yml: exclude ~/.gradle/caches/modules-2/modules-2.lock from Gradle cache 2021-03-26 21:52:28 +01:00
Karl Tauber
f801d61929 support running on WinPE (issue #279) 2021-03-26 21:51:11 +01:00
Karl Tauber
a143e5777c Extras: FlatInspector: fixed InaccessibleObjectException when running in Java 16 2021-03-26 21:44:41 +01:00
Karl Tauber
bf500e46e7 Window decorations: fixed wrong/missing window icon when application replaces InternalFrame.icon (issue #284) 2021-03-25 16:14:41 +01:00
Karl Tauber
4a2f79f390 Native window decorations: updated DLLs (issues #282 and #283)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/686023039
2021-03-25 11:10:13 +01:00
Karl Tauber
c24ce7c5bc Native window decorations: fixed broken maximizing window when restoring frame state at startup (issue #283) 2021-03-25 10:32:24 +01:00
Karl Tauber
8a6a0c7971 Native window decorations: fixed missing animations when minimizing, maximizing or restoring a window using window title bar buttons (issue #282) 2021-03-24 23:59:59 +01:00
Karl Tauber
de6e5bd800 fixed missing focus indicators in heavy-weight popups (issue #273) 2021-03-24 11:43:06 +01:00
Karl Tauber
e18a04f9e6 Merge pull request #278 from ingokegel/native_provider_setter
Add a setter for the native provider
2021-03-24 11:34:22 +01:00
Karl Tauber
14fc652f4b Window decorations: fixed right aligned progress bar in embedded menu bar was overlapping window title (issue #272) 2021-03-23 19:23:18 +01:00
Ingo Kegel
9a876e747a Added setter for native provider
This makes it possible to support situations where the extraction of a DLL at runtime is not possible
2021-03-23 16:47:08 +01:00
Karl Tauber
f8ee8b27fb InternalFrame: fixed translucent internal frame menu bar background if TitlePane.unifiedBackground is true (issue #274) 2021-03-23 15:08:01 +01:00
Karl Tauber
ce1a1487aa support menu bars in JDialog 2021-03-23 14:58:53 +01:00
Karl Tauber
fe1e364a1d Native window decorations: support disabling native window decorations per window via client property (issue #277) 2021-03-23 13:18:07 +01:00
Karl Tauber
eabb052107 Native window decorations: fixed double window title bar when first disposing a window and then showing it again (issue #277) 2021-03-23 10:07:43 +01:00
Karl Tauber
734f3621f1 Window decorations: Fixed NPE in FlatTitlePane.findHorizontalGlue() (issue #275) 2021-03-22 18:47:53 +01:00
Karl Tauber
9612a81f2e release 1.1 2021-03-21 14:03:36 +01:00
Karl Tauber
2945a36cef added since 1.1 2021-03-21 13:53:57 +01:00
Karl Tauber
b84dc5bfcc JIDE and SwingX: README.md: added links to dependencies on maven central 2021-03-21 13:29:23 +01:00
Karl Tauber
60486fd880 JIDE: build using latest version of JIDE library com.formdev:jide-oss:3.7.11.1 2021-03-20 19:19:33 +01:00
Karl Tauber
891091cebc SwingX: fixed compiling module-info (broken since previous commit) 2021-03-19 17:06:23 +01:00
Karl Tauber
1493ddcf41 SwingX: the library on Maven Central no longer depends on org.swinglabs.swingx:swingx-all:1.6.5-1 to avoid problems when another SwingX library should be used 2021-03-19 16:23:29 +01:00
Karl Tauber
4299c50537 JIDE: the library on Maven Central no longer depends on com.jidesoft:jide-oss:3.6.18 to avoid problems when another JIDE library should be used (issue #270) 2021-03-19 16:22:24 +01:00
Karl Tauber
14577c396d JIDE: fixed hover/selection background colors of JideSplitButton and JideSplitToggleButton 2021-03-19 15:59:59 +01:00
Karl Tauber
e9b566241d JIDE: support JideSplitButton and JideSplitToggleButton 2021-03-19 15:39:32 +01:00
Karl Tauber
d39b08c035 FlatArrowButton: refactored arrow painting to FlatUIUtils.paintArrow() so that it can be easily used other components (e.g. JideSplitButton) 2021-03-19 01:21:19 +01:00
Karl Tauber
69ac683c8c Support running in JetBrains Projector (https://jetbrains.com/projector/) 2021-03-17 00:43:08 +01:00
Karl Tauber
eafd0b3d06 use lambdas for listeners (where possible) instead of extending Basic*UI.*Handler classes
some of those `Basic*UI.*Handler` classes may be deprecated in a future Java version (see https://github.com/openjdk/jdk/pull/1958)

this should also avoid loading of those `Basic*UI.*Handler` classes at runtime
2021-03-17 00:34:35 +01:00
Karl Tauber
310a4989dc JIDE: made used fonts "active" and restored fonts modified in LookAndFeelFactory.installJideExtension() 2021-03-16 23:23:40 +01:00
Karl Tauber
3d0df51839 JIDE: support JideLabel to fix wrong text colors in dark themes 2021-03-16 22:52:13 +01:00
Karl Tauber
ede02aaaa5 TabbedPane: use float arc for tab area button background 2021-03-16 22:20:46 +01:00
Karl Tauber
beff149004 JIDE: support JideButton and JideToggleButton 2021-03-16 22:15:32 +01:00
Karl Tauber
07db6e8fb0 Extras: FlatInspector: fixed NPE if component class is in default package 2021-03-16 13:46:25 +01:00
Karl Tauber
46852c0780 JIDE: invoke LookAndFeelFactory.installJideExtension() early in FlatJidePopupMenuUI to be sure that Jide extensions are installed 2021-03-16 13:26:36 +01:00
Karl Tauber
a5e41c573f JIDE: UIDefaultsDump: dump UI defaults added by LookAndFeelFactory.installJideExtension() 2021-03-16 11:38:49 +01:00
SchiopuMatei
ed91aa4648 Added option for downscaling 2021-03-15 20:41:40 +02:00
Karl Tauber
9a94395d30 JIDE: split FlatJideOssTest (moved JideTabbedPane to FlatJideOssContainerTest`) 2021-03-15 17:40:31 +01:00
Karl Tauber
04aa61c2bb Merge pull request #268 from title-pane-improvements
Title pane improvements (Windows 10 only)
2021-03-14 17:39:50 +01:00
Karl Tauber
035a13df54 Window decorations: support unified backgrounds for window title bar, menu bar and main content (issue #254) 2021-03-14 15:13:26 +01:00
Karl Tauber
e8a6f0ca3d Native window decorations: added flatlaf-windows-x86.dll and updated flatlaf-windows-x86_64.dll
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/650060630
2021-03-14 00:20:22 +01:00
Karl Tauber
1fc519b9de natives.yml: run "Native Libraries" also when natives.yml changed 2021-03-14 00:02:01 +01:00
Karl Tauber
2bcf38e2e3 natives.yml: run "Native Libraries" on any change in native project (e.g. when changing Gradle build script) 2021-03-13 23:59:30 +01:00
Karl Tauber
8eb44a68cb Native window decorations: support 32-bit JREs 2021-03-13 23:41:38 +01:00
Karl Tauber
30c7b442a8 Window decorations:
- support customizing of window title alignment: left aligned or centered (default is left without embedded menubar and centered with embedded menubar)
- improved centering of window title with embedded menubar (issue #252)
2021-03-13 17:08:47 +01:00
Karl Tauber
cee2211108 Demo: added "users" icon to right side of menu bar to demonstrate this feature 2021-03-13 11:14:51 +01:00
Karl Tauber
b7bcbccd45 Window decorations: support right aligned extra components in JFrame title pane with embedded menu bar 2021-03-13 11:10:50 +01:00
Karl Tauber
d2ccb97eba Native window decorations: use LoggingFacade 2021-03-12 23:18:13 +01:00
Karl Tauber
39d56f2603 Merge pull request #267 from native-window-decorations
Native window decorations for Windows 10 (using JNI)
2021-03-12 23:15:19 +01:00
Karl Tauber
83e904dd2d Merge pull request #262 from native-window-decorations-jna
Native window decorations for Windows 10 (using JNA)
2021-03-12 23:08:35 +01:00
Karl Tauber
110c787eba Merge pull request #265 from ingokegel:optional_logging
Make the module dependency on java.logging optional
2021-03-12 22:57:04 +01:00
Karl Tauber
7c7ff289de removed module java.logging from module-info.javas 2021-03-12 22:52:59 +01:00
Karl Tauber
617a35c51b LoggingFacade:
- make LoggingFacadeImpl classes package private
- added missing @Override
- minor formatting changes
2021-03-12 21:16:57 +01:00
Karl Tauber
73487ccf65 Native window decorations:
- enabled by default (via UI property `TitlePane.useWindowDecorations`)
- dropped system property `flatlaf.useNativeWindowDecorations` and replaced with `flatlaf.useWindowDecorations`
- old functionality of system property `flatlaf.useWindowDecorations` removed
2021-03-11 10:54:23 +01:00
Ingo Kegel
712bff9c99 Use System.Logger for logging with Java 9+ 2021-03-10 17:56:27 +01:00
Ingo Kegel
eedfcf86aa LoggingFacade: moved to com.formdev.flatlaf.util, added license header, fixed NPEs in logging calls and removed overloads of logSevere 2021-03-10 17:06:12 +01:00
Karl Tauber
f730848928 Native window decorations: added flatlaf-windows-x86_64.dll
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/636694710
2021-03-10 16:16:50 +01:00
Karl Tauber
61d0574c5c Native window decorations: added READMEs 2021-03-09 19:08:53 +01:00
Karl Tauber
2f01e01ec1 Native window decorations: delete temporary DLLs on next startup (same approach as used in JNA) 2021-03-07 00:10:15 +01:00
Karl Tauber
cbcf66df7f Native window decorations: fixed enabled items is system menu 2021-03-06 16:23:10 +01:00
Karl Tauber
cfaeea039b Native window decorations: fixed enabled items is system menu 2021-03-06 16:21:22 +01:00
Karl Tauber
a891d1eb54 Native window decorations: never build :flatlaf-natives-windows:jar because it is not used/needed 2021-03-06 15:26:18 +01:00
Karl Tauber
4372052ef0 Native window decorations: do not try to build native library (on Windows) if no C++ compiler is available 2021-03-06 15:18:23 +01:00
Karl Tauber
8734b062dc Native window decorations: avoid using C-runtime, which reduces the DLL size from 100kb to 8kb 2021-03-06 12:01:49 +01:00
Ingo Kegel
343451de65 Make the module dependency on java.logging optional
Currently, FlatLaf has the following module dependencies:

$ jdeps --list-deps --multi-release 9 flatlaf-1.0.jar
   java.base
   java.desktop
   java.logging

This commit makes the java.logging dependency optional and hides logging behind a facade that falls back to printing to stderr if the java.logging module is not available.

To test, create a reduced JRE with a command like

jdk-15/bin/jlink.exe --module-path jdk-15/jmods --add-modules java.desktop --add-modules java.instrument --output jre-15-desktop-only

(adding java.instrument, so the FlatLafDemo main class can be started from IntelliJ IDEA)
2021-03-05 16:44:08 +01:00
Karl Tauber
144d65c776 Native window decorations: initial implementation in C++ using JNI 2021-03-05 10:31:31 +01:00
Karl Tauber
a6815574f7 Native window decorations: renamed project flatlaf-native-jna to flatlaf-natives/flatlaf-natives-jna
removed module-info.java because this JAR is not released/published
2021-03-04 11:04:47 +01:00
Karl Tauber
e5a116a0d4 Extras: FlatInspector: removed println (fixes #263) 2021-02-25 16:54:05 +01:00
Karl Tauber
0beef6b108 README.md: new applications using FlatLaf:
- install4j
2021-02-25 00:00:30 +01:00
Karl Tauber
7341008449 Native window decorations: fixed missing top border line 2021-02-24 23:17:41 +01:00
Karl Tauber
49bd53194a Native window decorations: show window system menu when left-clicking on application icon, close window on left-double-click on app icon 2021-02-23 23:31:36 +01:00
Karl Tauber
baf4437efc Native window decorations: show window system menu when right-clicking on caption 2021-02-23 01:10:59 +01:00
Karl Tauber
b244f80f81 Native window decorations: support autohide taskbar 2021-02-22 22:57:43 +01:00
Karl Tauber
e41c91a42b Native window decorations: fixed exception when switching Laf after closing a dialog 2021-02-22 09:56:40 +01:00
Karl Tauber
b9a2e3ceac Native window decorations: initial implementation (using JNA; will be replaced with JNI later) 2021-02-21 17:51:19 +01:00
Karl Tauber
fa7dd3bdc4 GitHub Actions: upload all built libs 2021-02-21 17:18:59 +01:00
Karl Tauber
9a8c68b846 GitHub Actions: renamed master to main 2021-02-19 16:38:25 +01:00
Karl Tauber
698e33ddf4 IntelliJ Themes: fixed text color of CheckBoxMenuItem and RadioButtonMenuItem in all "Arc" themes (issue #259) 2021-02-19 11:33:15 +01:00
Karl Tauber
909258ba14 README.md: added "Getting started" and direct links to documentation 2021-02-14 12:32:56 +01:00
Karl Tauber
2ad6bd1d23 release 1.0 2021-02-13 13:42:04 +01:00
Karl Tauber
510ffd41d8 PopupFactory: fixed NullPointerException when PopupFactory.getPopup() is invoked with parameter owner set to null 2021-02-13 13:31:30 +01:00
Karl Tauber
4f00591c4e Table: fixed wrong grid line thickness in dragged column on HiDPI screens on Java 9+ (issue #236) 2021-02-12 11:32:12 +01:00
Karl Tauber
5b65ed87cd FileChooser: fixed display of date in details view if current user is selected in "Look in" combobox (Windows 10 only; issue #249) 2021-02-12 11:10:25 +01:00
Karl Tauber
b0121c422d GitHub Actions: added Gradle wrapper validation 2021-02-11 23:52:11 +01:00
Karl Tauber
a9e9fad222 Extras: FlatInspector: tooltip is no longer limited to window bounds 2021-02-11 18:23:01 +01:00
Karl Tauber
b5fc07acc7 TabbedPane: custom TabbedPane.selectedForeground color did not work when TabbedPane.foreground has also custom color (issue #257) 2021-02-11 12:04:36 +01:00
126 changed files with 9970 additions and 1302 deletions

4
.gitattributes vendored
View File

@@ -15,8 +15,12 @@
# BINARY FILES:
# Disable line ending normalize on checkin.
*.dll binary
*.dylib binary
*.gif binary
*.jar binary
*.lib binary
*.png binary
*.sketch binary
*.so binary
*.zip binary

View File

@@ -33,6 +33,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v1
with:
@@ -60,12 +62,7 @@ jobs:
with:
name: FlatLaf-build-artifacts
path: |
flatlaf-core/build/libs
flatlaf-demo/build/libs
flatlaf-extras/build/libs
flatlaf-intellij-themes/build/libs
flatlaf-jide-oss/build/libs
flatlaf-swingx/build/libs
flatlaf-*/build/libs
!**/*-javadoc.jar
!**/*-sources.jar
@@ -75,7 +72,7 @@ jobs:
needs: build
if: |
github.event_name == 'push' &&
github.ref == 'refs/heads/master' &&
github.ref == 'refs/heads/main' &&
github.repository == 'JFormDesigner/FlatLaf'
steps:

58
.github/workflows/natives.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Native Libraries
on:
push:
branches:
- '*'
tags:
- '[0-9]*'
paths:
- 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml'
pull_request:
branches:
- '*'
paths:
- 'flatlaf-natives/flatlaf-natives-windows/**'
- '.github/workflows/natives.yml'
jobs:
Windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- name: Setup Java 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Cache Gradle wrapper
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
run: ./gradlew :flatlaf-natives-windows:build
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: FlatLaf-natives-windows-build-artifacts
path: |
flatlaf-natives/flatlaf-natives-windows/build

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@ out/
*.iml
*.ipr
*.iws
.vs/
.vscode/

View File

@@ -1,6 +1,120 @@
FlatLaf Change Log
==================
## 1.1.2
#### New features and improvements
- Native window decorations: Added API to check whether current platform
supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and
to toggle window decorations of all windows
(`FlatLaf.setUseNativeWindowDecorations(boolean)`).
- Native window decorations: Support changing title bar background and
foreground colors per window. (set client properties
`JRootPane.titleBarBackground` and `JRootPane.titleBarForeground` on root pane
to a `java.awt.Color`).
#### Fixed bugs
- Native window decorations: Fixed loading of native library when using Java
Platform Module System (JPMS) for application. (issue #289)
- Native window decorations: Removed superfluous pixel-line at top of screen
when window is maximized. (issue #296)
- Window decorations: Fixed random window title bar background in cases were
background is not filled by custom window or root pane components and unified
background is enabled.
- IntelliJ Themes: Fixed window title bar background if unified background is
enabled.
- IntelliJ Themes: Fixed system colors.
- Button and ToggleButton: Do not paint background of disabled (and unselected)
toolBar buttons. (issue #292; regression since fixing #112)
- ComboBox and Spinner: Fixed too wide arrow button if component is higher than
preferred. (issue #302)
- SplitPane: `JSplitPane.setContinuousLayout(false)` did not work. (issue #301)
- TabbedPane: Fixed NPE when creating/modifying in another thread. (issue #299)
- Fixed crash when running in Webswing. (issue #290)
## 1.1.1
#### New features and improvements
- Native window decorations: Support disabling native window decorations per
window. (set client property `JRootPane.useWindowDecorations` on root pane to
`false`).
- Support running on WinPE. (issue #279)
#### Fixed bugs
- Native window decorations: Fixed missing animations when minimizing,
maximizing or restoring a window using window title bar buttons. (issue #282)
- Native window decorations: Fixed broken maximizing window when restoring frame
state at startup. (issue #283)
- Native window decorations: Fixed double window title bar when first disposing
a window with `frame.dispose()` and then showing it again with
`frame.setVisible(true)`. (issue #277)
- Custom window decorations: Fixed NPE in `FlatTitlePane.findHorizontalGlue()`.
(issue #275)
- Custom window decorations: Fixed right aligned progress bar in embedded menu
bar was overlapping window title. (issue #272)
- Fixed missing focus indicators in heavy-weight popups. (issue #273)
- InternalFrame: Fixed translucent internal frame menu bar background if
`TitlePane.unifiedBackground` is `true`. (issue #274)
- Extras: UI Inspector: Fixed `InaccessibleObjectException` when running in Java 16.
## 1.1
#### New features and improvements
- Windows 10 only:
- Native window decorations for Windows 10 enables dark frame/dialog title bar
and embedded menu bar with all JREs, while still having native Windows 10
border drop shadows, resize behavior, window snapping and system window
menu. (PR #267)
- Custom window decorations: Support right aligned components in `JFrame`
title bar with embedded menu bar (using `Box.createHorizontalGlue()`). (PR
#268)
- Custom window decorations: Improved centering of window title with embedded
menu bar. (PR #268; issue #252)
- Custom window decorations: Support unified backgrounds for window title bar,
menu bar and main content. If enabled with `UIManager.put(
"TitlePane.unifiedBackground", true );` then window title bar and menu bar
use same background color as main content. (PR #268; issue #254)
- JIDE Common Layer: Support `JideButton`, `JideLabel`, `JideSplitButton`,
`JideToggleButton` and `JideToggleSplitButton`.
- JIDE Common Layer: The library on Maven Central no longer depends on
`com.jidesoft:jide-oss:3.6.18` to avoid problems when another JIDE library
should be used. (issue #270)
- SwingX: The library on Maven Central no longer depends on
`org.swinglabs.swingx:swingx-all:1.6.5-1` to avoid problems when another
SwingX library should be used.
- Support running in [JetBrains Projector](https://jetbrains.com/projector/).
#### Fixed bugs
- IntelliJ Themes: Fixed text color of CheckBoxMenuItem and RadioButtonMenuItem
in all "Arc" themes. (issue #259)
## 1.0
#### New features and improvements
- Extras: UI Inspector: Tooltip is no longer limited to window bounds.
#### Fixed bugs
- TabbedPane: Custom `TabbedPane.selectedForeground` color did not work when
`TabbedPane.foreground` has also custom color. (issue #257)
- FileChooser: Fixed display of date in details view if current user is selected
in "Look in" combobox. (Windows 10 only; issue #249)
- Table: Fixed wrong grid line thickness in dragged column on HiDPI screens on
Java 9+. (issue #236)
- PopupFactory: Fixed `NullPointerException` when `PopupFactory.getPopup()` is
invoked with parameter `owner` set to `null`.
## 1.0-rc3
#### New features and improvements

View File

@@ -73,17 +73,38 @@ Addons
- [JIDE Common Layer](flatlaf-jide-oss)
Getting started
---------------
To enable FlatLaf, add following code to your main method before you create any
Swing component:
~~~java
FlatLightLaf.install();
// create UI here...
~~~
Documentation
-------------
For more information and documentation visit
[FlatLaf Home](https://www.formdev.com/flatlaf/)
[FlatLaf Home](https://www.formdev.com/flatlaf/):
- [Themes](https://www.formdev.com/flatlaf/themes/)
- [Customizing](https://www.formdev.com/flatlaf/customizing/)
- [How to Customize](https://www.formdev.com/flatlaf/how-to-customize/)
- [Properties Files](https://www.formdev.com/flatlaf/properties-files/)
- [Client Properties](https://www.formdev.com/flatlaf/client-properties/)
- [System Properties](https://www.formdev.com/flatlaf/system-properties/)
Buzz
----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
@@ -94,6 +115,9 @@ Applications using FlatLaf
and much more
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- ![New](images/new.svg)
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
9.0 (**commercial**) - the powerful multi-platform Java installer builder
- ![New](images/new.svg) [DbVisualizer](https://www.dbvis.com/) 12.0
(**commercial**) - the universal database tool for developers, analysts and
DBAs

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
val releaseVersion = "1.0-rc3"
val developmentVersion = "1.0-rc4-SNAPSHOT"
val releaseVersion = "1.1.2"
val developmentVersion = "1.2-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion

View File

@@ -27,6 +27,10 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
}
}
dependencies {
add( "java9Compile", sourceSets.main.get().output )
}
tasks {
named<JavaCompile>( "compileJava9Java" ) {
sourceCompatibility = "9"

View File

@@ -33,9 +33,17 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets {
create( "module-info" ) {
java {
// include "src/main/java" here to get compile errors if classes are
// include "src/main/java" and "src/main/java9" here to get compile errors if classes are
// used from other modules that are not specified in module dependencies
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
setSrcDirs( listOf( "src/main/module-info", "src/main/java", "src/main/java9" ) )
// exclude Java 8 source file if an equally named Java 9+ source file exists
exclude {
if( it.isDirectory )
return@exclude false
val java9file = file( "${projectDir}/src/main/java9/${it.path}" )
java9file.exists() && java9file != it.file
}
}
}
}
@@ -48,7 +56,8 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
dependsOn( extension.paths )
options.compilerArgs.add( "--module-path" )
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath )
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath
+ File.pathSeparator + configurations.compileClasspath.get().asPath )
}
jar {

View File

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

View File

@@ -232,14 +232,63 @@ public interface FlatClientProperties
//---- JRootPane ----------------------------------------------------------
/**
* Specifies whether the menu bar is embedded into the title pane if custom
* window decorations are enabled. Default is {@code true}.
* Specifies whether FlatLaf native window decorations should be used
* for {@code JFrame} or {@code JDialog}.
* <p>
* Setting this enables/disables using FlatLaf native window decorations
* for the window that contains the root pane.
* <p>
* This client property has lower priority than system property
* {@link FlatSystemProperties#USE_WINDOW_DECORATIONS}, but higher priority
* than UI default {@code TitlePane.useWindowDecorations}.
* <p>
* (requires Window 10)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 1.1.1
*/
String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations";
/**
* Specifies whether the menu bar is embedded into the window title pane
* if window decorations are enabled.
* <p>
* Setting this enables/disables embedding
* for the window that contains the root pane.
* <p>
* This client property has lower priority than system property
* {@link FlatSystemProperties#MENUBAR_EMBEDDED}, but higher priority
* than UI default {@code TitlePane.menuBarEmbedded}.
* <p>
* (requires Window 10)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Background color of window title bar (requires enabled window decorations).
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Color}
*
* @since 1.1.2
*/
String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground";
/**
* Foreground color of window title bar (requires enabled window decorations).
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Color}
*
* @since 1.1.2
*/
String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground";
//---- JScrollBar / JScrollPane -------------------------------------------
/**

View File

@@ -38,8 +38,6 @@ import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
@@ -48,6 +46,7 @@ import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.LookAndFeel;
import javax.swing.PopupFactory;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.ActiveValue;
@@ -59,9 +58,11 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.text.StyleContext;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -74,7 +75,6 @@ import com.formdev.flatlaf.util.UIScale;
public abstract class FlatLaf
extends BasicLookAndFeel
{
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
private static List<Object> customDefaultsSources;
@@ -91,9 +91,6 @@ public abstract class FlatLaf
private Consumer<UIDefaults> postInitialization;
private Boolean oldFrameWindowDecorated;
private Boolean oldDialogWindowDecorated;
/**
* Sets the application look and feel to the given LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
@@ -103,7 +100,7 @@ public abstract class FlatLaf
UIManager.setLookAndFeel( newLookAndFeel );
return true;
} catch( Exception ex ) {
LOG.log( Level.SEVERE, "FlatLaf: Failed to initialize look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
return false;
}
}
@@ -145,28 +142,28 @@ public abstract class FlatLaf
* Returns whether FlatLaf supports custom window decorations.
* This depends on the operating system and on the used Java runtime.
* <p>
* To use custom window decorations in your application, enable them with
* following code (before creating any frames or dialogs). Then custom window
* decorations are only enabled if this method returns {@code true}.
* <pre>
* JFrame.setDefaultLookAndFeelDecorated( true );
* JDialog.setDefaultLookAndFeelDecorated( true );
* </pre>
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
* <p>
* Returns {@code true} on Windows 10, {@code false} otherwise.
* <p>
* Return also {@code false} if running on Windows 10 in
* Returns also {@code false} on Windows 10 if:
* <ul>
* <li>FlatLaf native window border support is available (requires Windows 10)</li>
* <li>running in
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
* and JBR supports custom window decorations. In this case, JBR custom decorations
* are enabled if {@link JFrame#isDefaultLookAndFeelDecorated()} or
* {@link JDialog#isDefaultLookAndFeelDecorated()} return {@code true}.
* and JBR supports custom window decorations
* </li>
* </ul>
* In this cases, custom decorations are enabled by the root pane.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
*/
@Override
public boolean getSupportsWindowDecorations() {
if( SystemInfo.isJetBrainsJVM_11_orLater &&
SystemInfo.isWindows_10_orLater &&
JBRCustomDecorations.isSupported() )
if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE )
return false;
if( SystemInfo.isWindows_10_orLater &&
FlatNativeWindowBorder.isSupported() )
return false;
return SystemInfo.isWindows_10_orLater;
@@ -265,16 +262,6 @@ public abstract class FlatLaf
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
}
};
// enable/disable window decorations, but only if system property is either
// "true" or "false"; in other cases it is not changed
Boolean useWindowDecorations = FlatSystemProperties.getBooleanStrict( FlatSystemProperties.USE_WINDOW_DECORATIONS, null );
if( useWindowDecorations != null ) {
oldFrameWindowDecorated = JFrame.isDefaultLookAndFeelDecorated();
oldDialogWindowDecorated = JDialog.isDefaultLookAndFeelDecorated();
JFrame.setDefaultLookAndFeelDecorated( useWindowDecorations );
JDialog.setDefaultLookAndFeelDecorated( useWindowDecorations );
}
}
@Override
@@ -307,14 +294,6 @@ public abstract class FlatLaf
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
postInitialization = null;
// restore enable/disable window decorations
if( oldFrameWindowDecorated != null ) {
JFrame.setDefaultLookAndFeelDecorated( oldFrameWindowDecorated );
JDialog.setDefaultLookAndFeelDecorated( oldDialogWindowDecorated );
oldFrameWindowDecorated = null;
oldDialogWindowDecorated = null;
}
super.uninitialize();
}
@@ -341,7 +320,7 @@ public abstract class FlatLaf
} else
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).newInstance();
} catch( Exception ex ) {
LOG.log( Level.SEVERE, "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
throw new IllegalStateException();
}
@@ -400,6 +379,12 @@ public abstract class FlatLaf
initIconColors( defaults, isDark() );
FlatInputMaps.initInputMaps( defaults );
// copy InternalFrame.icon (the Java cup) to TitlePane.icon
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
Object icon = defaults.remove( "InternalFrame.icon" );
defaults.put( "InternalFrame.icon", icon );
defaults.put( "TitlePane.icon", icon );
// get addons and sort them by priority
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
List<FlatDefaultsAddon> addons = new ArrayList<>();
@@ -461,8 +446,16 @@ public abstract class FlatLaf
if( SystemInfo.isWindows ) {
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
if( winFont != null )
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
if( winFont != null ) {
if( SystemInfo.isWinPE ) {
// on WinPE use "win.defaultGUI.font", which is usually Tahoma,
// because Segoe UI font is not available on WinPE
Font winPEFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.defaultGUI.font" );
if( winPEFont != null )
uiFont = createCompositeFont( winPEFont.getFamily(), winPEFont.getStyle(), winFont.getSize() );
} else
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
}
} else if( SystemInfo.isMacOS ) {
String fontName;
@@ -499,7 +492,7 @@ public abstract class FlatLaf
// use active value for all fonts to allow changing fonts in all components
// (similar as in Nimbus L&F) with:
// UIManager.put( "defaultFont", myFont );
Object activeFont = new ActiveFont( 1 );
Object activeFont = new ActiveFont( 1 );
// override fonts
for( Object key : defaults.keySet() ) {
@@ -522,6 +515,13 @@ public abstract class FlatLaf
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
}
/**
* @since 1.1
*/
public static ActiveValue createActiveFontValue( float scaleFactor ) {
return new ActiveFont( scaleFactor );
}
/**
* Adds the default color palette for action icons and object icons to the given UIDefaults.
* <p>
@@ -577,7 +577,7 @@ public abstract class FlatLaf
.invoke( null, true );
defaults.put( key, value );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );
}
}
@@ -684,7 +684,7 @@ public abstract class FlatLaf
// update UI
updateUI();
} catch( UnsupportedLookAndFeelException ex ) {
LOG.log( Level.SEVERE, "FlatLaf: Failed to reinitialize look and feel '" + lookAndFeel.getClass().getName() + "'.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to reinitialize look and feel '" + lookAndFeel.getClass().getName() + "'.", ex );
}
} );
}
@@ -717,6 +717,79 @@ public abstract class FlatLaf
} );
}
/**
* Returns whether native window decorations are supported on current platform.
* <p>
* This requires Windows 10, but may be disabled if running in special environments
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
* {@code false}, then this method also returns {@code false}.
*
* @since 1.1.2
*/
public static boolean supportsNativeWindowDecorations() {
return SystemInfo.isWindows_10_orLater && FlatNativeWindowBorder.isSupported();
}
/**
* Returns whether native window decorations are enabled.
*
* @since 1.1.2
*/
public static boolean isUseNativeWindowDecorations() {
return UIManager.getBoolean( "TitlePane.useWindowDecorations" );
}
/**
* Sets whether native window decorations are enabled.
* <p>
* Existing frames and dialogs will be updated.
*
* @since 1.1.2
*/
public static void setUseNativeWindowDecorations( boolean enabled ) {
UIManager.put( "TitlePane.useWindowDecorations", enabled );
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) )
return;
// update existing frames and dialogs
for( Window w : Window.getWindows() ) {
if( isDisplayableFrameOrDialog( w ) )
FlatRootPaneUI.updateNativeWindowBorder( ((RootPaneContainer)w).getRootPane() );
}
}
/**
* Revalidate and repaint all displayable frames and dialogs.
*
* @since 1.1.2
*/
public static void revalidateAndRepaintAllFramesAndDialogs() {
for( Window w : Window.getWindows() ) {
if( isDisplayableFrameOrDialog( w ) ) {
w.revalidate();
w.repaint();
}
}
}
/**
* Repaint all displayable frames and dialogs.
*
* @since 1.1.2
*/
public static void repaintAllFramesAndDialogs() {
for( Window w : Window.getWindows() ) {
if( isDisplayableFrameOrDialog( w ) )
w.repaint();
}
}
private static boolean isDisplayableFrameOrDialog( Window w ) {
return w.isDisplayable() && (w instanceof JFrame || w instanceof JDialog);
}
public static boolean isShowMnemonics() {
return MnemonicHandler.isShowMnemonics();
}
@@ -760,6 +833,10 @@ public abstract class FlatLaf
public Object createValue( UIDefaults table ) {
Font defaultFont = UIManager.getFont( "defaultFont" );
// fallback (to avoid NPE in case that this is used in another Laf)
if( defaultFont == null )
defaultFont = UIManager.getFont( "Label.font" );
if( lastDefaultFont != defaultFont ) {
lastDefaultFont = defaultFont;

View File

@@ -16,8 +16,7 @@
package com.formdev.flatlaf;
import javax.swing.JDialog;
import javax.swing.JFrame;
import com.formdev.flatlaf.util.UIScale;
/**
* Defines/documents own system properties used in FlatLaf.
@@ -35,6 +34,8 @@ public interface FlatSystemProperties
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
* which has the same syntax as this one.
* <p>
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
* <p>
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
*/
String UI_SCALE = "flatlaf.uiScale";
@@ -47,6 +48,17 @@ public interface FlatSystemProperties
*/
String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled";
/**
* Specifies whether values smaller than 100% are allowed for the user scale factor
* (see {@link UIScale#getUserScaleFactor()}).
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code false}
*
* @since 1.1.2
*/
String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.uiScale.allowScaleDown";
/**
* Specifies whether Ubuntu font should be used on Ubuntu Linux.
* By default, if not running in a JetBrains Runtime, the Liberation Sans font
@@ -58,11 +70,16 @@ public interface FlatSystemProperties
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
/**
* Specifies whether custom look and feel window decorations should be used
* Specifies whether native window decorations should be used
* when creating {@code JFrame} or {@code JDialog}.
* <p>
* If this system property is set, FlatLaf invokes {@link JFrame#setDefaultLookAndFeelDecorated(boolean)}
* and {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} on LaF initialization.
* Setting this to {@code true} forces using native window decorations
* even if they are not enabled by the application.<br>
* Setting this to {@code false} disables using native window decorations.
* <p>
* This system property has higher priority than client property
* {@link FlatClientProperties#USE_WINDOW_DECORATIONS} and
* UI default {@code TitlePane.useWindowDecorations}.
* <p>
* (requires Window 10)
* <p>
@@ -78,23 +95,31 @@ public interface FlatSystemProperties
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
* (based on OpenJDK).
* <p>
* Setting this to {@code true} forces using JetBrains Runtime custom window decorations
* even if they are not enabled by the application.
* Setting this to {@code false} disables using JetBrains Runtime custom window decorations.
* Then FlatLaf native window decorations are used.
* <p>
* (requires Window 10)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
* <strong>Default</strong> true
*/
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
/**
* Specifies whether menubar is embedded into custom window decorations.
* Specifies whether the menu bar is embedded into the window title pane
* if window decorations are enabled.
* <p>
* Setting this to {@code true} forces embedding.<br>
* Setting this to {@code false} disables embedding.
* <p>
* This system property has higher priority than client property
* {@link FlatClientProperties#MENU_BAR_EMBEDDED} and
* UI default {@code TitlePane.menuBarEmbedded}.
* <p>
* (requires Window 10)
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
* <strong>Default</strong> none
*/
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";

View File

@@ -30,11 +30,11 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.UIDefaults;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.json.Json;
import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
/**
@@ -76,7 +76,7 @@ public class IntelliJTheme
try {
return FlatLaf.install( createLaf( in ) );
} catch( Exception ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load IntelliJ theme", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load IntelliJ theme", ex );
return false;
}
}
@@ -324,7 +324,7 @@ public class IntelliJTheme
try {
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
} catch( RuntimeException ex ) {
UIDefaultsLoader.logParseError( Level.CONFIG, key, valueStr, ex );
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
return; // ignore invalid value
}
}

View File

@@ -28,7 +28,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -172,7 +173,7 @@ class LinuxFontPolicy
if( "1".equals( strs.get( 5 ) ) )
style |= Font.ITALIC;
} catch( RuntimeException ex ) {
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
}
}
@@ -186,7 +187,7 @@ class LinuxFontPolicy
if( dpi < 50 )
dpi = 50;
} catch( NumberFormatException ex ) {
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
}
}
@@ -225,7 +226,7 @@ class LinuxFontPolicy
while( (line = reader.readLine()) != null )
lines.add( line );
} catch( IOException ex ) {
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to read '" + filename + "'.", ex );
LoggingFacade.INSTANCE.logConfig( "FlatLaf: Failed to read '" + filename + "'.", ex );
}
return lines;
}

View File

@@ -28,6 +28,7 @@ import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.lang.ref.WeakReference;
import javax.swing.AbstractButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
@@ -137,10 +138,17 @@ class MnemonicHandler
// get menu bar and first menu
Component c = e.getComponent();
JRootPane rootPane = SwingUtilities.getRootPane( c );
Window window = (rootPane != null) ? SwingUtilities.getWindowAncestor( rootPane ) : null;
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
if( menuBar == null && window instanceof JFrame )
menuBar = ((JFrame)window).getJMenuBar();
if( menuBar == null ) {
// get menu bar from frame/dialog because there
// may be multiple nested root panes in a frame/dialog
// (e.g. each internal frame has its own root pane)
Window window = SwingUtilities.getWindowAncestor( c );
if( window instanceof JFrame )
menuBar = ((JFrame)window).getJMenuBar();
else if( window instanceof JDialog )
menuBar = ((JDialog)window).getJMenuBar();
}
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
// select first menu and show mnemonics

View File

@@ -33,7 +33,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.function.Function;
import java.util.logging.Level;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UIDefaults.ActiveValue;
@@ -48,6 +47,7 @@ import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.HSLColor;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -243,16 +243,20 @@ class UIDefaultsLoader
try {
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( Level.SEVERE, key, value, ex );
logParseError( key, value, ex, true );
}
}
} catch( IOException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load properties files.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load properties files.", ex );
}
}
static void logParseError( Level level, String key, String value, RuntimeException ex ) {
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
static void logParseError( String key, String value, RuntimeException ex, boolean severe ) {
String message = "FlatLaf: Failed to parse: '" + key + '=' + value + '\'';
if( severe )
LoggingFacade.INSTANCE.logSevere( message, ex );
else
LoggingFacade.INSTANCE.logConfig( message, ex );
}
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
@@ -440,7 +444,7 @@ class UIDefaultsLoader
try {
return findClass( value, addonClassLoaders ).newInstance();
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
return null;
}
};
@@ -451,7 +455,7 @@ class UIDefaultsLoader
try {
return findClass( value, addonClassLoaders );
} catch( ClassNotFoundException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to find class '" + value + "'.", ex );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to find class '" + value + "'.", ex );
return null;
}
};
@@ -928,7 +932,7 @@ class UIDefaultsLoader
Object value = UIManager.get( uiKey );
if( value == null && !optional )
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: '" + uiKey + "' not found in UI defaults." );
LoggingFacade.INSTANCE.logSevere( "FlatLaf: '" + uiKey + "' not found in UI defaults.", null );
return value;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2021 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.resources;
/**
* 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.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
*
* @author Karl Tauber
*/
interface EmptyPackage
{
}

View File

@@ -17,16 +17,13 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import javax.swing.JComponent;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicArrowButton;
@@ -190,73 +187,14 @@ public class FlatArrowButton
}
protected void paintArrow( Graphics2D g ) {
int direction = getDirection();
boolean vert = (direction == NORTH || direction == SOUTH);
// compute width/height
int w = scale( arrowWidth + (chevron ? 0 : 1) );
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
// rotate width/height
int rw = vert ? w : h;
int rh = vert ? h : w;
// chevron lines end 1px outside of width/height
if( chevron ) {
// add 1px to width/height for position calculation only
rw++;
rh++;
}
int x = Math.round( (getWidth() - rw) / 2f + scale( (float) xOffset ) );
int y = Math.round( (getHeight() - rh) / 2f + scale( (float) yOffset ) );
int x = 0;
// move arrow for round borders
Container parent = getParent();
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
// paint arrow
g.translate( x, y );
/*debug
debugPaint( g, vert, rw, rh );
debug*/
Shape arrowShape = createArrowShape( direction, chevron, w, h );
if( chevron ) {
g.setStroke( new BasicStroke( scale( 1f ) ) );
g.draw( arrowShape );
} else {
// triangle
g.fill( arrowShape );
}
g.translate( -x, -y );
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
}
public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
switch( direction ) {
case NORTH: return FlatUIUtils.createPath( !chevron, 0,h, (w / 2f),0, w,h );
case SOUTH: return FlatUIUtils.createPath( !chevron, 0,0, (w / 2f),h, w,0 );
case WEST: return FlatUIUtils.createPath( !chevron, h,0, 0,(w / 2f), h,w );
case EAST: return FlatUIUtils.createPath( !chevron, 0,0, h,(w / 2f), 0,w );
default: return new Path2D.Float();
}
}
/*debug
private void debugPaint( Graphics g, boolean vert, int w, int h ) {
Color oldColor = g.getColor();
g.setColor( Color.red );
g.drawRect( 0, 0, w - 1, h - 1 );
int xy1 = -2;
int xy2 = h + 1;
for( int i = 0; i < 20; i++ ) {
g.drawRect( vert ? 0 : xy1, vert ? xy1 : 0, 0, 0 );
g.drawRect( vert ? 0 : xy2, vert ? xy2 : 0, 0, 0 );
xy1 -= 2;
xy2 += 2;
}
g.setColor( oldColor );
}
debug*/
}

View File

@@ -388,41 +388,35 @@ public class FlatButtonUI
}
protected Color getBackground( JComponent c ) {
boolean toolBarButton = isToolBarButton( c );
// selected state
if( ((AbstractButton)c).isSelected() ) {
// in toolbar use same colors for disabled and enabled because
// in toolbar use same background colors for disabled and enabled because
// we assume that toolbar icon is shown disabled
boolean toolBarButton = isToolBarButton( c );
return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
null, null,
null,
null,
toolBarButton ? toolbarPressedBackground : pressedBackground );
}
if( !c.isEnabled() )
return disabledBackground;
// toolbar button
if( isToolBarButton( c ) ) {
ButtonModel model = ((AbstractButton)c).getModel();
if( model.isPressed() )
return toolbarPressedBackground;
if( model.isRollover() )
return toolbarHoverBackground;
// use component background if explicitly set
if( toolBarButton ) {
Color bg = c.getBackground();
if( isCustomBackground( bg ) )
return bg;
// do not paint background
return null;
return buttonStateColor( c,
isCustomBackground( bg ) ? bg : null,
null,
null,
toolbarHoverBackground,
toolbarPressedBackground );
}
boolean def = isDefaultButton( c );
return buttonStateColor( c,
getBackgroundBase( c, def ),
null,
disabledBackground,
isCustomBackground( c.getBackground() ) ? null : (def ? defaultFocusedBackground : focusedBackground),
def ? defaultHoverBackground : hoverBackground,
def ? defaultPressedBackground : pressedBackground );
@@ -444,16 +438,18 @@ public class FlatButtonUI
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
Color focusedColor, Color hoverColor, Color pressedColor )
{
AbstractButton b = (c instanceof AbstractButton) ? (AbstractButton) c : null;
if( !c.isEnabled() )
return disabledColor;
if( pressedColor != null && b != null && b.getModel().isPressed() )
return pressedColor;
if( c instanceof AbstractButton ) {
ButtonModel model = ((AbstractButton)c).getModel();
if( hoverColor != null && b != null && b.getModel().isRollover() )
return hoverColor;
if( pressedColor != null && model.isPressed() )
return pressedColor;
if( hoverColor != null && model.isRollover() )
return hoverColor;
}
if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
return focusedColor;

View File

@@ -38,7 +38,6 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import javax.swing.AbstractAction;
@@ -244,7 +243,24 @@ public class FlatComboBoxUI
public void layoutContainer( Container parent ) {
super.layoutContainer( parent );
if ( editor != null && padding != null ) {
if( arrowButton != null ) {
Insets insets = getInsets();
int buttonWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
if( buttonWidth != arrowButton.getWidth() ) {
// set width of arrow button to preferred height of combobox
int xOffset = comboBox.getComponentOrientation().isLeftToRight()
? arrowButton.getWidth() - buttonWidth
: 0;
arrowButton.setBounds( arrowButton.getX() + xOffset, arrowButton.getY(),
buttonWidth, arrowButton.getHeight() );
// update editor bounds
if( editor != null )
editor.setBounds( rectangleForCurrentValue() );
}
}
if( editor != null && padding != null ) {
// fix editor bounds by subtracting padding
editor.setBounds( FlatUIUtils.subtractInsets( editor.getBounds(), padding ) );
}
@@ -274,30 +290,28 @@ public class FlatComboBoxUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new BasicComboBoxUI.PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
Object source = e.getSource();
String propertyName = e.getPropertyName();
Object source = e.getSource();
String propertyName = e.getPropertyName();
if( editor != null &&
((source == comboBox && propertyName == "foreground") ||
(source == editor && propertyName == "enabled")) )
{
// fix editor component colors
updateEditorColors();
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
editor.applyComponentOrientation( o );
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
editor.repaint();
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
comboBox.repaint();
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
comboBox.revalidate();
}
if( editor != null &&
((source == comboBox && propertyName == "foreground") ||
(source == editor && propertyName == "enabled")) )
{
// fix editor component colors
updateEditorColors();
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
editor.applyComponentOrientation( o );
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
editor.repaint();
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
comboBox.repaint();
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
comboBox.revalidate();
};
}
@@ -648,14 +662,12 @@ public class FlatComboBoxUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new BasicComboPopup.PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
if( e.getPropertyName() == "renderer" )
list.setCellRenderer( new PopupListCellRenderer() );
}
if( e.getPropertyName() == "renderer" )
list.setCellRenderer( new PopupListCellRenderer() );
};
}

View File

@@ -31,13 +31,17 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -190,6 +194,62 @@ public class FlatFileChooserUI
}
}
@Override
protected JPanel createDetailsView( JFileChooser fc ) {
JPanel p = super.createDetailsView( fc );
if( !SystemInfo.isWindows )
return p;
// find scroll pane
JScrollPane scrollPane = null;
for( Component c : p.getComponents() ) {
if( c instanceof JScrollPane ) {
scrollPane = (JScrollPane) c;
break;
}
}
if( scrollPane == null )
return p;
// get scroll view, which should be a table
Component view = scrollPane.getViewport().getView();
if( !(view instanceof JTable) )
return p;
JTable table = (JTable) view;
// on Windows 10, the date may contain left-to-right (0x200e) and right-to-left (0x200f)
// mark characters (see https://en.wikipedia.org/wiki/Left-to-right_mark)
// when the "current user" item is selected in the "look in" combobox
// --> remove them
TableCellRenderer defaultRenderer = table.getDefaultRenderer( Object.class );
table.setDefaultRenderer( Object.class, new TableCellRenderer() {
@Override
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column )
{
// remove left-to-right and right-to-left mark characters
if( value instanceof String && ((String)value).startsWith( "\u200e" ) ) {
String str = (String) value;
char[] buf = new char[str.length()];
int j = 0;
for( int i = 0; i < buf.length; i++ ) {
char ch = str.charAt( i );
if( ch != '\u200e' && ch != '\u200f' )
buf[j++] = ch;
}
value = new String( buf, 0, j );
}
return defaultRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
}
} );
return p;
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );

View File

@@ -16,17 +16,24 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.LookAndFeel;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.SystemInfo;
@@ -40,6 +47,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuBar.background Color
* @uiDefault MenuBar.foreground Color
* @uiDefault MenuBar.border Border
* @uiDefault TitlePane.unifiedBackground boolean
*
* @author Karl Tauber
*/
@@ -55,6 +63,13 @@ public class FlatMenuBarUI
* Do not add any functionality here.
*/
@Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false );
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
@@ -67,6 +82,44 @@ public class FlatMenuBarUI
map.put( "takeFocus", new TakeFocus() );
}
@Override
public void update( Graphics g, JComponent c ) {
// paint background
Color background = getBackground( c );
if( background != null ) {
g.setColor( background );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
paint( g, c );
}
protected Color getBackground( JComponent c ) {
Color background = c.getBackground();
// paint background if opaque or if having custom background color
if( c.isOpaque() || !(background instanceof UIResource) )
return background;
// paint background if menu bar is not the "main" menu bar
JRootPane rootPane = SwingUtilities.getRootPane( c );
if( rootPane == null || !(rootPane.getParent() instanceof Window) || rootPane.getJMenuBar() != c )
return background;
// use parent background for unified title pane
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
if( UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ) )
background = FlatUIUtils.getParentBackground( c );
// paint background in full screen mode
if( FlatUIUtils.isFullScreen( rootPane ) )
return background;
// do not paint background if menu bar is embedded into title pane
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
}
//---- class TakeFocus ----------------------------------------------------
/**

View File

@@ -0,0 +1,348 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Window;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Support for custom window decorations with native window border.
*
* @author Karl Tauber
* @since 1.1
*/
public class FlatNativeWindowBorder
{
// can use window decorations if:
// - on Windows 10
// - not when running in JetBrains Projector, Webswing or WinPE
// - not disabled via system property
private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater &&
!SystemInfo.isProjector &&
!SystemInfo.isWebswing &&
!SystemInfo.isWinPE &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
private static final boolean canUseJBRCustomDecorations =
canUseWindowDecorations &&
SystemInfo.isJetBrainsJVM_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true );
private static Boolean supported;
private static Provider nativeProvider;
public static boolean isSupported() {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.isSupported();
initialize();
return supported;
}
static Object install( JRootPane rootPane ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.install( rootPane );
if( !isSupported() )
return null;
// Check whether root pane already has a window, which is the case when
// switching from another LaF to FlatLaf.
// Also check whether the window is displayable, which is required to install
// FlatLaf native window border.
// If the window is not displayable, then it was probably closed/disposed but not yet removed
// from the list of windows that AWT maintains and returns with Window.getWindows().
// It could be also be a window that is currently hidden, but may be shown later.
Window window = SwingUtilities.windowForComponent( rootPane );
if( window != null && window.isDisplayable() )
install( window );
// Install FlatLaf native window border, which must be done late,
// when the native window is already created, because it needs access to the window.
// Uninstall FlatLaf native window border when window is disposed (or root pane removed).
// "ancestor" property change event is fired from JComponent.addNotify() and removeNotify().
PropertyChangeListener ancestorListener = e -> {
Object newValue = e.getNewValue();
if( newValue instanceof Window )
install( (Window) newValue );
else if( newValue == null && e.getOldValue() instanceof Window )
uninstall( (Window) e.getOldValue() );
};
rootPane.addPropertyChangeListener( "ancestor", ancestorListener );
return ancestorListener;
}
static void install( Window window ) {
if( hasCustomDecoration( window ) )
return;
// do not enable native window border if LaF provides decorations
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
return;
if( window instanceof JFrame ) {
JFrame frame = (JFrame) window;
JRootPane rootPane = frame.getRootPane();
// check whether disabled via system property, client property or UI default
if( !useWindowDecorations( rootPane ) )
return;
// do not enable native window border if frame is undecorated
if( frame.isUndecorated() )
return;
// enable native window border for window
setHasCustomDecoration( frame, true );
// enable Swing window decoration
rootPane.setWindowDecorationStyle( JRootPane.FRAME );
} else if( window instanceof JDialog ) {
JDialog dialog = (JDialog) window;
JRootPane rootPane = dialog.getRootPane();
// check whether disabled via system property, client property or UI default
if( !useWindowDecorations( rootPane ) )
return;
// do not enable native window border if dialog is undecorated
if( dialog.isUndecorated() )
return;
// enable native window border for window
setHasCustomDecoration( dialog, true );
// enable Swing window decoration
rootPane.setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
}
}
static void uninstall( JRootPane rootPane, Object data ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.uninstall( rootPane, data );
return;
}
if( !isSupported() )
return;
// remove listener
if( data instanceof PropertyChangeListener )
rootPane.removePropertyChangeListener( "ancestor", (PropertyChangeListener) data );
// do not uninstall when switching to another FlatLaf theme and if still enabled
if( UIManager.getLookAndFeel() instanceof FlatLaf && useWindowDecorations( rootPane ) )
return;
// uninstall native window border
Window window = SwingUtilities.windowForComponent( rootPane );
if( window != null )
uninstall( window );
}
private static void uninstall( Window window ) {
if( !hasCustomDecoration( window ) )
return;
// disable native window border for window
setHasCustomDecoration( window, false );
if( window instanceof JFrame ) {
JFrame frame = (JFrame) window;
// disable Swing window decoration
frame.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
} else if( window instanceof JDialog ) {
JDialog dialog = (JDialog) window;
// disable Swing window decoration
dialog.getRootPane().setWindowDecorationStyle( JRootPane.NONE );
}
}
private static boolean useWindowDecorations( JRootPane rootPane ) {
return FlatUIUtils.getBoolean( rootPane,
FlatSystemProperties.USE_WINDOW_DECORATIONS,
FlatClientProperties.USE_WINDOW_DECORATIONS,
"TitlePane.useWindowDecorations",
false );
}
public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
if( !isSupported() )
return false;
return nativeProvider.hasCustomDecoration( window );
}
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
return;
}
if( !isSupported() )
return;
nativeProvider.setHasCustomDecoration( window, hasCustomDecoration );
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
{
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return;
}
if( !isSupported() )
return;
nativeProvider.setTitleBarHeight( window, titleBarHeight );
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
}
static boolean showWindow( Window window, int cmd ) {
if( canUseJBRCustomDecorations || !isSupported() )
return false;
return nativeProvider.showWindow( window, cmd );
}
private static void initialize() {
if( supported != null )
return;
supported = false;
if( !canUseWindowDecorations )
return;
try {
/*
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
Method m = cls.getMethod( "getInstance" );
setNativeProvider( (Provider) m.invoke( null ) );
*/
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
} catch( Exception ex ) {
// ignore
}
}
/**
* @since 1.1.1
*/
public static void setNativeProvider( Provider provider ) {
if( nativeProvider != null )
throw new IllegalStateException();
nativeProvider = provider;
supported = (nativeProvider != null);
}
//---- interface Provider -------------------------------------------------
public interface Provider
{
boolean hasCustomDecoration( Window window );
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void setTitleBarHeight( Window window, int titleBarHeight );
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
// commands for showWindow(); values must match Win32 API
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
int SW_MAXIMIZE = 3;
int SW_MINIMIZE = 6;
int SW_RESTORE = 9;
boolean showWindow( Window window, int cmd );
boolean isColorizationColorAffectsBorders();
Color getColorizationColor();
int getColorizationColorBalance();
void addChangeListener( ChangeListener l );
void removeChangeListener( ChangeListener l );
}
//---- class WindowTopBorder -------------------------------------------
static class WindowTopBorder
extends JBRCustomDecorations.JBRWindowTopBorder
{
private static WindowTopBorder instance;
static JBRWindowTopBorder getInstance() {
if( canUseJBRCustomDecorations )
return JBRWindowTopBorder.getInstance();
if( instance == null )
instance = new WindowTopBorder();
return instance;
}
@Override
void installListeners() {
nativeProvider.addChangeListener( e -> {
update();
// repaint top borders of all windows
for( Window window : Window.getWindows() ) {
if( window.isDisplayable() )
window.repaint( 0, 0, window.getWidth(), 1 );
}
} );
}
@Override
boolean isColorizationColorAffectsBorders() {
return nativeProvider.isColorizationColorAffectsBorders();
}
@Override
Color getColorizationColor() {
return nativeProvider.getColorizationColor();
}
@Override
int getColorizationColorBalance() {
return nativeProvider.getColorizationColorBalance();
}
}
}

View File

@@ -62,7 +62,7 @@ public class FlatPopupFactory
public Popup getPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException
{
Point pt = fixToolTipLocation( owner, contents, x, y );
Point pt = fixToolTipLocation( contents, x, y );
if( pt != null ) {
x = pt.x;
y = pt.y;
@@ -70,7 +70,7 @@ public class FlatPopupFactory
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) )
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
@@ -111,6 +111,7 @@ public class FlatPopupFactory
// check whether heavy weight popup window is on same screen as owner component
if( popupWindow == null ||
owner == null ||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
return popup;
@@ -211,7 +212,7 @@ public class FlatPopupFactory
* This method checks whether the current mouse location is within tooltip bounds
* 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 contents, int x, int y ) {
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
return null;
@@ -450,10 +451,10 @@ public class FlatPopupFactory
mediumWeightShown = true;
Window window = SwingUtilities.windowForComponent( owner );
if( window == null )
if( owner == null )
return;
Window window = SwingUtilities.windowForComponent( owner );
if( !(window instanceof RootPaneContainer) )
return;

View File

@@ -40,6 +40,7 @@ import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.RootPaneUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicRootPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
@@ -70,16 +71,13 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatRootPaneUI
extends BasicRootPaneUI
{
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
static final boolean canUseJBRCustomDecorations
= SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected JRootPane rootPane;
protected FlatTitlePane titlePane;
protected FlatWindowResizer windowResizer;
private Object nativeWindowBorderData;
private LayoutManager oldLayout;
public static ComponentUI createUI( JComponent c ) {
@@ -97,8 +95,7 @@ public class FlatRootPaneUI
else
installBorder();
if( canUseJBRCustomDecorations )
JBRCustomDecorations.install( rootPane );
installNativeWindowBorder();
}
protected void installBorder() {
@@ -113,6 +110,7 @@ public class FlatRootPaneUI
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
uninstallNativeWindowBorder();
uninstallClientDecorations();
rootPane = null;
}
@@ -138,11 +136,39 @@ public class FlatRootPaneUI
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
}
/**
* @since 1.1.2
*/
protected void installNativeWindowBorder() {
nativeWindowBorderData = FlatNativeWindowBorder.install( rootPane );
}
/**
* @since 1.1.2
*/
protected void uninstallNativeWindowBorder() {
FlatNativeWindowBorder.uninstall( rootPane, nativeWindowBorderData );
nativeWindowBorderData = null;
}
/**
* @since 1.1.2
*/
public static void updateNativeWindowBorder( JRootPane rootPane ) {
RootPaneUI rui = rootPane.getUI();
if( !(rui instanceof FlatRootPaneUI) )
return;
FlatRootPaneUI ui = (FlatRootPaneUI) rui;
ui.uninstallNativeWindowBorder();
ui.installNativeWindowBorder();
}
protected void installClientDecorations() {
boolean isJBRSupported = canUseJBRCustomDecorations && JBRCustomDecorations.isSupported();
boolean isNativeWindowBorderSupported = FlatNativeWindowBorder.isSupported();
// install border
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isJBRSupported )
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE && !isNativeWindowBorderSupported )
LookAndFeel.installBorder( rootPane, "RootPane.border" );
else
LookAndFeel.uninstallBorder( rootPane );
@@ -155,7 +181,7 @@ public class FlatRootPaneUI
rootPane.setLayout( createRootLayout() );
// install window resizer
if( !isJBRSupported )
if( !isNativeWindowBorderSupported )
windowResizer = createWindowResizer();
}
@@ -219,6 +245,10 @@ public class FlatRootPaneUI
installBorder();
break;
case FlatClientProperties.USE_WINDOW_DECORATIONS:
updateNativeWindowBorder( rootPane );
break;
case FlatClientProperties.MENU_BAR_EMBEDDED:
if( titlePane != null ) {
titlePane.menuBarChanged();
@@ -226,9 +256,22 @@ public class FlatRootPaneUI
rootPane.repaint();
}
break;
case FlatClientProperties.TITLE_BAR_BACKGROUND:
case FlatClientProperties.TITLE_BAR_FOREGROUND:
if( titlePane != null )
titlePane.titleBarColorsChanged();
break;
}
}
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
RootPaneUI ui = rootPane.getUI();
return ui instanceof FlatRootPaneUI &&
((FlatRootPaneUI)ui).titlePane != null &&
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
}
//---- class FlatRootLayout -----------------------------------------------
protected class FlatRootLayout
@@ -299,15 +342,16 @@ public class FlatRootPaneUI
rootPane.getGlassPane().setBounds( x, y, width, height );
int nextY = 0;
if( !isFullScreen && titlePane != null ) {
Dimension prefSize = titlePane.getPreferredSize();
titlePane.setBounds( 0, 0, width, prefSize.height );
nextY += prefSize.height;
if( titlePane != null ) {
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
titlePane.setBounds( 0, 0, width, prefHeight );
nextY += prefHeight;
}
JMenuBar menuBar = rootPane.getJMenuBar();
if( menuBar != null && menuBar.isVisible() ) {
if( !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded() ) {
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
if( embedded ) {
titlePane.validate();
menuBar.setBounds( titlePane.getMenuBarBounds() );
} else {
@@ -344,6 +388,9 @@ public class FlatRootPaneUI
//---- class FlatWindowBorder ---------------------------------------------
/**
* Window border used for non-native window decorations.
*/
public static class FlatWindowBorder
extends BorderUIResource.EmptyBorderUIResource
{
@@ -358,7 +405,7 @@ public class FlatRootPaneUI
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) ) {
// hide border if window is maximized
// hide border if window is maximized or full screen
insets.top = insets.left = insets.bottom = insets.right = 0;
return insets;
} else
@@ -421,7 +468,9 @@ public class FlatRootPaneUI
(parent instanceof JFrame &&
(((JFrame)parent).getJMenuBar() == null ||
!((JFrame)parent).getJMenuBar().isVisible())) ||
parent instanceof JDialog;
(parent instanceof JDialog &&
(((JDialog)parent).getJMenuBar() == null ||
!((JDialog)parent).getJMenuBar().isVisible()));
}
}
}

View File

@@ -23,7 +23,6 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Objects;
import javax.swing.InputMap;
@@ -168,30 +167,28 @@ public class FlatScrollBarUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new BasicScrollBarUI.PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
scrollbar.revalidate();
scrollbar.repaint();
break;
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
scrollbar.revalidate();
scrollbar.repaint();
break;
case "componentOrientation":
// this is missing in BasicScrollBarUI.Handler.propertyChange()
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
if( rtlInputMap != null ) {
rtlInputMap.setParent( inputMap );
inputMap = rtlInputMap;
}
case "componentOrientation":
// this is missing in BasicScrollBarUI.Handler.propertyChange()
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
if( rtlInputMap != null ) {
rtlInputMap.setParent( inputMap );
inputMap = rtlInputMap;
}
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
break;
}
}
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
break;
}
};
}

View File

@@ -105,19 +105,17 @@ public class FlatScrollPaneUI
@Override
protected MouseWheelListener createMouseWheelListener() {
return new BasicScrollPaneUI.MouseWheelHandler() {
@Override
public void mouseWheelMoved( MouseWheelEvent e ) {
if( isSmoothScrollingEnabled() &&
scrollpane.isWheelScrollingEnabled() &&
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
{
mouseWheelMovedSmooth( e );
} else
super.mouseWheelMoved( e );
}
MouseWheelListener superListener = super.createMouseWheelListener();
return e -> {
if( isSmoothScrollingEnabled() &&
scrollpane.isWheelScrollingEnabled() &&
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
{
mouseWheelMovedSmooth( e );
} else
superListener.mouseWheelMoved( e );
};
}
@@ -239,41 +237,39 @@ public class FlatScrollPaneUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new BasicScrollPaneUI.PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
JScrollBar vsb = scrollpane.getVerticalScrollBar();
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
if( vsb != null ) {
vsb.revalidate();
vsb.repaint();
}
if( hsb != null ) {
hsb.revalidate();
hsb.repaint();
}
break;
case ScrollPaneConstants.LOWER_LEFT_CORNER:
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
case ScrollPaneConstants.UPPER_LEFT_CORNER:
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
// remove border from buttons added to corners
Object corner = e.getNewValue();
if( corner instanceof JButton &&
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
scrollpane.getViewport() != null &&
scrollpane.getViewport().getView() instanceof JTable )
{
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false );
}
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
JScrollBar vsb = scrollpane.getVerticalScrollBar();
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
if( vsb != null ) {
vsb.revalidate();
vsb.repaint();
}
if( hsb != null ) {
hsb.revalidate();
hsb.repaint();
}
break;
}
case ScrollPaneConstants.LOWER_LEFT_CORNER:
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
case ScrollPaneConstants.UPPER_LEFT_CORNER:
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
// remove border from buttons added to corners
Object corner = e.getNewValue();
if( corner instanceof JButton &&
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
scrollpane.getViewport() != null &&
scrollpane.getViewport().getView() instanceof JTable )
{
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false );
}
break;
}
};
}

View File

@@ -375,8 +375,8 @@ public class FlatSpinnerUI
Rectangle editorRect = new Rectangle( r );
Rectangle buttonsRect = new Rectangle( r );
// make button area square
int buttonsWidth = r.height;
// make button area square (if spinner has preferred height)
int buttonsWidth = parent.getPreferredSize().height - insets.top - insets.bottom;
buttonsRect.width = buttonsWidth;
if( parent.getComponentOrientation().isLeftToRight() ) {

View File

@@ -46,10 +46,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault SplitPaneDivider.border Border
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
*
* <!-- JSplitPane -->
*
* @uiDefault SplitPane.continuousLayout boolean
*
* <!-- FlatSplitPaneUI -->
*
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault SplitPane.continuousLayout boolean
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
@@ -65,7 +68,6 @@ public class FlatSplitPaneUI
extends BasicSplitPaneUI
{
protected String arrowType;
private Boolean continuousLayout;
protected Color oneTouchArrowColor;
protected Color oneTouchHoverArrowColor;
protected Color oneTouchPressedArrowColor;
@@ -85,8 +87,6 @@ public class FlatSplitPaneUI
oneTouchPressedArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchPressedArrowColor" );
super.installDefaults();
continuousLayout = (Boolean) UIManager.get( "SplitPane.continuousLayout" );
}
@Override
@@ -98,11 +98,6 @@ public class FlatSplitPaneUI
oneTouchPressedArrowColor = null;
}
@Override
public boolean isContinuousLayout() {
return super.isContinuousLayout() || (continuousLayout != null && Boolean.TRUE.equals( continuousLayout ));
}
@Override
public BasicSplitPaneDivider createDefaultDivider() {
return new FlatSplitPaneDivider( this );

View File

@@ -722,6 +722,13 @@ public class FlatTabbedPaneUI
}
protected Insets getRealTabAreaInsets( int tabPlacement ) {
// this is to avoid potential NPE in ensureSelectedTabIsVisible()
// (see https://github.com/JFormDesigner/FlatLaf/issues/299)
// but now should actually never occur because added more checks to
// ensureSelectedTabIsVisibleLater() and ensureSelectedTabIsVisible()
if( tabAreaInsets == null )
tabAreaInsets = new Insets( 0, 0, 0, 0 );
Insets currentTabAreaInsets = super.getTabAreaInsets( tabPlacement );
Insets insets = (Insets) currentTabAreaInsets.clone();
@@ -893,7 +900,7 @@ public class FlatTabbedPaneUI
Color color;
if( tabPane.isEnabled() && tabPane.isEnabledAt( tabIndex ) ) {
color = tabPane.getForegroundAt( tabIndex );
if( isSelected && (color instanceof UIResource) && selectedForeground != null )
if( isSelected && selectedForeground != null && color == tabPane.getForeground() )
color = selectedForeground;
} else
color = disabledForeground;
@@ -1386,13 +1393,18 @@ public class FlatTabbedPaneUI
}
protected void ensureSelectedTabIsVisibleLater() {
// do nothing if not yet displayable or if not invoked from dispatch thread,
// which may be the case when creating/modifying in another thread
if( !tabPane.isDisplayable() || !EventQueue.isDispatchThread() )
return;
EventQueue.invokeLater( () -> {
ensureSelectedTabIsVisible();
} );
}
protected void ensureSelectedTabIsVisible() {
if( tabPane == null || tabViewport == null )
if( tabPane == null || tabViewport == null || !tabPane.isDisplayable() )
return;
ensureCurrentLayout();
@@ -1559,7 +1571,7 @@ public class FlatTabbedPaneUI
FlatUIUtils.paintComponentBackground( g, left, top,
getWidth() - left - right,
getHeight() - top - bottom,
0, scale( buttonArc ) );
0, scale( (float) buttonArc ) );
}
}

View File

@@ -33,7 +33,9 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.JTableHeader;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -213,15 +215,18 @@ public class FlatTableUI
// - do not paint last vertical grid line if line is on right edge of scroll pane
// - fix unstable grid line thickness when scaled at 125%, 150%, 175%, 225%, ...
// which paints either 1px or 2px lines depending on location
// - on Java 9+, fix wrong grid line thickness in dragged column
boolean hideLastVerticalLine = hideLastVerticalLine();
int tableWidth = table.getWidth();
JTableHeader header = table.getTableHeader();
boolean isDragging = (header != null && header.getDraggedColumn() != null);
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
// Java 8 uses drawLine() to paint grid lines
// Java 9+ uses fillRect() to paint grid lines
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
g = new Graphics2DProxy( (Graphics2D) g ) {
@Override
public void drawLine( int x1, int y1, int x2, int y2 ) {
@@ -231,6 +236,22 @@ public class FlatTableUI
wasInvokedFromPaintGrid() )
return;
// on Java 9+, fix wrong grid line thickness in dragged column
if( isDragging &&
SystemInfo.isJava_9_orLater &&
((horizontalLines && y1 == y2) || (verticalLines && x1 == x2)) &&
wasInvokedFromPaintDraggedArea() )
{
if( y1 == y2 ) {
// horizontal grid line
super.fill( new Rectangle2D.Double( x1, y1, x2 - x1 + 1, lineThickness ) );
} else if( x1 == x2 ) {
// vertical grid line
super.fill( new Rectangle2D.Double( x1, y1, lineThickness, y2 - y1 + 1 ) );
}
return;
}
super.drawLine( x1, y1, x2, y2 );
}
@@ -258,11 +279,23 @@ public class FlatTableUI
}
private boolean wasInvokedFromPaintGrid() {
return wasInvokedFromMethod( "paintGrid" );
}
private boolean wasInvokedFromPaintDraggedArea() {
return wasInvokedFromMethod( "paintDraggedArea" );
}
private boolean wasInvokedFromMethod( String methodName ) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for( int i = 0; i < 10 || i < stackTrace.length; i++ ) {
if( "javax.swing.plaf.basic.BasicTableUI".equals( stackTrace[i].getClassName() ) &&
"paintGrid".equals( stackTrace[i].getMethodName() ) )
return true;
if( "javax.swing.plaf.basic.BasicTableUI".equals( stackTrace[i].getClassName() ) ) {
String methodName2 = stackTrace[i].getMethodName();
if( "paintCell".equals( methodName2 ) )
return false;
if( methodName.equals( methodName2 ) )
return true;
}
}
return false;
}

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
@@ -47,6 +48,7 @@ import java.util.List;
import java.util.Objects;
import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
@@ -63,7 +65,7 @@ import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -77,12 +79,16 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.inactiveForeground Color
* @uiDefault TitlePane.embeddedForeground Color
* @uiDefault TitlePane.borderColor Color optional
* @uiDefault TitlePane.unifiedBackground boolean
* @uiDefault TitlePane.iconSize Dimension
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.icon Icon
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon
@@ -100,9 +106,11 @@ public class FlatTitlePane
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 );
protected final JRootPane rootPane;
@@ -147,9 +155,15 @@ public class FlatTitlePane
protected void addSubComponents() {
leftPanel = new JPanel();
iconLabel = new JLabel();
titleLabel = new JLabel();
titleLabel = new JLabel() {
@Override
public void updateUI() {
setUI( new FlatTitleLabelUI() );
}
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -159,9 +173,7 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded())
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
: new Dimension();
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getPreferredSize() : new Dimension();
}
};
leftPanel.add( menuBarPlaceholder );
@@ -184,6 +196,20 @@ public class FlatTitlePane
if( !getComponentOrientation().isLeftToRight() )
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
}
// If menu bar is embedded and contains a horizontal glue component,
// then move the title label to the same location as the glue component
// and give it the same width.
// This allows placing any component on the trailing side of the title pane.
JMenuBar menuBar = rootPane.getJMenuBar();
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, titleLabel );
titleLabel.setBounds( titleLabel.getX() + glueLocation.x, titleLabel.getY(),
horizontalGlue.getWidth(), titleLabel.getHeight() );
}
}
}
} );
@@ -240,10 +266,17 @@ public class FlatTitlePane
}
protected void activeChanged( boolean active ) {
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && rootPane.getJMenuBar().isVisible() && isMenuBarEmbedded();
Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
Color titleForeground = (hasEmbeddedMenuBar && active) ? FlatUIUtils.nonUIResource( embeddedForeground ) : foreground;
Color background = clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null );
Color foreground = clientPropertyColor( rootPane, TITLE_BAR_FOREGROUND, null );
Color titleForeground = foreground;
if( background == null )
background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
if( foreground == null ) {
foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
titleForeground = (active && hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ))
? FlatUIUtils.nonUIResource( embeddedForeground )
: foreground;
}
setBackground( background );
titleLabel.setForeground( titleForeground );
@@ -252,8 +285,6 @@ public class FlatTitlePane
restoreButton.setForeground( foreground );
closeButton.setForeground( foreground );
titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING );
// this is necessary because hover/pressed colors are derived from background color
iconifyButton.setBackground( background );
maximizeButton.setBackground( background );
@@ -323,7 +354,7 @@ public class FlatTitlePane
iconLabel.setIcon( FlatTitlePaneIcon.create( images, iconSize ) );
else {
// no icon set on window --> use default icon
Icon defaultIcon = UIManager.getIcon( "InternalFrame.icon" );
Icon defaultIcon = UIManager.getIcon( "TitlePane.icon" );
if( defaultIcon != null && (defaultIcon.getIconWidth() == 0 || defaultIcon.getIconHeight() == 0) )
defaultIcon = null;
if( defaultIcon != null ) {
@@ -337,7 +368,7 @@ public class FlatTitlePane
// show/hide icon
iconLabel.setVisible( hasIcon );
updateJBRHitTestSpotsAndTitleBarHeightLater();
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@Override
@@ -355,7 +386,7 @@ public class FlatTitlePane
installWindowListeners();
}
updateJBRHitTestSpotsAndTitleBarHeightLater();
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@Override
@@ -394,11 +425,23 @@ public class FlatTitlePane
window.removeComponentListener( handler );
}
/**
* Returns whether this title pane currently has an visible and embedded menubar.
*/
protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) {
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
}
/**
* Returns whether the menubar should be embedded into the title pane.
*/
protected boolean isMenuBarEmbedded() {
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
return UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) &&
FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.MENU_BAR_EMBEDDED, true ) &&
FlatSystemProperties.getBoolean( FlatSystemProperties.MENUBAR_EMBEDDED, true );
return FlatUIUtils.getBoolean( rootPane,
FlatSystemProperties.MENUBAR_EMBEDDED,
FlatClientProperties.MENU_BAR_EMBEDDED,
"TitlePane.menuBarEmbedded",
false );
}
protected Rectangle getMenuBarBounds() {
@@ -412,13 +455,42 @@ public class FlatTitlePane
Insets borderInsets = getBorder().getBorderInsets( this );
bounds.height += borderInsets.bottom;
return FlatUIUtils.subtractInsets( bounds, UIScale.scale( getMenuBarMargins() ) );
// If menu bar is embedded and contains a horizontal glue component,
// then make the menu bar wider so that it completely overlaps the title label.
// Since the menu bar is not opaque, the title label is still visible.
// The title label is moved to the location of the glue component by the layout manager.
// This allows placing any component on the trailing side of the title pane.
Component horizontalGlue = findHorizontalGlue( rootPane.getJMenuBar() );
if( horizontalGlue != null ) {
boolean leftToRight = getComponentOrientation().isLeftToRight();
int titleWidth = leftToRight
? buttonPanel.getX() - (leftPanel.getX() + leftPanel.getWidth())
: leftPanel.getX() - (buttonPanel.getX() + buttonPanel.getWidth());
titleWidth = Math.max( titleWidth, 0 ); // title width may be negative
bounds.width += titleWidth;
if( !leftToRight )
bounds.x -= titleWidth;
}
return bounds;
}
protected Insets getMenuBarMargins() {
return getComponentOrientation().isLeftToRight()
? menuBarMargins
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
protected Component findHorizontalGlue( JMenuBar menuBar ) {
if( menuBar == null )
return null;
int count = menuBar.getComponentCount();
for( int i = count - 1; i >= 0; i-- ) {
Component c = menuBar.getComponent( i );
if( c instanceof Box.Filler && c.getMaximumSize().width >= Short.MAX_VALUE )
return c;
}
return null;
}
protected void titleBarColorsChanged() {
activeChanged( window == null || window.isActive() );
repaint();
}
protected void menuBarChanged() {
@@ -435,7 +507,8 @@ public class FlatTitlePane
}
protected void menuBarLayouted() {
updateJBRHitTestSpotsAndTitleBarHeightLater();
updateNativeTitleBarHeightAndHitTestSpotsLater();
revalidate();
}
/*debug
@@ -449,15 +522,26 @@ public class FlatTitlePane
}
if( debugHitTestSpots != null ) {
g.setColor( Color.blue );
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
for( Rectangle r : debugHitTestSpots )
g.drawRect( r.x, r.y, r.width, r.height );
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
}
if( debugAppIconBounds != null ) {
g.setColor( Color.red );
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
Rectangle r = debugAppIconBounds;
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
}
}
debug*/
@Override
protected void paintComponent( Graphics g ) {
g.setColor( getBackground() );
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
? FlatUIUtils.getParentBackground( this )
: getBackground() );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
@@ -475,10 +559,12 @@ debug*/
* Iconifies the window.
*/
protected void iconify() {
if( window instanceof Frame ) {
Frame frame = (Frame) window;
if( !(window instanceof Frame) )
return;
Frame frame = (Frame) window;
if( !FlatNativeWindowBorder.showWindow( window, FlatNativeWindowBorder.Provider.SW_MINIMIZE ) )
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
}
}
/**
@@ -496,16 +582,17 @@ debug*/
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
// maximize window
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
if( !FlatNativeWindowBorder.showWindow( frame, FlatNativeWindowBorder.Provider.SW_MAXIMIZE ) )
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
}
protected void updateMaximizedBounds() {
Frame frame = (Frame) window;
// set maximized bounds to avoid that maximized window overlaps Windows task bar
// (if not running in JBR and if not modified from the application)
// (if not having native window border and if not modified from the application)
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
if( !hasJBRCustomDecoration() &&
if( !hasNativeCustomDecoration() &&
(oldMaximizedBounds == null ||
Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
{
@@ -584,8 +671,11 @@ debug*/
* Restores the window size.
*/
protected void restore() {
if( window instanceof Frame ) {
Frame frame = (Frame) window;
if( !(window instanceof Frame) )
return;
Frame frame = (Frame) window;
if( !FlatNativeWindowBorder.showWindow( window, FlatNativeWindowBorder.Provider.SW_RESTORE ) ) {
int state = frame.getExtendedState();
frame.setExtendedState( ((state & Frame.ICONIFIED) != 0)
? (state & ~Frame.ICONIFIED)
@@ -601,65 +691,118 @@ debug*/
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
}
protected boolean hasJBRCustomDecoration() {
return FlatRootPaneUI.canUseJBRCustomDecorations &&
window != null &&
JBRCustomDecorations.hasCustomDecoration( window );
private boolean hasJBRCustomDecoration() {
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
}
protected void updateJBRHitTestSpotsAndTitleBarHeightLater() {
/**
* Returns whether windows uses native window border and has custom decorations enabled.
*/
protected boolean hasNativeCustomDecoration() {
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
}
protected void updateNativeTitleBarHeightAndHitTestSpotsLater() {
EventQueue.invokeLater( () -> {
updateJBRHitTestSpotsAndTitleBarHeight();
updateNativeTitleBarHeightAndHitTestSpots();
} );
}
protected void updateJBRHitTestSpotsAndTitleBarHeight() {
protected void updateNativeTitleBarHeightAndHitTestSpots() {
if( !isDisplayable() )
return;
if( !hasJBRCustomDecoration() )
if( !hasNativeCustomDecoration() )
return;
List<Rectangle> hitTestSpots = new ArrayList<>();
if( iconLabel.isVisible() )
addJBRHitTestSpot( iconLabel, false, hitTestSpots );
addJBRHitTestSpot( buttonPanel, false, hitTestSpots );
addJBRHitTestSpot( menuBarPlaceholder, true, hitTestSpots );
int titleBarHeight = getHeight();
// slightly reduce height so that component receives mouseExit events
if( titleBarHeight > 0 )
titleBarHeight--;
JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight );
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) {
// compute real icon size (without insets)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets();
Rectangle iconBounds = new Rectangle(
location.x + iconInsets.left,
location.y + iconInsets.top,
iconLabel.getWidth() - iconInsets.left - iconInsets.right,
iconLabel.getHeight() - iconInsets.top - iconInsets.bottom );
if( hasJBRCustomDecoration() )
hitTestSpots.add( iconBounds );
else
appIconBounds = iconBounds;
}
Rectangle r = getNativeHitTestSpot( buttonPanel );
if( r != null )
hitTestSpots.add( r );
JMenuBar menuBar = rootPane.getJMenuBar();
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBarPlaceholder );
if( r != null ) {
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
// If menu bar is embedded and contains a horizontal glue component,
// then split the hit test spot into two spots so that
// the glue component area can used to move the window.
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
Rectangle r2;
if( getComponentOrientation().isLeftToRight() ) {
int trailingWidth = (r.x + r.width - HIT_TEST_SPOT_GROW) - glueLocation.x;
r.width -= trailingWidth;
r2 = new Rectangle( glueLocation.x + horizontalGlue.getWidth(), r.y, trailingWidth, r.height );
} else {
int leadingWidth = (glueLocation.x + horizontalGlue.getWidth()) - (r.x + HIT_TEST_SPOT_GROW);
r.x += leadingWidth;
r.width -= leadingWidth;
r2 = new Rectangle( glueLocation.x -leadingWidth, r.y, leadingWidth, r.height );
}
r2.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
hitTestSpots.add( r2 );
}
hitTestSpots.add( r );
}
}
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds );
/*debug
debugHitTestSpots = hitTestSpots;
debugTitleBarHeight = titleBarHeight;
debugHitTestSpots = hitTestSpots;
debugAppIconBounds = appIconBounds;
repaint();
debug*/
}
protected void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
protected Rectangle getNativeHitTestSpot( JComponent c ) {
Dimension size = c.getSize();
if( size.width <= 0 || size.height <= 0 )
return;
return null;
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
Rectangle r = new Rectangle( location, size );
if( subtractMenuBarMargins )
r = FlatUIUtils.subtractInsets( r, UIScale.scale( getMenuBarMargins() ) );
// slightly increase rectangle so that component receives mouseExit events
r.grow( 2, 2 );
hitTestSpots.add( r );
r.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
return r;
}
private static final int HIT_TEST_SPOT_GROW = 2;
/*debug
private List<Rectangle> debugHitTestSpots;
private int debugTitleBarHeight;
private List<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds;
debug*/
//---- class TitlePaneBorder ----------------------------------------------
//---- class FlatTitlePaneBorder ------------------------------------------
protected class FlatTitlePaneBorder
extends AbstractBorder
@@ -676,8 +819,8 @@ debug*/
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( hasJBRCustomDecoration() )
insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() );
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets;
}
@@ -695,13 +838,57 @@ debug*/
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
if( hasNativeCustomDecoration() && !isWindowMaximized( c ) )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
}
protected Border getMenuBarBorder() {
JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded()) ? menuBar.getBorder() : null;
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
}
protected boolean isWindowMaximized( Component c ) {
return window instanceof Frame
? (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
: false;
}
}
//---- class FlatTitleLabelUI ---------------------------------------------
/**
* @since 1.1
*/
protected class FlatTitleLabelUI
extends FlatLabelUI
{
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
int labelWidth = l.getWidth();
int textWidth = labelWidth - (textX * 2);
int gap = UIScale.scale( menuBarTitleGap );
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise leave it centered within free space (label bounds).
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
textX = centeredTextX;
} else {
// leading aligned
boolean leftToRight = getComponentOrientation().isLeftToRight();
Insets insets = l.getInsets();
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
textX = leadingTextX;
}
super.paintEnabledText( l, g, s, textX, textY );
}
}
@@ -730,7 +917,7 @@ debug*/
break;
case "componentOrientation":
updateJBRHitTestSpotsAndTitleBarHeightLater();
updateNativeTitleBarHeightAndHitTestSpotsLater();
break;
}
}
@@ -740,10 +927,10 @@ debug*/
@Override
public void windowActivated( WindowEvent e ) {
activeChanged( true );
updateJBRHitTestSpotsAndTitleBarHeight();
updateNativeTitleBarHeightAndHitTestSpots();
if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
if( hasNativeCustomDecoration() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
}
@@ -751,10 +938,10 @@ debug*/
@Override
public void windowDeactivated( WindowEvent e ) {
activeChanged( false );
updateJBRHitTestSpotsAndTitleBarHeight();
updateNativeTitleBarHeightAndHitTestSpots();
if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
if( hasNativeCustomDecoration() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
}
@@ -762,7 +949,7 @@ debug*/
@Override
public void windowStateChanged( WindowEvent e ) {
frameStateChanged();
updateJBRHitTestSpotsAndTitleBarHeight();
updateNativeTitleBarHeightAndHitTestSpots();
}
//---- interface MouseListener ----
@@ -775,7 +962,7 @@ debug*/
if( e.getSource() == iconLabel ) {
// double-click on icon closes window
close();
} else if( !hasJBRCustomDecoration() &&
} else if( !hasNativeCustomDecoration() &&
window instanceof Frame &&
((Frame)window).isResizable() )
{
@@ -808,8 +995,8 @@ debug*/
if( window == null )
return; // should newer occur
if( hasJBRCustomDecoration() )
return; // do nothing if running in JBR
if( hasNativeCustomDecoration() )
return; // do nothing if having native window border
// restore window if it is maximized
if( window instanceof Frame ) {
@@ -852,7 +1039,7 @@ debug*/
@Override
public void componentResized( ComponentEvent e ) {
updateJBRHitTestSpotsAndTitleBarHeightLater();
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@Override

View File

@@ -25,7 +25,6 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.CellRendererPane;
import javax.swing.Icon;
@@ -193,40 +192,38 @@ public class FlatTreeUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new BasicTreeUI.PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
if( e.getSource() == tree ) {
switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION:
case TREE_PAINT_SELECTION:
tree.repaint();
break;
if( e.getSource() == tree ) {
switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION:
case TREE_PAINT_SELECTION:
tree.repaint();
break;
case "dropLocation":
if( isWideSelection() ) {
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
repaintWideDropLocation( oldValue );
repaintWideDropLocation( tree.getDropLocation() );
}
break;
}
case "dropLocation":
if( isWideSelection() ) {
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
repaintWideDropLocation( oldValue );
repaintWideDropLocation( tree.getDropLocation() );
}
break;
}
}
private void repaintWideDropLocation(JTree.DropLocation loc) {
if( loc == null || isDropLine( loc ) )
return;
Rectangle r = tree.getPathBounds( loc.getPath() );
if( r != null )
tree.repaint( 0, r.y, tree.getWidth(), r.height );
}
};
}
private void repaintWideDropLocation(JTree.DropLocation loc) {
if( loc == null || isDropLine( loc ) )
return;
Rectangle r = tree.getPathBounds( loc.getPath() );
if( r != null )
tree.repaint( 0, r.y, tree.getWidth(), r.height );
}
/**
* Same as super.paintRow(), but supports wide selection and uses
* inactive selection background/foreground if tree is not focused.

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.ui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
@@ -30,6 +31,7 @@ import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Window;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
@@ -44,6 +46,7 @@ import java.util.function.Supplier;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
@@ -51,6 +54,7 @@ import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
@@ -119,6 +123,14 @@ public class FlatUIUtils
return (color != null) ? color : UIManager.getColor( defaultKey );
}
/**
* @since 1.1
*/
public static boolean getUIBoolean( String key, boolean defaultValue ) {
Object value = UIManager.get( key );
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
}
public static int getUIInt( String key, int defaultValue ) {
Object value = UIManager.get( key );
return (value instanceof Integer) ? (Integer) value : defaultValue;
@@ -129,6 +141,25 @@ public class FlatUIUtils
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
}
/**
* @since 1.1.2
*/
public static boolean getBoolean( JComponent c, String systemPropertyKey,
String clientPropertyKey, String uiKey, boolean defaultValue )
{
// check whether forced to true/false via system property
Boolean value = FlatSystemProperties.getBooleanStrict( systemPropertyKey, null );
if( value != null )
return value;
// check whether forced to true/false via client property
value = FlatClientProperties.clientPropertyBooleanStrict( c, clientPropertyKey, null );
if( value != null )
return value;
return getUIBoolean( uiKey, defaultValue );
}
public static boolean isChevron( String arrowType ) {
return !"triangle".equals( arrowType );
}
@@ -174,7 +205,8 @@ public class FlatUIUtils
/**
* Returns whether the given component is the permanent focus owner and
* is in the active window. Used to paint focus indicators.
* is in the active window or in a popup window owned by the active window.
* Used to paint focus indicators.
*/
@SuppressWarnings( "unchecked" )
public static boolean isPermanentFocusOwner( Component c ) {
@@ -184,12 +216,18 @@ public class FlatUIUtils
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
if( value instanceof Predicate ) {
return ((Predicate<JComponent>)value).test( (JComponent) c ) &&
keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c );
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
}
}
return keyboardFocusManager.getPermanentFocusOwner() == c &&
keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c );
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
}
private static boolean isInActiveWindow( Component c, Window activeWindow ) {
Window window = SwingUtilities.windowForComponent( c );
return window == activeWindow ||
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
}
/**
@@ -604,6 +642,111 @@ public class FlatUIUtils
return rect;
}
/**
* Paints a chevron or triangle arrow in the center of the given rectangle.
*
* @param g the graphics context used for painting
* @param x the x coordinate of the rectangle
* @param y the y coordinate of the rectangle
* @param width the width of the rectangle
* @param height the height of the rectangle
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
* @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 xOffset a 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)
*
* @since 1.1
*/
public static void paintArrow( Graphics2D g, int x, int y, int width, int height,
int direction, boolean chevron, int arrowSize, int xOffset, int yOffset )
{
// compute arrow width/height
int aw = UIScale.scale( arrowSize + (chevron ? 0 : 1) );
int ah = UIScale.scale( (arrowSize / 2) + (chevron ? 0 : 1) );
// rotate arrow width/height for horizontal directions
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
if( !vert ) {
int temp = aw;
aw = ah;
ah = temp;
}
// chevron lines end 1px outside of width/height
// --> add 1px to arrow width/height for position calculation
int extra = chevron ? 1 : 0;
// compute arrow location
int ax = x + Math.round( ((width - (aw + extra)) / 2f) + UIScale.scale( (float) xOffset ) );
int ay = y + Math.round( ((height - (ah + extra)) / 2f) + UIScale.scale( (float) yOffset ) );
// paint arrow
g.translate( ax, ay );
/*debug
debugPaintArrow( g, Color.red, vert, aw + extra, ah + extra );
debug*/
Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
if( chevron ) {
Stroke oldStroke = g.getStroke();
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
g.draw( arrowShape );
g.setStroke( oldStroke );
} else {
// triangle
g.fill( arrowShape );
}
g.translate( -ax, -ay );
}
/**
* Creates a chevron or triangle arrow shape for the given direction and size.
* <p>
* The chevron shape is a 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)}.
*
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
* @param w the width of the returned shape
* @param h the height of the returned shape
*
* @since 1.1
*/
public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
switch( direction ) {
case SwingConstants.NORTH: return createPath( !chevron, 0,h, (w / 2f),0, w,h );
case SwingConstants.SOUTH: return createPath( !chevron, 0,0, (w / 2f),h, w,0 );
case SwingConstants.WEST: return createPath( !chevron, w,0, 0,(h / 2f), w,h );
case SwingConstants.EAST: return createPath( !chevron, 0,0, w,(h / 2f), 0,h );
default: return new Path2D.Float();
}
}
/*debug
private static void debugPaintArrow( Graphics2D g, Color color, boolean vert, int w, int h ) {
Color oldColor = g.getColor();
g.setColor( color );
g.fill( createRectangle( 0, 0, w, h, 1 ) );
int xy1 = -2;
int x2 = w + 1;
int y2 = h + 1;
for( int i = 0; i < 20; i++ ) {
g.fillRect( 0, xy1, 1, 1 );
g.fillRect( 0, y2, 1, 1 );
g.fillRect( xy1, 0, 1, 1 );
g.fillRect( x2, 0, 1, 1 );
xy1 -= 2;
x2 += 2;
y2 += 2;
}
g.setColor( oldColor );
}
debug*/
/**
* Creates a closed path for the given points.
*/

View File

@@ -0,0 +1,405 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.geom.AffineTransform;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo;
//
// Interesting resources:
// https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
// https://docs.microsoft.com/en-us/windows/win32/dwm/customframe
// https://github.com/JetBrains/JetBrainsRuntime/blob/master/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp
// https://github.com/JetBrains/JetBrainsRuntime/commit/d2820524a1aa211b1c49b30f659b9b4d07a6f96e
// https://github.com/JetBrains/JetBrainsRuntime/pull/18
// https://medium.com/swlh/customizing-the-title-bar-of-an-application-window-50a4ac3ed27e
// https://github.com/kalbetredev/CustomDecoratedJFrame
// https://github.com/Guerra24/NanoUI-win32
// https://github.com/oberth/custom-chrome
// https://github.com/rossy/borderless-window
//
/**
* Native window border support for Windows 10 when using custom decorations.
* <p>
* If the application wants to use custom decorations, the Windows 10 title bar is hidden
* (including minimize, maximize and close buttons), but not the resize borders (including drop shadow).
* Windows 10 window snapping functionality will remain unaffected:
* https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241
*
* @author Karl Tauber
* @since 1.1
*/
class FlatWindowsNativeWindowBorder
implements FlatNativeWindowBorder.Provider
{
private final Map<Window, WndProc> windowsMap = Collections.synchronizedMap( new IdentityHashMap<>() );
private final EventListenerList listenerList = new EventListenerList();
private Timer fireStateChangedTimer;
private boolean colorizationUpToDate;
private boolean colorizationColorAffectsBorders;
private Color colorizationColor;
private int colorizationColorBalance;
private static NativeLibrary nativeLibrary;
private static FlatWindowsNativeWindowBorder instance;
static FlatNativeWindowBorder.Provider getInstance() {
// requires Windows 10
if( !SystemInfo.isWindows_10_orLater )
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( 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
if( !nativeLibrary.isLoaded() )
return null;
// create new instance
if( instance == null )
instance = new FlatWindowsNativeWindowBorder();
return instance;
}
private FlatWindowsNativeWindowBorder() {
}
@Override
public boolean hasCustomDecoration( Window window ) {
return windowsMap.containsKey( window );
}
/**
* Tell the window whether the application wants use custom decorations.
* If {@code true}, the Windows 10 title bar is hidden (including minimize,
* maximize and close buttons), but not the resize borders (including drop shadow).
*/
@Override
public void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( hasCustomDecoration )
install( window );
else
uninstall( window );
}
private void install( Window window ) {
// requires Windows 10
if( !SystemInfo.isWindows_10_orLater )
return;
// only JFrame and JDialog are supported
if( !(window instanceof JFrame) && !(window instanceof JDialog) )
return;
// not supported if frame/dialog is undecorated
if( (window instanceof Frame && ((Frame)window).isUndecorated()) ||
(window instanceof Dialog && ((Dialog)window).isUndecorated()) )
return;
// check whether already installed
if( windowsMap.containsKey( window ) )
return;
// install
WndProc wndProc = new WndProc( window );
if( wndProc.hwnd == 0 )
return;
windowsMap.put( window, wndProc );
}
private void uninstall( Window window ) {
WndProc wndProc = windowsMap.remove( window );
if( wndProc != null )
wndProc.uninstall();
}
@Override
public void setTitleBarHeight( Window window, int titleBarHeight ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.titleBarHeight = titleBarHeight;
}
@Override
public void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
}
@Override
public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
}
@Override
public boolean showWindow( Window window, int cmd ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return false;
wndProc.showWindow( wndProc.hwnd, cmd );
return true;
}
@Override
public boolean isColorizationColorAffectsBorders() {
updateColorization();
return colorizationColorAffectsBorders;
}
@Override
public Color getColorizationColor() {
updateColorization();
return colorizationColor;
}
@Override
public int getColorizationColorBalance() {
updateColorization();
return colorizationColorBalance;
}
private void updateColorization() {
if( colorizationUpToDate )
return;
colorizationUpToDate = true;
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
colorizationColorAffectsBorders = (value > 0);
value = registryGetIntValue( subKey, "ColorizationColor", -1 );
colorizationColor = (value != -1) ? new Color( value ) : null;
colorizationColorBalance = registryGetIntValue( subKey, "ColorizationColorBalance", -1 );
}
private native static int registryGetIntValue( String key, String valueName, int defaultValue );
@Override
public void addChangeListener( ChangeListener l ) {
listenerList.add( ChangeListener.class, l );
}
@Override
public void removeChangeListener( ChangeListener l ) {
listenerList.remove( ChangeListener.class, l );
}
private void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
if( listeners.length == 0 )
return;
ChangeEvent e = new ChangeEvent( this );
for( int i = 0; i < listeners.length; i += 2 ) {
if( listeners[i] == ChangeListener.class )
((ChangeListener)listeners[i+1]).stateChanged( e );
}
}
/**
* Because there may be sent many WM_DWMCOLORIZATIONCOLORCHANGED messages,
* slightly delay event firing and fire it only once (on the AWT thread).
*/
void fireStateChangedLaterOnce() {
EventQueue.invokeLater( () -> {
if( fireStateChangedTimer != null ) {
fireStateChangedTimer.restart();
return;
}
fireStateChangedTimer = new Timer( 300, e -> {
fireStateChangedTimer = null;
colorizationUpToDate = false;
fireStateChanged();
} );
fireStateChangedTimer.setRepeats( false );
fireStateChangedTimer.start();
} );
}
//---- class WndProc ------------------------------------------------------
private class WndProc
{
// WM_NCHITTEST mouse position codes
private static final int
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTTOP = 12;
private Window window;
private final long hwnd;
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private Rectangle appIconBounds;
WndProc( Window window ) {
this.window = window;
hwnd = installImpl( window );
// remove the OS window title bar
if( window instanceof JFrame && ((JFrame)window).getExtendedState() != 0 ) {
// In case that the frame should be maximized or minimized immediately
// when showing, then it is necessary to defer ::SetWindowPos() invocation.
// Otherwise the frame will not be maximized or minimized.
// This occurs only if frame.pack() was no invoked.
EventQueue.invokeLater( () -> {
updateFrame( hwnd );
});
} else
updateFrame( hwnd );
}
void uninstall() {
uninstallImpl( hwnd );
// cleanup
window = null;
}
private native long installImpl( Window window );
private native void uninstallImpl( long hwnd );
private native void updateFrame( long hwnd );
private native void showWindow( long hwnd, int cmd );
// invoked from native code
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
// scale-down mouse x/y
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
return HTSYSMENU;
boolean isOnTitleBar = (sy < titleBarHeight);
if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed
// in another thread while processing the array
Rectangle[] hitTestSpots2 = hitTestSpots;
for( Rectangle spot : hitTestSpots2 ) {
if( spot.contains( sx, sy ) )
return HTCLIENT;
}
return isOnResizeBorder ? HTTOP : HTCAPTION;
}
return isOnResizeBorder ? HTTOP : HTCLIENT;
}
/**
* Scales down in the same way as AWT.
* See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()
*/
private Point scaleDown( int x, int y ) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if( gc == null )
return new Point( x, y );
AffineTransform t = gc.getDefaultTransform();
return new Point( clipRound( x / t.getScaleX() ), clipRound( y / t.getScaleY() ) );
}
/**
* Rounds in the same way as AWT.
* See AwtWin32GraphicsDevice::ClipRound()
*/
private int clipRound( double value ) {
value -= 0.5;
if( value < Integer.MIN_VALUE )
return Integer.MIN_VALUE;
if( value > Integer.MAX_VALUE )
return Integer.MAX_VALUE;
return (int) Math.ceil( value );
}
// invoked from native code
private boolean isFullscreen() {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if( gc == null )
return false;
return gc.getDevice().getFullScreenWindow() == window;
}
// invoked from native code
private void fireStateChangedLaterOnce() {
FlatWindowsNativeWindowBorder.this.fireStateChangedLaterOnce();
}
}
}

View File

@@ -29,17 +29,14 @@ import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -55,26 +52,29 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
public class JBRCustomDecorations
{
private static boolean initialized;
private static Boolean supported;
private static Method Window_hasCustomDecoration;
private static Method Window_setHasCustomDecoration;
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
private static Method AWTAccessor_getComponentAccessor;
private static Method AWTAccessor_ComponentAccessor_getPeer;
public static boolean isSupported() {
initialize();
return Window_setHasCustomDecoration != null;
return supported;
}
static void install( JRootPane rootPane ) {
static Object install( JRootPane rootPane ) {
if( !isSupported() )
return;
return null;
// check whether root pane already has a parent, which is the case when switching LaF
if( rootPane.getParent() != null )
return;
Window window = SwingUtilities.windowForComponent( rootPane );
if( window != null ) {
FlatNativeWindowBorder.install( window );
return null;
}
// Use hierarchy listener to wait until the root pane is added to a window.
// Enabling JBR decorations must be done very early, probably before
@@ -88,8 +88,9 @@ public class JBRCustomDecorations
Container parent = e.getChangedParent();
if( parent instanceof Window )
install( (Window) parent );
FlatNativeWindowBorder.install( (Window) parent );
// remove listener since it is actually not possible to uninstall JBR decorations
// use invokeLater to remove listener to avoid that listener
// is removed while listener queue is processed
EventQueue.invokeLater( () -> {
@@ -98,54 +99,20 @@ public class JBRCustomDecorations
}
};
rootPane.addHierarchyListener( addListener );
return addListener;
}
static void install( Window window ) {
if( !isSupported() )
return;
static void uninstall( JRootPane rootPane, Object data ) {
// remove listener (if not yet done)
if( data instanceof HierarchyListener )
rootPane.removeHierarchyListener( (HierarchyListener) data );
// do not enable JBR decorations if LaF provides decorations
if( UIManager.getLookAndFeel().getSupportsWindowDecorations() )
return;
if( window instanceof JFrame ) {
JFrame frame = (JFrame) window;
// do not enable JBR decorations if JFrame should use system window decorations
// and if not forced to use JBR decorations
if( !JFrame.isDefaultLookAndFeelDecorated() &&
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
return;
// do not enable JBR decorations if frame is undecorated
if( frame.isUndecorated() )
return;
// enable JBR custom window decoration for window
setHasCustomDecoration( frame );
// enable Swing window decoration
frame.getRootPane().setWindowDecorationStyle( JRootPane.FRAME );
} else if( window instanceof JDialog ) {
JDialog dialog = (JDialog) window;
// do not enable JBR decorations if JDialog should use system window decorations
// and if not forced to use JBR decorations
if( !JDialog.isDefaultLookAndFeelDecorated() &&
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false ))
return;
// do not enable JBR decorations if dialog is undecorated
if( dialog.isUndecorated() )
return;
// enable JBR custom window decoration for window
setHasCustomDecoration( dialog );
// enable Swing window decoration
dialog.getRootPane().setWindowDecorationStyle( JRootPane.PLAIN_DIALOG );
}
// since it is actually not possible to uninstall JBR decorations,
// simply reduce titleBarHeight so that it is still possible to resize window
// and remove hitTestSpots
Window window = SwingUtilities.windowForComponent( rootPane );
if( window != null )
setHasCustomDecoration( window, false );
}
static boolean hasCustomDecoration( Window window ) {
@@ -155,48 +122,48 @@ public class JBRCustomDecorations
try {
return (Boolean) Window_hasCustomDecoration.invoke( window );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
return false;
}
}
static void setHasCustomDecoration( Window window ) {
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( !isSupported() )
return;
try {
Window_setHasCustomDecoration.invoke( window );
if( hasCustomDecoration )
Window_setHasCustomDecoration.invoke( window );
else
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
static void setHitTestSpotsAndTitleBarHeight( Window window, List<Rectangle> hitTestSpots, int titleBarHeight ) {
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
if( !isSupported() )
return;
try {
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
private static void initialize() {
if( initialized )
if( supported != null )
return;
initialized = true;
supported = false;
// requires JetBrains Runtime 11 and Windows 10
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
return;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) )
return;
try {
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
@@ -204,15 +171,17 @@ public class JBRCustomDecorations
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
Window_hasCustomDecoration.setAccessible( true );
Window_setHasCustomDecoration.setAccessible( true );
supported = true;
} catch( Exception ex ) {
// ignore
}
@@ -237,15 +206,22 @@ public class JBRCustomDecorations
return instance;
}
private JBRWindowTopBorder() {
JBRWindowTopBorder() {
super( 1, 0, 0, 0 );
colorizationAffectsBorders = calculateAffectsBorders();
activeColor = calculateActiveBorderColor();
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
colorizationAffectsBorders = calculateAffectsBorders();
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
} );
@@ -257,46 +233,50 @@ public class JBRCustomDecorations
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
}
private boolean calculateAffectsBorders() {
boolean isColorizationColorAffectsBorders() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
return (value instanceof Boolean) ? (Boolean) value : true;
}
Color getColorizationColor() {
return (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor" );
}
int getColorizationColorBalance() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColorBalance" );
return (value instanceof Integer) ? (Integer) value : -1;
}
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return defaultActiveBorder;
Toolkit toolkit = Toolkit.getDefaultToolkit();
Color colorizationColor = (Color) toolkit.getDesktopProperty( "win.dwm.colorizationColor" );
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
Object colorizationColorBalanceObj = toolkit.getDesktopProperty( "win.dwm.colorizationColorBalance" );
if( colorizationColorBalanceObj instanceof Integer ) {
int colorizationColorBalance = (Integer) colorizationColorBalanceObj;
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
return colorizationColor;
return new Color( r, g, b );
}
Color activeBorderColor = (Color) toolkit.getDesktopProperty( "win.frame.activeBorderColor" );
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}

View File

@@ -21,10 +21,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import com.formdev.flatlaf.FlatLaf;
/**
* Provides Java version compatibility methods.
@@ -58,7 +55,7 @@ public class JavaCompatibility
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );
}
}
@@ -70,7 +67,7 @@ public class JavaCompatibility
else
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );
}
}
@@ -94,7 +91,7 @@ public class JavaCompatibility
: "clipStringIfNecessary",
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );
}
}
@@ -103,7 +100,7 @@ public class JavaCompatibility
try {
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2021 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;
/**
* @since 1.1
*/
public interface LoggingFacade
{
LoggingFacade INSTANCE = new LoggingFacadeImpl();
void logSevere( String message, Throwable t );
void logConfig( String message, Throwable t );
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2021 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 com.formdev.flatlaf.FlatLaf;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @since 1.1
*/
class LoggingFacadeImpl
implements LoggingFacade
{
private static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
@Override
public void logSevere( String message, Throwable t ) {
LOG.log( Level.SEVERE, message, t );
}
@Override
public void logConfig( String message, Throwable t ) {
LOG.log( Level.CONFIG, message, t );
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2021 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.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
/**
* Helper class to load native library (.dll, .so or .dylib) stored in Jar.
* <p>
* Copies native library to users temporary folder before loading it.
*
* @author Karl Tauber
* @since 1.1
*/
public class NativeLibrary
{
private static final String DELETE_SUFFIX = ".delete";
private static boolean deletedTemporary;
private final boolean loaded;
/**
* Load native library from given classloader.
* <p>
* Note regarding Java Platform Module System (JPMS):
* If classloader is {@code null}, the library can be only loaded from the module
* that contains this class.
* If classloader is not {@code null}, then the package that contains the library
* must be specified as "open" in module-info.java of the module that contains the library.
*
* @param libraryName resource name of the native library (without "lib" prefix and without extension)
* @param classLoader the classloader used to locate the library, or {@code null}
* @param supported whether the native library is supported on the current platform
*/
public NativeLibrary( String libraryName, ClassLoader classLoader, boolean supported ) {
this.loaded = supported
? loadLibraryFromJar( libraryName, classLoader )
: false;
}
/**
* Returns whether the native library is loaded.
* <p>
* Returns {@code false} if not supported on current platform as specified in constructor
* or if loading failed.
*/
public boolean isLoaded() {
return loaded;
}
private static boolean loadLibraryFromJar( String libraryName, ClassLoader classLoader ) {
// add prefix and suffix to library name
libraryName = decorateLibraryName( libraryName );
// find library
URL libraryUrl = (classLoader != null)
? classLoader.getResource( libraryName )
: NativeLibrary.class.getResource( "/" + libraryName );
if( libraryUrl == null ) {
log( "Library '" + libraryName + "' not found", null );
return false;
}
File tempFile = null;
try {
// for development environment
if( "file".equals( libraryUrl.getProtocol() ) ) {
File libraryFile = new File( libraryUrl.getPath() );
if( libraryFile.isFile() ) {
// load library without copying
System.load( libraryFile.getCanonicalPath() );
return true;
}
}
// create temporary file
Path tempPath = createTempFile( libraryName );
tempFile = tempPath.toFile();
// copy library to temporary file
try( InputStream in = libraryUrl.openStream() ) {
Files.copy( in, tempPath, StandardCopyOption.REPLACE_EXISTING );
}
// load library
System.load( tempFile.getCanonicalPath() );
// delete library
deleteOrMarkForDeletion( tempFile );
return true;
} catch( Throwable ex ) {
log( null, ex );
if( tempFile != null )
deleteOrMarkForDeletion( tempFile );
return false;
}
}
private static String decorateLibraryName( String libraryName ) {
if( SystemInfo.isWindows )
return libraryName.concat( ".dll" );
String suffix = SystemInfo.isMacOS ? ".dylib" : ".so";
int sep = libraryName.lastIndexOf( '/' );
return (sep >= 0)
? libraryName.substring( 0, sep + 1 ) + "lib" + libraryName.substring( sep + 1 ) + suffix
: "lib" + libraryName + suffix;
}
private static void log( String msg, Throwable thrown ) {
LoggingFacade.INSTANCE.logSevere( msg, thrown );
}
private static Path createTempFile( String libraryName ) throws IOException {
int sep = libraryName.lastIndexOf( '/' );
String name = (sep >= 0) ? libraryName.substring( sep + 1 ) : libraryName;
int dot = name.lastIndexOf( '.' );
String prefix = ((dot >= 0) ? name.substring( 0, dot ) : name) + '-';
String suffix = (dot >= 0) ? name.substring( dot ) : "";
Path tempDir = getTempDir();
if( tempDir != null ) {
deleteTemporaryFiles( tempDir );
return Files.createTempFile( tempDir, prefix, suffix );
} else
return Files.createTempFile( prefix, suffix );
}
private static Path getTempDir() throws IOException {
if( SystemInfo.isWindows ) {
// On Windows, where File.delete() and File.deleteOnExit() does not work
// for loaded native libraries, they will be deleted on next application startup.
// The default temporary directory may contain hundreds or thousands of files.
// To make searching for "marked for deletion" files as fast as possible,
// use a sub directory that contains only our temporary native libraries.
Path tempDir = Paths.get( System.getProperty( "java.io.tmpdir" ) + "/flatlaf.temp" );
Files.createDirectories( tempDir );
return tempDir;
} else
return null; // use standard temporary directory
}
private static void deleteTemporaryFiles( Path tempDir ) {
if( deletedTemporary )
return;
deletedTemporary = true;
File[] markerFiles = tempDir.toFile().listFiles( (dir, name) -> name.endsWith( DELETE_SUFFIX ) );
if( markerFiles == null )
return;
for( File markerFile : markerFiles ) {
File toDeleteFile = new File( markerFile.getParent(), StringUtils.removeTrailing( markerFile.getName(), DELETE_SUFFIX ) );
if( !toDeleteFile.exists() || toDeleteFile.delete() )
markerFile.delete();
}
}
private static void deleteOrMarkForDeletion( File file ) {
// try to delete the native library
if( file.delete() )
return;
// not possible to delete on Windows because native library file is locked
// --> create "to delete" marker file (used at next startup)
try {
File markFile = new File( file.getParent(), file.getName() + DELETE_SUFFIX );
markFile.createNewFile();
} catch( IOException ex2 ) {
// ignore
}
}
}

View File

@@ -38,6 +38,9 @@ public class SystemInfo
public static final boolean isMacOS_10_14_Mojave_orLater;
public static final boolean isMacOS_10_15_Catalina_orLater;
// OS architecture
/** @since 1.1 */ public static final boolean isX86_64;
// Java versions
public static final long javaVersion;
public static final boolean isJava_9_orLater;
@@ -51,6 +54,11 @@ public class SystemInfo
// UI toolkits
public static final boolean isKDE;
// other
/** @since 1.1 */ public static final boolean isProjector;
/** @since 1.1.2 */ public static final boolean isWebswing;
/** @since 1.1.1 */ public static final boolean isWinPE;
static {
// platforms
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
@@ -65,6 +73,10 @@ public class SystemInfo
isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
// OS architecture
String osArch = System.getProperty( "os.arch" );
isX86_64 = osArch.equals( "amd64" ) || osArch.equals( "x86_64" );
// Java versions
javaVersion = scanVersion( System.getProperty( "java.version" ) );
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
@@ -78,6 +90,11 @@ public class SystemInfo
// UI toolkits
isKDE = (isLinux && System.getenv( "KDE_FULL_SESSION" ) != null);
// other
isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" );
isWebswing = (System.getProperty( "webswing.rootDir" ) != null);
isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) );
}
public static long scanVersion( String version ) {

View File

@@ -180,7 +180,7 @@ public class UIScale
// apply custom scale factor specified in system property "flatlaf.uiScale"
float customScaleFactor = getCustomScaleFactor();
if( customScaleFactor > 0 ) {
setUserScaleFactor( customScaleFactor );
setUserScaleFactor( customScaleFactor, false );
return;
}
@@ -230,7 +230,7 @@ public class UIScale
} else
newScaleFactor = computeScaleFactor( font );
setUserScaleFactor( newScaleFactor );
setUserScaleFactor( newScaleFactor, true );
}
private static float computeScaleFactor( Font font ) {
@@ -274,7 +274,7 @@ public class UIScale
if( scaleFactor == fontScaleFactor )
return font;
int newFontSize = Math.round( (font.getSize() / fontScaleFactor) * scaleFactor );
int newFontSize = Math.max( Math.round( (font.getSize() / fontScaleFactor) * scaleFactor ), 1 );
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
}
@@ -322,11 +322,18 @@ public class UIScale
/**
* Sets the user scale factor.
*/
private static void setUserScaleFactor( float scaleFactor ) {
if( scaleFactor <= 1f )
scaleFactor = 1f;
else // round scale factor to 1/4
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;
private static void setUserScaleFactor( float scaleFactor, boolean normalize ) {
if( normalize ) {
if( scaleFactor < 1f ) {
scaleFactor = FlatSystemProperties.getBoolean( FlatSystemProperties.UI_SCALE_ALLOW_SCALE_DOWN, false )
? Math.round( scaleFactor * 10f ) / 10f // round small scale factor to 1/10
: 1f;
} else if( scaleFactor > 1f ) // round scale factor to 1/4
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;
}
// minimum scale factor
scaleFactor = Math.max( scaleFactor, 0.1f );
float oldScaleFactor = UIScale.scaleFactor;
UIScale.scaleFactor = scaleFactor;

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2021 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 com.formdev.flatlaf.FlatLaf;
/**
* @since 1.1
*/
class LoggingFacadeImpl
implements LoggingFacade
{
private static final System.Logger LOG = System.getLogger( FlatLaf.class.getName() );
@Override
public void logSevere( String message, Throwable t ) {
LOG.log( System.Logger.Level.ERROR, message, t );
}
@Override
public void logConfig( String message, Throwable t ) {
LOG.log( System.Logger.Level.DEBUG, message, t );
}
}

View File

@@ -19,7 +19,6 @@
*/
module com.formdev.flatlaf {
requires java.desktop;
requires java.logging;
exports com.formdev.flatlaf;
exports com.formdev.flatlaf.icons;

View File

@@ -685,13 +685,17 @@ TitledBorder.border = 1,1,1,1,$Separator.foreground
#---- TitlePane ----
TitlePane.useWindowDecorations = true
TitlePane.menuBarEmbedded = true
TitlePane.unifiedBackground = false
TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,0
TitlePane.menuBarMargins = 0,8,0,22
TitlePane.titleMargins = 3,8,3,8
TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0
TitlePane.buttonSize = 44,30
TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.menuBarTitleGap = 20
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

@@ -22,6 +22,28 @@
# - https://www.formdev.com/flatlaf/how-to-customize/
#
#---- system colors ----
# fix (most) system colors because they are usually not set in .json files
desktop = lazy(TextField.background)
activeCaptionText = lazy(TextField.foreground)
inactiveCaptionText = lazy(TextField.foreground)
window = lazy(Panel.background)
windowBorder = lazy(TextField.foreground)
windowText = lazy(TextField.foreground)
menu = lazy(Menu.background)
menuText = lazy(Menu.foreground)
text = lazy(TextField.background)
textText = lazy(TextField.foreground)
textHighlight = lazy(TextField.selectionBackground)
textHighlightText = lazy(TextField.selectionForeground)
textInactiveText = lazy(TextField.inactiveForeground)
control = lazy(Panel.background)
controlText = lazy(TextField.foreground)
info = lazy(ToolTip.background)
infoText = lazy(ToolTip.foreground)
#---- Button ----
Button.startBackground = $Button.background
@@ -69,21 +91,33 @@ ToggleButton.endBackground = $ToggleButton.background
@ijMenuCheckBackgroundL20 = lighten(@selectionBackground,20%,derived noAutoInverse)
@ijMenuCheckBackgroundD10 = darken(@selectionBackground,10%,derived noAutoInverse)
[Arc_Theme]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]ProgressBar.selectionBackground = #000
[Arc_Theme]ProgressBar.selectionForeground = #fff
[Arc_Theme]List.selectionInactiveForeground = #fff
[Arc_Theme]Table.selectionInactiveForeground = #fff
[Arc_Theme]Tree.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]ProgressBar.selectionBackground = #000
[Arc_Theme_-_Orange]ProgressBar.selectionForeground = #fff
[Arc_Theme_-_Orange]List.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Table.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Tree.selectionInactiveForeground = #fff
[Arc_Theme_Dark]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark]ProgressBar.selectionForeground = #ddd
[Arc_Theme_Dark_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionForeground = #fff

View File

@@ -31,6 +31,7 @@ dependencies {
implementation( project( ":flatlaf-intellij-themes" ) )
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
// implementation( project( ":flatlaf-natives-jna" ) )
}
tasks {
@@ -38,6 +39,7 @@ tasks {
dependsOn( ":flatlaf-core:jar" )
dependsOn( ":flatlaf-extras:jar" )
dependsOn( ":flatlaf-intellij-themes:jar" )
// dependsOn( ":flatlaf-natives-jna:jar" )
manifest {
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )

View File

@@ -24,7 +24,6 @@ import java.util.prefs.Preferences;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*;
@@ -32,8 +31,11 @@ import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.components.FlatButton;
import com.formdev.flatlaf.extras.components.FlatButton.ButtonType;
import com.formdev.flatlaf.extras.FlatSVGUtils;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.util.SystemInfo;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
import net.miginfocom.layout.UnitValue;
@@ -141,26 +143,21 @@ class DemoFrame
private void windowDecorationsChanged() {
boolean windowDecorations = windowDecorationsCheckBoxMenuItem.isSelected();
// change window decoration of demo main frame
dispose();
setUndecorated( windowDecorations );
getRootPane().setWindowDecorationStyle( windowDecorations ? JRootPane.FRAME : JRootPane.NONE );
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
setVisible( true );
// change window decoration of all frames and dialogs
FlatLaf.setUseNativeWindowDecorations( windowDecorations );
// enable/disable window decoration for later created frames/dialogs
JFrame.setDefaultLookAndFeelDecorated( windowDecorations );
JDialog.setDefaultLookAndFeelDecorated( windowDecorations );
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
unifiedTitleBarMenuItem.setEnabled( windowDecorations );
}
private void menuBarEmbeddedChanged() {
getRootPane().putClientProperty( FlatClientProperties.MENU_BAR_EMBEDDED,
menuBarEmbeddedCheckBoxMenuItem.isSelected() ? null : false );
UIManager.put( "TitlePane.menuBarEmbedded", menuBarEmbeddedCheckBoxMenuItem.isSelected() );
FlatLaf.revalidateAndRepaintAllFramesAndDialogs();
}
// alternative method for all frames and menu bars in an application
// UIManager.put( "TitlePane.menuBarEmbedded", menuBarEmbeddedCheckBoxMenuItem.isSelected() );
// revalidate();
// repaint();
private void unifiedTitleBar() {
UIManager.put( "TitlePane.unifiedBackground", unifiedTitleBarMenuItem.isSelected() );
FlatLaf.repaintAllFramesAndDialogs();
}
private void underlineMenuSelection() {
@@ -268,7 +265,7 @@ class DemoFrame
// add font sizes
fontMenu.addSeparator();
ArrayList<String> sizes = new ArrayList<>( Arrays.asList(
"10", "12", "14", "16", "18", "20", "24", "28" ) );
"10", "11", "12", "14", "16", "18", "20", "24", "28" ) );
if( !sizes.contains( currentSize ) )
sizes.add( currentSize );
sizes.sort( String.CASE_INSENSITIVE_ORDER );
@@ -327,6 +324,7 @@ class DemoFrame
optionsMenu = new JMenu();
windowDecorationsCheckBoxMenuItem = new JCheckBoxMenuItem();
menuBarEmbeddedCheckBoxMenuItem = new JCheckBoxMenuItem();
unifiedTitleBarMenuItem = new JCheckBoxMenuItem();
underlineMenuSelectionMenuItem = new JCheckBoxMenuItem();
alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem();
animatedLafChangeMenuItem = new JCheckBoxMenuItem();
@@ -588,6 +586,11 @@ class DemoFrame
menuBarEmbeddedCheckBoxMenuItem.addActionListener(e -> menuBarEmbeddedChanged());
optionsMenu.add(menuBarEmbeddedCheckBoxMenuItem);
//---- unifiedTitleBarMenuItem ----
unifiedTitleBarMenuItem.setText("Unified window title bar");
unifiedTitleBarMenuItem.addActionListener(e -> unifiedTitleBar());
optionsMenu.add(unifiedTitleBarMenuItem);
//---- underlineMenuSelectionMenuItem ----
underlineMenuSelectionMenuItem.setText("Use underline menu selection");
underlineMenuSelectionMenuItem.addActionListener(e -> underlineMenuSelection());
@@ -702,6 +705,15 @@ class DemoFrame
buttonGroup1.add(radioButtonMenuItem3);
// JFormDesigner - End of component initialization //GEN-END:initComponents
// add "Users" button to menubar
FlatButton usersButton = new FlatButton();
usersButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/users.svg" ) );
usersButton.setButtonType( ButtonType.toolBarButton );
usersButton.setFocusable( false );
usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) );
menuBar1.add( Box.createGlue() );
menuBar1.add( usersButton );
undoMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/undo.svg" ) );
redoMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/redo.svg" ) );
@@ -721,10 +733,20 @@ class DemoFrame
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
boolean supportsWindowDecorations = UIManager.getLookAndFeel()
.getSupportsWindowDecorations() || JBRCustomDecorations.isSupported();
windowDecorationsCheckBoxMenuItem.setEnabled( supportsWindowDecorations && !JBRCustomDecorations.isSupported() );
menuBarEmbeddedCheckBoxMenuItem.setEnabled( supportsWindowDecorations );
if( FlatLaf.supportsNativeWindowDecorations() ) {
if( JBRCustomDecorations.isSupported() ) {
// If the JetBrains Runtime is used, it forces the use of it's own custom
// window decoration, which can not disabled.
windowDecorationsCheckBoxMenuItem.setEnabled( false );
}
} else {
unsupported( windowDecorationsCheckBoxMenuItem );
unsupported( menuBarEmbeddedCheckBoxMenuItem );
unsupported( unifiedTitleBarMenuItem );
}
if( SystemInfo.isMacOS )
unsupported( underlineMenuSelectionMenuItem );
// remove contentPanel bottom insets
MigLayout layout = (MigLayout) contentPanel.getLayout();
@@ -739,11 +761,18 @@ class DemoFrame
layout.setLayoutConstraints( lc );
}
private void unsupported( JCheckBoxMenuItem menuItem ) {
menuItem.setEnabled( false );
menuItem.setSelected( false );
menuItem.setToolTipText( "Not supported on your system." );
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenu fontMenu;
private JMenu optionsMenu;
private JCheckBoxMenuItem windowDecorationsCheckBoxMenuItem;
private JCheckBoxMenuItem menuBarEmbeddedCheckBoxMenuItem;
private JCheckBoxMenuItem unifiedTitleBarMenuItem;
private JCheckBoxMenuItem underlineMenuSelectionMenuItem;
private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem;
private JCheckBoxMenuItem animatedLafChangeMenuItem;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -360,6 +360,14 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuBarEmbeddedChanged", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "unifiedTitleBarMenuItem"
"text": "Unified window title bar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "unifiedTitleBar", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "underlineMenuSelectionMenuItem"
"text": "Use underline menu selection"

View File

@@ -17,8 +17,6 @@
package com.formdev.flatlaf.demo;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.FlatInspector;
@@ -46,10 +44,6 @@ public class FlatLafDemo
SwingUtilities.invokeLater( () -> {
DemoPrefs.init( PREFS_ROOT_PATH );
// enable window decorations
JFrame.setDefaultLookAndFeelDecorated( true );
JDialog.setDefaultLookAndFeelDecorated( true );
// application specific UI defaults
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><path d="M11.6 8.5c1.104 0 1.992-.88 1.992-1.964 0-1.085-.888-1.965-1.992-1.965s-2 .88-2 1.965c0 1.084.896 1.964 2 1.964zm-6-.786c1.328 0 2.392-1.053 2.392-2.357C7.992 4.053 6.928 3 5.6 3 4.272 3 3.2 4.053 3.2 5.357c0 1.304 1.072 2.357 2.4 2.357zm6 2.357c-1.464 0-4.4.723-4.4 2.161V14H16v-1.768c0-1.438-2.936-2.16-4.4-2.16zm-6-.785c-1.864 0-5.6.919-5.6 2.75V14h5.6v-1.768c0-.668.264-1.838 1.896-2.726-.696-.142-1.368-.22-1.896-.22z" fill="#6E6E6E"/></svg>

After

Width:  |  Height:  |  Size: 603 B

View File

@@ -38,6 +38,9 @@ import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Field;
@@ -48,6 +51,8 @@ import javax.swing.JRootPane;
import javax.swing.JToolBar;
import javax.swing.JToolTip;
import javax.swing.KeyStroke;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
@@ -55,6 +60,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -82,7 +88,6 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatInspector
{
private static final Integer HIGHLIGHT_LAYER = 401;
private static final Integer TOOLTIP_LAYER = 402;
private static final int KEY_MODIFIERS_MASK = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK;
@@ -90,6 +95,8 @@ public class FlatInspector
private final MouseMotionListener mouseMotionListener;
private final AWTEventListener keyListener;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport( this );
private final WindowListener windowListener;
private Window window;
private boolean enabled;
private Component lastComponent;
@@ -99,7 +106,7 @@ public class FlatInspector
private boolean wasCtrlOrShiftKeyPressed;
private JComponent highlightFigure;
private JToolTip tip;
private Popup popup;
/**
* Installs a key listener into the application that allows enabling and disabling
@@ -189,6 +196,18 @@ public class FlatInspector
}
}
};
windowListener = new WindowAdapter() {
@Override
public void windowActivated( WindowEvent e ) {
update();
}
@Override
public void windowDeactivated( WindowEvent e ) {
hidePopup();
}
};
}
private void uninstall() {
@@ -221,12 +240,26 @@ public class FlatInspector
rootPane.getGlassPane().setVisible( enabled );
// add/remove key listener
Toolkit toolkit = Toolkit.getDefaultToolkit();
if( enabled )
toolkit.addAWTEventListener( keyListener, AWTEvent.KEY_EVENT_MASK );
else
toolkit.removeAWTEventListener( keyListener );
// add/remove window listener
if( enabled ) {
window = SwingUtilities.windowForComponent( rootPane );
if( window != null )
window.addWindowListener( windowListener );
} else {
if( window != null ) {
window.removeWindowListener( windowListener );
window = null;
}
}
// show/hide popup
if( enabled ) {
Point pt = new Point( MouseInfo.getPointerInfo().getLocation() );
SwingUtilities.convertPointFromScreen( pt, rootPane );
@@ -242,14 +275,19 @@ public class FlatInspector
highlightFigure.getParent().remove( highlightFigure );
highlightFigure = null;
if( tip != null )
tip.getParent().remove( tip );
tip = null;
hidePopup();
}
propertyChangeSupport.firePropertyChange( "enabled", !enabled, enabled );
}
private void hidePopup() {
if( popup != null ) {
popup.hide();
popup = null;
}
}
public void update() {
if( !rootPane.getGlassPane().isVisible() )
return;
@@ -303,7 +341,7 @@ public class FlatInspector
continue;
// ignore highlight figure and tooltip
if( c == highlightFigure || c == tip )
if( c == highlightFigure )
continue;
// ignore glass pane
@@ -357,26 +395,24 @@ public class FlatInspector
}
private void showToolTip( Component c, int x, int y, int parentLevel ) {
if( c == null ) {
if( tip != null )
tip.setVisible( false );
hidePopup();
if( c == null || (window != null && !window.isActive()) )
return;
}
if( tip == null ) {
tip = new JToolTip();
rootPane.getLayeredPane().add( tip, TOOLTIP_LAYER );
} else
tip.setVisible( true );
JToolTip tip = new JToolTip();
tip.setTipText( buildToolTipText( c, parentLevel ) );
tip.putClientProperty( FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, true );
Point pt = new Point( x, y );
SwingUtilities.convertPointToScreen( pt, rootPane.getGlassPane() );
int tx = pt.x + UIScale.scale( 8 );
int ty = pt.y + UIScale.scale( 16 );
int tx = x + UIScale.scale( 8 );
int ty = y + UIScale.scale( 16 );
Dimension size = tip.getPreferredSize();
// position the tip in the visible area
Rectangle visibleRect = rootPane.getVisibleRect();
Rectangle visibleRect = rootPane.getGraphicsConfiguration().getBounds();
if( tx + size.width > visibleRect.x + visibleRect.width )
tx -= size.width + UIScale.scale( 16 );
if( ty + size.height > visibleRect.y + visibleRect.height )
@@ -386,8 +422,9 @@ public class FlatInspector
if( ty < visibleRect.y )
ty = visibleRect.y;
tip.setBounds( tx, ty, size.width, size.height );
tip.repaint();
PopupFactory popupFactory = PopupFactory.getSharedInstance();
popup = popupFactory.getPopup( c, tip, tx, ty );
popup.show();
}
private static String buildToolTipText( Component c, int parentLevel ) {
@@ -398,7 +435,8 @@ public class FlatInspector
String name = c.getClass().getName();
name = name.substring( name.lastIndexOf( '.' ) + 1 );
appendRow( buf, "Class", name + " (" + c.getClass().getPackage().getName() + ")" );
Package pkg = c.getClass().getPackage();
appendRow( buf, "Class", name + " (" + (pkg != null ? pkg.getName() : "-") + ")" );
appendRow( buf, "Size", c.getWidth() + ", " + c.getHeight() + "&nbsp;&nbsp; @ " + c.getX() + ", " + c.getY() );
if( c instanceof Container )
@@ -437,7 +475,7 @@ public class FlatInspector
f.setAccessible( true );
Object ui = f.get( c );
appendRow( buf, "UI", (ui != null ? ui.getClass().getName() : "null") );
} catch( NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex ) {
} catch( Exception ex ) {
// ignore
}
}
@@ -473,9 +511,9 @@ public class FlatInspector
}
private static void appendRow( StringBuilder buf, String key, String value ) {
buf.append( "<tr><td><b>" )
buf.append( "<tr><td>" )
.append( key )
.append( ":</b></td><td>" )
.append( ":</td><td>" )
.append( value )
.append( "</td></tr>" );
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2021 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.extras.resources;
/**
* 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.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
*
* @author Karl Tauber
*/
interface EmptyPackage
{
}

View File

@@ -210,3 +210,13 @@ ToggleButton.toolbar.pressedBackground = ToggleButton.background
ToggleButton.toolbar.selectedBackground = ToggleButton.background
ToggleButton.tab.hoverBackground = null
#---- JideButton ----
JideButton.focusedBackground = JideButton.background
JideButton.selectedAndFocusedBackground = JideButton.background
JideButton.selectedBackground = JideButton.background
JideButton.highlight = null

View File

@@ -17,25 +17,21 @@
package com.formdev.flatlaf.intellijthemes;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.util.LoggingFacade;
/**
* @author Karl Tauber
*/
class Utils
{
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
static IntelliJTheme loadTheme( String name ) {
try {
return new IntelliJTheme( Utils.class.getResourceAsStream(
"/com/formdev/flatlaf/intellijthemes/themes/" + name ) );
} catch( IOException ex ) {
String msg = "FlatLaf: Failed to load IntelliJ theme '" + name + "'";
LOG.log( Level.SEVERE, msg, ex );
LoggingFacade.INSTANCE.logSevere( msg, ex );
throw new RuntimeException( msg, ex );
}
}

View File

@@ -17,25 +17,21 @@
package com.formdev.flatlaf.intellijthemes.materialthemeuilite;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.util.LoggingFacade;
/**
* @author Karl Tauber
*/
class Utils
{
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
static IntelliJTheme loadTheme( String name ) {
try {
return new IntelliJTheme( Utils.class.getResourceAsStream(
"/com/formdev/flatlaf/intellijthemes/themes/material-theme-ui-lite/" + name ) );
} catch( IOException ex ) {
String msg = "FlatLaf: Failed to load IntelliJ theme '" + name + "'";
LOG.log( Level.SEVERE, msg, ex );
LoggingFacade.INSTANCE.logSevere( msg, ex );
throw new RuntimeException( msg, ex );
}
}

View File

@@ -19,7 +19,6 @@
*/
module com.formdev.flatlaf.intellijthemes {
requires java.desktop;
requires java.logging;
requires com.formdev.flatlaf;
exports com.formdev.flatlaf.intellijthemes;

View File

@@ -6,8 +6,13 @@ This addon for FlatLaf adds support for **some**
Following JIDE Common Layer components are currently supported by this addon:
- `JideButton`
- `JideLabel`
- `JidePopupMenu`
- `JideSplitButton`
- `JideTabbedPane`
- `JideToggleButton`
- `JideToggleSplitButton`
- `RangeSlider`
- `TristateCheckBox`
@@ -33,3 +38,9 @@ build script:
Otherwise download `flatlaf-jide-oss-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-jide-oss/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-jide-oss)
JIDE Common Layer library `jide-oss-<version>.jar` (or
`jide-common-<version>.jar`) is also required:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/jide-oss/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/jide-oss)

View File

@@ -21,7 +21,9 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "com.jidesoft:jide-oss:3.6.18" )
// use compileOnly() because there are various JIDE libraries available on Maven Central
compileOnly( "com.formdev:jide-oss:3.7.11.1" )
}
java {

View File

@@ -21,9 +21,13 @@ import java.util.HashMap;
import java.util.Map;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UIDefaults.ActiveValue;
import com.formdev.flatlaf.FlatDefaultsAddon;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.jideoss.ui.FlatJidePainter;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.plaf.LookAndFeelFactory.UIDefaultsCustomizer;
import com.jidesoft.plaf.LookAndFeelFactory.UIDefaultsInitializer;
@@ -66,6 +70,11 @@ public class FlatJideOssDefaultsAddon
* Because JIDE overwrites our UI defaults (from properties files) with its
* own UI defaults, we have to first remember our UI defaults in the initializer
* (invoked before JIDE overwrites UI defaults) and then restore them in the customizer.
* <p>
* Do not register this class yourself with JIDE.
* It is automatically registered.
* <p>
* Invoked from {@link LookAndFeelFactory#installJideExtension()}.
*/
public static class FlatJideUIDefaultsCustomizer
implements UIDefaultsInitializer, UIDefaultsCustomizer
@@ -74,6 +83,10 @@ public class FlatJideOssDefaultsAddon
@Override
public void initialize( UIDefaults defaults ) {
// do nothing in other Lafs if (wrongly) registered with LookAndFeelFactory.addUIDefaultsInitializer()
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) )
return;
jideDefaults = new HashMap<>();
for( Map.Entry<Object, Object> e : defaults.entrySet() ) {
@@ -91,11 +104,34 @@ public class FlatJideOssDefaultsAddon
@Override
public void customize( UIDefaults defaults ) {
// do nothing in other Lafs if (wrongly) registered with LookAndFeelFactory.addUIDefaultsCustomizer()
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) )
return;
if( jideDefaults != null ) {
defaults.putAll( jideDefaults );
jideDefaults = null;
}
// painter
UIDefaultsLookup.put( defaults, "Theme.painter", FlatJidePainter.getInstance() );
// avoid that JideButton and JideSplitButton shift icon on hover/selection
defaults.put( "Icon.floating", false );
// fonts
ActiveValue font = FlatLaf.createActiveFontValue( 1f );
defaults.put( "JideButton.font", font );
defaults.put( "JideLabel.font", font );
defaults.put( "JideSplitButton.font", font );
defaults.put( "JideTabbedPane.font", font );
defaults.put( "JideTabbedPane.selectedTabFont", font );
// reset standard fonts modified by LookAndFeelFactory.installJideExtension()
defaults.put( "FormattedTextField.font", font );
defaults.put( "Spinner.font", font );
defaults.put( "TextArea.font", font );
// TristateCheckBox
defaults.put( "TristateCheckBox.icon", null );
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2021 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.jideoss.ui;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideButtonUI;
/**
* Provides the Flat LaF UI delegate for {@link com.jidesoft.swing.JideButton}.
*
* @author Karl Tauber
* @since 1.1
*/
public class FlatJideButtonUI
extends BasicJideButtonUI
{
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in JideButton.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return new FlatJideButtonUI();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2021 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.jideoss.ui;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideLabelUI;
/**
* Provides the Flat LaF UI delegate for {@link com.jidesoft.swing.JideLabel}.
*
* @author Karl Tauber
* @since 1.1
*/
public class FlatJideLabelUI
extends BasicJideLabelUI
{
private Color disabledForeground;
private boolean defaults_initialized = false;
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in JideLabel.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return FlatUIUtils.createSharedUI( FlatJideLabelUI.class, FlatJideLabelUI::new );
}
@Override
protected void installDefaults( JLabel c ) {
super.installDefaults( c );
if( !defaults_initialized ) {
disabledForeground = UIManager.getColor( "JideLabel.disabledForeground" );
defaults_initialized = true;
}
}
@Override
protected void uninstallDefaults( JLabel c ) {
super.uninstallDefaults( c );
defaults_initialized = false;
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;
g.setColor( l.getForeground() );
FlatUIUtils.drawStringUnderlineCharAt( l, g, s, mnemIndex, textX, textY );
}
@Override
protected void paintDisabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;
g.setColor( disabledForeground );
FlatUIUtils.drawStringUnderlineCharAt( l, g, s, mnemIndex, textX, textY );
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2021 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.jideoss.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.plaf.basic.BasicPainter;
import com.jidesoft.plaf.basic.ThemePainter;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitButton;
/**
* @author Karl Tauber
* @since 1.1
*/
public class FlatJidePainter
extends BasicPainter
{
protected final int arc = UIManager.getInt( "Button.arc" );
public static ThemePainter getInstance() {
// always create a new instance of Laf switching
return new FlatJidePainter();
}
@Override
public void installDefaults() {
// avoid white background in arrow area of selected split button
if( _bk0 == null )
_bk0 = UIManager.getColor( "Panel.background" );
super.installDefaults();
}
@Override
protected void paintBackground( JComponent c, Graphics g, Rectangle rect,
Color borderColor, Color background, int orientation )
{
if( (c instanceof JideButton && ((JideButton)c).getButtonStyle() == JideButton.TOOLBAR_STYLE) ||
(c instanceof JideSplitButton && ((JideSplitButton)c).getButtonStyle() == JideButton.TOOLBAR_STYLE) )
{
Color oldColor = g.getColor();
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
if( c instanceof JideSplitButton ) {
// For split buttons, this method is invoked twice:
// - first for main button
// - second for arrow button
// To show a single rounded rectangle for the whole button we always paint
// the rounded rectangle with component bounds, but clip to the passed rectangle.
boolean horizontal = (((JideSplitButton)c).getOrientation() == SwingConstants.HORIZONTAL);
// for vertical orientation, the graphics context is rotated, but 1px wrong
if( !horizontal )
g.translate( 0, -1 );
Shape oldClip = g.getClip();
g.clipRect( rect.x, rect.y, rect.width, rect.height );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0,
horizontal ? c.getWidth() : c.getHeight(),
horizontal ? c.getHeight() : c.getWidth(),
0, UIScale.scale( (float) arc ) );
g.setClip( oldClip );
// paint separator line
if( rect.x > 0 ) {
g.setColor( borderColor );
((Graphics2D)g).fill( new Rectangle2D.Float( rect.x, rect.y, UIScale.scale( 1f ), rect.height ) );
}
if( !horizontal )
g.translate( 0, 1 );
} else {
FlatUIUtils.paintComponentBackground( (Graphics2D) g, rect.x, rect.y,
rect.width, rect.height, 0, UIScale.scale( (float) arc ) );
}
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
g.setColor( oldColor );
} else
super.paintBackground( c, g, rect, borderColor, background, orientation );
}
}

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.jideoss.ui;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatPopupMenuUI;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJidePopupMenuUI;
/**
@@ -28,6 +29,10 @@ public class FlatJidePopupMenuUI
extends FlatPopupMenuUI
{
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in JidePopupMenu.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return new FlatJidePopupMenuUI();
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2021 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.jideoss.ui;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import javax.swing.ButtonModel;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.plaf.basic.BasicJideSplitButtonUI;
import com.jidesoft.swing.JideSplitButton;
import com.jidesoft.swing.JideSwingUtilities;
/**
* Provides the Flat LaF UI delegate for {@link com.jidesoft.swing.JideSplitButton}.
*
* @author Karl Tauber
* @since 1.1
*/
public class FlatJideSplitButtonUI
extends BasicJideSplitButtonUI
{
protected String arrowType;
protected Color buttonArrowColor;
protected Color buttonDisabledArrowColor;
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in JideSplitButton.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return new FlatJideSplitButtonUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
arrowType = UIManager.getString( "Component.arrowType" );
buttonArrowColor = UIManager.getColor( "JideSplitButton.buttonArrowColor" );
buttonDisabledArrowColor = UIManager.getColor( "JideSplitButton.buttonDisabledArrowColor" );
}
@Override
protected int getRightMargin() {
// scale margins
_splitButtonMargin = UIScale.scale( 14 );
_splitButtonMarginOnMenu = UIScale.scale( 20 );
return super.getRightMargin();
}
@Override
protected Rectangle getButtonRect( JComponent c, int orientation, int width, int height ) {
return c.getComponentOrientation().isLeftToRight()
? new Rectangle( 0, 0, width - _splitButtonMargin + 1, height )
: new Rectangle( _splitButtonMargin - 1, 0, width - _splitButtonMargin + 1, height );
}
@Override
protected Rectangle getDropDownRect( JComponent c, int orientation, int width, int height ) {
return c.getComponentOrientation().isLeftToRight()
? new Rectangle( width - _splitButtonMargin, 0, _splitButtonMargin, height )
: new Rectangle( 0, 0, _splitButtonMargin, height );
}
@Override
protected void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect, String text ) {
ButtonModel model = menuItem.getModel();
if( !model.isEnabled() ||
(menuItem instanceof JideSplitButton && !((JideSplitButton)menuItem).isButtonEnabled()) )
{
FontMetrics fm = menuItem.getFontMetrics( menuItem.getFont() );
if( !menuItem.getComponentOrientation().isLeftToRight() &&
menuItem.getComponentOrientation().isHorizontal() )
{
Rectangle2D rectText = fm.getStringBounds( text, g );
textRect.x = (int) (menuItem.getWidth() - textRect.x - rectText.getWidth() + (4 + menuItem.getHeight() / 2 - 1));
}
g.setColor( UIDefaultsLookup.getColor( "Button.disabledForeground" ) );
drawStringUnderlineCharAt( menuItem, g, text, -1, textRect.x, textRect.y + fm.getAscent() );
} else
super.paintText( g, menuItem, textRect, text );
}
@Override
protected void paintArrow( JMenuItem menuItem, Graphics g ) {
g.setColor( menuItem.isEnabled() ? buttonArrowColor : buttonDisabledArrowColor );
int orientation = JideSwingUtilities.getOrientationOf( menuItem );
int menuWidth = (orientation == SwingConstants.HORIZONTAL) ? menuItem.getWidth() : menuItem.getHeight();
int menuHeight = (orientation == SwingConstants.HORIZONTAL) ? menuItem.getHeight() : menuItem.getWidth();
Rectangle r = getDropDownRect( menuItem, orientation, menuWidth, menuHeight );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintArrow( (Graphics2D) g, r.x, r.y, r.width, r.height,
SwingConstants.SOUTH, FlatUIUtils.isChevron( arrowType ), 6, 0, 0 );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
}

View File

@@ -32,7 +32,6 @@ import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.UIManager;
@@ -67,7 +66,10 @@ public class FlatJideTabbedPaneUI
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in JideTabbedPane.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return new FlatJideTabbedPaneUI();
}
@@ -108,19 +110,17 @@ public class FlatJideTabbedPaneUI
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
String propertyName = e.getPropertyName();
if( JideTabbedPane.PROPERTY_SELECTED_INDEX.equals( propertyName ) ) {
repaintTab( (Integer) e.getOldValue() );
repaintTab( (Integer) e.getNewValue() );
} else if( FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER.equals( propertyName ) ) {
_tabPane.revalidate();
_tabPane.repaint();
}
String propertyName = e.getPropertyName();
if( JideTabbedPane.PROPERTY_SELECTED_INDEX.equals( propertyName ) ) {
repaintTab( (Integer) e.getOldValue() );
repaintTab( (Integer) e.getNewValue() );
} else if( FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER.equals( propertyName ) ) {
_tabPane.revalidate();
_tabPane.repaint();
}
};
}

View File

@@ -70,7 +70,10 @@ public class FlatRangeSliderUI
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) {
// usually JIDE would invoke this in RangeSlider.updateUI(),
// but it does not because FlatLaf already has added the UI class to the UI defaults
LookAndFeelFactory.installJideExtension();
return new FlatRangeSliderUI();
}

View File

@@ -16,16 +16,60 @@
#---- UI delegates ----
JideButtonUI = com.formdev.flatlaf.jideoss.ui.FlatJideButtonUI
JideLabelUI = com.formdev.flatlaf.jideoss.ui.FlatJideLabelUI
JidePopupMenuUI = com.formdev.flatlaf.jideoss.ui.FlatJidePopupMenuUI
JideSplitButtonUI = com.formdev.flatlaf.jideoss.ui.FlatJideSplitButtonUI
JideTabbedPaneUI = com.formdev.flatlaf.jideoss.ui.FlatJideTabbedPaneUI
RangeSliderUI = com.formdev.flatlaf.jideoss.ui.FlatRangeSliderUI
#---- JideButton and JideToggleButton ----
JideButton.border = com.formdev.flatlaf.ui.FlatMarginBorder
JideButton.margin = $Button.toolbar.margin
JideButton.textIconGap = {scaledInteger}4
JideButton.background = $Button.background
JideButton.foreground = $Button.foreground
JideButton.focusedBackground = $Button.toolbar.hoverBackground
JideButton.selectedBackground = $Button.toolbar.selectedBackground
JideButton.selectedAndFocusedBackground = $Button.toolbar.pressedBackground
JideButton.borderColor = $Button.borderColor
JideButton.shadow = $Button.borderColor
JideButton.darkShadow = $Button.borderColor
JideButton.light = $Button.borderColor
JideButton.highlight = $Button.selectedBackground
Button.disabledForeground = $Button.disabledText
#---- JideLabel ----
JideLabel.background = $Label.background
JideLabel.foreground = $Label.foreground
JideLabel.disabledForeground = $Label.disabledForeground
#---- JidePopup ----
Resizable.resizeBorder = 4,4,4,4,$PopupMenu.borderColor
#---- JideSplitButton and JideToggleSplitButton ----
JideSplitButton.border = com.formdev.flatlaf.ui.FlatMarginBorder
JideSplitButton.margin = $Button.toolbar.margin
JideSplitButton.textIconGap = {scaledInteger}4
JideSplitButton.background = $Button.background
JideSplitButton.foreground = $Button.foreground
JideSplitButton.selectionForeground = $Button.foreground
JideSplitButton.buttonArrowColor = @buttonArrowColor
JideSplitButton.buttonDisabledArrowColor = @buttonDisabledArrowColor
#---- JideTabbedPane ----
JideTabbedPane.background = @background

View File

@@ -0,0 +1,5 @@
FlatLaf Native Libraries
========================
- [Windows 10 Native Library](flatlaf-natives-windows)
- [Natives using JNA](flatlaf-natives-jna) (for development only)

View File

@@ -0,0 +1,10 @@
FlatLaf Natives using JNA
=========================
This sub-project contains source code that uses
[JNA](https://github.com/java-native-access/jna) to access native operating
system API.
**Note:** Code in this sub-project is **not used** in FlatLaf libraries. It was
used to develop/test usage of some native operating system API in Java (with the
help of JNA) and was then converted to C++.

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
`java-library`
}
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "net.java.dev.jna:jna:5.7.0" )
implementation( "net.java.dev.jna:jna-platform:5.7.0" )
}

View File

@@ -0,0 +1,724 @@
/*
* Copyright 2021 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.natives.jna.windows;
import static com.sun.jna.platform.win32.ShellAPI.*;
import static com.sun.jna.platform.win32.WinReg.*;
import static com.sun.jna.platform.win32.WinUser.*;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.geom.AffineTransform;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.util.SystemInfo;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.Shell32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WTypes.LPWSTR;
import com.sun.jna.platform.win32.WinDef.HMENU;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HMONITOR;
import com.sun.jna.platform.win32.WinUser.WindowProc;
import com.sun.jna.win32.W32APIOptions;
//
// Interesting resources:
// https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
// https://docs.microsoft.com/en-us/windows/win32/dwm/customframe
// https://github.com/JetBrains/JetBrainsRuntime/blob/master/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp
// https://github.com/JetBrains/JetBrainsRuntime/commit/d2820524a1aa211b1c49b30f659b9b4d07a6f96e
// https://github.com/JetBrains/JetBrainsRuntime/pull/18
// https://medium.com/swlh/customizing-the-title-bar-of-an-application-window-50a4ac3ed27e
// https://github.com/kalbetredev/CustomDecoratedJFrame
// https://github.com/Guerra24/NanoUI-win32
// https://github.com/oberth/custom-chrome
// https://github.com/rossy/borderless-window
//
/**
* Native window border support for Windows 10 when using custom decorations.
* <p>
* If the application wants to use custom decorations, the Windows 10 title bar is hidden
* (including minimize, maximize and close buttons), but not the resize borders (including drop shadow).
* Windows 10 window snapping functionality will remain unaffected:
* https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241
*
* @author Karl Tauber
* @since 1.1
*/
public class FlatWindowsNativeWindowBorder
implements FlatNativeWindowBorder.Provider
{
private final Map<Window, WndProc> windowsMap = Collections.synchronizedMap( new IdentityHashMap<>() );
private final EventListenerList listenerList = new EventListenerList();
private Timer fireStateChangedTimer;
private boolean colorizationUpToDate;
private boolean colorizationColorAffectsBorders;
private Color colorizationColor;
private int colorizationColorBalance;
private static FlatWindowsNativeWindowBorder instance;
public static FlatNativeWindowBorder.Provider getInstance() {
if( instance == null )
instance = new FlatWindowsNativeWindowBorder();
return instance;
}
private FlatWindowsNativeWindowBorder() {
}
@Override
public boolean hasCustomDecoration( Window window ) {
return windowsMap.containsKey( window );
}
/**
* Tell the window whether the application wants use custom decorations.
* If {@code true}, the Windows 10 title bar is hidden (including minimize,
* maximize and close buttons), but not the resize borders (including drop shadow).
*/
@Override
public void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( hasCustomDecoration )
install( window );
else
uninstall( window );
}
private void install( Window window ) {
// requires Windows 10
if( !SystemInfo.isWindows_10_orLater )
return;
// only JFrame and JDialog are supported
if( !(window instanceof JFrame) && !(window instanceof JDialog) )
return;
// not supported if frame/dialog is undecorated
if( (window instanceof Frame && ((Frame)window).isUndecorated()) ||
(window instanceof Dialog && ((Dialog)window).isUndecorated()) )
return;
// check whether already installed
if( windowsMap.containsKey( window ) )
return;
// install
WndProc wndProc = new WndProc( window );
windowsMap.put( window, wndProc );
}
private void uninstall( Window window ) {
WndProc wndProc = windowsMap.remove( window );
if( wndProc != null )
wndProc.uninstall();
}
@Override
public void setTitleBarHeight( Window window, int titleBarHeight ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.titleBarHeight = titleBarHeight;
}
@Override
public void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
}
@Override
public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
}
@Override
public boolean showWindow( Window window, int cmd ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return false;
User32.INSTANCE.ShowWindow( wndProc.hwnd, cmd );
return true;
}
@Override
public boolean isColorizationColorAffectsBorders() {
updateColorization();
return colorizationColorAffectsBorders;
}
@Override
public Color getColorizationColor() {
updateColorization();
return colorizationColor;
}
@Override
public int getColorizationColorBalance() {
updateColorization();
return colorizationColorBalance;
}
private void updateColorization() {
if( colorizationUpToDate )
return;
colorizationUpToDate = true;
String subKey = "SOFTWARE\\Microsoft\\Windows\\DWM";
int value = registryGetIntValue( subKey, "ColorPrevalence", -1 );
colorizationColorAffectsBorders = (value > 0);
value = registryGetIntValue( subKey, "ColorizationColor", -1 );
colorizationColor = (value != -1) ? new Color( value ) : null;
colorizationColorBalance = registryGetIntValue( subKey, "ColorizationColorBalance", -1 );
}
private static int registryGetIntValue( String key, String valueName, int defaultValue ) {
try {
return Advapi32Util.registryGetIntValue( HKEY_CURRENT_USER, key, valueName );
} catch( RuntimeException ex ) {
return defaultValue;
}
}
@Override
public void addChangeListener( ChangeListener l ) {
listenerList.add( ChangeListener.class, l );
}
@Override
public void removeChangeListener( ChangeListener l ) {
listenerList.remove( ChangeListener.class, l );
}
private void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
if( listeners.length == 0 )
return;
ChangeEvent e = new ChangeEvent( this );
for( int i = 0; i < listeners.length; i += 2 ) {
if( listeners[i] == ChangeListener.class )
((ChangeListener)listeners[i+1]).stateChanged( e );
}
}
/**
* Because there may be sent many WM_DWMCOLORIZATIONCOLORCHANGED messages,
* slightly delay event firing and fire it only once (on the AWT thread).
*/
void fireStateChangedLaterOnce() {
EventQueue.invokeLater( () -> {
if( fireStateChangedTimer != null ) {
fireStateChangedTimer.restart();
return;
}
fireStateChangedTimer = new Timer( 300, e -> {
fireStateChangedTimer = null;
colorizationUpToDate = false;
fireStateChanged();
} );
fireStateChangedTimer.setRepeats( false );
fireStateChangedTimer.start();
} );
}
//---- class WndProc ------------------------------------------------------
private class WndProc
implements WindowProc
{
private static final int GWLP_WNDPROC = -4;
private static final int
WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084,
WM_NCRBUTTONUP = 0x00A5,
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
// WM_NCHITTEST mouse position codes
private static final int
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTTOP = 12;
private static final int ABS_AUTOHIDE = 0x0000001;
private static final int ABM_GETAUTOHIDEBAREX = 0x0000000b;
private static final int
SC_SIZE = 0xF000,
SC_MOVE = 0xF010,
SC_MINIMIZE = 0xF020,
SC_MAXIMIZE = 0xF030,
SC_CLOSE = 0xF060,
SC_RESTORE = 0xF120;
private static final int
MIIM_STATE = 0x00000001,
MFT_STRING = 0x00000000,
MF_ENABLED = 0x00000000,
MF_DISABLED = 0x00000002,
TPM_RETURNCMD = 0x0100;
private Window window;
private final HWND hwnd;
private final BaseTSD.LONG_PTR defaultWndProc;
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private Rectangle appIconBounds;
WndProc( Window window ) {
this.window = window;
// get window handle
hwnd = new HWND( Native.getComponentPointer( window ) );
// replace window procedure
if( SystemInfo.isX86_64 )
defaultWndProc = User32Ex.INSTANCE.SetWindowLongPtr( hwnd, GWLP_WNDPROC, this );
else
defaultWndProc = User32Ex.INSTANCE.SetWindowLong( hwnd, GWLP_WNDPROC, this );
// remove the OS window title bar
if( window instanceof JFrame && ((JFrame)window).getExtendedState() != 0 ) {
// In case that the frame should be maximized or minimized immediately
// when showing, then it is necessary to defer ::SetWindowPos() invocation.
// Otherwise the frame will not be maximized or minimized.
// This occurs only if frame.pack() was no invoked.
EventQueue.invokeLater( () -> {
updateFrame();
});
} else
updateFrame();
}
void uninstall() {
// restore original window procedure
if( SystemInfo.isX86_64 )
User32Ex.INSTANCE.SetWindowLongPtr( hwnd, GWLP_WNDPROC, defaultWndProc );
else
User32Ex.INSTANCE.SetWindowLong( hwnd, GWLP_WNDPROC, defaultWndProc );
// show the OS window title bar
updateFrame();
// cleanup
window = null;
}
private void updateFrame() {
// this sends WM_NCCALCSIZE and removes/shows the window title bar
User32.INSTANCE.SetWindowPos( hwnd, hwnd, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
}
/**
* NOTE: This method is invoked on the AWT-Windows thread (not the AWT-EventQueue thread).
*/
@Override
public LRESULT callback( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
switch( uMsg ) {
case WM_NCCALCSIZE:
return WmNcCalcSize( hwnd, uMsg, wParam, lParam );
case WM_NCHITTEST:
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
case WM_NCRBUTTONUP:
if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU )
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
break;
case WM_DWMCOLORIZATIONCOLORCHANGED:
fireStateChangedLaterOnce();
break;
case WM_DESTROY:
return WmDestroy( hwnd, uMsg, wParam, lParam );
}
return User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
}
/**
* Handle WM_DESTROY
*
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-destroy
*/
private LRESULT WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
// call original AWT window procedure because it may fire window closed event in AwtWindow::WmDestroy()
LRESULT lResult = User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
// restore original window procedure
if( SystemInfo.isX86_64 )
User32Ex.INSTANCE.SetWindowLongPtr( hwnd, GWLP_WNDPROC, defaultWndProc );
else
User32Ex.INSTANCE.SetWindowLong( hwnd, GWLP_WNDPROC, defaultWndProc );
// cleanup
windowsMap.remove( window );
window = null;
return lResult;
}
/**
* Handle WM_NCCALCSIZE
*
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
*
* See also NonClientIslandWindow::_OnNcCalcSize() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
private LRESULT WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
if( wParam.intValue() != 1 )
return User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
NCCALCSIZE_PARAMS params = new NCCALCSIZE_PARAMS( new Pointer( lParam.longValue() ) );
// store the original top before the default window proc applies the default frame
int originalTop = params.rgrc[0].top;
// apply the default frame
LRESULT lResult = User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
if( lResult.longValue() != 0 )
return lResult;
// re-read params from native memory because defaultWndProc changed it
params.read();
// re-apply the original top from before the size of the default frame was applied
params.rgrc[0].top = originalTop;
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
if( isMaximized && !isFullscreen() ) {
// When a window is maximized, its size is actually a little bit more
// than the monitor's work area. The window is positioned and sized in
// such a way that the resize handles are outside of the monitor and
// then the window is clipped to the monitor so that the resize handle
// do not appear because you don't need them (because you can't resize
// a window when it's maximized unless you restore it).
params.rgrc[0].top += getResizeHandleHeight();
// check whether taskbar is in the autohide state
APPBARDATA autohide = new APPBARDATA();
autohide.cbSize = new DWORD( autohide.size() );
int state = Shell32.INSTANCE.SHAppBarMessage( new DWORD( ABM_GETSTATE ), autohide ).intValue();
if( (state & ABS_AUTOHIDE) != 0 ) {
// get monitor info
// (using MONITOR_DEFAULTTONEAREST finds right monitor when restoring from minimized)
HMONITOR hMonitor = User32.INSTANCE.MonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST );
MONITORINFO monitorInfo = new MONITORINFO();
User32.INSTANCE.GetMonitorInfo( hMonitor, monitorInfo );
// If there's a taskbar on any side of the monitor, reduce our size
// a little bit on that edge.
if( hasAutohideTaskbar( ABE_TOP, monitorInfo.rcMonitor ) )
params.rgrc[0].top++;
if( hasAutohideTaskbar( ABE_BOTTOM, monitorInfo.rcMonitor ) )
params.rgrc[0].bottom--;
if( hasAutohideTaskbar( ABE_LEFT, monitorInfo.rcMonitor ) )
params.rgrc[0].left++;
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params.rgrc[0].right--;
}
}
// write changed params back to native memory
params.write();
return lResult;
}
/**
* Handle WM_NCHITTEST
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest
*
* See also NonClientIslandWindow::_OnNcHitTest() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
private LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
// this will handle the left, right and bottom parts of the frame because we didn't change them
LRESULT lResult = User32Ex.INSTANCE.CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
if( lResult.longValue() != HTCLIENT )
return lResult;
// get window rectangle needed to convert mouse x/y from screen to window coordinates
RECT rcWindow = new RECT();
User32.INSTANCE.GetWindowRect( hwnd, rcWindow );
// get mouse x/y in window coordinates
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
// scale-down mouse x/y
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
return new LRESULT( HTSYSMENU );
int resizeBorderHeight = getResizeHandleHeight();
boolean isOnResizeBorder = (y < resizeBorderHeight) &&
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
boolean isOnTitleBar = (sy < titleBarHeight);
if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed
// in another thread while processing the array
Rectangle[] hitTestSpots2 = hitTestSpots;
for( Rectangle spot : hitTestSpots2 ) {
if( spot.contains( sx, sy ) )
return new LRESULT( HTCLIENT );
}
return new LRESULT( isOnResizeBorder ? HTTOP : HTCAPTION );
}
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT );
}
/**
* Returns the height of the little space at the top of the window used to
* resize the window.
*
* See also NonClientIslandWindow::_GetResizeHandleHeight() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
private int getResizeHandleHeight() {
int dpi = User32Ex.INSTANCE.GetDpiForWindow( hwnd );
// there isn't a SM_CYPADDEDBORDER for the Y axis
return User32Ex.INSTANCE.GetSystemMetricsForDpi( SM_CXPADDEDBORDER, dpi )
+ User32Ex.INSTANCE.GetSystemMetricsForDpi( SM_CYSIZEFRAME, dpi );
}
/**
* Returns whether there is an autohide taskbar on the given edge.
*/
private boolean hasAutohideTaskbar( int edge, RECT rcMonitor ) {
APPBARDATA data = new APPBARDATA();
data.cbSize = new DWORD( data.size() );
data.uEdge = new UINT( edge );
data.rc = rcMonitor;
UINT_PTR hTaskbar = Shell32.INSTANCE.SHAppBarMessage( new DWORD( ABM_GETAUTOHIDEBAREX ), data );
return hTaskbar.longValue() != 0;
}
/**
* Scales down in the same way as AWT.
* See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()
*/
private Point scaleDown( int x, int y ) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if( gc == null )
return new Point( x, y );
AffineTransform t = gc.getDefaultTransform();
return new Point( clipRound( x / t.getScaleX() ), clipRound( y / t.getScaleY() ) );
}
/**
* Rounds in the same way as AWT.
* See AwtWin32GraphicsDevice::ClipRound()
*/
private int clipRound( double value ) {
value -= 0.5;
if( value < Integer.MIN_VALUE )
return Integer.MIN_VALUE;
if( value > Integer.MAX_VALUE )
return Integer.MAX_VALUE;
return (int) Math.ceil( value );
}
private boolean isFullscreen() {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if( gc == null )
return false;
return gc.getDevice().getFullScreenWindow() == window;
}
/**
* Same implementation as GET_X_LPARAM(lp) macro in windowsx.h.
* X-coordinate is in the low-order short and may be negative.
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
*/
private int GET_X_LPARAM( LPARAM lParam ) {
return (short) (lParam.longValue() & 0xffff);
}
/**
* Same implementation as GET_Y_LPARAM(lp) macro in windowsx.h.
* Y-coordinate is in the high-order short and may be negative.
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
*/
private int GET_Y_LPARAM( LPARAM lParam ) {
return (short) ((lParam.longValue() >> 16) & 0xffff);
}
/**
* Opens the window's system menu.
* The system menu is the menu that opens when the user presses Alt+Space or
* right clicks on the title bar
*/
private void openSystemMenu( HWND hwnd, int x, int y ) {
// get system menu
HMENU systemMenu = User32Ex.INSTANCE.GetSystemMenu( hwnd, false );
// update system menu
int style = User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE );
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
setMenuItemState( systemMenu, SC_RESTORE, isMaximized );
setMenuItemState( systemMenu, SC_MOVE, !isMaximized );
setMenuItemState( systemMenu, SC_SIZE, (style & WS_THICKFRAME) != 0 && !isMaximized );
setMenuItemState( systemMenu, SC_MINIMIZE, (style & WS_MINIMIZEBOX) != 0 );
setMenuItemState( systemMenu, SC_MAXIMIZE, (style & WS_MAXIMIZEBOX) != 0 && !isMaximized );
setMenuItemState( systemMenu, SC_CLOSE, true );
// make "Close" item the default to be consistent with the system menu shown
// when pressing Alt+Space
User32Ex.INSTANCE.SetMenuDefaultItem( systemMenu, SC_CLOSE, 0 );
// show system menu
int ret = User32Ex.INSTANCE.TrackPopupMenu( systemMenu, TPM_RETURNCMD,
x, y, 0, hwnd, null ).intValue();
if( ret != 0 )
User32Ex.INSTANCE.PostMessage( hwnd, WM_SYSCOMMAND, new WPARAM( ret ), null );
}
private void setMenuItemState( HMENU systemMenu, int item, boolean enabled ) {
MENUITEMINFO mii = new MENUITEMINFO();
mii.cbSize = new UINT( mii.size() );
mii.fMask = new UINT( MIIM_STATE );
mii.fType = new UINT( MFT_STRING );
mii.fState = new UINT( enabled ? MF_ENABLED : MF_DISABLED );
User32Ex.INSTANCE.SetMenuItemInfo( systemMenu, item, false, mii );
}
}
//---- interface User32Ex -------------------------------------------------
private interface User32Ex
extends User32
{
User32Ex INSTANCE = Native.load( "user32", User32Ex.class, W32APIOptions.DEFAULT_OPTIONS );
LONG_PTR SetWindowLongPtr( HWND hWnd, int nIndex, WindowProc wndProc );
LONG_PTR SetWindowLongPtr( HWND hWnd, int nIndex, LONG_PTR wndProc );
LONG_PTR SetWindowLong( HWND hWnd, int nIndex, WindowProc wndProc );
LONG_PTR SetWindowLong( HWND hWnd, int nIndex, LONG_PTR wndProc );
LRESULT CallWindowProc( LONG_PTR lpPrevWndFunc, HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam );
int GetDpiForWindow( HWND hwnd );
int GetSystemMetricsForDpi( int nIndex, int dpi );
boolean IsZoomed( HWND hWnd );
HANDLE GetProp( HWND hWnd, String lpString );
HMENU GetSystemMenu( HWND hWnd, boolean bRevert );
boolean SetMenuItemInfo( HMENU hmenu, int item, boolean fByPositon, MENUITEMINFO lpmii );
boolean SetMenuDefaultItem( HMENU hMenu, int uItem, int fByPos );
BOOL TrackPopupMenu( HMENU hMenu, int uFlags, int x, int y, int nReserved, HWND hWnd, RECT prcRect );
}
//---- class NCCALCSIZE_PARAMS --------------------------------------------
@FieldOrder( { "rgrc" } )
public static class NCCALCSIZE_PARAMS
extends Structure
{
// real structure contains 3 rectangles, but only first one is needed here
public RECT[] rgrc = new RECT[1];
// public WINDOWPOS lppos;
public NCCALCSIZE_PARAMS( Pointer pointer ) {
super( pointer );
read();
}
}
//---- class MENUITEMINFO -------------------------------------------------
@FieldOrder( { "cbSize", "fMask", "fType", "fState", "wID", "hSubMenu",
"hbmpChecked", "hbmpUnchecked", "dwItemData", "dwTypeData", "cch", "hbmpItem" } )
public static class MENUITEMINFO
extends Structure
{
public UINT cbSize;
public UINT fMask;
public UINT fType;
public UINT fState;
public UINT wID;
public HMENU hSubMenu;
public HBITMAP hbmpChecked;
public HBITMAP hbmpUnchecked;
public ULONG_PTR dwItemData;
public LPWSTR dwTypeData;
public UINT cch;
public HBITMAP hbmpItem;
}
}

View File

@@ -0,0 +1,17 @@
FlatLaf Windows 10 Native Library
=================================
This sub-project contains the source code for the FlatLaf Windows 10 native
library (DLL).
The native library can be built only on Windows and requires a C++ compiler.
Tested only with Microsoft Visual C++ 2019 (comes with Visual Studio 2019).
To be able to build FlatLaf on any platform, and without C++ compiler, the
pre-built DLL is checked into Git at
[flatlaf-core/src/main/resources/com/formdev/flatlaf/natives/](https://github.com/JFormDesigner/FlatLaf/tree/main/flatlaf-core/src/main/resources/com/formdev/flatlaf/natives).
The DLL was built on a GitHub server with the help of GitHub Actions. See:
[Native Libraries](https://github.com/JFormDesigner/FlatLaf/actions/workflows/natives.yml)
workflow. Then the produced Artifacts ZIP was downloaded and the DLL checked
into Git.

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id( "dev.nokee.jni-library" ) version "0.4.0"
id( "dev.nokee.cpp-language" ) version "0.4.0"
}
library {
targetMachines.set( listOf( machines.windows.x86, machines.windows.x86_64 ) )
variants.configureEach {
sharedLibrary {
compileTasks.configureEach {
onlyIf { isBuildable }
// depend on :flatlaf-core:compileJava because it generates the JNI headers
dependsOn( ":flatlaf-core:compileJava" )
doFirst {
println( "Used Tool Chain:" )
println( " - ${toolChain.get()}" )
println( "Available Tool Chains:" )
toolChains.forEach {
println( " - $it" )
}
// copy needed JNI headers
copy {
from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
into( "src/main/headers" )
include(
"com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder.h",
"com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
)
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
"eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
)
}
}
compilerArgs.addAll( toolChain.map {
when( it ) {
is Gcc, is Clang -> listOf( "-O2" )
is VisualCpp -> listOf( "/O2", "/Zl", "/GS-" )
else -> emptyList()
}
} )
}
linkTask.configure {
onlyIf { isBuildable }
val nativesDir = project( ":flatlaf-core" ).projectDir.resolve( "src/main/resources/com/formdev/flatlaf/natives" )
val is64Bit = targetMachine.architecture.is64Bit
val libraryName = if( is64Bit ) "flatlaf-windows-x86_64.dll" else "flatlaf-windows-x86.dll"
val jawt = if( is64Bit ) "lib/jawt-x86_64" else "lib/jawt-x86"
outputs.file( "$nativesDir/$libraryName" )
linkerArgs.addAll( toolChain.map {
when( it ) {
is Gcc, is Clang -> listOf( "-l${jawt}", "-lUser32", "-lshell32", "-lAdvAPI32", "-lKernel32" )
is VisualCpp -> listOf( "${jawt}.lib", "User32.lib", "shell32.lib", "AdvAPI32.lib", "Kernel32.lib", "/NODEFAULTLIB" )
else -> emptyList()
}
} )
doLast {
// copy shared library to flatlaf-core resources
copy {
from( linkedFile )
into( nativesDir )
rename( "flatlaf-natives-windows.dll", libraryName )
}
}
}
for( taskName in listOf( "jarX86", "jarX86-64" ) ) {
tasks.named( taskName ) {
onlyIf { false }
}
}
}
}
}

View File

@@ -0,0 +1,6 @@
Contains libraries used to compile FlatLaf Windows 10 native libraries (DLLs).
- `jawt-x86.lib` is `<jdk>/lib/jawt.lib` from AdoptOpenJDK jdk8u282-b08 32-bit,
which is required to build 32-bit DLL
- `jawt-x86_64.lib` is `<jdk>/lib/jawt.lib` from AdoptOpenJDK jdk8u282-b08
64-bit, which is required to build 64-bit DLL

View File

@@ -0,0 +1,420 @@
/*
* Copyright 2021 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.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <jawt.h>
#include <jawt_md.h>
#include "FlatWndProc.h"
#include "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc.h"
/**
* @author Karl Tauber
*/
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_installImpl
( JNIEnv *env, jobject obj, jobject window )
{
return reinterpret_cast<jlong>( FlatWndProc::install( env, obj, window ) );
}
extern "C"
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_uninstallImpl
( JNIEnv* env, jobject obj, jlong hwnd )
{
FlatWndProc::uninstall( env, obj, reinterpret_cast<HWND>( hwnd ) );
}
extern "C"
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_updateFrame
( JNIEnv* env, jobject obj, jlong hwnd )
{
FlatWndProc::updateFrame( reinterpret_cast<HWND>( hwnd ) );
}
extern "C"
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_showWindow
( JNIEnv* env, jobject obj, jlong hwnd, jint cmd )
{
::ShowWindow( reinterpret_cast<HWND>( hwnd ), cmd );
}
//---- class FlatWndProc fields -----------------------------------------------
int FlatWndProc::initialized = 0;
jmethodID FlatWndProc::onNcHitTestMID;
jmethodID FlatWndProc::isFullscreenMID;
jmethodID FlatWndProc::fireStateChangedLaterOnceMID;
HWNDMap* FlatWndProc::hwndMap;
//---- class FlatWndProc methods ----------------------------------------------
FlatWndProc::FlatWndProc() {
jvm = NULL;
env = NULL;
obj = NULL;
hwnd = NULL;
defaultWndProc = NULL;
}
HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
initIDs( env, obj );
if( initialized < 0 )
return 0;
// create HWND map
if( hwndMap == NULL )
hwndMap = new HWNDMap();
// get window handle
HWND hwnd = getWindowHandle( env, window );
if( hwnd == NULL || hwndMap->get( hwnd ) != NULL )
return 0;
FlatWndProc* fwp = new FlatWndProc();
env->GetJavaVM( &fwp->jvm );
fwp->obj = env->NewGlobalRef( obj );
fwp->hwnd = hwnd;
hwndMap->put( hwnd, fwp );
// replace window procedure
fwp->defaultWndProc = reinterpret_cast<WNDPROC>(
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) FlatWndProc::StaticWindowProc ) );
return hwnd;
}
void FlatWndProc::uninstall( JNIEnv *env, jobject obj, HWND hwnd ) {
if( hwnd == NULL )
return;
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
if( fwp == NULL )
return;
hwndMap->remove( hwnd );
// restore original window procedure
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) fwp->defaultWndProc );
// show the OS window title bar
updateFrame( hwnd );
// cleanup
env->DeleteGlobalRef( fwp->obj );
delete fwp;
}
void FlatWndProc::initIDs( JNIEnv *env, jobject obj ) {
if( initialized )
return;
initialized = -1;
jclass cls = env->GetObjectClass( obj );
onNcHitTestMID = env->GetMethodID( cls, "onNcHitTest", "(IIZ)I" );
isFullscreenMID = env->GetMethodID( cls, "isFullscreen", "()Z" );
fireStateChangedLaterOnceMID = env->GetMethodID( cls, "fireStateChangedLaterOnce", "()V" );
// check whether all IDs were found
if( onNcHitTestMID != NULL &&
isFullscreenMID != NULL &&
fireStateChangedLaterOnceMID != NULL )
initialized = 1;
}
void FlatWndProc::updateFrame( HWND hwnd ) {
// this sends WM_NCCALCSIZE and removes/shows the window title bar
::SetWindowPos( hwnd, hwnd, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
}
LRESULT CALLBACK FlatWndProc::StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
FlatWndProc* fwp = (FlatWndProc*) hwndMap->get( hwnd );
return fwp->WindowProc( hwnd, uMsg, wParam, lParam );
}
/**
* NOTE: This method is invoked on the AWT-Windows thread (not the AWT-EventQueue thread).
*/
LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch( uMsg ) {
case WM_NCCALCSIZE:
return WmNcCalcSize( hwnd, uMsg, wParam, lParam );
case WM_NCHITTEST:
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
case WM_NCRBUTTONUP:
if( wParam == HTCAPTION || wParam == HTSYSMENU )
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
break;
case WM_DWMCOLORIZATIONCOLORCHANGED:
fireStateChangedLaterOnce();
break;
case WM_DESTROY:
return WmDestroy( hwnd, uMsg, wParam, lParam );
}
return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
}
/**
* Handle WM_DESTROY
*
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-destroy
*/
LRESULT FlatWndProc::WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
// restore original window procedure
::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) defaultWndProc );
WNDPROC defaultWndProc2 = defaultWndProc;
// cleanup
getEnv()->DeleteGlobalRef( obj );
hwndMap->remove( hwnd );
delete this;
// call original AWT window procedure because it may fire window closed event in AwtWindow::WmDestroy()
return ::CallWindowProc( defaultWndProc2, hwnd, uMsg, wParam, lParam );
}
/**
* Handle WM_NCCALCSIZE
*
* https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
*
* See also NonClientIslandWindow::_OnNcCalcSize() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
if( wParam != TRUE )
return ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
NCCALCSIZE_PARAMS* params = reinterpret_cast<NCCALCSIZE_PARAMS*>( lParam );
// store the original top before the default window proc applies the default frame
int originalTop = params->rgrc[0].top;
// apply the default frame
LRESULT lResult = ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
if( lResult != 0 )
return lResult;
// re-apply the original top from before the size of the default frame was applied
params->rgrc[0].top = originalTop;
bool isMaximized = ::IsZoomed( hwnd );
if( isMaximized && !isFullscreen() ) {
// When a window is maximized, its size is actually a little bit more
// than the monitor's work area. The window is positioned and sized in
// such a way that the resize handles are outside of the monitor and
// then the window is clipped to the monitor so that the resize handle
// do not appear because you don't need them (because you can't resize
// a window when it's maximized unless you restore it).
params->rgrc[0].top += getResizeHandleHeight();
// check whether taskbar is in the autohide state
APPBARDATA autohide{ 0 };
autohide.cbSize = sizeof( autohide );
UINT state = (UINT) ::SHAppBarMessage( ABM_GETSTATE, &autohide );
if( (state & ABS_AUTOHIDE) != 0 ) {
// get monitor info
// (using MONITOR_DEFAULTTONEAREST finds right monitor when restoring from minimized)
HMONITOR hMonitor = ::MonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST );
MONITORINFO monitorInfo{ 0 };
::GetMonitorInfo( hMonitor, &monitorInfo );
// If there's a taskbar on any side of the monitor, reduce our size
// a little bit on that edge.
if( hasAutohideTaskbar( ABE_TOP, monitorInfo.rcMonitor ) )
params->rgrc[0].top++;
if( hasAutohideTaskbar( ABE_BOTTOM, monitorInfo.rcMonitor ) )
params->rgrc[0].bottom--;
if( hasAutohideTaskbar( ABE_LEFT, monitorInfo.rcMonitor ) )
params->rgrc[0].left++;
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params->rgrc[0].right--;
}
}
return lResult;
}
/**
* Handle WM_NCHITTEST
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest
*
* See also NonClientIslandWindow::_OnNcHitTest() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
// this will handle the left, right and bottom parts of the frame because we didn't change them
LRESULT lResult = ::CallWindowProc( defaultWndProc, hwnd, uMsg, wParam, lParam );
if( lResult != HTCLIENT )
return lResult;
// get window rectangle needed to convert mouse x/y from screen to window coordinates
RECT rcWindow;
::GetWindowRect( hwnd, &rcWindow );
// get mouse x/y in window coordinates
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
int resizeBorderHeight = getResizeHandleHeight();
bool isOnResizeBorder = (y < resizeBorderHeight) &&
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
return onNcHitTest( x, y, isOnResizeBorder );
}
/**
* Returns the height of the little space at the top of the window used to
* resize the window.
*
* See also NonClientIslandWindow::_GetResizeHandleHeight() here:
* https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
*/
int FlatWndProc::getResizeHandleHeight() {
int dpi = ::GetDpiForWindow( hwnd );
// there isn't a SM_CYPADDEDBORDER for the Y axis
return ::GetSystemMetricsForDpi( SM_CXPADDEDBORDER, dpi )
+ ::GetSystemMetricsForDpi( SM_CYSIZEFRAME, dpi );
}
/**
* Returns whether there is an autohide taskbar on the given edge.
*/
bool FlatWndProc::hasAutohideTaskbar( UINT edge, RECT rcMonitor ) {
APPBARDATA data{ 0 };
data.cbSize = sizeof( data );
data.uEdge = edge;
data.rc = rcMonitor;
HWND hTaskbar = (HWND) ::SHAppBarMessage( ABM_GETAUTOHIDEBAREX, &data );
return hTaskbar != nullptr;
}
BOOL FlatWndProc::isFullscreen() {
JNIEnv* env = getEnv();
if( env == NULL )
return FALSE;
return env->CallBooleanMethod( obj, isFullscreenMID );
}
int FlatWndProc::onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
JNIEnv* env = getEnv();
if( env == NULL )
return isOnResizeBorder ? HTTOP : HTCLIENT;
return env->CallIntMethod( obj, onNcHitTestMID, (jint) x, (jint) y, (jboolean) isOnResizeBorder );
}
void FlatWndProc::fireStateChangedLaterOnce() {
JNIEnv* env = getEnv();
if( env == NULL )
return;
env->CallVoidMethod( obj, fireStateChangedLaterOnceMID );
}
// similar to JNU_GetEnv() in jni_util.c
JNIEnv* FlatWndProc::getEnv() {
if( env != NULL )
return env;
jvm->GetEnv( (void **) &env, JNI_VERSION_1_2 );
return env;
}
/**
* Opens the window's system menu.
* The system menu is the menu that opens when the user presses Alt+Space or
* right clicks on the title bar
*/
void FlatWndProc::openSystemMenu( HWND hwnd, int x, int y ) {
// get system menu
HMENU systemMenu = ::GetSystemMenu( hwnd, false );
// update system menu
LONG style = ::GetWindowLong( hwnd, GWL_STYLE );
bool isMaximized = ::IsZoomed( hwnd );
setMenuItemState( systemMenu, SC_RESTORE, isMaximized );
setMenuItemState( systemMenu, SC_MOVE, !isMaximized );
setMenuItemState( systemMenu, SC_SIZE, (style & WS_THICKFRAME) != 0 && !isMaximized );
setMenuItemState( systemMenu, SC_MINIMIZE, (style & WS_MINIMIZEBOX) != 0 );
setMenuItemState( systemMenu, SC_MAXIMIZE, (style & WS_MAXIMIZEBOX) != 0 && !isMaximized );
setMenuItemState( systemMenu, SC_CLOSE, true );
// make "Close" item the default to be consistent with the system menu shown
// when pressing Alt+Space
::SetMenuDefaultItem( systemMenu, SC_CLOSE, 0 );
// show system menu
int ret = ::TrackPopupMenu( systemMenu, TPM_RETURNCMD, x, y, 0, hwnd, nullptr );
if( ret != 0 )
::PostMessage( hwnd, WM_SYSCOMMAND, ret, 0 );
}
void FlatWndProc::setMenuItemState( HMENU systemMenu, int item, bool enabled ) {
MENUITEMINFO mii{ 0 };
mii.cbSize = sizeof( mii );
mii.fMask = MIIM_STATE;
mii.fType = MFT_STRING;
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
::SetMenuItemInfo( systemMenu, item, FALSE, &mii );
}
HWND FlatWndProc::getWindowHandle( JNIEnv* env, jobject window ) {
JAWT awt;
awt.version = JAWT_VERSION_1_4;
if( !JAWT_GetAWT( env, &awt ) )
return 0;
jawt_DrawingSurface* ds = awt.GetDrawingSurface( env, window );
if( ds == NULL )
return 0;
jint lock = ds->Lock( ds );
if( (lock & JAWT_LOCK_ERROR) != 0 ) {
awt.FreeDrawingSurface( ds );
return 0;
}
JAWT_DrawingSurfaceInfo* dsi = ds->GetDrawingSurfaceInfo( ds );
JAWT_Win32DrawingSurfaceInfo* wdsi = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo;
HWND hwnd = wdsi->hwnd;
ds->FreeDrawingSurfaceInfo( dsi );
ds->Unlock( ds );
awt.FreeDrawingSurface( ds );
return hwnd;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2021 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.
*/
#include <windows.h>
#include "HWNDMap.h"
/**
* @author Karl Tauber
*/
class FlatWndProc
{
public:
static HWND install( JNIEnv *env, jobject obj, jobject window );
static void uninstall( JNIEnv *env, jobject obj, HWND hwnd );
static void updateFrame( HWND hwnd );
private:
static int initialized;
static jmethodID onNcHitTestMID;
static jmethodID isFullscreenMID;
static jmethodID fireStateChangedLaterOnceMID;
static HWNDMap* hwndMap;
JavaVM* jvm;
JNIEnv* env; // attached to AWT-Windows/Win32 thread
jobject obj;
HWND hwnd;
WNDPROC defaultWndProc;
FlatWndProc();
static void initIDs( JNIEnv *env, jobject obj );
static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WmDestroy( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
int getResizeHandleHeight();
bool hasAutohideTaskbar( UINT edge, RECT rcMonitor );
BOOL isFullscreen();
int onNcHitTest( int x, int y, boolean isOnResizeBorder );
void fireStateChangedLaterOnce();
JNIEnv* getEnv();
void openSystemMenu( HWND hwnd, int x, int y );
void setMenuItemState( HMENU systemMenu, int item, bool enabled );
static HWND getWindowHandle( JNIEnv* env, jobject window );
};

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2021 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.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <stdio.h>
#include "HWNDMap.h"
#define DEFAULT_CAPACITY 20
#define INCREASE_CAPACITY 10
/**
* @author Karl Tauber
*/
class LOCK {
LPCRITICAL_SECTION lpCriticalSection;
public:
LOCK( LPCRITICAL_SECTION lpCriticalSection ) {
this->lpCriticalSection = lpCriticalSection;
::EnterCriticalSection( lpCriticalSection );
}
~LOCK() {
::LeaveCriticalSection( lpCriticalSection );
}
};
HWNDMap::HWNDMap() {
size = 0;
capacity = DEFAULT_CAPACITY;
table = new Entry[capacity];
::InitializeCriticalSection( &criticalSection );
// dump( "<init>" );
}
LPVOID HWNDMap::get( HWND key ) {
LOCK lock( &criticalSection );
int index = binarySearch( key );
return (index >= 0) ? table[index].value : NULL;
}
void HWNDMap::put( HWND key, LPVOID value ) {
LOCK lock( &criticalSection );
int index = binarySearch( key );
// printf( "put %p %p = %d --\n", key, value, index );
if( index >= 0 ) {
// key already in map --> replace
table[index].value = value;
} else {
// insert new key
ensureCapacity( size + 1 );
// make roor for new entry
index = -(index + 1);
for( int i = size - 1; i >= index; i-- )
table[i + 1] = table[i];
size++;
// insert entry
table[index].key = key;
table[index].value = value;
}
// dump( "put" );
}
void HWNDMap::remove( HWND key ) {
LOCK lock( &criticalSection );
// search for key
int index = binarySearch( key );
// printf( "remove %p = %d --\n", key, index );
if( index < 0 )
return;
// remove entry
for( int i = index + 1; i < size; i++ )
table[i - 1] = table[i];
size--;
// dump( "remove" );
}
int HWNDMap::binarySearch( HWND key ) {
int low = 0;
int high = size - 1;
while( low <= high ) {
int mid = (low + high) >> 1;
HWND midKey = table[mid].key;
int cmp = midKey - key;
if( cmp < 0 )
low = mid + 1;
else if( cmp > 0 )
high = mid - 1;
else
return mid;
}
return -(low + 1);
}
void HWNDMap::ensureCapacity( int minCapacity ) {
if( minCapacity <= capacity )
return;
// allocate new table
int newCapacity = minCapacity + INCREASE_CAPACITY;
Entry* newTable = new Entry[newCapacity];
// copy old table to new table
for( int i = 0; i < capacity; i++ )
newTable[i] = table[i];
// delete old table
delete table;
table = newTable;
capacity = newCapacity;
}
/*
void HWNDMap::dump( char* msg ) {
printf( "---- %s -----------------------\n", msg );
printf( "size %d\n", size );
printf( "capacity %d\n", capacity );
printf( "table %p\n", table );
for( int i = 0; i < capacity; i++ )
printf( " %d: %p - %p %s\n", i, table[i].key, table[i].value, i >= size ? "UNUSED" : "" );
}
*/

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2021 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.
*/
#include <windows.h>
/**
* A simple map that uses a sorted array to store key/value pairs.
*
* @author Karl Tauber
*/
struct Entry
{
HWND key;
LPVOID value;
};
class HWNDMap
{
private:
int size; // used entries in table
int capacity; // total size of table
Entry* table;
// used to synchronize to make it thread safe
CRITICAL_SECTION criticalSection;
public:
HWNDMap();
LPVOID get( HWND key );
void put( HWND key, LPVOID value );
void remove( HWND key );
private:
int binarySearch( HWND key );
void ensureCapacity( int newCapacity );
// void dump( char* msg );
};

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2021 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.
*/
#include <windows.h>
#include <winreg.h>
#include <winerror.h>
#include <jni.h>
#include "com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder.h"
/**
* @author Karl Tauber
*/
//---- JNI methods ------------------------------------------------------------
extern "C"
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_registryGetIntValue
( JNIEnv* env, jclass cls, jstring key, jstring valueName, jint defaultValue )
{
const char* skey = env->GetStringUTFChars( key, NULL );
const char* svalueName = env->GetStringUTFChars( valueName, NULL );
DWORD data = 0;
DWORD cbData = sizeof( data );
int rc = ::RegGetValueA( HKEY_CURRENT_USER, skey, svalueName, RRF_RT_DWORD, NULL, &data, &cbData );
env->ReleaseStringUTFChars( key, skey );
env->ReleaseStringUTFChars( valueName, svalueName );
return (rc == ERROR_SUCCESS) ? data : defaultValue;
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2021 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.
*/
// avoid inlining of printf()
#define _NO_CRT_STDIO_INLINE
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
/**
* Methods that replace C-runtime methods and allow linking/running without C-runtime.
*
* WARNING: Constructors/destructors of static objects are not invoked!
*
* https://documentation.help/Far-Manager/msdnmag-issues-01-01-hood-default.aspx.html
* www.catch22.net/tuts/win32/reducing-executable-size#the-c-runtime-and-default-libraries
* https://www.mvps.org/user32/nocrt.html
*
* see also LIBCTINY on "Downloads" page here: http://www.wheaty.net/
* or https://github.com/leepa/libctiny
*
* @author Karl Tauber
*/
extern "C"
BOOL WINAPI _DllMainCRTStartup( HINSTANCE instance, DWORD reason, LPVOID reserved ) {
return TRUE;
}
void* __cdecl operator new( size_t cb ) {
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
}
void* __cdecl operator new[]( size_t cb ) {
return ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, cb );
}
void __cdecl operator delete( void* pv, size_t cb ) {
if( pv != NULL )
::HeapFree( ::GetProcessHeap(), 0, pv );
}
/*
extern "C"
int __cdecl printf( const char* format, ... ) {
char szBuff[1024];
int retValue;
DWORD cbWritten;
va_list argptr;
va_start( argptr, format );
retValue = wvsprintfA( szBuff, format, argptr );
va_end( argptr );
WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), szBuff, retValue, &cbWritten, NULL );
return retValue;
}
*/

View File

@@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder */
#ifndef _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
#define _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder
* Method: registryGetIntValue
* Signature: (Ljava/lang/String;Ljava/lang/String;I)I
*/
JNIEXPORT jint JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_registryGetIntValue
(JNIEnv *, jclass, jstring, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,53 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc */
#ifndef _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
#define _Included_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
#ifdef __cplusplus
extern "C" {
#endif
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLIENT
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLIENT 1L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION 2L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU 3L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
* Method: installImpl
* Signature: (Ljava/awt/Window;)J
*/
JNIEXPORT jlong JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_installImpl
(JNIEnv *, jobject, jobject);
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
* Method: uninstallImpl
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_uninstallImpl
(JNIEnv *, jobject, jlong);
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
* Method: updateFrame
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_updateFrame
(JNIEnv *, jobject, jlong);
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
* Method: showWindow
* Signature: (JI)V
*/
JNIEXPORT void JNICALL Java_com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_00024WndProc_showWindow
(JNIEnv *, jobject, jlong, jint);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -38,3 +38,8 @@ build script:
Otherwise download `flatlaf-swingx-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-swingx/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-swingx)
SwingX library `swingx-all-<version>.jar` is also required:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.swinglabs.swingx/swingx-all/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/org.swinglabs.swingx/swingx-all)

View File

@@ -22,7 +22,9 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "org.swinglabs.swingx:swingx-all:1.6.5-1" )
// use compileOnly() because there are various SwingX libraries available on Maven Central
compileOnly( "org.swinglabs.swingx:swingx-all:1.6.5-1" )
}
flatlafModuleInfo {

View File

@@ -24,7 +24,6 @@ import java.awt.Shape;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import com.formdev.flatlaf.icons.FlatAbstractIcon;
import com.formdev.flatlaf.ui.FlatArrowButton;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
@@ -56,14 +55,14 @@ public class FlatMonthDownIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
int h = chevron ? 4 : 5;
int w = chevron ? 8 : 9;
int x = Math.round( (width - h) / 2f );
int y = Math.round( (height - w) / 2f );
int w = chevron ? 4 : 5;
int h = chevron ? 8 : 9;
int x = Math.round( (width - w) / 2f );
int y = Math.round( (height - h) / 2f );
g.setColor( c.isEnabled() ? arrowColor : disabledArrowColor );
g.translate( x, y );
Shape arrowShape = FlatArrowButton.createArrowShape( direction, chevron, w, h );
Shape arrowShape = FlatUIUtils.createArrowShape( direction, chevron, w, h );
if( chevron ) {
// chevron arrow
g.setStroke( new BasicStroke( 1f ) );

View File

@@ -36,7 +36,6 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import org.jdesktop.swingx.JXTaskPane;
import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI;
import com.formdev.flatlaf.ui.FlatArrowButton;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -223,7 +222,7 @@ public class FlatTaskPaneUI
// create arrow shape
int direction = group.isCollapsed() ? SwingConstants.SOUTH : SwingConstants.NORTH;
Shape arrowShape = FlatArrowButton.createArrowShape( direction, true, cw, ch );
Shape arrowShape = FlatUIUtils.createArrowShape( direction, true, cw, ch );
// fix position of controls
x = group.getComponentOrientation().isLeftToRight() ? (group.getWidth() - width - y) : y;

View File

@@ -32,12 +32,13 @@ dependencies {
implementation( project( ":flatlaf-jide-oss" ) )
implementation( project( ":flatlaf-intellij-themes" ) )
implementation( project( ":flatlaf-demo" ) )
// implementation( project( ":flatlaf-natives-jna" ) )
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
implementation( "org.swinglabs.swingx:swingx-all:1.6.5-1" )
implementation( "org.swinglabs.swingx:swingx-beaninfo:1.6.5-1" )
implementation( "com.jidesoft:jide-oss:3.6.18" )
implementation( "com.formdev:jide-oss:3.7.11.1" )
implementation( "com.glazedlists:glazedlists:1.11.0" )
implementation( "org.netbeans.api:org-openide-awt:RELEASE112" )

View File

@@ -7,17 +7,28 @@
- Name FlatLaf Dark
+ Name FlatLaf Darcula
#---- Button ----
- Button.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
+ Button.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
- Button.innerFocusWidth 1
+ Button.innerFocusWidth 0
#---- CheckBox ----
- CheckBox.icon.focusedBackground #466d944d 30% javax.swing.plaf.ColorUIResource [UI]
#---- ComboBox ----
- ComboBox.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
+ ComboBox.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
#---- Component ----
- Component.arrowType chevron
+ Component.arrowType triangle
@@ -30,42 +41,74 @@
- Component.innerOutlineWidth 1.0
+ Component.innerOutlineWidth 0
#---- FormattedTextField ----
- FormattedTextField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ FormattedTextField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- HelpButton ----
- HelpButton.icon [lazy] 22,22 com.formdev.flatlaf.icons.FlatHelpButtonIcon [UI]
+ HelpButton.icon [lazy] 26,26 com.formdev.flatlaf.icons.FlatHelpButtonIcon [UI]
- HelpButton.innerFocusWidth 1
+ HelpButton.innerFocusWidth 0
#---- JXDatePicker ----
- JXDatePicker.border [lazy] 1,1,1,1 false com.formdev.flatlaf.swingx.ui.FlatDatePickerBorder [UI]
+ JXDatePicker.border [lazy] 3,3,3,3 false com.formdev.flatlaf.swingx.ui.FlatDatePickerBorder [UI]
#---- PasswordField ----
- PasswordField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ PasswordField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- ProgressBar ----
- ProgressBar.foreground #4a88c7 javax.swing.plaf.ColorUIResource [UI]
+ ProgressBar.foreground #a0a0a0 javax.swing.plaf.ColorUIResource [UI]
- ProgressBar.selectionForeground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
+ ProgressBar.selectionForeground #3c3f41 javax.swing.plaf.ColorUIResource [UI]
#---- RadioButton ----
- RadioButton.icon.centerDiameter 8
+ RadioButton.icon.centerDiameter 5
#---- ScrollPane ----
- ScrollPane.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatBorder [UI]
+ ScrollPane.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatBorder [UI]
#---- Spinner ----
- Spinner.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
+ Spinner.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
#---- Table ----
- Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatBorder [UI]
+ Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatBorder [UI]
#---- TextField ----
- TextField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ TextField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- ToggleButton ----
- ToggleButton.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
+ ToggleButton.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]

View File

@@ -80,6 +80,7 @@ Button.default.pressedBackground #3f6796 com.formdev.flatlaf.util.DerivedColo
Button.defaultButtonFollowsFocus false
Button.disabledBackground #3c3f41 javax.swing.plaf.ColorUIResource [UI]
Button.disabledBorderColor #5e6060 javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #888888 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBackground #53585a com.formdev.flatlaf.util.DerivedColor [UI] lighten(3% autoInverse)
Button.disabledText #888888 javax.swing.plaf.ColorUIResource [UI]
Button.focusedBorderColor #466d94 javax.swing.plaf.ColorUIResource [UI]
@@ -441,11 +442,50 @@ JXTitledPanel.titleBackground #4c5052 javax.swing.plaf.ColorUIResource [UI]
JXTitledPanel.titleForeground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
#---- JideButton ----
JideButton.background #4c5052 javax.swing.plaf.ColorUIResource [UI]
JideButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideButton.borderColor #5e6060 javax.swing.plaf.ColorUIResource [UI]
JideButton.darkShadow #5e6060 javax.swing.plaf.ColorUIResource [UI]
JideButton.focusedBackground #4e5355 com.formdev.flatlaf.util.DerivedColor [UI] lighten(1% autoInverse)
JideButton.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
JideButton.highlight #656a6c com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse)
JideButton.light #5e6060 javax.swing.plaf.ColorUIResource [UI]
JideButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideButton.selectedAndFocusedBackground #565a5d com.formdev.flatlaf.util.DerivedColor [UI] lighten(4% autoInverse)
JideButton.selectedBackground #5d6265 com.formdev.flatlaf.util.DerivedColor [UI] lighten(7% autoInverse)
JideButton.shadow #5e6060 javax.swing.plaf.ColorUIResource [UI]
JideButton.textIconGap [active] 4
JideButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideButtonUI
#---- JideLabel ----
JideLabel.background #3c3f41 javax.swing.plaf.ColorUIResource [UI]
JideLabel.disabledForeground #888888 javax.swing.plaf.ColorUIResource [UI]
JideLabel.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
JideLabelUI com.formdev.flatlaf.jideoss.ui.FlatJideLabelUI
#---- JidePopupMenu ----
JidePopupMenuUI com.formdev.flatlaf.jideoss.ui.FlatJidePopupMenuUI
#---- JideSplitButton ----
JideSplitButton.background #4c5052 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideSplitButton.buttonArrowColor #9a9da1 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.buttonDisabledArrowColor #5a5d61 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideSplitButton.selectionForeground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.textIconGap [active] 4
JideSplitButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideSplitButtonUI
#---- JideTabbedPane ----
JideTabbedPane.background #3c3f41 javax.swing.plaf.ColorUIResource [UI]
@@ -1126,6 +1166,8 @@ TitlePane.buttonHoverBackground #484c4f com.formdev.flatlaf.util.DerivedColor
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonPressedBackground #616569 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI]
@@ -1133,16 +1175,19 @@ TitlePane.closePressedBackground #e8112399 60% javax.swing.plaf.ColorUIResou
TitlePane.closePressedForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.embeddedForeground #959595 javax.swing.plaf.ColorUIResource [UI]
TitlePane.foreground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
TitlePane.iconMargins 3,8,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.icon [lazy] 16,16 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
TitlePane.iconMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.iconSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.iconifyIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowIconifyIcon [UI]
TitlePane.inactiveBackground #303234 javax.swing.plaf.ColorUIResource [UI]
TitlePane.inactiveForeground #888888 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.useWindowDecorations true
#---- TitledBorder ----

View File

@@ -7,6 +7,8 @@
- Name FlatLaf Light
+ Name FlatLaf IntelliJ
#---- Button ----
- Button.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
+ Button.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
@@ -40,15 +42,24 @@
- Button.focusedBackground #e3f1fa javax.swing.plaf.ColorUIResource [UI]
#---- CheckBox ----
- CheckBox.icon.focusedBackground #e3f1fa javax.swing.plaf.ColorUIResource [UI]
+ CheckBox.icon.style filled
- CheckBox.icon[filled].selectedFocusedCheckmarkColor #e3f1fa javax.swing.plaf.ColorUIResource [UI]
#---- ComboBox ----
- ComboBox.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
+ ComboBox.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
#---- Component ----
- Component.arrowType chevron
+ Component.arrowType triangle
@@ -61,32 +72,58 @@
- Component.innerOutlineWidth 1.0
+ Component.innerOutlineWidth 0
#---- FormattedTextField ----
- FormattedTextField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ FormattedTextField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- HelpButton ----
- HelpButton.focusedBackground #e3f1fa javax.swing.plaf.ColorUIResource [UI]
- HelpButton.icon [lazy] 22,22 com.formdev.flatlaf.icons.FlatHelpButtonIcon [UI]
+ HelpButton.icon [lazy] 26,26 com.formdev.flatlaf.icons.FlatHelpButtonIcon [UI]
#---- JXDatePicker ----
- JXDatePicker.border [lazy] 1,1,1,1 false com.formdev.flatlaf.swingx.ui.FlatDatePickerBorder [UI]
+ JXDatePicker.border [lazy] 3,3,3,3 false com.formdev.flatlaf.swingx.ui.FlatDatePickerBorder [UI]
#---- PasswordField ----
- PasswordField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ PasswordField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- ScrollPane ----
- ScrollPane.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatBorder [UI]
+ ScrollPane.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatBorder [UI]
#---- Spinner ----
- Spinner.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
+ Spinner.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
#---- Table ----
- Table.scrollPaneBorder [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatBorder [UI]
+ Table.scrollPaneBorder [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatBorder [UI]
#---- TextField ----
- TextField.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
+ TextField.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatTextBorder [UI]
#---- ToggleButton ----
- ToggleButton.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
+ ToggleButton.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]

View File

@@ -80,6 +80,7 @@ Button.default.pressedBackground #e6e6e6 com.formdev.flatlaf.util.DerivedColo
Button.defaultButtonFollowsFocus false
Button.disabledBackground #f2f2f2 javax.swing.plaf.ColorUIResource [UI]
Button.disabledBorderColor #cfcfcf javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBackground #dedede com.formdev.flatlaf.util.DerivedColor [UI] darken(13% autoInverse)
Button.disabledText #8c8c8c javax.swing.plaf.ColorUIResource [UI]
Button.focusedBackground #e3f1fa javax.swing.plaf.ColorUIResource [UI]
@@ -446,11 +447,50 @@ JXTitledPanel.titleBackground #dfdfdf javax.swing.plaf.ColorUIResource [UI]
JXTitledPanel.titleForeground #222222 javax.swing.plaf.ColorUIResource [UI]
#---- JideButton ----
JideButton.background #ffffff javax.swing.plaf.ColorUIResource [UI]
JideButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideButton.borderColor #c4c4c4 javax.swing.plaf.ColorUIResource [UI]
JideButton.darkShadow #c4c4c4 javax.swing.plaf.ColorUIResource [UI]
JideButton.focusedBackground #e0e0e0 com.formdev.flatlaf.util.DerivedColor [UI] darken(12% autoInverse)
JideButton.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
JideButton.highlight #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse)
JideButton.light #c4c4c4 javax.swing.plaf.ColorUIResource [UI]
JideButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideButton.selectedAndFocusedBackground #d9d9d9 com.formdev.flatlaf.util.DerivedColor [UI] darken(15% autoInverse)
JideButton.selectedBackground #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse)
JideButton.shadow #c4c4c4 javax.swing.plaf.ColorUIResource [UI]
JideButton.textIconGap [active] 4
JideButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideButtonUI
#---- JideLabel ----
JideLabel.background #f2f2f2 javax.swing.plaf.ColorUIResource [UI]
JideLabel.disabledForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI]
JideLabel.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
JideLabelUI com.formdev.flatlaf.jideoss.ui.FlatJideLabelUI
#---- JidePopupMenu ----
JidePopupMenuUI com.formdev.flatlaf.jideoss.ui.FlatJidePopupMenuUI
#---- JideSplitButton ----
JideSplitButton.background #ffffff javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideSplitButton.buttonArrowColor #666666 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.buttonDisabledArrowColor #a6a6a6 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideSplitButton.selectionForeground #000000 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.textIconGap [active] 4
JideSplitButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideSplitButtonUI
#---- JideTabbedPane ----
JideTabbedPane.background #f2f2f2 javax.swing.plaf.ColorUIResource [UI]
@@ -1131,6 +1171,8 @@ TitlePane.buttonHoverBackground #e6e6e6 com.formdev.flatlaf.util.DerivedColor
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonPressedBackground #cccccc com.formdev.flatlaf.util.DerivedColor [UI] darken(20% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI]
@@ -1138,16 +1180,19 @@ TitlePane.closePressedBackground #e8112399 60% javax.swing.plaf.ColorUIResou
TitlePane.closePressedForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.embeddedForeground #595959 javax.swing.plaf.ColorUIResource [UI]
TitlePane.foreground #000000 javax.swing.plaf.ColorUIResource [UI]
TitlePane.iconMargins 3,8,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.icon [lazy] 16,16 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
TitlePane.iconMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.iconSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.iconifyIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowIconifyIcon [UI]
TitlePane.inactiveBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.inactiveForeground #8c8c8c javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.useWindowDecorations true
#---- TitledBorder ----

View File

@@ -64,6 +64,7 @@ BusyLabelUI com.formdev.flatlaf.swingx.ui.FlatBusyLabelUI
Button.arc 6
Button.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
Button.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatButtonBorder [UI]
Button.borderColor #00ff00 javax.swing.plaf.ColorUIResource [UI]
Button.borderWidth 1
Button.darkShadow #696969 javax.swing.plaf.ColorUIResource [UI]
Button.default.borderWidth 1
@@ -81,6 +82,7 @@ Button.default.startBorderColor #ff0000 javax.swing.plaf.ColorUIResource [UI]
Button.defaultButtonFollowsFocus false
Button.disabledBackground #e0e0e0 javax.swing.plaf.ColorUIResource [UI]
Button.disabledBorderColor #000088 javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #000088 javax.swing.plaf.ColorUIResource [UI]
Button.disabledText #000088 javax.swing.plaf.ColorUIResource [UI]
Button.endBackground #bbbbbb javax.swing.plaf.ColorUIResource [UI]
Button.endBorderColor #ff0000 javax.swing.plaf.ColorUIResource [UI]
@@ -98,6 +100,7 @@ Button.margin 2,14,2,14 javax.swing.plaf.InsetsUIResource [U
Button.minimumWidth 72
Button.pressedBackground #ffc800 javax.swing.plaf.ColorUIResource [UI]
Button.rollover true
Button.selectedBackground #ffbbbb javax.swing.plaf.ColorUIResource [UI]
Button.shadow #a0a0a0 javax.swing.plaf.ColorUIResource [UI]
Button.startBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
Button.startBorderColor #0000ff javax.swing.plaf.ColorUIResource [UI]
@@ -106,6 +109,7 @@ Button.textShiftOffset 0
Button.toolbar.hoverBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
Button.toolbar.pressedBackground #eeeeee javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.selectedBackground #dddddd javax.swing.plaf.ColorUIResource [UI]
Button.toolbar.spacingInsets 1,2,1,2 javax.swing.plaf.InsetsUIResource [UI]
ButtonUI com.formdev.flatlaf.ui.FlatButtonUI
@@ -434,11 +438,50 @@ JXTitledPanel.titleBackground #ffff00 javax.swing.plaf.ColorUIResource [UI]
JXTitledPanel.titleForeground #ff00ff javax.swing.plaf.ColorUIResource [UI]
#---- JideButton ----
JideButton.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
JideButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideButton.borderColor #00ff00 javax.swing.plaf.ColorUIResource [UI]
JideButton.darkShadow #00ff00 javax.swing.plaf.ColorUIResource [UI]
JideButton.focusedBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
JideButton.foreground #ff0000 javax.swing.plaf.ColorUIResource [UI]
JideButton.highlight #ffbbbb javax.swing.plaf.ColorUIResource [UI]
JideButton.light #00ff00 javax.swing.plaf.ColorUIResource [UI]
JideButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideButton.selectedAndFocusedBackground #eeeeee javax.swing.plaf.ColorUIResource [UI]
JideButton.selectedBackground #dddddd javax.swing.plaf.ColorUIResource [UI]
JideButton.shadow #00ff00 javax.swing.plaf.ColorUIResource [UI]
JideButton.textIconGap [active] 4
JideButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideButtonUI
#---- JideLabel ----
JideLabel.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
JideLabel.disabledForeground #000088 javax.swing.plaf.ColorUIResource [UI]
JideLabel.foreground #008800 javax.swing.plaf.ColorUIResource [UI]
JideLabelUI com.formdev.flatlaf.jideoss.ui.FlatJideLabelUI
#---- JidePopupMenu ----
JidePopupMenuUI com.formdev.flatlaf.jideoss.ui.FlatJidePopupMenuUI
#---- JideSplitButton ----
JideSplitButton.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.border [lazy] 0,0,0,0 false com.formdev.flatlaf.ui.FlatMarginBorder [UI]
JideSplitButton.buttonArrowColor #666666 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.buttonDisabledArrowColor #ababab javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.foreground #ff0000 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
JideSplitButton.selectionForeground #ff0000 javax.swing.plaf.ColorUIResource [UI]
JideSplitButton.textIconGap [active] 4
JideSplitButtonUI com.formdev.flatlaf.jideoss.ui.FlatJideSplitButtonUI
#---- JideTabbedPane ----
JideTabbedPane.background #ccffcc javax.swing.plaf.ColorUIResource [UI]
@@ -1122,24 +1165,30 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
#---- TitlePane ----
TitlePane.background #00ff00 javax.swing.plaf.ColorUIResource [UI]
TitlePane.borderColor #ff0000 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true
TitlePane.closeHoverBackground #e81123 javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeHoverForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.closeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowCloseIcon [UI]
TitlePane.closePressedBackground #e8112399 60% javax.swing.plaf.ColorUIResource [UI]
TitlePane.closePressedForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.foreground #0000ff javax.swing.plaf.ColorUIResource [UI]
TitlePane.iconMargins 3,8,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.icon [lazy] 16,16 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
TitlePane.iconMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.iconSize 16,16 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.iconifyIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowIconifyIcon [UI]
TitlePane.inactiveBackground #008800 javax.swing.plaf.ColorUIResource [UI]
TitlePane.inactiveForeground #ffffff javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarMargins 0,8,0,22 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.menuBarTitleGap 20
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.titleMargins 3,8,3,8 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.useWindowDecorations true
#---- TitledBorder ----

View File

@@ -0,0 +1,269 @@
#---- ButtonPanel ----
+ ButtonPanel.buttonGap 6
+ ButtonPanel.groupGap 6
+ ButtonPanel.minButtonWidth 75
+ ButtonPanel.oppositeOrder H
+ ButtonPanel.order ACO
#---- Content ----
+ Content.background [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
#---- Cursor ----
+ Cursor.delete 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.drag 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.dragStop 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.dragText 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.dragTextStop 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.east 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.float 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.horizontal 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.hsplit 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.moveEast 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.moveWest 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.north 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.percentage 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.south 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.tab 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.vertical 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.vsplit 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
+ Cursor.west 32,32 javax.swing.ImageIcon (java.awt.image.BufferedImage)
#---- FolderChooser ----
+ FolderChooserUI com.jidesoft.plaf.basic.BasicFolderChooserUI
#---- FormattedTextField ----
- FormattedTextField.font [lazy] SansSerif plain 12 javax.swing.plaf.FontUIResource [UI]
+ FormattedTextField.font SansSerif plain 12 javax.swing.plaf.FontUIResource [UI]
#---- Gripper ----
+ Gripper.foreground [active] #b2b2b2 javax.swing.plaf.ColorUIResource [UI]
+ Gripper.painter [unknown type] com.jidesoft.plaf.vsnet.VsnetWindowsUtils$9 [UI]
+ Gripper.size 8
+ GripperUI com.jidesoft.plaf.vsnet.VsnetGripperUI
#---- HeaderBox ----
+ HeaderBox.background [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ HeaderBoxUI com.jidesoft.plaf.basic.BasicHeaderBoxUI
#---- Icon ----
+ Icon.floating true
#---- JideButton ----
+ JideButton.background [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.border 0,0,0,0 false javax.swing.plaf.basic.BasicBorders$MarginBorder [UI]
+ JideButton.borderColor [active] #0078d7 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.darkShadow [active] #696969 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.focusedBackground [active] #cfeafe javax.swing.plaf.ColorUIResource [UI]
+ JideButton.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ JideButton.foreground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.highlight [active] #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideButton.light [active] #e3e3e3 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.margin 3,3,3,3 javax.swing.plaf.InsetsUIResource [UI]
+ JideButton.selectedAndFocusedBackground [active] #88caff javax.swing.plaf.ColorUIResource [UI]
+ JideButton.selectedBackground [active] #acdafe javax.swing.plaf.ColorUIResource [UI]
+ JideButton.shadow [active] #a0a0a0 javax.swing.plaf.ColorUIResource [UI]
+ JideButton.textIconGap 4
+ JideButton.textShiftOffset 0
+ JideButtonUI com.jidesoft.plaf.basic.BasicJideButtonUI
#---- JideComboBox ----
+ JideComboBoxUI com.jidesoft.plaf.basic.BasicJideComboBoxUI
#---- JideLabel ----
+ JideLabel.background [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ JideLabel.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ JideLabel.foreground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideLabelUI com.jidesoft.plaf.basic.BasicJideLabelUI
#---- JidePopupMenu ----
+ JidePopupMenuUI com.jidesoft.plaf.windows.WindowsJidePopupMenuUI
#---- JidePopup ----
+ JidePopupUI com.jidesoft.plaf.basic.BasicJidePopupUI
#---- JideScrollPane ----
+ JideScrollPane.border [active] 1,1,1,1 true javax.swing.plaf.BorderUIResource [UI]
#---- JideSplitButton ----
+ JideSplitButton.border 0,0,0,0 false javax.swing.plaf.basic.BasicBorders$MarginBorder [UI]
+ JideSplitButton.borderPainted false
+ JideSplitButton.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ JideSplitButton.margin 3,3,3,7 javax.swing.plaf.InsetsUIResource [UI]
+ JideSplitButton.selectionForeground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideSplitButton.textIconGap 4
+ JideSplitButtonUI com.jidesoft.plaf.basic.BasicJideSplitButtonUI
#---- JideSplitPane ----
+ JideSplitPane.dividerSize 4
#---- JideSplitPaneDivider ----
+ JideSplitPaneDivider.background [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ JideSplitPaneDivider.border 0,0,0,0 false javax.swing.plaf.BorderUIResource [UI]
+ JideSplitPaneDivider.gripperPainter [unknown type] com.jidesoft.plaf.vsnet.VsnetWindowsUtils$9 [UI]
#---- JideTabbedPane ----
+ JideTabbedPane.background [active] #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.border 0,0,0,0 false javax.swing.plaf.BorderUIResource [UI]
+ JideTabbedPane.buttonMargin 5
+ JideTabbedPane.buttonSize 18
+ JideTabbedPane.closeButtonAlignment 11
+ JideTabbedPane.closeButtonLeftMargin 2
+ JideTabbedPane.closeButtonMargin 2
+ JideTabbedPane.closeButtonMarginHorizonal 3
+ JideTabbedPane.closeButtonMarginSize 6
+ JideTabbedPane.closeButtonMarginVertical 3
+ JideTabbedPane.closeButtonRightMargin 2
+ JideTabbedPane.compressedStyleCloseButtonMarginHorizontal 0
+ JideTabbedPane.compressedStyleCloseButtonMarginVertical 0
+ JideTabbedPane.compressedStyleIconMargin 12
+ JideTabbedPane.compressedStyleNoIconRectSize 24
+ JideTabbedPane.contentBorderInsets 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]
+ JideTabbedPane.darkShadow [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.defaultResizeMode 1
+ JideTabbedPane.defaultTabBorderShadowColor #736d63 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.defaultTabColorTheme 3
+ JideTabbedPane.defaultTabShape 2
+ JideTabbedPane.fitStyleBoundSize 8
+ JideTabbedPane.fitStyleFirstTabMargin 4
+ JideTabbedPane.fitStyleIconMinWidth 24
+ JideTabbedPane.fitStyleTextMinWidth 16
+ JideTabbedPane.fixedStyleRectSize 60
+ JideTabbedPane.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ JideTabbedPane.foreground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.gripLeftMargin 4
+ JideTabbedPane.gripperPainter [unknown type] com.jidesoft.plaf.vsnet.VsnetWindowsUtils$9 [UI]
+ JideTabbedPane.highlight [active] #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.iconMargin 5
+ JideTabbedPane.ignoreContentBorderInsetsIfNoTabs false
+ JideTabbedPane.light [active] #e3e3e3 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.noIconMargin 2
+ JideTabbedPane.selectedTabBackground [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.selectedTabBackgroundDk #ffc73c javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.selectedTabBackgroundLt #e68b2c javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.selectedTabFont Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ JideTabbedPane.selectedTabTextForeground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.shadow [active] #a0a0a0 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.showCloseButtonOnTab false
+ JideTabbedPane.showIconOnTab true
+ JideTabbedPane.tabAreaBackground [active] #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.tabAreaBackgroundDk [active] #f0f0f0 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.tabAreaBackgroundLt [active] #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.tabAreaInsets 2,4,0,4 javax.swing.plaf.InsetsUIResource [UI]
+ JideTabbedPane.tabInsets 1,4,1,4 javax.swing.plaf.InsetsUIResource [UI]
+ JideTabbedPane.tabListBackground #ffffff javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPane.tabRectPadding 2
+ JideTabbedPane.tabRunOverlay 2
+ JideTabbedPane.textIconGap 4
+ JideTabbedPane.textMarginVertical 4
+ JideTabbedPane.textPadding 6
+ JideTabbedPane.unselectedTabTextForeground [active] #000000 javax.swing.plaf.ColorUIResource [UI]
+ JideTabbedPaneUI com.jidesoft.plaf.vsnet.VsnetJideTabbedPaneUI
#---- Label ----
- Label.font [lazy] Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ Label.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
#---- MeterProgressBar ----
+ MeterProgressBar.background #000000 javax.swing.plaf.ColorUIResource [UI]
+ MeterProgressBar.border 1,1,1,1 true javax.swing.plaf.BorderUIResource [UI]
+ MeterProgressBar.cellBackground #008000 javax.swing.plaf.ColorUIResource [UI]
+ MeterProgressBar.cellForeground #00ff00 javax.swing.plaf.ColorUIResource [UI]
+ MeterProgressBar.cellLength 2
+ MeterProgressBar.cellSpacing 2
+ MeterProgressBar.foreground #00ff00 javax.swing.plaf.ColorUIResource [UI]
+ MeterProgressBarUI com.jidesoft.plaf.basic.MeterProgressBarUI
#---- RangeSlider ----
+ RangeSliderUI com.jidesoft.plaf.windows.WindowsRangeSliderUI
#---- Resizable ----
+ Resizable.resizeBorder [active] 4,4,4,4 true com.jidesoft.plaf.vsnet.ResizeFrameBorder [UI]
#---- Spinner ----
- Spinner.font [lazy] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ Spinner.font SansSerif plain 12 javax.swing.plaf.FontUIResource [UI]
#---- StyledLabel ----
+ StyledLabelUI com.jidesoft.plaf.basic.BasicStyledLabelUI
#---- TextArea ----
- TextArea.font [lazy] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ TextArea.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
#---- TextField ----
- TextField.font [lazy] SansSerif plain 12 javax.swing.plaf.FontUIResource [UI]
+ TextField.font SansSerif plain 12 javax.swing.plaf.FontUIResource [UI]
#---- Theme ----
+ Theme.highContrast [active] false
+ Theme.painter [unknown type] java.util.HashMap
#---- ToolBar ----
- ToolBar.font [lazy] Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
+ ToolBar.font Dialog plain 12 javax.swing.plaf.FontUIResource [UI]
#---- TristateCheckBox ----
+ TristateCheckBox.icon 13,13 com.jidesoft.plaf.windows.WindowsIconFactory$CheckBoxIcon [UI]
#---- jidesoft ----
+ jidesoft.extensionInstalled true
+ jidesoft.extensionStyle 0

View File

@@ -0,0 +1,38 @@
#---- JideButton ----
+ JideButton.focusInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI]
ENTER pressed
SPACE pressed
released ENTER released
released SPACE released
#---- JideSplitButton ----
+ JideSplitButton.focusInputMap [lazy] 6 javax.swing.plaf.InputMapUIResource [UI]
DOWN downPressed
ENTER pressed
SPACE pressed
released DOWN downReleased
released ENTER released
released SPACE released
#---- JideTabbedPane ----
+ JideTabbedPane.ancestorInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI]
ctrl KP_UP requestFocus
ctrl PAGE_DOWN navigatePageDown
ctrl PAGE_UP navigatePageUp
ctrl UP requestFocus
+ JideTabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI]
ctrl DOWN requestFocusForVisibleComponent
ctrl KP_DOWN requestFocusForVisibleComponent
DOWN navigateDown
KP_DOWN navigateDown
KP_LEFT navigateLeft
KP_RIGHT navigateRight
KP_UP navigateUp
LEFT navigateLeft
RIGHT navigateRight
UP navigateUp

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