mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
Compare commits
189 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
025f6564dc | ||
|
|
35f97368fa | ||
|
|
09e5c86488 | ||
|
|
8998371cae | ||
|
|
29e1dc6b55 | ||
|
|
439e63b52f | ||
|
|
eea341fb33 | ||
|
|
359eedf773 | ||
|
|
866751ffc1 | ||
|
|
38a3a0768d | ||
|
|
03b42749cd | ||
|
|
60fd78e082 | ||
|
|
9edaf58929 | ||
|
|
5000186f85 | ||
|
|
cacf0ea987 | ||
|
|
067501cbe7 | ||
|
|
9fe0cf496b | ||
|
|
9d0823038e | ||
|
|
5a05efefdd | ||
|
|
988d171bdd | ||
|
|
e6f72bf343 | ||
|
|
89c5a0c57b | ||
|
|
d97146393c | ||
|
|
1c52f1f76c | ||
|
|
9bd3a68115 | ||
|
|
f58780d36b | ||
|
|
6eb15ab437 | ||
|
|
00dc7004f5 | ||
|
|
8ec0e57235 | ||
|
|
d75dc9e70c | ||
|
|
ec2fccbb0e | ||
|
|
34861166e8 | ||
|
|
584fa0a26e | ||
|
|
6c48489d89 | ||
|
|
ba9c884a0c | ||
|
|
360f0bafe0 | ||
|
|
4327c13dca | ||
|
|
4f2256f713 | ||
|
|
5167cd368f | ||
|
|
ef7289d11a | ||
|
|
cb11d98bf7 | ||
|
|
992349da8c | ||
|
|
2e7637f274 | ||
|
|
1f8eaf4a64 | ||
|
|
46ac7a9dc7 | ||
|
|
0d86d39217 | ||
|
|
1f591f3d1b | ||
|
|
30c6ddba37 | ||
|
|
406eeaec96 | ||
|
|
2fe5652bc6 | ||
|
|
39bf68a6bd | ||
|
|
a7a4a19824 | ||
|
|
7f906ba0ea | ||
|
|
07bf6e4506 | ||
|
|
a331760321 | ||
|
|
d9c240d729 | ||
|
|
d9526c19e7 | ||
|
|
1798ccd284 | ||
|
|
ab1ce7fab1 | ||
|
|
e9b2f17171 | ||
|
|
d3bf4433b7 | ||
|
|
ba0f43455b | ||
|
|
638af4bcd7 | ||
|
|
5eab843d97 | ||
|
|
c55f0e239e | ||
|
|
32d9381745 | ||
|
|
77fc564e70 | ||
|
|
3b84314c45 | ||
|
|
5729c20386 | ||
|
|
a4d70d8095 | ||
|
|
8fcce349d5 | ||
|
|
5a94676a3a | ||
|
|
f32d72ee62 | ||
|
|
e35fc8620c | ||
|
|
277c288952 | ||
|
|
240b08e55c | ||
|
|
fe7f345661 | ||
|
|
c8db01c958 | ||
|
|
f456185f7d | ||
|
|
801b555835 | ||
|
|
eee177e64b | ||
|
|
63639f8e96 | ||
|
|
de1b0b1bb6 | ||
|
|
bbdd7fc2b4 | ||
|
|
6addb5c4b4 | ||
|
|
b47e0c88d6 | ||
|
|
d06993d940 | ||
|
|
d31f167b9e | ||
|
|
f12ee6c167 | ||
|
|
983b341f33 | ||
|
|
f3e6642f05 | ||
|
|
0a63990d21 | ||
|
|
6909bb4b03 | ||
|
|
620aa8bcee | ||
|
|
6db39d1860 | ||
|
|
1762ead89f | ||
|
|
d13ddeb944 | ||
|
|
1b5da0e1d1 | ||
|
|
7a2d0e7fcb | ||
|
|
477c3b6b1e | ||
|
|
95312c3650 | ||
|
|
98a3c4b0f5 | ||
|
|
6e990a7e31 | ||
|
|
8e49904f8d | ||
|
|
69f52c8abd | ||
|
|
d7b0754327 | ||
|
|
2a00de11f1 | ||
|
|
923cc51f3e | ||
|
|
c8f7478170 | ||
|
|
9006e835c6 | ||
|
|
f801d61929 | ||
|
|
a143e5777c | ||
|
|
bffac60bf8 | ||
|
|
bf500e46e7 | ||
|
|
4a2f79f390 | ||
|
|
c24ce7c5bc | ||
|
|
8a6a0c7971 | ||
|
|
de6e5bd800 | ||
|
|
e18a04f9e6 | ||
|
|
14fc652f4b | ||
|
|
9a876e747a | ||
|
|
f8ee8b27fb | ||
|
|
ce1a1487aa | ||
|
|
fe1e364a1d | ||
|
|
eabb052107 | ||
|
|
734f3621f1 | ||
|
|
ae8323e2f8 | ||
|
|
9612a81f2e | ||
|
|
2945a36cef | ||
|
|
b84dc5bfcc | ||
|
|
60486fd880 | ||
|
|
891091cebc | ||
|
|
1493ddcf41 | ||
|
|
4299c50537 | ||
|
|
14577c396d | ||
|
|
e9b566241d | ||
|
|
d39b08c035 | ||
|
|
69ac683c8c | ||
|
|
eafd0b3d06 | ||
|
|
310a4989dc | ||
|
|
3d0df51839 | ||
|
|
ede02aaaa5 | ||
|
|
beff149004 | ||
|
|
07db6e8fb0 | ||
|
|
46852c0780 | ||
|
|
a5e41c573f | ||
|
|
ed91aa4648 | ||
|
|
9a94395d30 | ||
|
|
04aa61c2bb | ||
|
|
035a13df54 | ||
|
|
e8a6f0ca3d | ||
|
|
1fc519b9de | ||
|
|
2bcf38e2e3 | ||
|
|
8eb44a68cb | ||
|
|
30c7b442a8 | ||
|
|
cee2211108 | ||
|
|
b7bcbccd45 | ||
|
|
d2ccb97eba | ||
|
|
39d56f2603 | ||
|
|
83e904dd2d | ||
|
|
110c787eba | ||
|
|
7c7ff289de | ||
|
|
617a35c51b | ||
|
|
73487ccf65 | ||
|
|
712bff9c99 | ||
|
|
eedfcf86aa | ||
|
|
f730848928 | ||
|
|
61d0574c5c | ||
|
|
2f01e01ec1 | ||
|
|
cbcf66df7f | ||
|
|
cfaeea039b | ||
|
|
a891d1eb54 | ||
|
|
4372052ef0 | ||
|
|
8734b062dc | ||
|
|
343451de65 | ||
|
|
144d65c776 | ||
|
|
a6815574f7 | ||
|
|
e5a116a0d4 | ||
|
|
0beef6b108 | ||
|
|
7341008449 | ||
|
|
49bd53194a | ||
|
|
baf4437efc | ||
|
|
b244f80f81 | ||
|
|
e41c91a42b | ||
|
|
b9a2e3ceac | ||
|
|
fa7dd3bdc4 | ||
|
|
9a8c68b846 | ||
|
|
698e33ddf4 | ||
|
|
909258ba14 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -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
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -62,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
|
||||
|
||||
@@ -77,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
58
.github/workflows/natives.yml
vendored
Normal 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
2
.gitignore
vendored
@@ -9,3 +9,5 @@ out/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
167
CHANGELOG.md
167
CHANGELOG.md
@@ -1,6 +1,171 @@
|
||||
FlatLaf Change Log
|
||||
==================
|
||||
|
||||
## 1.2
|
||||
|
||||
#### New features and improvements
|
||||
|
||||
- Renamed `Flat*Laf.install()` methods to `Flat*Laf.setup()` to avoid confusion
|
||||
with `UIManager.installLookAndFeel(LookAndFeelInfo info)`. The old
|
||||
`Flat*Laf.install()` methods are still there, but marked as deprecated. They
|
||||
will be removed in a future version.
|
||||
- Button and ToggleButton: Support borderless button style (set client property
|
||||
`JButton.buttonType` to `borderless`). (PR #276)
|
||||
- ComboBox: Support using as cell renderer (e.g. in `JTable`).
|
||||
- DesktopPane: Improved layout of iconified internal frames in dock:
|
||||
- Always placed at bottom-left in desktop pane.
|
||||
- Newly iconified frames are added to the right side of the dock.
|
||||
- If frame is deiconified, dock is compacted (icons move to the left).
|
||||
- If dock is wider than desktop width, additional rows are used.
|
||||
- If desktop pane is resized, layout of dock is updated.
|
||||
- TableHeader: Moved table header column border painting from
|
||||
`FlatTableHeaderUI` to new border `FlatTableHeaderBorder` to improve
|
||||
compatibility with custom table header implementations. (issue #228)
|
||||
- Linux: Enable text anti-aliasing if no Gnome or KDE Desktop properties are
|
||||
available. (issue #218)
|
||||
- IntelliJ Themes: Added "Material Theme UI Lite / GitHub Dark" theme.
|
||||
- JIDE Common Layer: Improved support for `JideTabbedPane`. (PR #306)
|
||||
- Extras: `FlatSVGIcon` improvements:
|
||||
- Each icon can now have its own color filter. (PR #303)
|
||||
- Use mapper function in color filter to dynamically map colors. (PR #303)
|
||||
- Color filter supports light and dark themes.
|
||||
- Getters for icon name, classloader, etc.
|
||||
- Extras: UI Inspector: Show class hierarchies when pressing <kbd>Alt</kbd> key
|
||||
and prettified class names (dimmed package name).
|
||||
- Extras: `FlatSVGUtils.createWindowIconImages()` now returns a single
|
||||
multi-resolution image that creates requested image sizes on demand from SVG
|
||||
(only on Windows with Java 9+).
|
||||
|
||||
#### Fixed bugs
|
||||
|
||||
- CheckBox and RadioButton: Do not fill background if used as cell renderer,
|
||||
except if cell is selected or has different background color. (issue #311)
|
||||
- DesktopPane:
|
||||
- Fixed missing preview of iconified internal frames in dock when using a
|
||||
custom desktop manager. (PR #294)
|
||||
- Fixed incomplete preview of iconified internal frames in dock when switching
|
||||
LaF.
|
||||
- On HiDPI screens, use high-resolution images for preview of iconified
|
||||
internal frames in dock.
|
||||
- PopupFactory: Fixed occasional `NullPointerException` in
|
||||
`FlatPopupFactory.fixToolTipLocation()`. (issue #305)
|
||||
- Tree: Fill cell background if
|
||||
`DefaultTreeCellRenderer.setBackgroundNonSelectionColor(Color)` was used.
|
||||
(issue #322)
|
||||
- IntelliJ Themes: Fixed background colors of DesktopPane and DesktopIcon in all
|
||||
themes.
|
||||
- Native window decorations:
|
||||
- Fixed slow application startup under particular conditions. (e.g. incomplete
|
||||
custom JRE) (issue #319)
|
||||
- Fixed occasional double window title bar when creating many frames or
|
||||
dialogs. (issue #315)
|
||||
- Fixed broken maximizing window (under special conditions) when restoring
|
||||
frame state at startup.
|
||||
- Title icon: For multi-resolution images now use `getResolutionVariant(width,
|
||||
height)` (instead of `getResolutionVariants()`) to allow creation of
|
||||
requested size on demand. This also avoids creation of all resolution
|
||||
variants.
|
||||
- Double-click at upper-left corner of maximized frame did not close window.
|
||||
(issue #326)
|
||||
- Linux: Fixed/improved detection of user font settings. (issue #309)
|
||||
|
||||
|
||||
## 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
|
||||
@@ -68,7 +233,7 @@ FlatLaf Change Log
|
||||
- CheckBox and RadioButton: Fill component background as soon as background
|
||||
color is different to default background color, even if component is not
|
||||
opaque (which is the default). This paints selection if using the component as
|
||||
cell renderer a Table, Tree or List.
|
||||
cell renderer in Table, Tree or List.
|
||||
- TextComponents: Border of focused non-editable text components had wrong
|
||||
color.
|
||||
- Custom window decorations: Fixed top window border in dark themes when running
|
||||
|
||||
26
README.md
26
README.md
@@ -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
|
||||
- 
|
||||
[install4j](https://www.ej-technologies.com/products/install4j/overview.html)
|
||||
9.0 (**commercial**) - the powerful multi-platform Java installer builder
|
||||
-  [DbVisualizer](https://www.dbvis.com/) 12.0
|
||||
(**commercial**) - the universal database tool for developers, analysts and
|
||||
DBAs
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
val releaseVersion = "1.0"
|
||||
val developmentVersion = "1.1-SNAPSHOT"
|
||||
val releaseVersion = "1.2"
|
||||
val developmentVersion = "1.3-SNAPSHOT"
|
||||
|
||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||
|
||||
@@ -47,19 +47,35 @@ allprojects {
|
||||
targetCompatibility = "1.8"
|
||||
|
||||
options.encoding = "ISO-8859-1"
|
||||
options.isDeprecation = false
|
||||
}
|
||||
|
||||
withType<Jar>().configureEach {
|
||||
// manifest for all created JARs
|
||||
manifest.attributes(mapOf(
|
||||
manifest.attributes(
|
||||
"Implementation-Vendor" to "FormDev Software GmbH",
|
||||
"Implementation-Copyright" to "Copyright (C) 2019-${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
||||
"Implementation-Version" to project.version))
|
||||
"Implementation-Version" to project.version
|
||||
)
|
||||
|
||||
// add META-INF/LICENSE to all created JARs
|
||||
from("${rootDir}/LICENSE") {
|
||||
into("META-INF")
|
||||
from( "${rootDir}/LICENSE" ) {
|
||||
into( "META-INF" )
|
||||
}
|
||||
}
|
||||
|
||||
withType<Javadoc>().configureEach {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
|
||||
title = "${project.name} $version"
|
||||
header = title
|
||||
isUse = true
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||
links( "https://docs.oracle.com/en/java/javase/11/docs/api/" )
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add( "java9Compile", sourceSets.main.get().output )
|
||||
}
|
||||
|
||||
tasks {
|
||||
named<JavaCompile>( "compileJava9Java" ) {
|
||||
sourceCompatibility = "9"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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" )
|
||||
|
||||
@@ -35,21 +45,11 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
use( true )
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
|
||||
named<Jar>("sourcesJar" ) {
|
||||
named<Jar>( "sourcesJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
}
|
||||
|
||||
named<Jar>("javadocJar" ) {
|
||||
named<Jar>( "javadocJar" ) {
|
||||
archiveBaseName.set( "flatlaf" )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,9 @@ public interface FlatClientProperties
|
||||
* {@link #BUTTON_TYPE_SQUARE},
|
||||
* {@link #BUTTON_TYPE_ROUND_RECT},
|
||||
* {@link #BUTTON_TYPE_TAB},
|
||||
* {@link #BUTTON_TYPE_HELP} or
|
||||
* {@link BUTTON_TYPE_TOOLBAR_BUTTON}
|
||||
* {@link #BUTTON_TYPE_HELP},
|
||||
* {@link #BUTTON_TYPE_TOOLBAR_BUTTON} or
|
||||
* {@link #BUTTON_TYPE_BORDERLESS}
|
||||
*/
|
||||
String BUTTON_TYPE = "JButton.buttonType";
|
||||
|
||||
@@ -89,6 +90,16 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
|
||||
|
||||
/**
|
||||
* Paint the button without a border in the unfocused state.
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||
*
|
||||
* @see #BUTTON_TYPE
|
||||
* @since 1.2
|
||||
*/
|
||||
String BUTTON_TYPE_BORDERLESS = "borderless";
|
||||
|
||||
/**
|
||||
* Specifies selected state of a checkbox.
|
||||
* <p>
|
||||
@@ -232,14 +243,67 @@ 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>
|
||||
* (requires Window 10)
|
||||
* <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>
|
||||
* (requires Window 10)
|
||||
* <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 -------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a dark color scheme and looks like Darcula LaF.
|
||||
* <p>
|
||||
@@ -29,10 +31,28 @@ public class FlatDarculaLaf
|
||||
{
|
||||
public static final String NAME = "FlatLaf Darcula";
|
||||
|
||||
public static boolean install() {
|
||||
return install( new FlatDarculaLaf() );
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarculaLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDarculaLaf.class );
|
||||
}
|
||||
|
||||
@@ -34,8 +34,16 @@ public class FlatDarkLaf
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatDarkLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return install( new FlatDarkLaf() );
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.formdev.flatlaf;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
/**
|
||||
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
|
||||
* <p>
|
||||
@@ -29,10 +31,28 @@ public class FlatIntelliJLaf
|
||||
{
|
||||
public static final String NAME = "FlatLaf IntelliJ";
|
||||
|
||||
public static boolean install() {
|
||||
return install( new FlatIntelliJLaf() );
|
||||
/**
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatIntelliJLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
|
||||
* to query available LaFs and display them to the user in a combobox.
|
||||
*/
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatIntelliJLaf.class );
|
||||
}
|
||||
|
||||
@@ -32,14 +32,13 @@ import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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 +47,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;
|
||||
@@ -55,13 +55,16 @@ import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.plaf.ColorUIResource;
|
||||
import javax.swing.plaf.FontUIResource;
|
||||
import javax.swing.plaf.IconUIResource;
|
||||
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 +77,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,23 +93,28 @@ 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)}.
|
||||
*/
|
||||
public static boolean install( LookAndFeel newLookAndFeel ) {
|
||||
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
||||
try {
|
||||
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 setup look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup(LookAndFeel)} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install( LookAndFeel newLookAndFeel ) {
|
||||
return setup( newLookAndFeel );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given look and feel to the set of available look and feels.
|
||||
* <p>
|
||||
@@ -145,28 +152,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;
|
||||
@@ -184,8 +191,10 @@ public abstract class FlatLaf
|
||||
|
||||
@Override
|
||||
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
||||
if( icon instanceof DisabledIconProvider )
|
||||
return ((DisabledIconProvider)icon).getDisabledIcon();
|
||||
if( icon instanceof DisabledIconProvider ) {
|
||||
Icon disabledIcon = ((DisabledIconProvider)icon).getDisabledIcon();
|
||||
return !(disabledIcon instanceof UIResource) ? new IconUIResource( disabledIcon ) : disabledIcon;
|
||||
}
|
||||
|
||||
if( icon instanceof ImageIcon ) {
|
||||
Object grayFilter = UIManager.get( "Component.grayFilter" );
|
||||
@@ -265,16 +274,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 +306,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();
|
||||
}
|
||||
|
||||
@@ -339,9 +330,9 @@ public abstract class FlatLaf
|
||||
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
|
||||
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
|
||||
} else
|
||||
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).newInstance();
|
||||
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().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 +391,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 +458,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 +504,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 +527,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>
|
||||
@@ -553,6 +565,8 @@ public abstract class FlatLaf
|
||||
defaults.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
|
||||
} else if( SystemInfo.isJava_9_orLater ) {
|
||||
Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS );
|
||||
if( desktopHints == null )
|
||||
desktopHints = fallbackAATextInfo();
|
||||
if( desktopHints instanceof Map ) {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Map<Object, Object> hints = (Map<Object, Object>) desktopHints;
|
||||
@@ -575,9 +589,52 @@ public abstract class FlatLaf
|
||||
Object value = Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
||||
.getMethod( "getAATextInfo", boolean.class )
|
||||
.invoke( null, true );
|
||||
if( value == null )
|
||||
value = fallbackAATextInfo();
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object fallbackAATextInfo() {
|
||||
// do nothing if explicitly overridden
|
||||
if( System.getProperty( "awt.useSystemAAFontSettings" ) != null )
|
||||
return null;
|
||||
|
||||
Object aaHint = null;
|
||||
Integer lcdContrastHint = null;
|
||||
|
||||
if( SystemInfo.isLinux ) {
|
||||
// see sun.awt.UNIXToolkit.getDesktopAAHints()
|
||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||
if( toolkit.getDesktopProperty( "gnome.Xft/Antialias" ) == null &&
|
||||
toolkit.getDesktopProperty( "fontconfig/Antialias" ) == null )
|
||||
{
|
||||
// no Gnome or KDE Desktop properties available
|
||||
// --> enable antialiasing
|
||||
aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
if( aaHint == null )
|
||||
return null;
|
||||
|
||||
if( SystemInfo.isJava_9_orLater ) {
|
||||
Map<Object, Object> hints = new HashMap<>();
|
||||
hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, aaHint );
|
||||
hints.put( RenderingHints.KEY_TEXT_LCD_CONTRAST, lcdContrastHint );
|
||||
return hints;
|
||||
} else {
|
||||
// Java 8
|
||||
try {
|
||||
return Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
||||
.getConstructor( Object.class, Integer.class )
|
||||
.newInstance( aaHint, lcdContrastHint );
|
||||
} catch( Exception ex ) {
|
||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||
throw new RuntimeException( ex );
|
||||
}
|
||||
}
|
||||
@@ -684,7 +741,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 +774,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 +890,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;
|
||||
|
||||
|
||||
@@ -34,8 +34,16 @@ public class FlatLightLaf
|
||||
* Sets the application look and feel to this LaF
|
||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||
*/
|
||||
public static boolean setup() {
|
||||
return setup( new FlatLightLaf() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return install( new FlatLightLaf() );
|
||||
return setup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,8 +95,8 @@ 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>
|
||||
@@ -89,12 +106,20 @@ public interface FlatSystemProperties
|
||||
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";
|
||||
|
||||
|
||||
@@ -30,11 +30,12 @@ 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.ColorFunctions;
|
||||
import com.formdev.flatlaf.util.LoggingFacade;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -67,20 +68,28 @@ public class IntelliJTheme
|
||||
|
||||
/**
|
||||
* Loads a IntelliJ .theme.json file from the given input stream,
|
||||
* creates a Laf instance for it and installs it.
|
||||
* creates a Laf instance for it and sets it up.
|
||||
*
|
||||
* The input stream is automatically closed.
|
||||
* Using a buffered input stream is not necessary.
|
||||
*/
|
||||
public static boolean install( InputStream in ) {
|
||||
public static boolean setup( InputStream in ) {
|
||||
try {
|
||||
return FlatLaf.install( createLaf( in ) );
|
||||
return FlatLaf.setup( 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup(InputStream)} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install( InputStream in ) {
|
||||
return setup( in );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a IntelliJ .theme.json file from the given input stream and
|
||||
* creates a Laf instance for it.
|
||||
@@ -215,6 +224,12 @@ public class IntelliJTheme
|
||||
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
||||
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
||||
|
||||
// fix DesktopPane background (use Panel.background and make it 5% darker/lighter)
|
||||
Color desktopBackgroundBase = defaults.getColor( "Panel.background" );
|
||||
Color desktopBackground = ColorFunctions.applyFunctions( desktopBackgroundBase,
|
||||
new ColorFunctions.HSLIncreaseDecrease( 2, dark, 5, false, true ) );
|
||||
defaults.put( "Desktop.background", new ColorUIResource( desktopBackground ) );
|
||||
|
||||
// fix List and Table background colors in Material UI Lite themes
|
||||
if( isMaterialUILite ) {
|
||||
defaults.put( "List.background", defaults.get( "Tree.background" ) );
|
||||
@@ -241,9 +256,10 @@ public class IntelliJTheme
|
||||
// remove theme specific UI defaults and remember only those for current theme
|
||||
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
||||
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
||||
String currentThemeAndAuthorPrefix = '[' + name.replace( ' ', '_' ) + "---" + author.replace( ' ', '_' ) + ']';
|
||||
String currentAuthorPrefix = "[author-" + author.replace( ' ', '_' ) + ']';
|
||||
String allThemesPrefix = "[*]";
|
||||
String[] prefixes = { currentThemePrefix, currentAuthorPrefix, allThemesPrefix };
|
||||
String[] prefixes = { currentThemePrefix, currentThemeAndAuthorPrefix, currentAuthorPrefix, allThemesPrefix };
|
||||
for( String key : themeSpecificKeys ) {
|
||||
Object value = defaults.remove( key );
|
||||
for( String prefix : prefixes ) {
|
||||
@@ -324,7 +340,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
|
||||
}
|
||||
}
|
||||
@@ -344,6 +360,10 @@ public class IntelliJTheme
|
||||
|
||||
// replace all values in UI defaults that match the wildcard key
|
||||
for( Object k : defaultsKeysCache ) {
|
||||
if( k.equals( "Desktop.background" ) ||
|
||||
k.equals( "DesktopIcon.background" ) )
|
||||
continue;
|
||||
|
||||
if( k instanceof String ) {
|
||||
// support replacing of mapped keys
|
||||
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
||||
|
||||
@@ -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;
|
||||
@@ -54,24 +55,38 @@ class LinuxFontPolicy
|
||||
|
||||
String family = "";
|
||||
int style = Font.PLAIN;
|
||||
int size = 10;
|
||||
double dsize = 10;
|
||||
|
||||
// parse pango font description
|
||||
// see https://developer.gnome.org/pango/1.46/pango-Fonts.html#pango-font-description-from-string
|
||||
StringTokenizer st = new StringTokenizer( (String) fontName );
|
||||
while( st.hasMoreTokens() ) {
|
||||
String word = st.nextToken();
|
||||
|
||||
if( word.equalsIgnoreCase( "italic" ) )
|
||||
// remove trailing ',' (e.g. in "Ubuntu Condensed, 11" or "Ubuntu Condensed, Bold 11")
|
||||
if( word.endsWith( "," ) )
|
||||
word = word.substring( 0, word.length() - 1 ).trim();
|
||||
|
||||
String lword = word.toLowerCase();
|
||||
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
|
||||
style |= Font.ITALIC;
|
||||
else if( word.equalsIgnoreCase( "bold" ) )
|
||||
else if( lword.equals( "bold" ) )
|
||||
style |= Font.BOLD;
|
||||
else if( Character.isDigit( word.charAt( 0 ) ) ) {
|
||||
try {
|
||||
size = Integer.parseInt( word );
|
||||
dsize = Double.parseDouble( word );
|
||||
} catch( NumberFormatException ex ) {
|
||||
// ignore
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
// remove '-' from "Semi-Bold", "Extra-Light", etc
|
||||
if( lword.startsWith( "semi-" ) || lword.startsWith( "demi-" ) )
|
||||
word = word.substring( 0, 4 ) + word.substring( 5 );
|
||||
else if( lword.startsWith( "extra-" ) || lword.startsWith( "ultra-" ) )
|
||||
word = word.substring( 0, 5 ) + word.substring( 6 );
|
||||
|
||||
family = family.isEmpty() ? word : (family + ' ' + word);
|
||||
}
|
||||
}
|
||||
|
||||
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
|
||||
@@ -82,8 +97,8 @@ class LinuxFontPolicy
|
||||
family = "Liberation Sans";
|
||||
|
||||
// scale font size
|
||||
double dsize = size * getGnomeFontScale();
|
||||
size = (int) (dsize + 0.5);
|
||||
dsize *= getGnomeFontScale();
|
||||
int size = (int) (dsize + 0.5);
|
||||
if( size < 1 )
|
||||
size = 1;
|
||||
|
||||
@@ -92,7 +107,37 @@ class LinuxFontPolicy
|
||||
if( logicalFamily != null )
|
||||
family = logicalFamily;
|
||||
|
||||
return createFont( family, style, size, dsize );
|
||||
return createFontEx( family, style, size, dsize );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a font for the given family, style and size.
|
||||
* If the font family does not match any font on the system,
|
||||
* then the last word (usually a font weight) from the family name is removed and tried again.
|
||||
* E.g. family 'URW Bookman Light' is not found, but 'URW Bookman' is found.
|
||||
* If still not found, then font of family 'Dialog' is returned.
|
||||
*/
|
||||
private static Font createFontEx( String family, int style, int size, double dsize ) {
|
||||
for(;;) {
|
||||
Font font = createFont( family, style, size, dsize );
|
||||
|
||||
// if the font family does not match any font on the system, "Dialog" family is returned
|
||||
if( !"Dialog".equals( font.getFamily() ) || "Dialog".equals( family ) )
|
||||
return font;
|
||||
|
||||
// find last word in family
|
||||
int index = family.lastIndexOf( ' ' );
|
||||
if( index < 0 )
|
||||
return createFont( "Dialog", style, size, dsize );;
|
||||
|
||||
// check whether last work contains some font weight (e.g. Ultra-Bold or Heavy)
|
||||
String lastWord = family.substring( index + 1 ).toLowerCase();
|
||||
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
|
||||
style |= Font.BOLD;
|
||||
|
||||
// remove last word from family and try again
|
||||
family = family.substring( 0, index );
|
||||
}
|
||||
}
|
||||
|
||||
private static Font createFont( String family, int style, int size, double dsize ) {
|
||||
@@ -172,7 +217,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 +231,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 +270,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) {
|
||||
@@ -438,9 +442,9 @@ class UIDefaultsLoader
|
||||
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
|
||||
return (LazyValue) t -> {
|
||||
try {
|
||||
return findClass( value, addonClassLoaders ).newInstance();
|
||||
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
|
||||
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
|
||||
} catch( Exception 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
@@ -144,6 +141,21 @@ public class FlatArrowButton
|
||||
return FlatUIUtils.deriveColor( foreground, this.foreground );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color used to paint the arrow.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
protected Color getArrowColor() {
|
||||
return isEnabled()
|
||||
? (pressedForeground != null && isPressed()
|
||||
? pressedForeground
|
||||
: (hoverForeground != null && isHover()
|
||||
? hoverForeground
|
||||
: foreground))
|
||||
: disabledForeground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return scale( super.getPreferredSize() );
|
||||
@@ -173,13 +185,7 @@ public class FlatArrowButton
|
||||
}
|
||||
|
||||
// paint arrow
|
||||
g.setColor( deriveForeground( isEnabled()
|
||||
? (pressedForeground != null && isPressed()
|
||||
? pressedForeground
|
||||
: (hoverForeground != null && isHover()
|
||||
? hoverForeground
|
||||
: foreground))
|
||||
: disabledForeground ) );
|
||||
g.setColor( deriveForeground( getArrowColor() ) );
|
||||
paintArrow( (Graphics2D) g );
|
||||
|
||||
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
|
||||
@@ -190,73 +196,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*/
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ public class FlatButtonBorder
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
if( FlatButtonUI.isContentAreaFilled( c ) &&
|
||||
!FlatButtonUI.isToolBarButton( c ) &&
|
||||
(!FlatButtonUI.isBorderlessButton( c ) || FlatUIUtils.isPermanentFocusOwner( c )) &&
|
||||
!FlatButtonUI.isHelpButton( c ) &&
|
||||
!FlatToggleButtonUI.isTabButton( c ) )
|
||||
super.paintBorder( c, g, x, y, width, height );
|
||||
|
||||
@@ -285,6 +285,10 @@ public class FlatButtonUI
|
||||
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
||||
}
|
||||
|
||||
static boolean isBorderlessButton( Component c ) {
|
||||
return c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_BORDERLESS );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||
@@ -332,8 +336,9 @@ public class FlatButtonUI
|
||||
|
||||
// paint shadow
|
||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||
if( !isToolBarButton && shadowColor != null && shadowWidth > 0 && focusWidth > 0 &&
|
||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) && c.isEnabled() )
|
||||
if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && c.isEnabled() &&
|
||||
!isToolBarButton && !isBorderlessButton( c ) &&
|
||||
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) )
|
||||
{
|
||||
g2.setColor( shadowColor );
|
||||
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
||||
@@ -388,41 +393,35 @@ public class FlatButtonUI
|
||||
}
|
||||
|
||||
protected Color getBackground( JComponent c ) {
|
||||
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( 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 +443,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;
|
||||
@@ -465,7 +466,7 @@ public class FlatButtonUI
|
||||
if( !c.isEnabled() )
|
||||
return disabledText;
|
||||
|
||||
if( ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
|
||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
||||
return selectedForeground;
|
||||
|
||||
// use component foreground if explicitly set
|
||||
|
||||
@@ -38,11 +38,11 @@ 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;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.ComboBoxEditor;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.InputMap;
|
||||
@@ -244,7 +244,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 +291,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();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -376,6 +391,15 @@ public class FlatComboBoxUI
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||
float arc = FlatUIUtils.getBorderArc( c );
|
||||
boolean paintBackground = true;
|
||||
|
||||
// check whether used as cell renderer
|
||||
boolean isCellRenderer = c.getParent() instanceof CellRendererPane;
|
||||
if( isCellRenderer ) {
|
||||
focusWidth = 0;
|
||||
arc = 0;
|
||||
paintBackground = isCellRendererBackgroundChanged();
|
||||
}
|
||||
|
||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||
@@ -393,27 +417,29 @@ public class FlatComboBoxUI
|
||||
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
|
||||
|
||||
// paint background
|
||||
g2.setColor( getBackground( enabled ) );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
|
||||
// paint arrow button background
|
||||
if( enabled ) {
|
||||
g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
if( paintBackground || c.isOpaque() ) {
|
||||
g2.setColor( getBackground( enabled ) );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
|
||||
// paint vertical line between value and arrow button
|
||||
if( paintButton ) {
|
||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||
float lw = scale( 1f );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||
// paint arrow button background
|
||||
if( enabled && !isCellRenderer ) {
|
||||
g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
|
||||
Shape oldClip = g2.getClip();
|
||||
if( isLeftToRight )
|
||||
g2.clipRect( arrowX, 0, width - arrowX, height );
|
||||
else
|
||||
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
|
||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
|
||||
g2.setClip( oldClip );
|
||||
}
|
||||
|
||||
// paint vertical line between value and arrow button
|
||||
if( paintButton ) {
|
||||
g2.setColor( enabled ? borderColor : disabledBorderColor );
|
||||
float lw = scale( 1f );
|
||||
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
|
||||
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
|
||||
}
|
||||
}
|
||||
|
||||
// avoid that the "current value" renderer is invoked with enabled antialiasing
|
||||
@@ -540,6 +566,16 @@ public class FlatComboBoxUI
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCellRenderer() {
|
||||
return comboBox.getParent() instanceof CellRendererPane;
|
||||
}
|
||||
|
||||
private boolean isCellRendererBackgroundChanged() {
|
||||
// parent is a CellRendererPane, parentParent is e.g. a JTable
|
||||
Container parentParent = comboBox.getParent().getParent();
|
||||
return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() );
|
||||
}
|
||||
|
||||
//---- class FlatComboBoxButton -------------------------------------------
|
||||
|
||||
protected class FlatComboBoxButton
|
||||
@@ -566,6 +602,14 @@ public class FlatComboBoxUI
|
||||
protected boolean isPressed() {
|
||||
return super.isPressed() || (!comboBox.isEditable() ? pressed : false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color getArrowColor() {
|
||||
if( isCellRenderer() && isCellRendererBackgroundChanged() )
|
||||
return comboBox.getForeground();
|
||||
|
||||
return super.getArrowColor();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatComboPopup -----------------------------------------------
|
||||
@@ -648,14 +692,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() );
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Insets;
|
||||
@@ -28,11 +30,13 @@ import java.awt.Point;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyVetoException;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDesktopPane;
|
||||
import javax.swing.event.MouseInputAdapter;
|
||||
import javax.swing.event.MouseInputListener;
|
||||
import javax.swing.JLabel;
|
||||
@@ -45,6 +49,7 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicDesktopIconUI;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
@@ -75,11 +80,21 @@ public class FlatDesktopIconUI
|
||||
private JToolTip titleTip;
|
||||
private ActionListener closeListener;
|
||||
private MouseInputListener mouseInputListener;
|
||||
private PropertyChangeListener ancestorListener;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatDesktopIconUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
// update dock icon preview if already iconified
|
||||
if( c.isDisplayable() )
|
||||
updateDockIconPreviewLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallUI( JComponent c ) {
|
||||
super.uninstallUI( c );
|
||||
@@ -136,6 +151,17 @@ public class FlatDesktopIconUI
|
||||
};
|
||||
closeButton.addActionListener( closeListener );
|
||||
closeButton.addMouseListener( mouseInputListener );
|
||||
|
||||
ancestorListener = e -> {
|
||||
if( e.getNewValue() != null ) {
|
||||
// update dock icon preview if desktopIcon is added to desktop (internal frame was iconified)
|
||||
updateDockIconPreviewLater();
|
||||
} else {
|
||||
// remove preview icon to release memory
|
||||
dockIcon.setIcon( null );
|
||||
}
|
||||
};
|
||||
desktopIcon.addPropertyChangeListener( "ancestor", ancestorListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,6 +172,9 @@ public class FlatDesktopIconUI
|
||||
closeButton.removeMouseListener( mouseInputListener );
|
||||
closeListener = null;
|
||||
mouseInputListener = null;
|
||||
|
||||
desktopIcon.removePropertyChangeListener( "ancestor", ancestorListener );
|
||||
ancestorListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,15 +257,30 @@ public class FlatDesktopIconUI
|
||||
return getPreferredSize( c );
|
||||
}
|
||||
|
||||
void updateDockIcon() {
|
||||
@Override
|
||||
public void update( Graphics g, JComponent c ) {
|
||||
if( c.isOpaque() ) {
|
||||
// fill background with color derived from desktop pane
|
||||
Color background = c.getBackground();
|
||||
JDesktopPane desktopPane = desktopIcon.getDesktopPane();
|
||||
g.setColor( (desktopPane != null)
|
||||
? FlatUIUtils.deriveColor( background, desktopPane.getBackground() )
|
||||
: background );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
}
|
||||
|
||||
paint( g, c );
|
||||
}
|
||||
|
||||
private void updateDockIconPreviewLater() {
|
||||
// use invoke later to make sure that components are updated when switching LaF
|
||||
EventQueue.invokeLater( () -> {
|
||||
if( dockIcon != null )
|
||||
updateDockIconLater();
|
||||
updateDockIconPreview();
|
||||
} );
|
||||
}
|
||||
|
||||
private void updateDockIconLater() {
|
||||
protected void updateDockIconPreview() {
|
||||
// make sure that frame is not selected
|
||||
if( frame.isSelected() ) {
|
||||
try {
|
||||
@@ -246,13 +290,22 @@ public class FlatDesktopIconUI
|
||||
}
|
||||
}
|
||||
|
||||
// layout internal frame title pane, which was recreated when switching Laf
|
||||
// (directly invoke doLayout() because frame.validate() does not work here
|
||||
// because frame is not displayable)
|
||||
if( !frame.isValid() )
|
||||
frame.doLayout();
|
||||
for( Component c : frame.getComponents() ) {
|
||||
if( !c.isValid() )
|
||||
c.doLayout();
|
||||
}
|
||||
|
||||
// paint internal frame to buffered image
|
||||
int frameWidth = Math.max( frame.getWidth(), 1 );
|
||||
int frameHeight = Math.max( frame.getHeight(), 1 );
|
||||
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
|
||||
Graphics2D g = frameImage.createGraphics();
|
||||
try {
|
||||
//TODO fix missing internal frame header when switching LaF
|
||||
frame.paint( g );
|
||||
} finally {
|
||||
g.dispose();
|
||||
@@ -270,6 +323,27 @@ public class FlatDesktopIconUI
|
||||
|
||||
// scale preview
|
||||
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
|
||||
if( MultiResolutionImageSupport.isAvailable() ) {
|
||||
// On HiDPI screens, create preview images for 1x, 2x and current scale factor.
|
||||
// The icon then chooses the best resolution for painting, which is usually
|
||||
// the one for the current scale factor. But if changing scale factor or
|
||||
// moving window to another screen with different scale factor, then another
|
||||
// resolution may be used because the preview icon is not updated.
|
||||
Image previewImage2x = frameImage.getScaledInstance( previewWidth * 2, previewHeight * 2, Image.SCALE_SMOOTH );
|
||||
double scaleFactor = UIScale.getSystemScaleFactor( desktopIcon.getGraphicsConfiguration() );
|
||||
if( scaleFactor != 1 && scaleFactor != 2 ) {
|
||||
Image previewImageCurrent = frameImage.getScaledInstance(
|
||||
(int) Math.round( previewWidth * scaleFactor ),
|
||||
(int) Math.round( previewHeight * scaleFactor ),
|
||||
Image.SCALE_SMOOTH );
|
||||
|
||||
// the images must be ordered by resolution
|
||||
previewImage = (scaleFactor < 2)
|
||||
? MultiResolutionImageSupport.create( 0, previewImage, previewImageCurrent, previewImage2x )
|
||||
: MultiResolutionImageSupport.create( 0, previewImage, previewImage2x, previewImageCurrent );
|
||||
} else
|
||||
previewImage = MultiResolutionImageSupport.create( 0, previewImage, previewImage2x );
|
||||
}
|
||||
dockIcon.setIcon( new ImageIcon( previewImage ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import javax.swing.DefaultDesktopManager;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ContainerEvent;
|
||||
import java.awt.event.ContainerListener;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.JInternalFrame.JDesktopIcon;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||
|
||||
/**
|
||||
@@ -36,30 +41,96 @@ import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||
public class FlatDesktopPaneUI
|
||||
extends BasicDesktopPaneUI
|
||||
{
|
||||
private LayoutDockListener layoutDockListener;
|
||||
private boolean layoutDockPending;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatDesktopPaneUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDesktopManager() {
|
||||
desktopManager = desktop.getDesktopManager();
|
||||
if( desktopManager == null ) {
|
||||
desktopManager = new FlatDesktopManager();
|
||||
desktop.setDesktopManager( desktopManager );
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners() {
|
||||
super.installListeners();
|
||||
|
||||
layoutDockListener = new LayoutDockListener();
|
||||
desktop.addContainerListener( layoutDockListener );
|
||||
desktop.addComponentListener( layoutDockListener );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners() {
|
||||
super.uninstallListeners();
|
||||
|
||||
desktop.removeContainerListener( layoutDockListener );
|
||||
desktop.removeComponentListener( layoutDockListener );
|
||||
layoutDockListener = null;
|
||||
}
|
||||
|
||||
private void layoutDockLaterOnce() {
|
||||
if( layoutDockPending )
|
||||
return;
|
||||
layoutDockPending = true;
|
||||
|
||||
EventQueue.invokeLater( () -> {
|
||||
layoutDockPending = false;
|
||||
if( desktop != null )
|
||||
layoutDock();
|
||||
} );
|
||||
}
|
||||
|
||||
protected void layoutDock() {
|
||||
Dimension desktopSize = desktop.getSize();
|
||||
int x = 0;
|
||||
int y = desktopSize.height;
|
||||
int rowHeight = 0;
|
||||
|
||||
for( Component c : desktop.getComponents() ) {
|
||||
if( !(c instanceof JDesktopIcon) )
|
||||
continue;
|
||||
|
||||
JDesktopIcon icon = (JDesktopIcon) c;
|
||||
Dimension iconSize = icon.getPreferredSize();
|
||||
|
||||
if( x + iconSize.width > desktopSize.width ) {
|
||||
// new row
|
||||
x = 0;
|
||||
y -= rowHeight;
|
||||
rowHeight = 0;
|
||||
}
|
||||
|
||||
icon.setLocation( x, y - iconSize.height );
|
||||
|
||||
x += iconSize.width;
|
||||
rowHeight = Math.max( iconSize.height, rowHeight );
|
||||
}
|
||||
}
|
||||
|
||||
//---- class FlatDesktopManager -------------------------------------------
|
||||
//---- class LayoutDockListener -------------------------------------------
|
||||
|
||||
private class FlatDesktopManager
|
||||
extends DefaultDesktopManager
|
||||
implements UIResource
|
||||
private class LayoutDockListener
|
||||
extends ComponentAdapter
|
||||
implements ContainerListener
|
||||
{
|
||||
@Override
|
||||
public void iconifyFrame( JInternalFrame f ) {
|
||||
super.iconifyFrame( f );
|
||||
public void componentAdded( ContainerEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
((FlatDesktopIconUI)f.getDesktopIcon().getUI()).updateDockIcon();
|
||||
@Override
|
||||
public void componentRemoved( ContainerEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
layoutDockLaterOnce();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ----------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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 );
|
||||
|
||||
// avoid double window title bar if enabling native window border failed
|
||||
if( !hasCustomDecoration( frame ) )
|
||||
return;
|
||||
|
||||
// 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 );
|
||||
|
||||
// avoid double window title bar if enabling native window border failed
|
||||
if( !hasCustomDecoration( dialog ) )
|
||||
return;
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import java.awt.Insets;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Panel;
|
||||
import java.awt.Point;
|
||||
import java.awt.PointerInfo;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ComponentEvent;
|
||||
@@ -70,7 +71,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
|
||||
@@ -216,7 +217,11 @@ public class FlatPopupFactory
|
||||
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
|
||||
return null;
|
||||
|
||||
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
|
||||
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||
if( pointerInfo == null )
|
||||
return null;
|
||||
|
||||
Point mouseLocation = pointerInfo.getLocation();
|
||||
Dimension tipSize = contents.getPreferredSize();
|
||||
|
||||
// check whether mouse location is within tooltip bounds
|
||||
|
||||
@@ -18,11 +18,14 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Objects;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.CellRendererPane;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.LookAndFeel;
|
||||
import javax.swing.UIManager;
|
||||
@@ -120,10 +123,11 @@ public class FlatRadioButtonUI
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
// fill background even if not opaque if
|
||||
// - contentAreaFilled is true and
|
||||
// - if background was explicitly set to a non-UIResource color
|
||||
// - if background color is different to default background color
|
||||
// (this paints selection if using the component as cell renderer)
|
||||
if( !c.isOpaque() &&
|
||||
((AbstractButton)c).isContentAreaFilled() &&
|
||||
!defaultBackground.equals( c.getBackground() ) )
|
||||
!Objects.equals( c.getBackground(), getDefaultBackground( c ) ) )
|
||||
{
|
||||
g.setColor( c.getBackground() );
|
||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||
@@ -160,6 +164,18 @@ public class FlatRadioButtonUI
|
||||
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default background color of the component.
|
||||
* If the component is used as cell renderer (e.g. in JTable),
|
||||
* then the background color of the renderer container is returned.
|
||||
*/
|
||||
private Color getDefaultBackground( JComponent c ) {
|
||||
Container parent = c.getParent();
|
||||
return (parent instanceof CellRendererPane && parent.getParent() != null)
|
||||
? parent.getParent().getBackground()
|
||||
: defaultBackground;
|
||||
}
|
||||
|
||||
private int getIconFocusWidth( JComponent c ) {
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
return (b.getIcon() == null && getDefaultIcon() instanceof FlatCheckBoxIcon)
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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() ) {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -58,6 +58,8 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.IntConsumer;
|
||||
import javax.accessibility.Accessible;
|
||||
import javax.accessibility.AccessibleContext;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.ButtonModel;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
@@ -325,7 +327,7 @@ public class FlatTabbedPaneUI
|
||||
// the default also includes Ctrl+TAB/Ctrl+Shift+TAB, which we need to switch tabs
|
||||
if( focusForwardTraversalKeys == null ) {
|
||||
focusForwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ) );
|
||||
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_MASK ) );
|
||||
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK ) );
|
||||
}
|
||||
// Ideally we should use `LookAndFeel.installProperty( tabPane, "focusTraversalKeysForward", keys )` here
|
||||
// instead of `tabPane.setFocusTraversalKeys()`, but WindowsTabbedPaneUI also uses later method
|
||||
@@ -490,6 +492,20 @@ public class FlatTabbedPaneUI
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installKeyboardActions() {
|
||||
super.installKeyboardActions();
|
||||
|
||||
// get shared action map, used for all tabbed panes
|
||||
ActionMap map = SwingUtilities.getUIActionMap( tabPane );
|
||||
if( map != null ) {
|
||||
// this is required for the case that those actions are used from outside
|
||||
// (e.g. wheel tab scroller in NetBeans)
|
||||
RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsForwardAction" );
|
||||
RunWithOriginalLayoutManagerDelegateAction.install( map, "scrollTabsBackwardAction" );
|
||||
}
|
||||
}
|
||||
|
||||
private Handler getHandler() {
|
||||
if( handler == null )
|
||||
handler = new Handler();
|
||||
@@ -722,6 +738,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();
|
||||
|
||||
@@ -1386,13 +1409,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 +1587,7 @@ public class FlatTabbedPaneUI
|
||||
FlatUIUtils.paintComponentBackground( g, left, top,
|
||||
getWidth() - left - right,
|
||||
getHeight() - top - bottom,
|
||||
0, scale( buttonArc ) );
|
||||
0, scale( (float) buttonArc ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2947,4 +2975,51 @@ public class FlatTabbedPaneUI
|
||||
scrollBackwardButtonPrefSize = backwardButton.getPreferredSize();
|
||||
}
|
||||
}
|
||||
|
||||
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
|
||||
|
||||
private static class RunWithOriginalLayoutManagerDelegateAction
|
||||
implements Action
|
||||
{
|
||||
private final Action delegate;
|
||||
|
||||
static void install( ActionMap map, String key ) {
|
||||
Action oldAction = map.get( key );
|
||||
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
|
||||
return; // not found or already installed
|
||||
|
||||
map.put( key, new RunWithOriginalLayoutManagerDelegateAction( oldAction ) );
|
||||
}
|
||||
|
||||
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue( String key ) {
|
||||
return delegate.getValue( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return delegate.isEnabled();
|
||||
}
|
||||
|
||||
@Override public void putValue( String key, Object value ) {}
|
||||
@Override public void setEnabled( boolean b ) {}
|
||||
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
|
||||
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
|
||||
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e ) {
|
||||
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
|
||||
ComponentUI ui = tabbedPane.getUI();
|
||||
if( ui instanceof FlatTabbedPaneUI ) {
|
||||
((FlatTabbedPaneUI)ui).runWithOriginalLayoutManager( () -> {
|
||||
delegate.actionPerformed( e );
|
||||
} );
|
||||
} else
|
||||
delegate.actionPerformed( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableColumn;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
/**
|
||||
* Cell border for {@code sun.swing.table.DefaultTableCellHeaderRenderer}
|
||||
* (used by {@link javax.swing.table.JTableHeader}).
|
||||
* <p>
|
||||
* Uses separate cell margins from UI defaults to allow easy customizing.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since 1.2
|
||||
*/
|
||||
public class FlatTableHeaderBorder
|
||||
extends FlatEmptyBorder
|
||||
{
|
||||
protected Color separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
||||
protected Color bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||
|
||||
public FlatTableHeaderBorder() {
|
||||
super( UIManager.getInsets( "TableHeader.cellMargins" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||
JTableHeader header = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||
boolean leftToRight = (header != null ? header : c).getComponentOrientation().isLeftToRight();
|
||||
boolean paintLeft = !leftToRight;
|
||||
boolean paintRight = leftToRight;
|
||||
|
||||
if( header != null ) {
|
||||
int hx = SwingUtilities.convertPoint( c, x, y, header ).x;
|
||||
if( isDraggedColumn( header, hx ) )
|
||||
paintLeft = paintRight = true;
|
||||
else {
|
||||
if( hx <= 0 && !leftToRight && hideTrailingVerticalLine( header ) )
|
||||
paintLeft = false;
|
||||
if( hx + width >= header.getWidth() && leftToRight && hideTrailingVerticalLine( header ) )
|
||||
paintRight = false;
|
||||
}
|
||||
}
|
||||
|
||||
float lineWidth = UIScale.scale( 1f );
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
// paint column separator lines
|
||||
g2.setColor( separatorColor );
|
||||
if( paintLeft )
|
||||
g2.fill( new Rectangle2D.Float( x, y, lineWidth, height - lineWidth ) );
|
||||
if( paintRight )
|
||||
g2.fill( new Rectangle2D.Float( x + width - lineWidth, y, lineWidth, height - lineWidth ) );
|
||||
|
||||
// paint bottom line
|
||||
g2.setColor( bottomSeparatorColor );
|
||||
g2.fill( new Rectangle2D.Float( x, y + height - lineWidth, width, lineWidth ) );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isDraggedColumn( JTableHeader header, int x ) {
|
||||
TableColumn draggedColumn = header.getDraggedColumn();
|
||||
if( draggedColumn == null )
|
||||
return false;
|
||||
|
||||
int draggedDistance = header.getDraggedDistance();
|
||||
if( draggedDistance == 0 )
|
||||
return false;
|
||||
|
||||
int columnCount = header.getColumnModel().getColumnCount();
|
||||
for( int i = 0; i < columnCount; i++ ) {
|
||||
if( header.getHeaderRect( i ).x + draggedDistance == x )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean hideTrailingVerticalLine( JTableHeader header ) {
|
||||
Container viewport = header.getParent();
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return true;
|
||||
|
||||
JScrollBar vsb = ((JScrollPane)viewportParent).getVerticalScrollBar();
|
||||
if( vsb == null || !vsb.isVisible() )
|
||||
return true;
|
||||
|
||||
// if "ScrollPane.fillUpperCorner" is true, then javax.swing.ScrollPaneLayout
|
||||
// extends the vertical scrollbar into the upper right/left corner
|
||||
return vsb.getY() == viewport.getY();
|
||||
}
|
||||
}
|
||||
@@ -18,20 +18,16 @@ package com.formdev.flatlaf.ui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Objects;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
@@ -39,7 +35,6 @@ import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
@@ -54,17 +49,21 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*
|
||||
* <!-- FlatTableHeaderUI -->
|
||||
*
|
||||
* @uiDefault TableHeader.separatorColor Color
|
||||
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||
* @uiDefault TableHeader.height int
|
||||
* @uiDefault TableHeader.sortIconPosition String right (default), left, top or bottom
|
||||
*
|
||||
* <!-- FlatTableHeaderBorder -->
|
||||
*
|
||||
* @uiDefault TableHeader.cellMargins Insets
|
||||
* @uiDefault TableHeader.separatorColor Color
|
||||
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class FlatTableHeaderUI
|
||||
extends BasicTableHeaderUI
|
||||
{
|
||||
protected Color separatorColor;
|
||||
protected Color bottomSeparatorColor;
|
||||
protected int height;
|
||||
protected int sortIconPosition;
|
||||
@@ -77,7 +76,6 @@ public class FlatTableHeaderUI
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
|
||||
separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
||||
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||
height = UIManager.getInt( "TableHeader.height" );
|
||||
switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
|
||||
@@ -93,27 +91,38 @@ public class FlatTableHeaderUI
|
||||
protected void uninstallDefaults() {
|
||||
super.uninstallDefaults();
|
||||
|
||||
separatorColor = null;
|
||||
bottomSeparatorColor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint( Graphics g, JComponent c ) {
|
||||
if( header.getColumnModel().getColumnCount() <= 0 )
|
||||
TableColumnModel columnModel = header.getColumnModel();
|
||||
if( columnModel.getColumnCount() <= 0 )
|
||||
return;
|
||||
|
||||
// do not paint borders if JTableHeader.setDefaultRenderer() was used
|
||||
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||
boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
|
||||
if( !paintBorders ) {
|
||||
// check whether the renderer delegates to the system default renderer
|
||||
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
||||
header.getTable(), "", false, false, -1, 0 );
|
||||
paintBorders = isSystemDefaultRenderer( rendererComponent );
|
||||
}
|
||||
// compute total width of all columns
|
||||
int columnCount = columnModel.getColumnCount();
|
||||
int totalWidth = 0;
|
||||
for( int i = 0; i < columnCount; i++ )
|
||||
totalWidth += columnModel.getColumn( i ).getWidth();
|
||||
|
||||
if( paintBorders )
|
||||
paintColumnBorders( g, c );
|
||||
if( totalWidth < header.getWidth() ) {
|
||||
// do not paint bottom separator if JTableHeader.setDefaultRenderer() was used
|
||||
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||
boolean paintBottomSeparator = isSystemDefaultRenderer( defaultRenderer );
|
||||
if( !paintBottomSeparator && header.getTable() != null ) {
|
||||
// check whether the renderer delegates to the system default renderer
|
||||
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
||||
header.getTable(), "", false, false, -1, 0 );
|
||||
paintBottomSeparator = isSystemDefaultRenderer( rendererComponent );
|
||||
}
|
||||
|
||||
if( paintBottomSeparator ) {
|
||||
int w = c.getWidth() - totalWidth;
|
||||
int x = header.getComponentOrientation().isLeftToRight() ? c.getWidth() - w : 0;
|
||||
paintBottomSeparator( g, c, x, w );
|
||||
}
|
||||
}
|
||||
|
||||
// temporary use own default renderer if necessary
|
||||
FlatTableCellHeaderRenderer sortIconRenderer = null;
|
||||
@@ -130,9 +139,6 @@ public class FlatTableHeaderUI
|
||||
sortIconRenderer.reset();
|
||||
header.setDefaultRenderer( sortIconRenderer.delegate );
|
||||
}
|
||||
|
||||
if( paintBorders )
|
||||
paintDraggedColumnBorders( g, c );
|
||||
}
|
||||
|
||||
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||
@@ -141,17 +147,8 @@ public class FlatTableHeaderUI
|
||||
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
|
||||
}
|
||||
|
||||
protected void paintColumnBorders( Graphics g, JComponent c ) {
|
||||
int width = c.getWidth();
|
||||
int height = c.getHeight();
|
||||
protected void paintBottomSeparator( Graphics g, JComponent c, int x, int w ) {
|
||||
float lineWidth = UIScale.scale( 1f );
|
||||
float topLineIndent = lineWidth;
|
||||
float bottomLineIndent = lineWidth * 3;
|
||||
TableColumnModel columnModel = header.getColumnModel();
|
||||
int columnCount = columnModel.getColumnCount();
|
||||
int sepCount = columnCount;
|
||||
if( hideLastVerticalLine() )
|
||||
sepCount--;
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
@@ -159,78 +156,7 @@ public class FlatTableHeaderUI
|
||||
|
||||
// paint bottom line
|
||||
g2.setColor( bottomSeparatorColor );
|
||||
g2.fill( new Rectangle2D.Float( 0, height - lineWidth, width, lineWidth ) );
|
||||
|
||||
// paint column separator lines
|
||||
g2.setColor( separatorColor );
|
||||
|
||||
float y = topLineIndent;
|
||||
float h = height - bottomLineIndent;
|
||||
|
||||
if( header.getComponentOrientation().isLeftToRight() ) {
|
||||
int x = 0;
|
||||
for( int i = 0; i < sepCount; i++ ) {
|
||||
x += columnModel.getColumn( i ).getWidth();
|
||||
g2.fill( new Rectangle2D.Float( x - lineWidth, y, lineWidth, h ) );
|
||||
}
|
||||
|
||||
// paint trailing separator (on right side)
|
||||
if( !hideTrailingVerticalLine() )
|
||||
g2.fill( new Rectangle2D.Float( header.getWidth() - lineWidth, y, lineWidth, h ) );
|
||||
} else {
|
||||
Rectangle cellRect = header.getHeaderRect( 0 );
|
||||
int x = cellRect.x + cellRect.width;
|
||||
for( int i = 0; i < sepCount; i++ ) {
|
||||
x -= columnModel.getColumn( i ).getWidth();
|
||||
g2.fill( new Rectangle2D.Float( x - (i < sepCount - 1 ? lineWidth : 0), y, lineWidth, h ) );
|
||||
}
|
||||
|
||||
// paint trailing separator (on left side)
|
||||
if( !hideTrailingVerticalLine() )
|
||||
g2.fill( new Rectangle2D.Float( 0, y, lineWidth, h ) );
|
||||
}
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void paintDraggedColumnBorders( Graphics g, JComponent c ) {
|
||||
TableColumn draggedColumn = header.getDraggedColumn();
|
||||
if( draggedColumn == null )
|
||||
return;
|
||||
|
||||
// find index of dragged column
|
||||
TableColumnModel columnModel = header.getColumnModel();
|
||||
int columnCount = columnModel.getColumnCount();
|
||||
int draggedColumnIndex = -1;
|
||||
for( int i = 0; i < columnCount; i++ ) {
|
||||
if( columnModel.getColumn( i ) == draggedColumn ) {
|
||||
draggedColumnIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( draggedColumnIndex < 0 )
|
||||
return;
|
||||
|
||||
float lineWidth = UIScale.scale( 1f );
|
||||
float topLineIndent = lineWidth;
|
||||
float bottomLineIndent = lineWidth * 3;
|
||||
Rectangle r = header.getHeaderRect( draggedColumnIndex );
|
||||
r.x += header.getDraggedDistance();
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
|
||||
// paint dragged bottom line
|
||||
g2.setColor( bottomSeparatorColor );
|
||||
g2.fill( new Rectangle2D.Float( r.x, r.y + r.height - lineWidth, r.width, lineWidth ) );
|
||||
|
||||
// paint dragged column separator lines
|
||||
g2.setColor( separatorColor );
|
||||
g2.fill( new Rectangle2D.Float( r.x, topLineIndent, lineWidth, r.height - bottomLineIndent ) );
|
||||
g2.fill( new Rectangle2D.Float( r.x + r.width - lineWidth, r.y + topLineIndent, lineWidth, r.height - bottomLineIndent ) );
|
||||
g2.fill( new Rectangle2D.Float( x, c.getHeight() - lineWidth, w, lineWidth ) );
|
||||
} finally {
|
||||
g2.dispose();
|
||||
}
|
||||
@@ -244,32 +170,6 @@ public class FlatTableHeaderUI
|
||||
return size;
|
||||
}
|
||||
|
||||
protected boolean hideLastVerticalLine() {
|
||||
Container viewport = header.getParent();
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return false;
|
||||
|
||||
Rectangle cellRect = header.getHeaderRect( header.getColumnModel().getColumnCount() - 1 );
|
||||
|
||||
// using component orientation of scroll pane here because it is also used in FlatTableUI
|
||||
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||
return scrollPane.getComponentOrientation().isLeftToRight()
|
||||
? cellRect.x + cellRect.width >= viewport.getWidth()
|
||||
: cellRect.x <= 0;
|
||||
}
|
||||
|
||||
protected boolean hideTrailingVerticalLine() {
|
||||
Container viewport = header.getParent();
|
||||
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
|
||||
if( !(viewportParent instanceof JScrollPane) )
|
||||
return false;
|
||||
|
||||
JScrollPane scrollPane = (JScrollPane) viewportParent;
|
||||
return viewport == scrollPane.getColumnHeader() &&
|
||||
scrollPane.getCorner( ScrollPaneConstants.UPPER_TRAILING_CORNER ) == null;
|
||||
}
|
||||
|
||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 );
|
||||
@@ -320,10 +351,10 @@ public class FlatTitlePane
|
||||
|
||||
// set icon
|
||||
if( !images.isEmpty() )
|
||||
iconLabel.setIcon( FlatTitlePaneIcon.create( images, iconSize ) );
|
||||
iconLabel.setIcon( new FlatTitlePaneIcon( 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
|
||||
@@ -448,16 +521,27 @@ public class FlatTitlePane
|
||||
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
|
||||
}
|
||||
if( debugHitTestSpots != null ) {
|
||||
g.setColor( Color.blue );
|
||||
g.setColor( Color.red );
|
||||
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.blue);
|
||||
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,133 @@ 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; 1px wider for easier hitting)
|
||||
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
||||
Insets iconInsets = iconLabel.getInsets();
|
||||
Rectangle iconBounds = new Rectangle(
|
||||
location.x + iconInsets.left - 1,
|
||||
location.y + iconInsets.top - 1,
|
||||
iconLabel.getWidth() - iconInsets.left - iconInsets.right + 2,
|
||||
iconLabel.getHeight() - iconInsets.top - iconInsets.bottom + 2 );
|
||||
|
||||
// if frame is maximized, increase icon bounds to upper-left corner
|
||||
// of window to allow closing window via double-click in upper-left corner
|
||||
if( window instanceof Frame &&
|
||||
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
||||
{
|
||||
iconBounds.height += iconBounds.y;
|
||||
iconBounds.y = 0;
|
||||
|
||||
if( window.getComponentOrientation().isLeftToRight() ) {
|
||||
iconBounds.width += iconBounds.x;
|
||||
iconBounds.x = 0;
|
||||
} else
|
||||
iconBounds.width += iconInsets.right;
|
||||
}
|
||||
|
||||
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 +834,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 +853,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 +932,7 @@ debug*/
|
||||
break;
|
||||
|
||||
case "componentOrientation":
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -740,10 +942,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 +953,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 +964,7 @@ debug*/
|
||||
@Override
|
||||
public void windowStateChanged( WindowEvent e ) {
|
||||
frameStateChanged();
|
||||
updateJBRHitTestSpotsAndTitleBarHeight();
|
||||
updateNativeTitleBarHeightAndHitTestSpots();
|
||||
}
|
||||
|
||||
//---- interface MouseListener ----
|
||||
@@ -775,7 +977,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 +1010,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 +1054,7 @@ debug*/
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
updateJBRHitTestSpotsAndTitleBarHeightLater();
|
||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,8 +20,6 @@ import java.awt.Dimension;
|
||||
import java.awt.Image;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||
|
||||
@@ -31,40 +29,43 @@ import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||
public class FlatTitlePaneIcon
|
||||
extends ScaledImageIcon
|
||||
{
|
||||
public static Icon create( List<Image> images, Dimension size ) {
|
||||
// collect all images including multi-resolution variants
|
||||
private final List<Image> images;
|
||||
|
||||
/**
|
||||
* @since 1.2
|
||||
*/
|
||||
public FlatTitlePaneIcon( List<Image> images, Dimension size ) {
|
||||
super( null, size.width, size.height );
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Image getResolutionVariant( int destImageWidth, int destImageHeight ) {
|
||||
// collect all images including multi-resolution variants for requested size
|
||||
List<Image> allImages = new ArrayList<>();
|
||||
for( Image image : images ) {
|
||||
if( MultiResolutionImageSupport.isMultiResolutionImage( image ) )
|
||||
allImages.addAll( MultiResolutionImageSupport.getResolutionVariants( image ) );
|
||||
allImages.add( MultiResolutionImageSupport.getResolutionVariant( image, destImageWidth, destImageHeight ) );
|
||||
else
|
||||
allImages.add( image );
|
||||
}
|
||||
|
||||
if( allImages.size() == 1 )
|
||||
return allImages.get( 0 );
|
||||
|
||||
// sort images by size
|
||||
allImages.sort( (image1, image2) -> {
|
||||
return image1.getWidth( null ) - image2.getWidth( null );
|
||||
} );
|
||||
|
||||
// create icon
|
||||
return new FlatTitlePaneIcon( allImages, size );
|
||||
}
|
||||
|
||||
private final List<Image> images;
|
||||
|
||||
private FlatTitlePaneIcon( List<Image> images, Dimension size ) {
|
||||
super( new ImageIcon( images.get( 0 ) ), size.width, size.height );
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Image getResolutionVariant( int destImageWidth, int destImageHeight ) {
|
||||
for( Image image : images ) {
|
||||
// search for optimal image size
|
||||
for( Image image : allImages ) {
|
||||
if( destImageWidth <= image.getWidth( null ) &&
|
||||
destImageHeight <= image.getHeight( null ) )
|
||||
return image;
|
||||
}
|
||||
|
||||
return images.get( images.size() - 1 );
|
||||
// use largest image
|
||||
return allImages.get( allImages.size() - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -108,6 +107,8 @@ public class FlatTreeUI
|
||||
protected boolean wideSelection;
|
||||
protected boolean showCellFocusIndicator;
|
||||
|
||||
private Color defaultCellNonSelectionBackground;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatTreeUI();
|
||||
}
|
||||
@@ -126,6 +127,8 @@ public class FlatTreeUI
|
||||
wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
|
||||
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
|
||||
|
||||
defaultCellNonSelectionBackground = UIManager.getColor( "Tree.textBackground" );
|
||||
|
||||
// scale
|
||||
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
|
||||
if( rowHeight > 0 )
|
||||
@@ -145,6 +148,8 @@ public class FlatTreeUI
|
||||
selectionInactiveBackground = null;
|
||||
selectionInactiveForeground = null;
|
||||
selectionBorderColor = null;
|
||||
|
||||
defaultCellNonSelectionBackground = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,40 +198,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.
|
||||
@@ -309,24 +312,24 @@ public class FlatTreeUI
|
||||
}
|
||||
} else {
|
||||
// non-wide selection
|
||||
int xOffset = 0;
|
||||
int imageOffset = 0;
|
||||
|
||||
if( rendererComponent instanceof JLabel ) {
|
||||
JLabel label = (JLabel) rendererComponent;
|
||||
Icon icon = label.getIcon();
|
||||
imageOffset = (icon != null && label.getText() != null)
|
||||
? icon.getIconWidth() + Math.max( label.getIconTextGap() - 1, 0 )
|
||||
: 0;
|
||||
xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0;
|
||||
}
|
||||
|
||||
g.fillRect( bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height );
|
||||
paintCellBackground( g, rendererComponent, bounds );
|
||||
}
|
||||
|
||||
// this is actually not necessary because renderer should always set color
|
||||
// before painting, but doing anyway to avoid any side effect (in bad renderers)
|
||||
g.setColor( oldColor );
|
||||
} else {
|
||||
// paint cell background if DefaultTreeCellRenderer.getBackgroundNonSelectionColor() is set
|
||||
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
||||
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
|
||||
Color bg = renderer.getBackgroundNonSelectionColor();
|
||||
if( bg != null && !bg.equals( defaultCellNonSelectionBackground ) ) {
|
||||
Color oldColor = g.getColor();
|
||||
g.setColor( bg );
|
||||
paintCellBackground( g, rendererComponent, bounds );
|
||||
g.setColor( oldColor );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// paint renderer
|
||||
@@ -340,6 +343,22 @@ public class FlatTreeUI
|
||||
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
||||
}
|
||||
|
||||
private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds ) {
|
||||
int xOffset = 0;
|
||||
int imageOffset = 0;
|
||||
|
||||
if( rendererComponent instanceof JLabel ) {
|
||||
JLabel label = (JLabel) rendererComponent;
|
||||
Icon icon = label.getIcon();
|
||||
imageOffset = (icon != null && label.getText() != null)
|
||||
? icon.getIconWidth() + Math.max( label.getIconTextGap() - 1, 0 )
|
||||
: 0;
|
||||
xOffset = label.getComponentOrientation().isLeftToRight() ? imageOffset : 0;
|
||||
}
|
||||
|
||||
g.fillRect( bounds.x + xOffset, bounds.y, bounds.width - imageOffset, bounds.height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether dropping on a row.
|
||||
* See DefaultTreeCellRenderer.getTreeCellRendererComponent().
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 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 );
|
||||
if( hwnd == 0 )
|
||||
return;
|
||||
|
||||
// remove the OS window title bar
|
||||
updateFrame( hwnd, (window instanceof JFrame) ? ((JFrame)window).getExtendedState() : 0 );
|
||||
}
|
||||
|
||||
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, int state );
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
@@ -256,11 +256,6 @@ public class Graphics2DProxy
|
||||
delegate.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() {
|
||||
delegate.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.FileAlreadyExistsException;
|
||||
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();
|
||||
|
||||
// Note:
|
||||
// Not using Files.createTempFile() here because it uses random number generator SecureRandom,
|
||||
// which may take 5-10 seconds to initialize under particular conditions.
|
||||
|
||||
// Use current time in nanoseconds instead of a random number.
|
||||
// To avoid (theoretical) collisions, append a counter.
|
||||
long nanoTime = System.nanoTime();
|
||||
for( int i = 0;; i++ ) {
|
||||
String s = prefix + Long.toUnsignedString( nanoTime ) + i + suffix;
|
||||
try {
|
||||
return Files.createFile( tempDir.resolve( s ) );
|
||||
} catch( FileAlreadyExistsException ex ) {
|
||||
// ignore --> increment counter and try again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getTempDir() throws IOException {
|
||||
// get standard temporary directory
|
||||
String tmpdir = System.getProperty( "java.io.tmpdir" );
|
||||
|
||||
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.
|
||||
tmpdir += "\\flatlaf.temp";
|
||||
}
|
||||
|
||||
// create temporary directory
|
||||
Path tempDir = Paths.get( tmpdir );
|
||||
Files.createDirectories( tempDir );
|
||||
|
||||
// delete no longer needed temporary files (from already exited applications)
|
||||
if( SystemInfo.isWindows )
|
||||
deleteTemporaryFiles( tempDir );
|
||||
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ debug*/
|
||||
double scaleFactor = systemScaleFactor * userScaleFactor;
|
||||
|
||||
// paint input image icon if not necessary to scale
|
||||
if( scaleFactor == 1 && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
|
||||
if( scaleFactor == 1 && imageIcon != null && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
|
||||
imageIcon.paintIcon( c, g, x, y );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -43,7 +43,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
|
||||
* <p>
|
||||
* Two scaling modes are supported by FlatLaf for HiDPI displays:
|
||||
*
|
||||
* <h3>1) system scaling mode</h3>
|
||||
* <h2>1) system scaling mode</h2>
|
||||
*
|
||||
* This mode is supported since Java 9 on all platforms and in some Java 8 VMs
|
||||
* (e.g. Apple and JetBrains). The JRE determines the scale factor per-display and
|
||||
@@ -54,7 +54,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
|
||||
* The scale factor may be different for each connected display.
|
||||
* The scale factor may change for a window when moving the window from one display to another one.
|
||||
*
|
||||
* <h3>2) user scaling mode</h3>
|
||||
* <h2>2) user scaling mode</h2>
|
||||
*
|
||||
* This mode is mainly for Java 8 compatibility, but is also used on Linux
|
||||
* or if the default font is changed.
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
module com.formdev.flatlaf {
|
||||
requires java.desktop;
|
||||
requires java.logging;
|
||||
|
||||
exports com.formdev.flatlaf;
|
||||
exports com.formdev.flatlaf.icons;
|
||||
|
||||
@@ -166,7 +166,7 @@ Desktop.background = #3E434C
|
||||
|
||||
#---- DesktopIcon ----
|
||||
|
||||
DesktopIcon.background = lighten($Desktop.background,10%)
|
||||
DesktopIcon.background = lighten($Desktop.background,10%,derived)
|
||||
|
||||
|
||||
#---- InternalFrame ----
|
||||
|
||||
@@ -642,7 +642,8 @@ Table.dropLineShortColor = @dropLineShortColor
|
||||
#---- TableHeader ----
|
||||
|
||||
TableHeader.height = 25
|
||||
TableHeader.cellBorder = 2,3,2,3
|
||||
TableHeader.cellBorder = com.formdev.flatlaf.ui.FlatTableHeaderBorder
|
||||
TableHeader.cellMargins = 2,3,2,3
|
||||
TableHeader.focusCellBackground = $TableHeader.background
|
||||
TableHeader.background = @textComponentBackground
|
||||
|
||||
@@ -685,13 +686,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
|
||||
|
||||
@@ -172,7 +172,7 @@ Desktop.background = #E6EBF0
|
||||
|
||||
#---- DesktopIcon ----
|
||||
|
||||
DesktopIcon.background = darken($Desktop.background,10%)
|
||||
DesktopIcon.background = darken($Desktop.background,10%,derived)
|
||||
|
||||
|
||||
#---- HelpButton ----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -99,8 +133,8 @@ ToggleButton.endBackground = $ToggleButton.background
|
||||
|
||||
[Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived)
|
||||
|
||||
[Dracula]ProgressBar.selectionBackground = #fff
|
||||
[Dracula]ProgressBar.selectionForeground = #fff
|
||||
[Dracula---Zihan_Ma]ProgressBar.selectionBackground = #fff
|
||||
[Dracula---Zihan_Ma]ProgressBar.selectionForeground = #fff
|
||||
|
||||
[Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
|
||||
[Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
|
||||
@@ -147,7 +181,7 @@ ToggleButton.endBackground = $ToggleButton.background
|
||||
[One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
|
||||
[One_Dark]Slider.focusedColor = fade(#568af2,40%)
|
||||
|
||||
[Solarized_Dark]Slider.focusedColor = fade($Component.focusColor,80%,derived)
|
||||
[Solarized_Dark---4lex4]Slider.focusedColor = fade($Component.focusColor,80%,derived)
|
||||
|
||||
[vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
|
||||
[vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
|
||||
@@ -164,6 +198,9 @@ ToggleButton.endBackground = $ToggleButton.background
|
||||
[dark][author-Mallowigi]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
|
||||
[dark][author-Mallowigi]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
|
||||
|
||||
[Dracula---Mallowigi]ProgressBar.selectionBackground = #fff
|
||||
[Dracula---Mallowigi]ProgressBar.selectionForeground = #fff
|
||||
|
||||
[Dracula_Contrast]ProgressBar.selectionBackground = #fff
|
||||
[Dracula_Contrast]ProgressBar.selectionForeground = #fff
|
||||
|
||||
@@ -203,14 +240,14 @@ ToggleButton.endBackground = $ToggleButton.background
|
||||
[Night_Owl_Contrast]ProgressBar.selectionBackground = #ddd
|
||||
[Night_Owl_Contrast]ProgressBar.selectionForeground = #ddd
|
||||
|
||||
[Solarized_Dark]ProgressBar.selectionBackground = #ccc
|
||||
[Solarized_Dark]ProgressBar.selectionForeground = #ccc
|
||||
[Solarized_Dark---Mallowigi]ProgressBar.selectionBackground = #ccc
|
||||
[Solarized_Dark---Mallowigi]ProgressBar.selectionForeground = #ccc
|
||||
|
||||
[Material_Solarized_Dark_Contrast]ProgressBar.selectionBackground = #ccc
|
||||
[Material_Solarized_Dark_Contrast]ProgressBar.selectionForeground = #ccc
|
||||
[Solarized_Dark_Contrast]ProgressBar.selectionBackground = #ccc
|
||||
[Solarized_Dark_Contrast]ProgressBar.selectionForeground = #ccc
|
||||
|
||||
[Solarized_Light]ProgressBar.selectionBackground = #222
|
||||
[Solarized_Light]ProgressBar.selectionForeground = #fff
|
||||
[Solarized_Light---Mallowigi]ProgressBar.selectionBackground = #222
|
||||
[Solarized_Light---Mallowigi]ProgressBar.selectionForeground = #fff
|
||||
|
||||
[Material_Solarized_Light_Contrast]ProgressBar.selectionBackground = #222
|
||||
[Material_Solarized_Light_Contrast]ProgressBar.selectionForeground = #fff
|
||||
[Solarized_Light_Contrast]ProgressBar.selectionBackground = #222
|
||||
[Solarized_Light_Contrast]ProgressBar.selectionForeground = #fff
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -18,19 +18,13 @@ plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
// for using MigLayout snapshot
|
||||
url = uri( "https://oss.sonatype.org/content/repositories/snapshots/" )
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation( project( ":flatlaf-core" ) )
|
||||
implementation( project( ":flatlaf-extras" ) )
|
||||
implementation( project( ":flatlaf-intellij-themes" ) )
|
||||
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
|
||||
implementation( "com.miglayout:miglayout-swing:5.3" )
|
||||
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
|
||||
// implementation( project( ":flatlaf-natives-jna" ) )
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -38,6 +32,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" )
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -50,7 +50,7 @@ public class DemoPrefs
|
||||
state = Preferences.userRoot().node( rootPath );
|
||||
}
|
||||
|
||||
public static void initLaf( String[] args ) {
|
||||
public static void setupLaf( String[] args ) {
|
||||
// set look and feel
|
||||
try {
|
||||
if( args.length > 0 )
|
||||
@@ -60,11 +60,11 @@ public class DemoPrefs
|
||||
if( IntelliJTheme.ThemeLaf.class.getName().equals( lafClassName ) ) {
|
||||
String theme = state.get( KEY_LAF_THEME, "" );
|
||||
if( theme.startsWith( RESOURCE_PREFIX ) )
|
||||
IntelliJTheme.install( IJThemesPanel.class.getResourceAsStream( IJThemesPanel.THEMES_PACKAGE + theme.substring( RESOURCE_PREFIX.length() ) ) );
|
||||
IntelliJTheme.setup( IJThemesPanel.class.getResourceAsStream( IJThemesPanel.THEMES_PACKAGE + theme.substring( RESOURCE_PREFIX.length() ) ) );
|
||||
else if( theme.startsWith( FILE_PREFIX ) )
|
||||
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( theme.substring( FILE_PREFIX.length() ) ) ) );
|
||||
FlatLaf.setup( IntelliJTheme.createLaf( new FileInputStream( theme.substring( FILE_PREFIX.length() ) ) ) );
|
||||
else
|
||||
FlatLightLaf.install();
|
||||
FlatLightLaf.setup();
|
||||
|
||||
if( !theme.isEmpty() )
|
||||
UIManager.getLookAndFeelDefaults().put( THEME_UI_KEY, theme );
|
||||
@@ -73,9 +73,9 @@ public class DemoPrefs
|
||||
if( theme.startsWith( FILE_PREFIX ) ) {
|
||||
File themeFile = new File( theme.substring( FILE_PREFIX.length() ) );
|
||||
String themeName = StringUtils.removeTrailing( themeFile.getName(), ".properties" );
|
||||
FlatLaf.install( new FlatPropertiesLaf( themeName, themeFile ) );
|
||||
FlatLaf.setup( new FlatPropertiesLaf( themeName, themeFile ) );
|
||||
} else
|
||||
FlatLightLaf.install();
|
||||
FlatLightLaf.setup();
|
||||
|
||||
if( !theme.isEmpty() )
|
||||
UIManager.getLookAndFeelDefaults().put( THEME_UI_KEY, theme );
|
||||
@@ -86,7 +86,7 @@ public class DemoPrefs
|
||||
ex.printStackTrace();
|
||||
|
||||
// fallback
|
||||
FlatLightLaf.install();
|
||||
FlatLightLaf.setup();
|
||||
}
|
||||
|
||||
// remember active look and feel
|
||||
|
||||
@@ -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,15 +44,11 @@ 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" );
|
||||
|
||||
// set look and feel
|
||||
DemoPrefs.initLaf( args );
|
||||
DemoPrefs.setupLaf( args );
|
||||
|
||||
// install inspectors
|
||||
FlatInspector.install( "ctrl shift alt X" );
|
||||
|
||||
@@ -18,8 +18,13 @@ package com.formdev.flatlaf.demo.extras;
|
||||
|
||||
import javax.swing.*;
|
||||
import com.formdev.flatlaf.extras.*;
|
||||
import com.formdev.flatlaf.extras.FlatSVGIcon.ColorFilter;
|
||||
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
|
||||
import com.formdev.flatlaf.util.HSLColor;
|
||||
import net.miginfocom.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.HierarchyEvent;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author Karl Tauber
|
||||
@@ -27,6 +32,9 @@ import net.miginfocom.swing.*;
|
||||
public class ExtrasPanel
|
||||
extends JPanel
|
||||
{
|
||||
private Timer rainbowIconTimer;
|
||||
private int rainbowCounter = 0;
|
||||
|
||||
public ExtrasPanel() {
|
||||
initComponents();
|
||||
|
||||
@@ -50,6 +58,34 @@ public class ExtrasPanel
|
||||
addSVGIcon( "errorDialog.svg" );
|
||||
addSVGIcon( "informationDialog.svg" );
|
||||
addSVGIcon( "warningDialog.svg" );
|
||||
|
||||
initRainbowIcon();
|
||||
}
|
||||
|
||||
private void initRainbowIcon() {
|
||||
FlatSVGIcon icon = new FlatSVGIcon( "com/formdev/flatlaf/demo/extras/svg/informationDialog.svg" );
|
||||
icon.setColorFilter( new ColorFilter( color -> {
|
||||
rainbowCounter += 1;
|
||||
rainbowCounter %= 255;
|
||||
return Color.getHSBColor( rainbowCounter / 255f, 1, 1 );
|
||||
} ) );
|
||||
rainbowIcon.setIcon( icon );
|
||||
|
||||
rainbowIconTimer = new Timer( 30, e -> {
|
||||
rainbowIcon.repaint();
|
||||
} );
|
||||
|
||||
// start rainbow timer only if panel is shown ("Extras" tab is active)
|
||||
addHierarchyListener( e -> {
|
||||
if( e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
|
||||
(e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 )
|
||||
{
|
||||
if( isShowing() )
|
||||
rainbowIconTimer.start();
|
||||
else
|
||||
rainbowIconTimer.stop();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private void addSVGIcon( String name ) {
|
||||
@@ -60,6 +96,36 @@ public class ExtrasPanel
|
||||
triStateLabel1.setText( triStateCheckBox1.getState().toString() );
|
||||
}
|
||||
|
||||
private void redChanged() {
|
||||
brighterToggleButton.setSelected( false );
|
||||
|
||||
Function<Color, Color> mapper = null;
|
||||
if( redToggleButton.isSelected() ) {
|
||||
float[] redHSL = HSLColor.fromRGB( Color.red );
|
||||
mapper = color -> {
|
||||
float[] hsl = HSLColor.fromRGB( color );
|
||||
return HSLColor.toRGB( redHSL[0], 70, hsl[2] );
|
||||
};
|
||||
}
|
||||
FlatSVGIcon.ColorFilter.getInstance().setMapper( mapper );
|
||||
|
||||
// repaint whole application window because global color filter also affects
|
||||
// icons in menubar, toolbar, etc.
|
||||
SwingUtilities.windowForComponent( this ).repaint();
|
||||
}
|
||||
|
||||
private void brighterChanged() {
|
||||
redToggleButton.setSelected( false );
|
||||
|
||||
FlatSVGIcon.ColorFilter.getInstance().setMapper( brighterToggleButton.isSelected()
|
||||
? color -> color.brighter().brighter()
|
||||
: null );
|
||||
|
||||
// repaint whole application window because global color filter also affects
|
||||
// icons in menubar, toolbar, etc.
|
||||
SwingUtilities.windowForComponent( this ).repaint();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
|
||||
label4 = new JLabel();
|
||||
@@ -69,6 +135,13 @@ public class ExtrasPanel
|
||||
label2 = new JLabel();
|
||||
svgIconsPanel = new JPanel();
|
||||
label3 = new JLabel();
|
||||
separator1 = new JSeparator();
|
||||
label5 = new JLabel();
|
||||
label6 = new JLabel();
|
||||
rainbowIcon = new JLabel();
|
||||
label7 = new JLabel();
|
||||
redToggleButton = new JToggleButton();
|
||||
brighterToggleButton = new JToggleButton();
|
||||
|
||||
//======== this ========
|
||||
setLayout(new MigLayout(
|
||||
@@ -81,6 +154,10 @@ public class ExtrasPanel
|
||||
"[]para" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]" +
|
||||
"[]"));
|
||||
|
||||
//---- label4 ----
|
||||
@@ -119,6 +196,30 @@ public class ExtrasPanel
|
||||
//---- label3 ----
|
||||
label3.setText("The icons may change colors when switching to another theme.");
|
||||
add(label3, "cell 1 3 2 1");
|
||||
add(separator1, "cell 1 4 2 1,growx");
|
||||
|
||||
//---- label5 ----
|
||||
label5.setText("Color filters can be also applied to icons. Globally or for each instance.");
|
||||
add(label5, "cell 1 5 2 1");
|
||||
|
||||
//---- label6 ----
|
||||
label6.setText("Rainbow color filter");
|
||||
add(label6, "cell 1 6 2 1");
|
||||
add(rainbowIcon, "cell 1 6 2 1");
|
||||
|
||||
//---- label7 ----
|
||||
label7.setText("Global icon color filter");
|
||||
add(label7, "cell 1 7 2 1");
|
||||
|
||||
//---- redToggleButton ----
|
||||
redToggleButton.setText("Toggle RED");
|
||||
redToggleButton.addActionListener(e -> redChanged());
|
||||
add(redToggleButton, "cell 1 7 2 1");
|
||||
|
||||
//---- brighterToggleButton ----
|
||||
brighterToggleButton.setText("Toggle brighter");
|
||||
brighterToggleButton.addActionListener(e -> brighterChanged());
|
||||
add(brighterToggleButton, "cell 1 7 2 1");
|
||||
// JFormDesigner - End of component initialization //GEN-END:initComponents
|
||||
}
|
||||
|
||||
@@ -130,5 +231,12 @@ public class ExtrasPanel
|
||||
private JLabel label2;
|
||||
private JPanel svgIconsPanel;
|
||||
private JLabel label3;
|
||||
private JSeparator separator1;
|
||||
private JLabel label5;
|
||||
private JLabel label6;
|
||||
private JLabel rainbowIcon;
|
||||
private JLabel label7;
|
||||
private JToggleButton redToggleButton;
|
||||
private JToggleButton brighterToggleButton;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
|
||||
JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
@@ -6,7 +6,7 @@ new FormModel {
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets dialog,hidemode 3"
|
||||
"$columnConstraints": "[][][left]"
|
||||
"$rowConstraints": "[]para[][][]"
|
||||
"$rowConstraints": "[]para[][][][][][][]"
|
||||
} ) {
|
||||
name: "this"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
@@ -56,6 +56,48 @@ new FormModel {
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 3 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JSeparator" ) {
|
||||
name: "separator1"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 4 2 1,growx"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label5"
|
||||
"text": "Color filters can be also applied to icons. Globally or for each instance."
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 5 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label6"
|
||||
"text": "Rainbow color filter"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 6 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "rainbowIcon"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 6 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "label7"
|
||||
"text": "Global icon color filter"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 7 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JToggleButton" ) {
|
||||
name: "redToggleButton"
|
||||
"text": "Toggle RED"
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 7 2 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JToggleButton" ) {
|
||||
name: "brighterToggleButton"
|
||||
"text": "Toggle brighter"
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "brighterChanged", false ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 7 2 1"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 500, 300 )
|
||||
|
||||
@@ -160,14 +160,22 @@ public class IJThemesClassGenerator
|
||||
"{\n" +
|
||||
" public static final String NAME = \"${themeName}\";\n" +
|
||||
"\n" +
|
||||
" public static boolean install() {\n" +
|
||||
" public static boolean setup() {\n" +
|
||||
" try {\n" +
|
||||
" return install( new ${themeClass}() );\n" +
|
||||
" return setup( new ${themeClass}() );\n" +
|
||||
" } catch( RuntimeException ex ) {\n" +
|
||||
" return false;\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" /**\n" +
|
||||
" * @deprecated use {@link #setup()} instead; this method will be removed in a future version\n" +
|
||||
" */\n" +
|
||||
" @Deprecated\n" +
|
||||
" public static boolean install() {\n" +
|
||||
" return setup();\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" public static void installLafInfo() {\n" +
|
||||
" installLafInfo( NAME, ${themeClass}.class );\n" +
|
||||
" }\n" +
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.demo.intellijthemes;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import com.formdev.flatlaf.json.Json;
|
||||
import com.formdev.flatlaf.json.ParseException;
|
||||
|
||||
/**
|
||||
* This tool checks whether there are duplicate name fields in all theme .json files.
|
||||
*
|
||||
* This is important for following file, where the name is used for theme specific UI defaults:
|
||||
* flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
public class IJThemesDuplicateNameChecker
|
||||
{
|
||||
public static void main( String[] args ) {
|
||||
IJThemesManager themesManager = new IJThemesManager();
|
||||
themesManager.loadBundledThemes();
|
||||
|
||||
HashSet<String> names = new HashSet<>();
|
||||
for( IJThemeInfo ti : themesManager.bundledThemes ) {
|
||||
if( ti.sourceCodeUrl == null || ti.sourceCodePath == null )
|
||||
continue;
|
||||
|
||||
String jsonPath = "../flatlaf-intellij-themes/src/main/resources" + IJThemesPanel.THEMES_PACKAGE + ti.resourceName;
|
||||
String name;
|
||||
try {
|
||||
name = readNameFromJson( jsonPath );
|
||||
} catch( IOException ex ) {
|
||||
System.err.println( "Failed to read '" + jsonPath + "'" );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( names.contains( name ) )
|
||||
System.out.println( "Duplicate name '" + name + "'" );
|
||||
names.add( name );
|
||||
}
|
||||
}
|
||||
|
||||
private static String readNameFromJson( String jsonPath ) throws IOException {
|
||||
try( Reader reader = new InputStreamReader( new FileInputStream( jsonPath ), StandardCharsets.UTF_8 ) ) {
|
||||
@SuppressWarnings( "unchecked" )
|
||||
Map<String, Object> json = (Map<String, Object>) Json.parse( reader );
|
||||
return (String) json.get( "name" );
|
||||
} catch( ParseException ex ) {
|
||||
throw new IOException( ex.getMessage(), ex );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,9 +267,9 @@ public class IJThemesPanel
|
||||
|
||||
try {
|
||||
if( themeInfo.themeFile.getName().endsWith( ".properties" ) ) {
|
||||
FlatLaf.install( new FlatPropertiesLaf( themeInfo.name, themeInfo.themeFile ) );
|
||||
FlatLaf.setup( new FlatPropertiesLaf( themeInfo.name, themeInfo.themeFile ) );
|
||||
} else
|
||||
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( themeInfo.themeFile ) ) );
|
||||
FlatLaf.setup( IntelliJTheme.createLaf( new FileInputStream( themeInfo.themeFile ) ) );
|
||||
|
||||
DemoPrefs.getState().put( DemoPrefs.KEY_LAF_THEME, DemoPrefs.FILE_PREFIX + themeInfo.themeFile );
|
||||
} catch( Exception ex ) {
|
||||
@@ -279,7 +279,7 @@ public class IJThemesPanel
|
||||
} else {
|
||||
FlatAnimatedLafChange.showSnapshot();
|
||||
|
||||
IntelliJTheme.install( getClass().getResourceAsStream( THEMES_PACKAGE + themeInfo.resourceName ) );
|
||||
IntelliJTheme.setup( getClass().getResourceAsStream( THEMES_PACKAGE + themeInfo.resourceName ) );
|
||||
DemoPrefs.getState().put( DemoPrefs.KEY_LAF_THEME, DemoPrefs.RESOURCE_PREFIX + themeInfo.resourceName );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 |
@@ -302,6 +302,22 @@
|
||||
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
|
||||
"sourceCodePath": "blob/master/src/main/resources/themes/GitHub Contrast.theme.json"
|
||||
},
|
||||
"material-theme-ui-lite/GitHub Dark.theme.json": {
|
||||
"name": "Material Theme UI Lite / GitHub Dark",
|
||||
"dark": true,
|
||||
"license": "MIT",
|
||||
"licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt",
|
||||
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
|
||||
"sourceCodePath": "blob/master/src/main/resources/themes/GitHub Dark.theme.json"
|
||||
},
|
||||
"material-theme-ui-lite/GitHub Dark Contrast.theme.json": {
|
||||
"name": "Material Theme UI Lite / GitHub Dark Contrast",
|
||||
"dark": true,
|
||||
"license": "MIT",
|
||||
"licenseFile": "material-theme-ui-lite/Material Theme UI Lite.LICENSE.txt",
|
||||
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
|
||||
"sourceCodePath": "blob/master/src/main/resources/themes/GitHub Dark Contrast.theme.json"
|
||||
},
|
||||
"material-theme-ui-lite/Light Owl.theme.json": {
|
||||
"name": "Material Theme UI Lite / Light Owl",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -34,18 +34,6 @@ java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
javadoc {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
use( true )
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
}
|
||||
|
||||
flatlafPublish {
|
||||
artifactId = "flatlaf-extras"
|
||||
name = "FlatLaf Extras"
|
||||
|
||||
@@ -46,6 +46,7 @@ import java.beans.PropertyChangeSupport;
|
||||
import java.lang.reflect.Field;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JRootPane;
|
||||
import javax.swing.JToolBar;
|
||||
@@ -55,6 +56,7 @@ import javax.swing.Popup;
|
||||
import javax.swing.PopupFactory;
|
||||
import javax.swing.RootPaneContainer;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
@@ -87,9 +89,13 @@ import com.formdev.flatlaf.util.UIScale;
|
||||
*/
|
||||
public class FlatInspector
|
||||
{
|
||||
private static final Integer HIGHLIGHT_LAYER = 401;
|
||||
private static final Integer HIGHLIGHT_LAYER = JLayeredPane.POPUP_LAYER - 1;
|
||||
|
||||
private static final int KEY_MODIFIERS_MASK = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK;
|
||||
private static final int KEY_MODIFIERS_MASK =
|
||||
InputEvent.CTRL_DOWN_MASK |
|
||||
InputEvent.SHIFT_DOWN_MASK |
|
||||
InputEvent.ALT_DOWN_MASK |
|
||||
InputEvent.META_DOWN_MASK;
|
||||
|
||||
private final JRootPane rootPane;
|
||||
private final MouseMotionListener mouseMotionListener;
|
||||
@@ -103,7 +109,8 @@ public class FlatInspector
|
||||
private int lastX;
|
||||
private int lastY;
|
||||
private int inspectParentLevel;
|
||||
private boolean wasCtrlOrShiftKeyPressed;
|
||||
private boolean wasModifierKeyPressed;
|
||||
private boolean showClassHierarchy;
|
||||
|
||||
private JComponent highlightFigure;
|
||||
private Popup popup;
|
||||
@@ -160,9 +167,9 @@ public class FlatInspector
|
||||
if( id == KeyEvent.KEY_PRESSED ) {
|
||||
// this avoids that the inspection level is changed when UI inspector
|
||||
// is enabled with keyboard shortcut (e.g. Ctrl+Shift+Alt+X)
|
||||
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT )
|
||||
wasCtrlOrShiftKeyPressed = true;
|
||||
} else if( id == KeyEvent.KEY_RELEASED && wasCtrlOrShiftKeyPressed ) {
|
||||
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT )
|
||||
wasModifierKeyPressed = true;
|
||||
} else if( id == KeyEvent.KEY_RELEASED && wasModifierKeyPressed ) {
|
||||
if( keyCode == KeyEvent.VK_CONTROL ) {
|
||||
inspectParentLevel++;
|
||||
int parentLevel = inspect( lastX, lastY );
|
||||
@@ -179,6 +186,9 @@ public class FlatInspector
|
||||
inspectParentLevel = Math.max( parentLevel - 1, 0 );
|
||||
inspect( lastX, lastY );
|
||||
}
|
||||
} else if( keyCode == KeyEvent.VK_ALT && lastComponent != null) {
|
||||
showClassHierarchy = !showClassHierarchy;
|
||||
showToolTip( lastComponent, lastX, lastY, inspectParentLevel );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,12 +259,10 @@ public class FlatInspector
|
||||
|
||||
// add/remove window listener
|
||||
if( enabled ) {
|
||||
System.out.println( "add "+window );
|
||||
window = SwingUtilities.windowForComponent( rootPane );
|
||||
if( window != null )
|
||||
window.addWindowListener( windowListener );
|
||||
} else {
|
||||
System.out.println( "rem" );
|
||||
if( window != null ) {
|
||||
window.removeWindowListener( windowListener );
|
||||
window = null;
|
||||
@@ -403,7 +411,7 @@ public class FlatInspector
|
||||
return;
|
||||
|
||||
JToolTip tip = new JToolTip();
|
||||
tip.setTipText( buildToolTipText( c, parentLevel ) );
|
||||
tip.setTipText( buildToolTipText( c, parentLevel, showClassHierarchy ) );
|
||||
tip.putClientProperty( FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, true );
|
||||
|
||||
Point pt = new Point( x, y );
|
||||
@@ -429,15 +437,13 @@ public class FlatInspector
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private static String buildToolTipText( Component c, int parentLevel ) {
|
||||
private static String buildToolTipText( Component c, int parentLevel, boolean classHierarchy ) {
|
||||
StringBuilder buf = new StringBuilder( 1500 );
|
||||
buf.append( "<html><style>" );
|
||||
buf.append( "td { padding: 0 10 0 0; }" );
|
||||
buf.append( "</style><table>" );
|
||||
|
||||
String name = c.getClass().getName();
|
||||
name = name.substring( name.lastIndexOf( '.' ) + 1 );
|
||||
appendRow( buf, "Class", name + " (" + c.getClass().getPackage().getName() + ")" );
|
||||
appendRow( buf, "Class", toString( c.getClass(), classHierarchy ) );
|
||||
appendRow( buf, "Size", c.getWidth() + ", " + c.getHeight() + " @ " + c.getX() + ", " + c.getY() );
|
||||
|
||||
if( c instanceof Container )
|
||||
@@ -464,7 +470,7 @@ public class FlatInspector
|
||||
appendRow( buf, "Max size", maxSize.width + ", " + maxSize.height );
|
||||
|
||||
if( c instanceof JComponent )
|
||||
appendRow( buf, "Border", toString( ((JComponent)c).getBorder() ) );
|
||||
appendRow( buf, "Border", toString( ((JComponent)c).getBorder(), classHierarchy ) );
|
||||
|
||||
appendRow( buf, "Background", toString( c.getBackground() ) );
|
||||
appendRow( buf, "Foreground", toString( c.getForeground() ) );
|
||||
@@ -475,8 +481,8 @@ public class FlatInspector
|
||||
Field f = JComponent.class.getDeclaredField( "ui" );
|
||||
f.setAccessible( true );
|
||||
Object ui = f.get( c );
|
||||
appendRow( buf, "UI", (ui != null ? ui.getClass().getName() : "null") );
|
||||
} catch( NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex ) {
|
||||
appendRow( buf, "UI", (ui != null ? toString( ui.getClass(), classHierarchy ) : "null") );
|
||||
} catch( Exception ex ) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -484,7 +490,7 @@ public class FlatInspector
|
||||
if( c instanceof Container ) {
|
||||
LayoutManager layout = ((Container)c).getLayout();
|
||||
if( layout != null )
|
||||
appendRow( buf, "Layout", layout.getClass().getName() );
|
||||
appendRow( buf, "Layout", toString( layout.getClass(), classHierarchy ) );
|
||||
}
|
||||
|
||||
appendRow( buf, "Enabled", String.valueOf( c.isEnabled() ) );
|
||||
@@ -494,16 +500,23 @@ public class FlatInspector
|
||||
appendRow( buf, "ContentAreaFilled", String.valueOf( ((AbstractButton)c).isContentAreaFilled() ) );
|
||||
appendRow( buf, "Focusable", String.valueOf( c.isFocusable() ) );
|
||||
appendRow( buf, "Left-to-right", String.valueOf( c.getComponentOrientation().isLeftToRight() ) );
|
||||
appendRow( buf, "Parent", (c.getParent() != null ? c.getParent().getClass().getName() : "null") );
|
||||
appendRow( buf, "Parent", (c.getParent() != null ? toString( c.getParent().getClass(), classHierarchy ) : "null") );
|
||||
|
||||
// append parent level
|
||||
buf.append( "<tr><td colspan=\"2\">" );
|
||||
if( parentLevel > 0 )
|
||||
buf.append( "<br>Parent level: " + parentLevel );
|
||||
|
||||
if( parentLevel > 0 )
|
||||
buf.append( "<br>(press Ctrl/Shift to increase/decrease level)" );
|
||||
else
|
||||
buf.append( "<br>(press Ctrl key to inspect parent)" );
|
||||
// append modifier keys hint
|
||||
buf.append( "<br>(" )
|
||||
.append( (parentLevel > 0)
|
||||
? "press <b>Ctrl/Shift</b> to increase/decrease level"
|
||||
: "press <b>Ctrl</b> key to inspect parent" )
|
||||
.append( "; " )
|
||||
.append( classHierarchy
|
||||
? "press <b>Alt</b> key to hide class hierarchy"
|
||||
: "press <b>Alt</b> key to show class hierarchy" )
|
||||
.append( ')' );
|
||||
|
||||
buf.append( "</td></tr>" );
|
||||
buf.append( "</table></html>" );
|
||||
@@ -512,13 +525,43 @@ public class FlatInspector
|
||||
}
|
||||
|
||||
private static void appendRow( StringBuilder buf, String key, String value ) {
|
||||
buf.append( "<tr><td>" )
|
||||
buf.append( "<tr><td valign=\"top\">" )
|
||||
.append( key )
|
||||
.append( ":</td><td>" )
|
||||
.append( value )
|
||||
.append( "</td></tr>" );
|
||||
}
|
||||
|
||||
private static String toString( Class<?> cls, boolean classHierarchy ) {
|
||||
StringBuilder buf = new StringBuilder( 100 );
|
||||
int level = 0;
|
||||
|
||||
while( cls != null ) {
|
||||
if( level > 0 ) {
|
||||
if( cls == Object.class )
|
||||
break;
|
||||
buf.append( "<br> " );
|
||||
for( int i = 1; i < level; i++ )
|
||||
buf.append( " " );
|
||||
buf.append( "\u2570 " );
|
||||
}
|
||||
level++;
|
||||
|
||||
String name = cls.getName();
|
||||
int dot = name.lastIndexOf( '.' );
|
||||
String pkg = (dot >= 0) ? name.substring( 0, dot ) : "-";
|
||||
String simpleName = (dot >= 0) ? name.substring( dot + 1 ) : name;
|
||||
buf.append( simpleName ).append( ' ' ).append( toDimmedText( "(" + pkg + ")" ) );
|
||||
|
||||
if( !classHierarchy )
|
||||
break;
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String toString( Insets insets ) {
|
||||
if( insets == null )
|
||||
return "null";
|
||||
@@ -564,11 +607,11 @@ public class FlatInspector
|
||||
+ (f instanceof UIResource ? " UI" : "");
|
||||
}
|
||||
|
||||
private static String toString( Border b ) {
|
||||
private static String toString( Border b, boolean classHierarchy ) {
|
||||
if( b == null )
|
||||
return "null";
|
||||
|
||||
String s = b.getClass().getName();
|
||||
String s = toString( b.getClass(), classHierarchy );
|
||||
|
||||
if( b instanceof EmptyBorder )
|
||||
s += '(' + toString( ((EmptyBorder)b).getBorderInsets() ) + ')';
|
||||
@@ -578,4 +621,14 @@ public class FlatInspector
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static String toDimmedText( String text ) {
|
||||
Color color = UIManager.getColor( "Label.disabledForeground" );
|
||||
if( color == null )
|
||||
color = UIManager.getColor( "Label.disabledText" );
|
||||
if( color == null )
|
||||
color = Color.GRAY;
|
||||
return String.format( "<span color=\"#%06x\">%s</span>",
|
||||
color.getRGB() & 0xffffff, text );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RGBImageFilter;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -66,6 +67,8 @@ public class FlatSVGIcon
|
||||
private final boolean disabled;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private ColorFilter colorFilter;
|
||||
|
||||
private SVGDiagram diagram;
|
||||
private boolean dark;
|
||||
|
||||
@@ -159,17 +162,82 @@ public class FlatSVGIcon
|
||||
this( name, -1, -1, scale, false, classLoader );
|
||||
}
|
||||
|
||||
private FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader ) {
|
||||
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader ) {
|
||||
this.name = name;
|
||||
this.classLoader = classLoader;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.scale = scale;
|
||||
this.disabled = disabled;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the SVG resource (a '/'-separated path).
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom icon width specified in {@link #FlatSVGIcon(String, int, int)},
|
||||
* {@link #FlatSVGIcon(String, int, int, ClassLoader)} or {@link #derive(int, int)}.
|
||||
* Otherwise {@code -1} is returned.
|
||||
* <p>
|
||||
* To get the painted icon width, use {@link #getIconWidth()}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom icon height specified in {@link #FlatSVGIcon(String, int, int)},
|
||||
* {@link #FlatSVGIcon(String, int, int, ClassLoader)} or {@link #derive(int, int)}.
|
||||
* Otherwise {@code -1} is returned.
|
||||
* <p>
|
||||
* To get the painted icon height, use {@link #getIconHeight()}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount by which the icon size is scaled. Usually {@code 1}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public float getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the icon is pained in "disabled" state.
|
||||
*
|
||||
* @see #getDisabledIcon()
|
||||
* @since 1.2
|
||||
*/
|
||||
public boolean isDisabled() {
|
||||
return disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class loader used to load the SVG resource.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new icon with given width and height, which is derived from this icon.
|
||||
* <p>
|
||||
* If the icon has a color filter, then it is shared with the new icon.
|
||||
*
|
||||
* @param width the width of the new icon
|
||||
* @param height the height of the new icon
|
||||
@@ -179,7 +247,8 @@ public class FlatSVGIcon
|
||||
if( width == this.width && height == this.height )
|
||||
return this;
|
||||
|
||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, false, classLoader );
|
||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
|
||||
icon.colorFilter = colorFilter;
|
||||
icon.diagram = diagram;
|
||||
icon.dark = dark;
|
||||
return icon;
|
||||
@@ -187,6 +256,8 @@ public class FlatSVGIcon
|
||||
|
||||
/**
|
||||
* Creates a new icon with given scaling, which is derived from this icon.
|
||||
* <p>
|
||||
* If the icon has a color filter, then it is shared with the new icon.
|
||||
*
|
||||
* @param scale the amount by which the icon size is scaled
|
||||
* @return a new icon
|
||||
@@ -195,7 +266,8 @@ public class FlatSVGIcon
|
||||
if( scale == this.scale )
|
||||
return this;
|
||||
|
||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, false, classLoader );
|
||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
|
||||
icon.colorFilter = colorFilter;
|
||||
icon.diagram = diagram;
|
||||
icon.dark = dark;
|
||||
return icon;
|
||||
@@ -203,6 +275,8 @@ public class FlatSVGIcon
|
||||
|
||||
/**
|
||||
* Creates a new icon with disabled appearance, which is derived from this icon.
|
||||
* <p>
|
||||
* If the icon has a color filter, then it is shared with the new icon.
|
||||
*
|
||||
* @return a new icon
|
||||
*/
|
||||
@@ -212,11 +286,42 @@ public class FlatSVGIcon
|
||||
return this;
|
||||
|
||||
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader );
|
||||
icon.colorFilter = colorFilter;
|
||||
icon.diagram = diagram;
|
||||
icon.dark = dark;
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active color filter or {@code null}.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public ColorFilter getColorFilter() {
|
||||
return colorFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a color filter that can freely modify colors of this icon during painting.
|
||||
* <p>
|
||||
* This method accepts a {@link ColorFilter}. Usually you would want to use a ColorFilter created using the
|
||||
* {@link ColorFilter#ColorFilter(Function)} constructor.
|
||||
* <p>
|
||||
* This can be used to brighten colors of the icon:
|
||||
* <pre>icon.setColorFilter( new FlatSVGIcon.ColorFilter( color -> color.brighter() ) );</pre>
|
||||
* <p>
|
||||
* Using a filter, icons can also be turned monochrome (painted with a single color):
|
||||
* <pre>icon.setColorFilter( new FlatSVGIcon.ColorFilter( color -> Color.RED ) );</pre>
|
||||
* <p>
|
||||
* Note: If a filter is already set, it will be replaced.
|
||||
*
|
||||
* @param colorFilter The color filter
|
||||
* @since 1.2
|
||||
*/
|
||||
public void setColorFilter( ColorFilter colorFilter ) {
|
||||
this.colorFilter = colorFilter;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if( dark == isDarkLaf() && diagram != null )
|
||||
return;
|
||||
@@ -303,7 +408,7 @@ public class FlatSVGIcon
|
||||
: GrayFilter.createDisabledIconFilter( dark );
|
||||
}
|
||||
|
||||
Graphics2D g2 = new GraphicsFilter( (Graphics2D) g.create(), ColorFilter.getInstance(), grayFilter );
|
||||
Graphics2D g2 = new GraphicsFilter( (Graphics2D) g.create(), colorFilter, ColorFilter.getInstance(), grayFilter );
|
||||
|
||||
try {
|
||||
FlatUIUtils.setRenderingHints( g2 );
|
||||
@@ -383,7 +488,14 @@ public class FlatSVGIcon
|
||||
|
||||
private static Boolean darkLaf;
|
||||
|
||||
private static boolean isDarkLaf() {
|
||||
/**
|
||||
* Checks whether the current look and feel is dark.
|
||||
* <p>
|
||||
* Uses {@link FlatLaf#isLafDark()} and caches the result.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public static boolean isDarkLaf() {
|
||||
if( darkLaf == null ) {
|
||||
lafChanged();
|
||||
|
||||
@@ -401,53 +513,253 @@ public class FlatSVGIcon
|
||||
|
||||
//---- class ColorFilter --------------------------------------------------
|
||||
|
||||
/**
|
||||
* A color filter that can modify colors of a painted {@link FlatSVGIcon}.
|
||||
* <p>
|
||||
* The ColorFilter modifies color in two ways.
|
||||
* Either using a color map, where specific colors are mapped to different ones.
|
||||
* And/or by modifying the colors in a mapper function.
|
||||
* <p>
|
||||
* When filtering a color, mappings are applied first, then the mapper function is applied.
|
||||
* <p>
|
||||
* Global {@link FlatSVGIcon} ColorFilter can be retrieved using the {@link ColorFilter#getInstance()} method.
|
||||
*/
|
||||
public static class ColorFilter
|
||||
{
|
||||
private static ColorFilter instance;
|
||||
|
||||
private final Map<Integer, String> rgb2keyMap = new HashMap<>();
|
||||
private final Map<Color, Color> color2colorMap = new HashMap<>();
|
||||
private Map<Integer, String> rgb2keyMap;
|
||||
private Map<Color, Color> colorMap;
|
||||
private Map<Color, Color> darkColorMap;
|
||||
private Function<Color, Color> mapper;
|
||||
|
||||
/**
|
||||
* Returns the global ColorFilter that is applied to all icons.
|
||||
*/
|
||||
public static ColorFilter getInstance() {
|
||||
if( instance == null )
|
||||
if( instance == null ) {
|
||||
instance = new ColorFilter();
|
||||
|
||||
// add default color palette
|
||||
instance.rgb2keyMap = new HashMap<>();
|
||||
for( FlatIconColors c : FlatIconColors.values() )
|
||||
instance.rgb2keyMap.put( c.rgb, c.key );
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty color filter.
|
||||
*/
|
||||
public ColorFilter() {
|
||||
for( FlatIconColors c : FlatIconColors.values() )
|
||||
rgb2keyMap.put( c.rgb, c.key );
|
||||
}
|
||||
|
||||
public void addAll( Map<Color, Color> from2toMap ) {
|
||||
color2colorMap.putAll( from2toMap );
|
||||
/**
|
||||
* Creates a color filter with a color modifying function that changes painted colors.
|
||||
* The {@link Function} gets passed the original color and returns a modified one.
|
||||
* <p>
|
||||
* Examples:
|
||||
* A ColorFilter can be used to brighten colors of the icon:
|
||||
* <pre>new ColorFilter( color -> color.brighter() );</pre>
|
||||
* <p>
|
||||
* Using a ColorFilter, icons can also be turned monochrome (painted with a single color):
|
||||
* <pre>new ColorFilter( color -> Color.RED );</pre>
|
||||
*
|
||||
* @param mapper The color mapper function
|
||||
* @since 1.2
|
||||
*/
|
||||
public ColorFilter( Function<Color, Color> mapper ) {
|
||||
setMapper( mapper );
|
||||
}
|
||||
|
||||
public void add( Color from, Color to ) {
|
||||
color2colorMap.put( from, to );
|
||||
/**
|
||||
* Returns a color modifying function or {@code null}
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public Function<Color, Color> getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public void remove( Color from ) {
|
||||
color2colorMap.remove( from );
|
||||
/**
|
||||
* Sets a color modifying function that changes painted colors.
|
||||
* The {@link Function} gets passed the original color and returns a modified one.
|
||||
* <p>
|
||||
* Examples:
|
||||
* A ColorFilter can be used to brighten colors of the icon:
|
||||
* <pre>filter.setMapper( color -> color.brighter() );</pre>
|
||||
* <p>
|
||||
* Using a ColorFilter, icons can also be turned monochrome (painted with a single color):
|
||||
* <pre>filter.setMapper( color -> Color.RED );</pre>
|
||||
*
|
||||
* @param mapper The color mapper function
|
||||
* @since 1.2
|
||||
*/
|
||||
public void setMapper( Function<Color, Color> mapper ) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color mappings used for light themes.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public Map<Color, Color> getLightColorMap() {
|
||||
return (colorMap != null)
|
||||
? Collections.unmodifiableMap( colorMap )
|
||||
: Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color mappings used for dark themes.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public Map<Color, Color> getDarkColorMap() {
|
||||
return (darkColorMap != null)
|
||||
? Collections.unmodifiableMap( darkColorMap )
|
||||
: getLightColorMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds color mappings. Used for light and dark themes.
|
||||
*/
|
||||
public ColorFilter addAll( Map<Color, Color> from2toMap ) {
|
||||
ensureColorMap();
|
||||
|
||||
colorMap.putAll( from2toMap );
|
||||
if( darkColorMap != null )
|
||||
darkColorMap.putAll( from2toMap );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a color mappings, which has different colors for light and dark themes.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public ColorFilter addAll( Map<Color, Color> from2toLightMap, Map<Color, Color> from2toDarkMap ) {
|
||||
ensureColorMap();
|
||||
ensureDarkColorMap();
|
||||
|
||||
colorMap.putAll( from2toLightMap );
|
||||
darkColorMap.putAll( from2toDarkMap );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a color mapping. Used for light and dark themes.
|
||||
*/
|
||||
public ColorFilter add( Color from, Color to ) {
|
||||
ensureColorMap();
|
||||
|
||||
colorMap.put( from, to );
|
||||
if( darkColorMap != null )
|
||||
darkColorMap.put( from, to );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a color mapping, which has different colors for light and dark themes.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public ColorFilter add( Color from, Color toLight, Color toDark ) {
|
||||
ensureColorMap();
|
||||
ensureDarkColorMap();
|
||||
|
||||
if( toLight != null )
|
||||
colorMap.put( from, toLight );
|
||||
if( toDark != null )
|
||||
darkColorMap.put( from, toDark );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific color mapping.
|
||||
*/
|
||||
public ColorFilter remove( Color from ) {
|
||||
if( colorMap != null )
|
||||
colorMap.remove( from );
|
||||
if( darkColorMap != null )
|
||||
darkColorMap.remove( from );
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all color mappings.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public ColorFilter removeAll() {
|
||||
colorMap = null;
|
||||
darkColorMap = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ensureColorMap() {
|
||||
if( colorMap == null )
|
||||
colorMap = new HashMap<>();
|
||||
}
|
||||
|
||||
private void ensureDarkColorMap() {
|
||||
if( darkColorMap == null )
|
||||
darkColorMap = new HashMap<>( colorMap );
|
||||
}
|
||||
|
||||
public Color filter( Color color ) {
|
||||
Color newColor = color2colorMap.get( color );
|
||||
if( newColor != null )
|
||||
return newColor;
|
||||
// apply mappings
|
||||
color = applyMappings( color );
|
||||
|
||||
String colorKey = rgb2keyMap.get( color.getRGB() & 0xffffff );
|
||||
if( colorKey == null )
|
||||
return color;
|
||||
// apply mapper function
|
||||
if( mapper != null )
|
||||
color = mapper.apply( color );
|
||||
|
||||
newColor = UIManager.getColor( colorKey );
|
||||
if( newColor == null )
|
||||
return color;
|
||||
|
||||
return (newColor.getAlpha() != color.getAlpha())
|
||||
? new Color( (newColor.getRGB() & 0x00ffffff) | (color.getRGB() & 0xff000000) )
|
||||
: newColor;
|
||||
return color;
|
||||
};
|
||||
|
||||
private Color applyMappings( Color color ) {
|
||||
if( colorMap != null ) {
|
||||
Map<Color, Color> map = (darkColorMap != null && isDarkLaf()) ? darkColorMap : colorMap;
|
||||
Color newColor = map.get( color );
|
||||
if( newColor != null )
|
||||
return newColor;
|
||||
}
|
||||
|
||||
if( rgb2keyMap != null ) {
|
||||
// RGB is mapped to a key in UI defaults, which contains the real color.
|
||||
// IntelliJ themes define such theme specific icon colors in .theme.json files.
|
||||
String colorKey = rgb2keyMap.get( color.getRGB() & 0xffffff );
|
||||
if( colorKey == null )
|
||||
return color;
|
||||
|
||||
Color newColor = UIManager.getColor( colorKey );
|
||||
if( newColor == null )
|
||||
return color;
|
||||
|
||||
// preserve alpha of original color
|
||||
return (newColor.getAlpha() != color.getAlpha())
|
||||
? new Color( (newColor.getRGB() & 0x00ffffff) | (color.getRGB() & 0xff000000) )
|
||||
: newColor;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color modifying function that uses {@link RGBImageFilter#filterRGB(int, int, int)}.
|
||||
* Can be set to a {@link ColorFilter} using {@link ColorFilter#setMapper(Function)}.
|
||||
*
|
||||
* @see GrayFilter
|
||||
* @since 1.2
|
||||
*/
|
||||
public static Function<Color, Color> createRGBImageFilterFunction( RGBImageFilter rgbImageFilter ) {
|
||||
return color -> {
|
||||
int oldRGB = color.getRGB();
|
||||
int newRGB = rgbImageFilter.filterRGB( 0, 0, oldRGB );
|
||||
return (newRGB != oldRGB) ? new Color( newRGB, true ) : color;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//---- class GraphicsFilter -----------------------------------------------
|
||||
@@ -456,11 +768,15 @@ public class FlatSVGIcon
|
||||
extends Graphics2DProxy
|
||||
{
|
||||
private final ColorFilter colorFilter;
|
||||
private final ColorFilter globalColorFilter;
|
||||
private final RGBImageFilter grayFilter;
|
||||
|
||||
public GraphicsFilter( Graphics2D delegate, ColorFilter colorFilter, RGBImageFilter grayFilter ) {
|
||||
GraphicsFilter( Graphics2D delegate, ColorFilter colorFilter,
|
||||
ColorFilter globalColorFilter, RGBImageFilter grayFilter )
|
||||
{
|
||||
super( delegate );
|
||||
this.colorFilter = colorFilter;
|
||||
this.globalColorFilter = globalColorFilter;
|
||||
this.grayFilter = grayFilter;
|
||||
}
|
||||
|
||||
@@ -477,8 +793,14 @@ public class FlatSVGIcon
|
||||
}
|
||||
|
||||
private Color filterColor( Color color ) {
|
||||
if( colorFilter != null )
|
||||
color = colorFilter.filter( color );
|
||||
if( colorFilter != null ) {
|
||||
Color newColor = colorFilter.filter( color );
|
||||
color = (newColor != color)
|
||||
? newColor
|
||||
: globalColorFilter.filter( color );
|
||||
} else
|
||||
color = globalColorFilter.filter( color );
|
||||
|
||||
if( grayFilter != null ) {
|
||||
int oldRGB = color.getRGB();
|
||||
int newRGB = grayFilter.filterRGB( 0, 0, oldRGB );
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.formdev.flatlaf.extras;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.RenderingHints;
|
||||
@@ -23,8 +24,11 @@ import java.awt.image.BufferedImage;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.swing.JWindow;
|
||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||
import com.formdev.flatlaf.util.SystemInfo;
|
||||
import com.kitfox.svg.SVGCache;
|
||||
import com.kitfox.svg.SVGDiagram;
|
||||
import com.kitfox.svg.SVGException;
|
||||
@@ -40,22 +44,51 @@ public class FlatSVGUtils
|
||||
* Creates from the given SVG a list of icon images with different sizes that
|
||||
* can be used for windows headers. The SVG should have a size of 16x16,
|
||||
* otherwise it is scaled.
|
||||
* <p>
|
||||
* If running on Windows in Java 9 or later and multi-resolution image support is available,
|
||||
* then a single multi-resolution image is returned that creates images on demand
|
||||
* for requested sizes from SVG.
|
||||
* This has the advantage that only images for used sizes are created.
|
||||
* Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
|
||||
*
|
||||
* @param svgName the name of the SVG resource (a '/'-separated path)
|
||||
* @return list of icon images with different sizes (16x16, 24x24, 32x32, 48x48 and 64x64)
|
||||
* @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
|
||||
* @throws RuntimeException if failed to load or render SVG file
|
||||
* @see JWindow#setIconImages(List)
|
||||
*/
|
||||
public static List<Image> createWindowIconImages( String svgName ) {
|
||||
SVGDiagram diagram = loadSVG( svgName );
|
||||
|
||||
return Arrays.asList(
|
||||
svg2image( diagram, 16, 16 ),
|
||||
svg2image( diagram, 24, 24 ),
|
||||
svg2image( diagram, 32, 32 ),
|
||||
svg2image( diagram, 48, 48 ),
|
||||
svg2image( diagram, 64, 64 )
|
||||
);
|
||||
if( SystemInfo.isWindows && MultiResolutionImageSupport.isAvailable() ) {
|
||||
// use a multi-resolution image that creates images on demand for requested sizes
|
||||
return Collections.singletonList( MultiResolutionImageSupport.create( 0,
|
||||
new Dimension[] {
|
||||
// Listing all these sizes here is actually not necessary because
|
||||
// any size is created on demand when
|
||||
// MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight)
|
||||
// is invoked.
|
||||
// This sizes are only used by MultiResolutionImage.getResolutionVariants().
|
||||
new Dimension( 16, 16 ), // 100%
|
||||
new Dimension( 20, 20 ), // 125%
|
||||
new Dimension( 24, 24 ), // 150%
|
||||
new Dimension( 28, 28 ), // 175%
|
||||
new Dimension( 32, 32 ), // 200%
|
||||
new Dimension( 48, 48 ), // 300%
|
||||
new Dimension( 64, 64 ), // 400%
|
||||
}, dim -> {
|
||||
return svg2image( diagram, dim.width, dim.height );
|
||||
} ) );
|
||||
} else {
|
||||
return Arrays.asList(
|
||||
svg2image( diagram, 16, 16 ), // 100%
|
||||
svg2image( diagram, 20, 20 ), // 125%
|
||||
svg2image( diagram, 24, 24 ), // 150%
|
||||
svg2image( diagram, 28, 28 ), // 175%
|
||||
svg2image( diagram, 32, 32 ), // 200%
|
||||
svg2image( diagram, 48, 48 ), // 300%
|
||||
svg2image( diagram, 64, 64 ) // 400%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ public class FlatButton
|
||||
implements FlatComponentExtension
|
||||
{
|
||||
// NOTE: enum names must be equal to allowed strings
|
||||
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton };
|
||||
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless }
|
||||
|
||||
/**
|
||||
* Returns type of a button.
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -88,6 +88,11 @@ ComboBox.buttonPressedArrowColor = ComboBox.buttonArrowColor
|
||||
Component.custom.borderColor = null
|
||||
|
||||
|
||||
#---- HelpButton ----
|
||||
|
||||
DesktopIcon.background = Desktop.background
|
||||
|
||||
|
||||
#---- HelpButton ----
|
||||
|
||||
HelpButton.disabledBackground = HelpButton.background
|
||||
@@ -210,3 +215,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
|
||||
|
||||
@@ -86,6 +86,8 @@ Name | Class
|
||||
[Dracula Contrast (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatDraculaContrastIJTheme`
|
||||
[GitHub (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubIJTheme`
|
||||
[GitHub Contrast (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubContrastIJTheme`
|
||||
[GitHub Dark (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubDarkIJTheme`
|
||||
[GitHub Dark Contrast (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubDarkContrastIJTheme`
|
||||
[Light Owl (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatLightOwlIJTheme`
|
||||
[Light Owl Contrast (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatLightOwlContrastIJTheme`
|
||||
[Material Darker (Material)](https://github.com/mallowigi/material-theme-ui-lite) | `com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMaterialDarkerIJTheme`
|
||||
|
||||
@@ -33,17 +33,6 @@ java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
javadoc {
|
||||
options {
|
||||
this as StandardJavadocDocletOptions
|
||||
use( true )
|
||||
tags = listOf( "uiDefault", "clientProperty" )
|
||||
}
|
||||
isFailOnError = false
|
||||
}
|
||||
}
|
||||
|
||||
flatlafPublish {
|
||||
artifactId = "flatlaf-intellij-themes"
|
||||
name = "FlatLaf IntelliJ Themes Pack"
|
||||
|
||||
@@ -68,6 +68,8 @@ public class FlatAllIJThemes
|
||||
new FlatIJLookAndFeelInfo( "Dracula Contrast (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatDraculaContrastIJTheme", true ),
|
||||
new FlatIJLookAndFeelInfo( "GitHub (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubIJTheme", false ),
|
||||
new FlatIJLookAndFeelInfo( "GitHub Contrast (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubContrastIJTheme", false ),
|
||||
new FlatIJLookAndFeelInfo( "GitHub Dark (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubDarkIJTheme", true ),
|
||||
new FlatIJLookAndFeelInfo( "GitHub Dark Contrast (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatGitHubDarkContrastIJTheme", true ),
|
||||
new FlatIJLookAndFeelInfo( "Light Owl (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatLightOwlIJTheme", false ),
|
||||
new FlatIJLookAndFeelInfo( "Light Owl Contrast (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatLightOwlContrastIJTheme", false ),
|
||||
new FlatIJLookAndFeelInfo( "Material Darker (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMaterialDarkerIJTheme", true ),
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatArcDarkIJTheme
|
||||
{
|
||||
public static final String NAME = "Arc Dark";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatArcDarkIJTheme() );
|
||||
return setup( new FlatArcDarkIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatArcDarkIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatArcDarkOrangeIJTheme
|
||||
{
|
||||
public static final String NAME = "Arc Dark - Orange";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatArcDarkOrangeIJTheme() );
|
||||
return setup( new FlatArcDarkOrangeIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatArcDarkOrangeIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatArcIJTheme
|
||||
{
|
||||
public static final String NAME = "Arc";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatArcIJTheme() );
|
||||
return setup( new FlatArcIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatArcIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatArcOrangeIJTheme
|
||||
{
|
||||
public static final String NAME = "Arc - Orange";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatArcOrangeIJTheme() );
|
||||
return setup( new FlatArcOrangeIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatArcOrangeIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatCarbonIJTheme
|
||||
{
|
||||
public static final String NAME = "Carbon";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatCarbonIJTheme() );
|
||||
return setup( new FlatCarbonIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatCarbonIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatCobalt2IJTheme
|
||||
{
|
||||
public static final String NAME = "Cobalt 2";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatCobalt2IJTheme() );
|
||||
return setup( new FlatCobalt2IJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatCobalt2IJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatCyanLightIJTheme
|
||||
{
|
||||
public static final String NAME = "Cyan light";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatCyanLightIJTheme() );
|
||||
return setup( new FlatCyanLightIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatCyanLightIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatDarkFlatIJTheme
|
||||
{
|
||||
public static final String NAME = "Dark Flat";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatDarkFlatIJTheme() );
|
||||
return setup( new FlatDarkFlatIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDarkFlatIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatDarkPurpleIJTheme
|
||||
{
|
||||
public static final String NAME = "Dark purple";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatDarkPurpleIJTheme() );
|
||||
return setup( new FlatDarkPurpleIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDarkPurpleIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatDraculaIJTheme
|
||||
{
|
||||
public static final String NAME = "Dracula";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatDraculaIJTheme() );
|
||||
return setup( new FlatDraculaIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatDraculaIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGradiantoDarkFuchsiaIJTheme
|
||||
{
|
||||
public static final String NAME = "Gradianto Dark Fuchsia";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGradiantoDarkFuchsiaIJTheme() );
|
||||
return setup( new FlatGradiantoDarkFuchsiaIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGradiantoDarkFuchsiaIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGradiantoDeepOceanIJTheme
|
||||
{
|
||||
public static final String NAME = "Gradianto Deep Ocean";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGradiantoDeepOceanIJTheme() );
|
||||
return setup( new FlatGradiantoDeepOceanIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGradiantoDeepOceanIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGradiantoMidnightBlueIJTheme
|
||||
{
|
||||
public static final String NAME = "Gradianto Midnight Blue";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGradiantoMidnightBlueIJTheme() );
|
||||
return setup( new FlatGradiantoMidnightBlueIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGradiantoMidnightBlueIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGradiantoNatureGreenIJTheme
|
||||
{
|
||||
public static final String NAME = "Gradianto Nature Green";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGradiantoNatureGreenIJTheme() );
|
||||
return setup( new FlatGradiantoNatureGreenIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGradiantoNatureGreenIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGrayIJTheme
|
||||
{
|
||||
public static final String NAME = "Gray";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGrayIJTheme() );
|
||||
return setup( new FlatGrayIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGrayIJTheme.class );
|
||||
}
|
||||
|
||||
@@ -31,14 +31,22 @@ public class FlatGruvboxDarkHardIJTheme
|
||||
{
|
||||
public static final String NAME = "Gruvbox Dark Hard";
|
||||
|
||||
public static boolean install() {
|
||||
public static boolean setup() {
|
||||
try {
|
||||
return install( new FlatGruvboxDarkHardIJTheme() );
|
||||
return setup( new FlatGruvboxDarkHardIJTheme() );
|
||||
} catch( RuntimeException ex ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #setup()} instead; this method will be removed in a future version
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean install() {
|
||||
return setup();
|
||||
}
|
||||
|
||||
public static void installLafInfo() {
|
||||
installLafInfo( NAME, FlatGruvboxDarkHardIJTheme.class );
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user