Compare commits

...

119 Commits

Author SHA1 Message Date
Karl Tauber
44a04cca2c FlatSmoothScrollingTest:
- better list/tree/etc items for easier recognizing jittery scrolling
- sliders to modify animation duration and resolution
- slider to invoke `scrollRectToVisible()`
- option to show row header for table
- use viewport.viewPosition for chart (instead of scrollbar.value)
- highlight methods in stack of tooltip (e.g. JViewport.setViewPosition())
2023-09-02 17:56:11 +02:00
Karl Tauber
b32b8db97a FlatSmoothScrollingTest: refactored line chart panel into own class for easier use in other test apps 2023-08-30 00:20:45 +02:00
Karl Tauber
c529dcb747 Smooth Scrolling:
- fixed jittery repeating-scrolling with PageUp/Down keys when reaching the top/bottom/left/right of the viewport (see FlatScrollBarUI.setValueAnimated())
- temporary change viewport scroll mode only if it is JViewport.BLIT_SCROLL_MODE
- use JViewport.SIMPLE_SCROLL_MODE when temporary disabling blitting
2023-08-27 14:31:30 +02:00
Karl Tauber
04658c2ef0 SmoothScrollingTest: fixed error reported by Error Prone 2023-08-25 17:43:58 +02:00
Karl Tauber
5cdef5409b Smooth Scrolling: fixed jittery scrolling with trackpad or Magic Mouse (if smooth scrolling is enabled) 2023-08-25 15:24:28 +02:00
Karl Tauber
6dfc204e40 SmoothScrollingTest added (from https://github.com/JFormDesigner/FlatLaf/pull/683#issuecomment-1585667066) 2023-08-25 15:23:59 +02:00
Karl Tauber
542e7d5f60 Smooth Scrolling: fixes too slow repeating block (page) scrolling (e.g. hold down PageUp key) for Tree, TextArea, TextPane and EditorPane 2023-08-24 22:38:52 +02:00
Karl Tauber
3628a03c9d introduced FlatUIAction 2023-08-24 11:54:32 +02:00
Karl Tauber
6ce2198cd6 FlatSmoothScrollingTest:
- added slider to horizontally scale chart
- improved chart legend
- record stack for points in chart and show in tooltip on hover
2023-08-23 15:53:55 +02:00
Karl Tauber
e2e3fd31e9 FlatSmoothScrollingTest:
- added small vertical line to indicate data points in chart
- added split pane to allow changing height of components
- Alt+C clears chart without moving focus to "Clear" button
- separate chart lines for smooth and non-smooth scrolling
2023-08-23 09:51:57 +02:00
Karl Tauber
cf70cfb50c ScrollBar: fixed temporary painting at wrong location during smooth scrolling when using mouse-wheel or scroll bar
(still occurs when scrolling by moving selection via keyboard)

many thanks to @Chrriis for the idea to temporary disable blitting mode on viewport
2023-08-23 09:51:57 +02:00
Karl Tauber
29f6c5fae9 FlatAnimatorTest: added test for precise scrolling with trackpad 2023-08-23 09:51:57 +02:00
Karl Tauber
419a689ca4 FlatAnimatorTest: added test for wheel scrolling (including chart) 2023-08-23 09:51:57 +02:00
Karl Tauber
865a56875f FlatSmoothScrollingTest: added "custom" scroll pane for testing smooth scrolling in case that scroll view does not implement Scrollable interface 2023-08-23 09:51:57 +02:00
Karl Tauber
3573188025 ScrollBar: support smooth scrolling via keyboard 2023-08-23 09:51:57 +02:00
Karl Tauber
1f2622819a FlatSmoothScrollingTest: support dark themes and added "Show table grid" and "Auto-resize mode" check boxes 2023-08-23 09:51:57 +02:00
Karl Tauber
305e9e602e ScrollBar: fixed jittery scrolling when in repeating mode (hold down mouse button) and smooth scrolling enabled 2023-08-23 09:51:57 +02:00
Karl Tauber
1ae31588c4 FlatSmoothScrollingTest: paint "temporary" scrollbar values in line chart using a lighter color 2023-08-23 09:51:56 +02:00
Karl Tauber
d64a8e93e1 FlatSmoothScrollingTest:
- use ChangeListener instead of AdjustmentListener because this is invoked before all other scrollbar listeners (which may run 20-30ms) and avoids a delay in the line chart
- use System.nanoTime() instead of System.currentTimeMillis() for better precision
- paint vertical lines in chart at every 200ms (was 1sec)
- print elapsed time between scrollbar events
2023-08-23 09:51:56 +02:00
Karl Tauber
e603bd81a1 FlatSmoothScrollingTest: added simple line chart that shows changes to scrollbar values 2023-08-23 09:51:56 +02:00
Karl Tauber
522ebb6fa3 FlatSmoothScrollingTest: allow enabling/disabling smooth scrolling with Alt+S without moving focus to checkbox; removed unused tree model 2023-08-23 09:51:56 +02:00
Karl Tauber
7a582c2d1f ScrollBar: fixed issue with updating thumb location (regressing since commit 2c3ef226692fa39b7e6eca3192d197c0b0753aa1) 2023-08-23 09:51:56 +02:00
Karl Tauber
762fe89867 FlatSmoothScrollingTest: added JTree, JTable, JTextArea, JTextPane and JEditorPane for testing smooth scrolling 2023-08-23 09:51:56 +02:00
Karl Tauber
1ebfe00f3c added system properties "flatlaf.animation" and "flatlaf.smoothScrolling" to disable all animations or smooth scrolling via command line (without modifying the application) 2023-08-23 09:51:56 +02:00
Karl Tauber
fdabca99b2 ScrollBar: fixed NPE when switching LaF while smooth scrolling animation is running (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
736305849a ScrollBar: set valueIsAdjusting property to true while smooth scrolling animation is running (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
889b5ea56a ScrollBar: fixed smooth scrolling issues when continuously scrolling (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
82514ccbfc Demo: added "Options > Smooth Scrolling" to menu (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
b67b701d1e ScrollPane: use smooth scrolling when rotating the mouse wheel (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
7f226a2742 ScrollBar: use smooth scrolling when clicking on track or on arrow button (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
c953ff84d0 added explicit file encoding for Eclipse projects 2023-08-12 22:45:02 +02:00
Karl Tauber
96cd207df3 README.md: minor updates 2023-08-12 17:04:21 +02:00
Karl Tauber
7a75f62a6a README.md: new applications using FlatLaf:
- Constellation
- Ghidra
- jadx
- muCommander
- Guiffy
- HashGarten
- MediathekView
- Astah
- Big Faceless (BFO) PDF Viewer
- Chatty
- BGBlitz
- Linux Task Manager (LTM)
2023-08-12 16:35:00 +02:00
Karl Tauber
61e5fe58c2 README.md: organized applications using FlatLaf into categories 2023-08-12 15:57:32 +02:00
Karl Tauber
1a3baba702 README.md: removed versions and 'New' badge from applications using FlatLaf 2023-08-12 14:04:23 +02:00
Karl Tauber
58dc14bb46 Gradle: use System.getProperty( "org.gradle.parallel" ) instead of rootProject.property( "org.gradle.parallel" ) because this did not work with VM command-line option -Dorg.gradle.parallel=false 2023-08-12 13:38:42 +02:00
Karl Tauber
a5b7e04943 Gradle: check whether parallel build is enabled when running 'publish' task
https://stackoverflow.com/questions/72664149/gradle-maven-publish-sonatype-creates-multiple-repositories-that-cant-be-clos
2023-08-12 13:23:11 +02:00
Karl Tauber
22f2aa5475 README.md: introduced "Sponsor" badge icon
SVGO option `convertPathData` enabled
2023-08-11 13:21:56 +02:00
Karl Tauber
d4e9cb12be updated sigtest for FlatLaf 3.2
(generated in clean workspace with gradle task `sigtestGenerate`)
2023-08-10 23:31:26 +02:00
Karl Tauber
75da361480 GitHub Actions: disable parallel build for publishing to maven central; otherwise two staging repos are created, which can not be closed and released
https://stackoverflow.com/questions/72664149/gradle-maven-publish-sonatype-creates-multiple-repositories-that-cant-be-clos
2023-08-10 19:31:55 +02:00
Karl Tauber
7488bcb7b0 update to Gradle 8.2.1 2023-08-10 19:06:10 +02:00
Karl Tauber
1b1a9be107 release 3.2 2023-08-10 18:34:38 +02:00
Karl Tauber
db2f94aa53 IntelliJ Themes: fixed Table background in "One Dark" theme 2023-08-10 16:06:11 +02:00
Karl Tauber
810146b993 Demo: auto-reload current theme in development environment if .properties files have changed 2023-08-09 14:22:05 +02:00
Karl Tauber
93091662ab IntelliJ Themes: fixed colors for selection background/foreground, Separator, Slider track and ProgressBar background in various themes 2023-08-09 13:58:40 +02:00
Karl Tauber
d349227fbf IntelliJ Themes: fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light Flat", "Nord", "Solarized Dark" and "Solarized Light" themes 2023-08-08 17:13:30 +02:00
Karl Tauber
c9423e3aa8 CHANGELOG.md: fixed type on previous commit 2023-08-07 20:00:00 +02:00
Karl Tauber
b9737ca4f1 Merge PR #709: x86: Narrow version range for not using system icons 2023-08-07 19:55:11 +02:00
Karl Tauber
4b4990635d FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17 64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue #403) 2023-08-07 19:35:04 +02:00
Karl Tauber
afaa2c8c78 FileChooser: show localized text for all locales supported by Metal (issue #680) 2023-08-06 18:35:25 +02:00
Karl Tauber
f506ef0d4f Theme Editor: improve order of directories in combobox 2023-08-06 18:34:29 +02:00
Sung Ho Yoon
d30fe66cac Narrow version range for not using system icons 2023-08-06 09:35:00 +09:00
Karl Tauber
270e998e86 Theme Editor:
- fixed missing icon on "open" button (regression in commit 35fa3197c8)
- added icons to "File" menu for add and save actions
2023-08-05 19:18:51 +02:00
Karl Tauber
c395386c05 Merge PR #702: Window decorations: support toolbox-style "small" window title bar 2023-08-05 17:05:54 +02:00
Karl Tauber
4f1207b0db Merge PR #703: TabbedPane: support rounded underline selection and rounded card tabs 2023-08-05 16:59:57 +02:00
Karl Tauber
dc3878e290 Native window libraries: added flatlaf-windows-arm64.dll (for issue #443, PR #707)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/5771160235

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

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

View File

@@ -26,9 +26,9 @@ jobs:
- 17 # LTS
- 19
toolchain: [""]
# include:
# - java: 17
# toolchain: 19 # latest
include:
- java: 17
toolchain: 20 # latest
steps:
- uses: actions/checkout@v3
@@ -40,9 +40,13 @@ jobs:
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
cache: gradle
- name: Check with Error Prone
if: matrix.java == '11'
run: ./gradlew errorprone clean -Dtoolchain=${{ matrix.toolchain }}
- name: Build with Gradle
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
@@ -73,11 +77,11 @@ jobs:
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Publish snapshot to oss.sonatype.org
run: ./gradlew publish :flatlaf-theme-editor:build -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true
run: ./gradlew publish :flatlaf-theme-editor:build -PskipFonts -Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.parallel=false
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
@@ -109,11 +113,11 @@ jobs:
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Release a new stable version to Maven Central
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease
run: ./gradlew publish :flatlaf-demo:build :flatlaf-theme-editor:build -PskipFonts -Prelease -Dorg.gradle.parallel=false
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

View File

@@ -36,7 +36,7 @@ jobs:
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
distribution: temurin # pre-installed on ubuntu-latest
cache: gradle
- name: Build with Gradle

View File

@@ -33,7 +33,7 @@ jobs:
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt
distribution: temurin
cache: gradle
- name: Build with Gradle

View File

@@ -1,12 +1,103 @@
FlatLaf Change Log
==================
## 3.2
#### New features and improvements
- TabbedPane: Support rounded underline selection and rounded card tabs. (PR
#703)
- FlatLaf window decorations:
- Support for Windows on ARM 64-bit. (issue #443, PR #707)
- Support toolbox-style "small" window title bar. (issue #659, PR #702)
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
library (instead of svgSalamander) for rendering. JSVG provides improved SVG
rendering and uses less memory compared to svgSalamander. (PR #684)
- ComboBox: Improved location of selected item in popup if list is large and
scrollable.
- FileChooser: Show localized text for all locales supported by Java's Metal
look and feel. (issue #680)
- Added system property `flatlaf.useNativeLibrary` to allow disabling loading of
FlatLaf native library. (issue #674)
- IntelliJ Themes:
- Reduced memory footprint by releasing Json data and ignoring IntelliJ UI
properties that are not used in FlatLaf.
- Updated "Hiberbee Dark" and "Gradianto" themes.
#### Fixed bugs
- Styling: Fixed scaling of some styling properties (`rowHeight` for Table and
Tree; `iconTextGap` for Button, CheckBox and RadioButton). (issue #682)
- Fixed `IllegalComponentStateException` when invoker is not showing in
`SubMenuUsabilityHelper`. (issue #692)
- macOS themes: Changing `@accentColor` variable in FlatLaf properties files did
not change all accent related colors for all components.
- IntelliJ Themes:
- "Light Owl" theme: Fixed wrong (unreadable) text color in selected menu
items, selected text in text components, and selection in ComboBox popup
list. (issue #687)
- "Gradianto Midnight Blue" theme: Fixed color of ScrollBar track, which was
not visible. (issue #686)
- "Monocai" theme: Fixed unreadable text color of default buttons. (issue
#693)
- "Vuesion" theme: Fixed foreground colors of disabled text.
- "Material UI Lite" themes: Fixed non-editable ComboBox button background.
- CheckBox and RadioButton: Fixed unselected icon colors for themes "Atom One
Light", "Cyan Light", "GitHub", "Light Owl", "Material Lighter" and
"Solarized Light".
- TabbedPane: Fixed focused tab background color for themes "Arc *", "Material
Design Dark", "Monocai", "One Dark", "Spacegray" and "Xcode-Dark". (issue
#697)
- TextComponents, ComboBox and Spinner: Fixed background colors of enabled
text components, to distinguish from disabled, for themes "Carbon", "Cobalt
2", "Gradianto *", "Gruvbox *", "Monocai", "Spacegray", "Vuesion",
"Xcode-Dark", "GitHub", and "Light Owl". (issue #528)
- Fixed wrong disabled text colors in "Dark Flat", "Hiberbee Dark", "Light
Flat", "Nord", "Solarized Dark" and "Solarized Light" themes.
- Fixed colors for selection background/foreground, Separator, Slider track
and ProgressBar background in various themes.
- Native Windows libraries: Fixed crash when running in Java 8 and newer Java
version is installed in `PATH` environment variable and using class
`SystemInfo` before AWT initialization. (issue #673)
- ComboBox: Fixed search in item list for text with spaces. (issue #691)
- FormattedTextField: On Linux, fixed `IllegalArgumentException: Invalid
location` if `JFormattedTextField.setDocument()` is invoked in a focus gained
listener on that formatted text field. (issue #698)
- PopupMenu: Make sure that popup menu does not overlap any operating system
task bar. (issue #701)
- FileChooser: Use system icons on Windows with Java 17.0.3 (and later) 32-bit.
Only Java 17 - 17.0.2 32-bit do not use system icons because of a bug in Java
32-bit that crashes the application. (PR #709)
- FileChooser: Fixed crash on Windows with Java 17 to 17.0.2 32-bit. Java 17
64-bit is not affected. (regression since FlatLaf 2.3; PR #522, see also issue
#403)
#### Incompatibilities
- Extras: Class `FlatSVGIcon` now uses [JSVG](https://github.com/weisJ/jsvg)
library for SVG rendering. You need to replace svgSalamander with JSVG in your
build scripts and distribute `jsvg.jar` with your application. Also replace
`com.kitfox.svg` with `com.github.weisj.jsvg` in `module-info.java` files.
- IntelliJ Themes: Removed all "Contrast" themes from "Material UI Lite".
## 3.1.1
- IntelliJ Themes:
- Fixed too large menu item paddings and too large table/tree row heights (all
"Material Theme UI Lite" themes; issue #667; regression in FlatLaf 3.1).
- Fixed too large tree row height in "Carbon", "Dark Purple", "Gray",
"Material Design Dark", "Monokai Pro", "One Dark" and "Spacegray" themes.
- Native libraries: Fixed `IllegalArgumentException: URI scheme is not "file"`
when using FlatLaf in WebStart. (issue #668; regression in FlatLaf 3.1)
## 3.1
#### New features and improvements
- Windows 11: Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
native Windows 11 rounded borders and drop shadows.
native Windows 11 rounded borders and drop shadows. (PR #643)
- Fonts:
- Added **Roboto Mono** (https://fonts.google.com/specimen/Roboto+Mono). (PR
#639, issue #638)
@@ -20,6 +111,18 @@ FlatLaf Change Log
tab, then this color is now used even if the tab is focused or selected.
- TableHeader: Support column hover and pressed background and foreground
colors. (issue #636)
- Native libraries: Made it easier to distribute FlatLaf native libraries
(Windows `.dll` and Linux `.so`) to avoid problems on operating systems with
enabled execution restrictions.
See https://www.formdev.com/flatlaf/native-libraries/ for more details. (issue #624)
- Published native libraries to Maven Central for easy using them as
dependencies in Gradle and Maven.
- If available, native libraries are now loaded from same location as
`flatlaf.jar`, otherwise they are extract from `flatlaf.jar` to temporary
folder and loaded from there.
- Windows DLLs are now digitally signed with FormDev Software GmbH
certificate.
#### Fixed bugs

278
README.md
View File

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

View File

@@ -14,10 +14,9 @@
* limitations under the License.
*/
val releaseVersion = "3.1"
val developmentVersion = "3.2-SNAPSHOT"
import net.ltgt.gradle.errorprone.errorprone
version = if( rootProject.hasProperty( "release" ) ) releaseVersion else developmentVersion
version = property( if( hasProperty( "release" ) ) "flatlaf.releaseVersion" else "flatlaf.developmentVersion" ) as String
allprojects {
version = rootProject.version
@@ -43,6 +42,10 @@ if( !toolchainJavaVersion.isNullOrEmpty() )
println()
plugins {
alias( libs.plugins.errorprone ) apply false
}
allprojects {
tasks {
withType<JavaCompile>().configureEach {
@@ -81,4 +84,56 @@ allprojects {
isFailOnError = false
}
}
//---- Error Prone ----
tasks.register( "errorprone" ) {
group = "verification"
tasks.withType<JavaCompile>().forEach {
dependsOn( it )
}
}
val useErrorProne = gradle.startParameter.taskNames.contains( "errorprone" )
if( useErrorProne ) {
plugins.withType<JavaPlugin> {
apply( plugin = libs.plugins.errorprone.get().pluginId )
dependencies {
"errorprone"( libs.errorprone )
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add( "-Werror" )
options.errorprone {
disable(
"ReferenceEquality", // reports usage of '==' for objects
"StringSplitter", // reports String.split()
"JavaTimeDefaultTimeZone", // reports Year.now()
"MissingSummary", // reports `/** @since 2 */`
"InvalidBlockTag", // reports @uiDefault in javadoc
"AlreadyChecked", // reports false positives
"InlineMeSuggester", // suggests using Error Prone annotations for deprecated methods
"TypeParameterUnusedInFormals",
"UnsynchronizedOverridesSynchronized",
"NonApiType", // reports ArrayList/HashSet in parameter or return type
)
when( project.name ) {
"flatlaf-intellij-themes" -> disable(
"MutablePublicArray", // reports FlatAllIJThemes.INFOS
)
"flatlaf-theme-editor" -> disable(
"CatchAndPrintStackTrace",
)
"flatlaf-testing" -> disable(
"CatchAndPrintStackTrace",
"JdkObsolete", // reports Hashtable used for JSlider.setLabelTable()
"JavaUtilDate", // reports usage of class Date
)
}
}
}
}
}
}

View File

@@ -122,3 +122,11 @@ signing {
tasks.withType<Sign>().configureEach {
onlyIf { rootProject.hasProperty( "release" ) }
}
// check whether parallel build is enabled
tasks.withType<PublishToMavenRepository>().configureEach {
doFirst {
if( System.getProperty( "org.gradle.parallel" ) == "true" )
throw RuntimeException( "Publishing does not work correctly with enabled parallel build. Disable parallel build with VM option '-Dorg.gradle.parallel=false'." )
}
}

View File

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

View File

@@ -27,12 +27,11 @@ plugins {
val sigtest = configurations.create( "sigtest" )
dependencies {
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
// https://github.com/jtulach/netbeans-apitest
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.7" )
sigtest( libs.sigtest )
}
java {
@@ -130,6 +129,7 @@ flatlafPublish {
nativeArtifacts = listOf(
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
)
}

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 3.0
#Version 3.2
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -12,6 +12,7 @@ fld public final static java.lang.String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarBu
fld public final static java.lang.String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"
fld public final static java.lang.String COMPONENT_ROUND_RECT = "JComponent.roundRect"
fld public final static java.lang.String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption"
fld public final static java.lang.String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight"
fld public final static java.lang.String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"
fld public final static java.lang.String MINIMUM_HEIGHT = "JComponent.minimumHeight"
fld public final static java.lang.String MINIMUM_WIDTH = "JComponent.minimumWidth"
@@ -19,6 +20,7 @@ fld public final static java.lang.String OUTLINE = "JComponent.outline"
fld public final static java.lang.String OUTLINE_ERROR = "error"
fld public final static java.lang.String OUTLINE_WARNING = "warning"
fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placeholderText"
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
@@ -94,6 +96,8 @@ fld public final static java.lang.String TITLE_BAR_SHOW_TITLE = "JRootPane.title
fld public final static java.lang.String TREE_PAINT_SELECTION = "JTree.paintSelection"
fld public final static java.lang.String TREE_WIDE_SELECTION = "JTree.wideSelection"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "JRootPane.useWindowDecorations"
fld public final static java.lang.String WINDOW_STYLE = "Window.style"
fld public final static java.lang.String WINDOW_STYLE_SMALL = "small"
meth public static <%0 extends java.lang.Object> {%%0} clientProperty(javax.swing.JComponent,java.lang.String,{%%0},java.lang.Class<{%%0}>)
meth public static boolean clientPropertyBoolean(javax.swing.JComponent,java.lang.String,boolean)
meth public static boolean clientPropertyEquals(javax.swing.JComponent,java.lang.String,java.lang.Object)
@@ -278,6 +282,7 @@ fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.ui
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
@@ -296,7 +301,7 @@ meth public static boolean setup(java.io.InputStream)
meth public static com.formdev.flatlaf.FlatLaf createLaf(com.formdev.flatlaf.IntelliJTheme)
meth public static com.formdev.flatlaf.FlatLaf createLaf(java.io.InputStream) throws java.io.IOException
supr java.lang.Object
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyInverseMapping,uiKeyMapping
hfds checkboxDuplicateColors,checkboxKeyMapping,colors,icons,isMaterialUILite,namedColors,ui,uiKeyCopying,uiKeyDoNotOverride,uiKeyExcludes,uiKeyInverseMapping,uiKeyMapping
CLSS public static com.formdev.flatlaf.IntelliJTheme$ThemeLaf
outer com.formdev.flatlaf.IntelliJTheme

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.IllegalComponentStateException;
import java.awt.Window;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
@@ -124,6 +126,7 @@ public interface FlatClientProperties
*/
String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
/**
@@ -266,6 +269,7 @@ public interface FlatClientProperties
*/
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
//---- Popup --------------------------------------------------------------
/**
@@ -305,6 +309,7 @@ public interface FlatClientProperties
*/
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
//---- JProgressBar -------------------------------------------------------
/**
@@ -323,6 +328,7 @@ public interface FlatClientProperties
*/
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
//---- JRootPane ----------------------------------------------------------
/**
@@ -472,6 +478,37 @@ public interface FlatClientProperties
*/
String GLASS_PANE_FULL_HEIGHT = "JRootPane.glassPaneFullHeight";
/**
* Specifies the style of the window title bar.
* Besides the default title bar style, you can use a Utility-style title bar,
* which is smaller than the default title bar.
* <p>
* On Windows 10/11, this requires FlatLaf window decorations.
* On macOS, Java supports this out of the box.
* <p>
* Note that this client property must be set before the window becomes displayable.
* Otherwise an {@link IllegalComponentStateException} is thrown.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #WINDOW_STYLE_SMALL}
*
* @since 3.2
*/
String WINDOW_STYLE = "Window.style";
/**
* The window has Utility-style title bar, which is smaller than default title bar.
* <p>
* This is the same as using {@link Window#setType}( {@link Window.Type#UTILITY} ).
*
* @see #WINDOW_STYLE
* @since 3.2
*/
String WINDOW_STYLE_SMALL = "small";
//---- JScrollBar / JScrollPane -------------------------------------------
/**
@@ -490,6 +527,7 @@ public interface FlatClientProperties
*/
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JSplitPane ---------------------------------------------------------
/**
@@ -524,6 +562,7 @@ public interface FlatClientProperties
*/
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
//---- JTabbedPane --------------------------------------------------------
/**
@@ -919,6 +958,7 @@ public interface FlatClientProperties
*/
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
//---- JTextField ---------------------------------------------------------
/**
@@ -1088,6 +1128,7 @@ public interface FlatClientProperties
*/
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
//---- JToggleButton ------------------------------------------------------
/**
@@ -1129,6 +1170,7 @@ public interface FlatClientProperties
*/
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
//---- JTree --------------------------------------------------------------
/**
@@ -1148,6 +1190,7 @@ public interface FlatClientProperties
*/
String TREE_PAINT_SELECTION = "JTree.paintSelection";
//---- helper methods -----------------------------------------------------
/**

View File

@@ -50,7 +50,8 @@ class FlatInputMaps
}
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
"SPACE", "spacePopup",
// Space key still shows popup, but from FlatComboBoxUI.FlatKeySelectionManager
// "SPACE", "spacePopup",
"UP", mac( "selectPrevious2", "selectPrevious" ),
"DOWN", mac( "selectNext2", "selectNext" ),

View File

@@ -71,6 +71,7 @@ import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.text.StyleContext;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
@@ -391,7 +392,7 @@ public abstract class FlatLaf
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
} else
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).getDeclaredConstructor().newInstance();
aquaLaf = Class.forName( aquaLafClassName ).asSubclass( BasicLookAndFeel.class ).getDeclaredConstructor().newInstance();
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
throw new IllegalStateException();
@@ -543,7 +544,7 @@ public abstract class FlatLaf
// which can happen in applications that use some plugin system
// and load FlatLaf in a plugin that uses its own classloader.
// (e.g. Apache NetBeans)
if( defaults.get( "FileChooser.fileNameHeaderText" ) != null )
if( defaults.get( "TabbedPane.moreTabsButtonToolTipText" ) != null )
return;
// load FlatLaf resource bundle and add content to defaults
@@ -581,10 +582,13 @@ public abstract class FlatLaf
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
// override fonts
List<String> fontKeys = new ArrayList<>( 50 );
for( Object key : defaults.keySet() ) {
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
defaults.put( key, activeFont );
fontKeys.add( (String) key );
}
for( String key : fontKeys )
defaults.put( key, activeFont );
// add fonts that are not set in BasicLookAndFeel
defaults.put( "RootPane.font", activeFont );
@@ -1428,26 +1432,36 @@ public abstract class FlatLaf
private class FlatUIDefaults
extends UIDefaults
{
private UIDefaults metalDefaults;
FlatUIDefaults( int initialCapacity, float loadFactor ) {
super( initialCapacity, loadFactor );
}
@Override
public Object get( Object key ) {
Object value = getValue( key );
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key );
return get( key, null );
}
@Override
public Object get( Object key, Locale l ) {
Object value = getValue( key );
return (value != null) ? (value != NULL_VALUE ? value : null) : super.get( key, l );
Object value = getFromUIDefaultsGetters( key );
if( value != null )
return (value != NULL_VALUE) ? value : null;
value = super.get( key, l );
if( value != null )
return value;
// get file chooser texts from Metal
return (key instanceof String && ((String)key).startsWith( "FileChooser." ))
? getFromMetal( (String) key, l )
: null;
}
private Object getValue( Object key ) {
private Object getFromUIDefaultsGetters( Object key ) {
// use local variable for getters to avoid potential multi-threading issues
List<Function<Object, Object>> uiDefaultsGetters = FlatLaf.this.uiDefaultsGetters;
if( uiDefaultsGetters == null )
return null;
@@ -1459,6 +1473,22 @@ public abstract class FlatLaf
return null;
}
private synchronized Object getFromMetal( String key, Locale l ) {
if( metalDefaults == null ) {
metalDefaults = new MetalLookAndFeel() {
// avoid unnecessary initialization
@Override protected void initClassDefaults( UIDefaults table ) {}
@Override protected void initSystemColorDefaults( UIDefaults table ) {}
}.getDefaults();
// empty not needed defaults (to save memory) because we're only interested
// in resource bundle strings, which are stored in another internal map
metalDefaults.clear();
}
return metalDefaults.get( key, l );
}
}
//---- class ActiveFont ---------------------------------------------------
@@ -1541,7 +1571,7 @@ public abstract class FlatLaf
int newStyle = (style != -1)
? style
: (styleChange != 0)
? baseStyle & ~((styleChange >> 16) & 0xffff) | (styleChange & 0xffff)
? (baseStyle & ~((styleChange >> 16) & 0xffff)) | (styleChange & 0xffff)
: baseStyle;
// new size

View File

@@ -21,6 +21,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Properties;
/**
@@ -96,7 +97,7 @@ public class FlatPropertiesLaf
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
switch( baseTheme.toLowerCase() ) {
switch( baseTheme.toLowerCase( Locale.ENGLISH ) ) {
default:
case "light":
lafClasses.add( FlatLightLaf.class );

View File

@@ -132,6 +132,14 @@ public interface FlatSystemProperties
*/
String ANIMATION = "flatlaf.animation";
/**
* Specifies whether smooth scrolling is enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String SMOOTH_SCROLLING = "flatlaf.smoothScrolling";
/**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p>
@@ -150,10 +158,24 @@ public interface FlatSystemProperties
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*
* @since 2.5
*/
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
/**
* Specifies whether FlatLaf native library should be used.
* <p>
* Setting this to {@code false} disables loading native library,
* which also disables some features that depend on the native library.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*
* @since 3.2
*/
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
/**
* Specifies a directory in which the native FlatLaf libraries have been extracted.
* The path can be absolute or relative to current application working directory.

View File

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

View File

@@ -23,11 +23,14 @@ import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -68,7 +71,7 @@ class LinuxFontPolicy
if( word.endsWith( "," ) )
word = word.substring( 0, word.length() - 1 ).trim();
String lword = word.toLowerCase();
String lword = word.toLowerCase( Locale.ENGLISH );
if( lword.equals( "italic" ) || lword.equals( "oblique" ) )
style |= Font.ITALIC;
else if( lword.equals( "bold" ) )
@@ -104,7 +107,7 @@ class LinuxFontPolicy
size = 1;
// handle logical font names
String logicalFamily = mapFcName( family.toLowerCase() );
String logicalFamily = mapFcName( family.toLowerCase( Locale.ENGLISH ) );
if( logicalFamily != null )
family = logicalFamily;
@@ -143,7 +146,7 @@ class LinuxFontPolicy
return createFont( Font.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();
String lastWord = family.substring( index + 1 ).toLowerCase( Locale.ENGLISH );
if( lastWord.contains( "bold" ) || lastWord.contains( "heavy" ) || lastWord.contains( "black" ) )
style |= Font.BOLD;
@@ -257,6 +260,7 @@ class LinuxFontPolicy
return createFont( family, style, size, dsize );
}
@SuppressWarnings( "MixedMutabilityReturnType" ) // Error Prone
private static List<String> readConfig( String filename ) {
File userHome = new File( System.getProperty( "user.home" ) );
@@ -277,7 +281,9 @@ class LinuxFontPolicy
// read config file
ArrayList<String> lines = new ArrayList<>( 200 );
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
try( BufferedReader reader = new BufferedReader( new InputStreamReader(
new FileInputStream( file ), StandardCharsets.US_ASCII ) ) )
{
String line;
while( (line = reader.readLine()) != null )
lines.add( line );

View File

@@ -153,7 +153,7 @@ debug*/
// get invoker screen bounds
Component invoker = popup.getInvoker();
invokerBounds = (invoker != null)
invokerBounds = (invoker != null && invoker.isShowing())
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
: null;

View File

@@ -27,8 +27,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -271,8 +275,9 @@ class UIDefaultsLoader
continue;
}
String value = resolveValue( (String) e.getValue(), propertiesGetter );
String value = (String) e.getValue();
try {
value = resolveValue( value, propertiesGetter );
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex, true );
@@ -297,7 +302,9 @@ class UIDefaultsLoader
LoggingFacade.INSTANCE.logConfig( message, ex );
}
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
static String resolveValue( String value, Function<String, String> propertiesGetter )
throws IllegalArgumentException
{
value = value.trim();
String value0 = value;
@@ -326,7 +333,9 @@ class UIDefaultsLoader
return resolveValue( newValue, propertiesGetter );
}
static String resolveValueFromUIManager( String value ) {
static String resolveValueFromUIManager( String value )
throws IllegalArgumentException
{
if( value.startsWith( VARIABLE_PREFIX ) ) {
@SuppressWarnings( "unchecked" )
Map<String, String> variables = (Map<String, String>) UIManager.get( KEY_VARIABLES );
@@ -348,8 +357,11 @@ class UIDefaultsLoader
// convert binary color to string
if( newValue instanceof Color ) {
Color color = (Color) newValue;
int rgb = color.getRGB() & 0xffffff;
int alpha = color.getAlpha();
return String.format( (alpha != 255) ? "#%06x%02x" : "#%06x", color.getRGB() & 0xffffff, alpha );
return (alpha != 255)
? String.format( "#%06x%02x", rgb, alpha )
: String.format( "#%06x", rgb );
}
throw new IllegalArgumentException( "property value type '" + newValue.getClass().getName() + "' not supported in references" );
@@ -362,12 +374,15 @@ class UIDefaultsLoader
private static Map<Class<?>, ValueType> javaValueTypes;
private static Map<String, ValueType> knownValueTypes;
static Object parseValue( String key, String value, Class<?> valueType ) {
static Object parseValue( String key, String value, Class<?> valueType )
throws IllegalArgumentException
{
return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
}
static Object parseValue( String key, String value, Class<?> javaValueType, ValueType[] resultValueType,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
throws IllegalArgumentException
{
if( resultValueType == null )
resultValueType = tempResultValueType;
@@ -397,7 +412,7 @@ class UIDefaultsLoader
if( value.startsWith( "if(" ) && value.endsWith( ")" ) ) {
List<String> params = splitFunctionParams( value.substring( 3, value.length() - 1 ), ',' );
if( params.size() != 3 )
throwMissingParametersException( value );
throw newMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, addonClassLoaders );
String ifValue = params.get( ifCondition ? 1 : 2 );
@@ -537,7 +552,7 @@ class UIDefaultsLoader
case INTEGERORFLOAT:return parseIntegerOrFloat( value );
case FLOAT: return parseFloat( value );
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
case ICON: return parseInstance( value, addonClassLoaders );
case ICON: return parseInstance( value, resolver, addonClassLoaders );
case INSETS: return parseInsets( value );
case DIMENSION: return parseDimension( value );
case COLOR: return parseColorOrFunction( value, resolver );
@@ -546,7 +561,7 @@ class UIDefaultsLoader
case SCALEDFLOAT: return parseScaledFloat( value );
case SCALEDINSETS: return parseScaledInsets( value );
case SCALEDDIMENSION:return parseScaledDimension( value );
case INSTANCE: return parseInstance( value, addonClassLoaders );
case INSTANCE: return parseInstance( value, resolver, addonClassLoaders );
case CLASS: return parseClass( value, addonClassLoaders );
case GRAYFILTER: return parseGrayFilter( value );
case UNKNOWN:
@@ -608,9 +623,11 @@ class UIDefaultsLoader
}
}
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
throws IllegalArgumentException
{
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
@@ -625,12 +642,28 @@ class UIDefaultsLoader
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value, addonClassLoaders );
return parseInstance( value, resolver, addonClassLoaders );
}
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
private static Object parseInstance( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
return (LazyValue) t -> {
try {
if( value.indexOf( ',' ) >= 0 ) {
// Syntax: className,param1,param2,...
List<String> parts = splitFunctionParams( value, ',' );
String className = parts.get( 0 );
Class<?> cls = findClass( className, addonClassLoaders );
Constructor<?>[] constructors = cls.getDeclaredConstructors();
Object result = invokeConstructorOrStaticMethod( constructors, parts, resolver );
if( result != null )
return result;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + className
+ "': no constructor found for parameters '"
+ value.substring( value.indexOf( ',' + 1 ) ) + "'.", null );
return null;
} else
return findClass( value, addonClassLoaders ).getDeclaredConstructor().newInstance();
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to instantiate '" + value + "'.", ex );
@@ -668,7 +701,9 @@ class UIDefaultsLoader
}
}
private static Insets parseInsets( String value ) {
private static Insets parseInsets( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
return new InsetsUIResource(
@@ -681,7 +716,9 @@ class UIDefaultsLoader
}
}
private static Dimension parseDimension( String value ) {
private static Dimension parseDimension( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
return new DimensionUIResource(
@@ -692,7 +729,9 @@ class UIDefaultsLoader
}
}
private static Object parseColorOrFunction( String value, Function<String, String> resolver ) {
private static Object parseColorOrFunction( String value, Function<String, String> resolver )
throws IllegalArgumentException
{
if( value.endsWith( ")" ) )
return parseColorFunctions( value, resolver );
@@ -702,10 +741,10 @@ class UIDefaultsLoader
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as color object.
*
* @throws IllegalArgumentException
*/
static ColorUIResource parseColor( String value ) {
static ColorUIResource parseColor( String value )
throws IllegalArgumentException
{
int rgba = parseColorRGBA( value );
return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
@@ -716,10 +755,10 @@ class UIDefaultsLoader
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as {@code rgba} integer suitable for {@link java.awt.Color},
* which includes alpha component in bits 24-31.
*
* @throws IllegalArgumentException
*/
static int parseColorRGBA( String value ) {
static int parseColorRGBA( String value )
throws IllegalArgumentException
{
int len = value.length();
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
throw newInvalidColorException( value );
@@ -760,7 +799,9 @@ class UIDefaultsLoader
return new IllegalArgumentException( "invalid color '" + value + "'" );
}
private static Object parseColorFunctions( String value, Function<String, String> resolver ) {
private static Object parseColorFunctions( String value, Function<String, String> resolver )
throws IllegalArgumentException
{
int paramsStart = value.indexOf( '(' );
if( paramsStart < 0 )
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
@@ -768,7 +809,7 @@ class UIDefaultsLoader
String function = StringUtils.substringTrimmed( value, 0, paramsStart );
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
if( params.isEmpty() )
throwMissingParametersException( value );
throw newMissingParametersException( value );
if( parseColorDepth > 100 )
throw new IllegalArgumentException( "endless recursion in color function '" + value + "'" );
@@ -813,9 +854,11 @@ class UIDefaultsLoader
* This "if" function is only used if the "if" is passed as parameter to another
* color function. Otherwise, the general "if" function is used.
*/
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver ) {
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
if( params.size() != 3 )
throwMissingParametersException( value );
throw newMissingParametersException( value );
boolean ifCondition = parseCondition( params.get( 0 ), resolver, Collections.emptyList() );
String ifValue = params.get( ifCondition ? 1 : 2 );
@@ -827,9 +870,11 @@ class UIDefaultsLoader
* - name: system color name
* - defaultValue: default color value used if system color is not available
*/
private static Object parseColorSystemColor( String value, List<String> params, Function<String, String> resolver ) {
private static Object parseColorSystemColor( String value, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
if( params.size() < 1 )
throwMissingParametersException( value );
throw newMissingParametersException( value );
ColorUIResource systemColor = getSystemColor( params.get( 0 ) );
if( systemColor != null )
@@ -869,6 +914,7 @@ class UIDefaultsLoader
*/
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
Function<String, String> resolver )
throws IllegalArgumentException
{
if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color
@@ -898,7 +944,9 @@ class UIDefaultsLoader
* - lightness: a percentage 0-100%
* - alpha: a percentage 0-100%
*/
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params )
throws IllegalArgumentException
{
int hue = parseInteger( params.get( 0 ), 0, 360, false );
int saturation = parsePercentage( params.get( 1 ) );
int lightness = parsePercentage( params.get( 2 ) );
@@ -918,6 +966,7 @@ class UIDefaultsLoader
*/
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
@@ -961,7 +1010,9 @@ class UIDefaultsLoader
* - amount: percentage 0-100%
* - options: [derived] [lazy]
*/
private static Object parseColorFade( List<String> params, Function<String, String> resolver ) {
private static Object parseColorFade( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
boolean derived = false;
@@ -995,7 +1046,9 @@ class UIDefaultsLoader
* - angle: number of degrees to rotate
* - options: [derived]
*/
private static Object parseColorSpin( List<String> params, Function<String, String> resolver ) {
private static Object parseColorSpin( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ) );
boolean derived = false;
@@ -1023,6 +1076,7 @@ class UIDefaultsLoader
*/
private static Object parseColorChange( int hslIndex,
List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
int value = (hslIndex == 0)
@@ -1051,7 +1105,9 @@ class UIDefaultsLoader
* - weight: the weight (in range 0-100%) to mix the two colors
* larger weight uses more of first color, smaller weight more of second color
*/
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver ) {
private static Object parseColorMix( String color1Str, List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
int i = 0;
if( color1Str == null )
color1Str = params.get( i++ );
@@ -1078,7 +1134,9 @@ class UIDefaultsLoader
* - threshold: the threshold (in range 0-100%) to specify where the transition
* from "dark" to "light" is (default is 43%)
*/
private static Object parseColorContrast( List<String> params, Function<String, String> resolver ) {
private static Object parseColorContrast( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String colorStr = params.get( 0 );
String darkStr = params.get( 1 );
String lightStr = params.get( 2 );
@@ -1104,7 +1162,9 @@ class UIDefaultsLoader
* the alpha of this color is used as weight to mix the two colors
* - background: a background color (e.g. #f00) or a color function
*/
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver ) {
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver )
throws IllegalArgumentException
{
String foregroundStr = params.get( 0 );
String backgroundStr = params.get( 1 );
@@ -1128,6 +1188,7 @@ class UIDefaultsLoader
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver )
throws IllegalArgumentException
{
// parse base color
String resolvedColorStr = resolver.apply( colorStr );
@@ -1159,7 +1220,9 @@ class UIDefaultsLoader
/**
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
*/
private static Object parseFont( String value ) {
private static Object parseFont( String value )
throws IllegalArgumentException
{
Object font = fontCache.get( value );
if( font != null )
return font;
@@ -1257,7 +1320,9 @@ class UIDefaultsLoader
return font;
}
private static int parsePercentage( String value ) {
private static int parsePercentage( String value )
throws IllegalArgumentException, NumberFormatException
{
if( !value.endsWith( "%" ) )
throw new NumberFormatException( "invalid percentage '" + value + "'" );
@@ -1273,7 +1338,9 @@ class UIDefaultsLoader
return val;
}
private static Boolean parseBoolean( String value ) {
private static Boolean parseBoolean( String value )
throws IllegalArgumentException
{
switch( value ) {
case "false": return false;
case "true": return true;
@@ -1281,13 +1348,17 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
}
private static Character parseCharacter( String value ) {
private static Character parseCharacter( String value )
throws IllegalArgumentException
{
if( value.length() != 1 )
throw new IllegalArgumentException( "invalid character '" + value + "'" );
return value.charAt( 0 );
}
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage ) {
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage )
throws IllegalArgumentException, NumberFormatException
{
if( allowPercentage && value.endsWith( "%" ) ) {
int percent = parsePercentage( value );
return (max * percent) / 100;
@@ -1299,7 +1370,9 @@ class UIDefaultsLoader
return integer;
}
private static Integer parseInteger( String value ) {
private static Integer parseInteger( String value )
throws NumberFormatException
{
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
@@ -1307,7 +1380,9 @@ class UIDefaultsLoader
}
}
private static Number parseIntegerOrFloat( String value ) {
private static Number parseIntegerOrFloat( String value )
throws NumberFormatException
{
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
@@ -1319,7 +1394,9 @@ class UIDefaultsLoader
}
}
private static Float parseFloat( String value ) {
private static Float parseFloat( String value )
throws NumberFormatException
{
try {
return Float.parseFloat( value );
} catch( NumberFormatException ex ) {
@@ -1327,35 +1404,45 @@ class UIDefaultsLoader
}
}
private static ActiveValue parseScaledInteger( String value ) {
private static ActiveValue parseScaledInteger( String value )
throws NumberFormatException
{
int val = parseInteger( value );
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledFloat( String value ) {
private static ActiveValue parseScaledFloat( String value )
throws NumberFormatException
{
float val = parseFloat( value );
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledInsets( String value ) {
private static ActiveValue parseScaledInsets( String value )
throws IllegalArgumentException
{
Insets insets = parseInsets( value );
return t -> {
return UIScale.scale( insets );
};
}
private static ActiveValue parseScaledDimension( String value ) {
private static ActiveValue parseScaledDimension( String value )
throws IllegalArgumentException
{
Dimension dimension = parseDimension( value );
return t -> {
return UIScale.scale( dimension );
};
}
private static Object parseGrayFilter( String value ) {
private static Object parseGrayFilter( String value )
throws IllegalArgumentException
{
List<String> numbers = StringUtils.split( value, ',', true, false );
try {
int brightness = Integer.parseInt( numbers.get( 0 ) );
@@ -1399,6 +1486,86 @@ class UIDefaultsLoader
return strs;
}
private static Object invokeConstructorOrStaticMethod( Executable[] constructorsOrMethods,
List<String> parts, Function<String, String> resolver )
throws Exception
{
// order constructors/methods by parameter types:
// - String parameters to the end
// - int before float parameters
constructorsOrMethods = constructorsOrMethods.clone();
Arrays.sort( constructorsOrMethods, (c1, c2) -> {
Class<?>[] ptypes1 = c1.getParameterTypes();
Class<?>[] ptypes2 = c2.getParameterTypes();
if( ptypes1.length != ptypes2.length )
return ptypes1.length - ptypes2.length;
for( int i = 0; i < ptypes1.length; i++ ) {
Class<?> pt1 = ptypes1[i];
Class<?> pt2 = ptypes2[i];
if( pt1 == pt2 )
continue;
// order methods with String parameters to the end
if( pt1 == String.class )
return 2;
if( pt2 == String.class )
return -2;
// order int before float
if( pt1 == int.class )
return -1;
if( pt2 == int.class )
return 1;
}
return 0;
} );
// search for best constructor/method for given parameter values
for( Executable cm : constructorsOrMethods ) {
if( cm.getParameterCount() != parts.size() - 1 )
continue;
Object[] params = parseMethodParams( cm.getParameterTypes(), parts, resolver );
if( params == null )
continue;
// invoke constructor or static method
if( cm instanceof Constructor )
return ((Constructor<?>)cm).newInstance( params );
else
return ((Method)cm).invoke( null, params );
}
return null;
}
private static Object[] parseMethodParams( Class<?>[] paramTypes, List<String> parts, Function<String, String> resolver ) {
Object[] params = new Object[paramTypes.length];
try {
for( int i = 0; i < params.length; i++ ) {
Class<?> paramType = paramTypes[i];
String paramValue = parts.get( i + 1 );
if( paramType == String.class )
params[i] = paramValue;
else if( paramType == boolean.class )
params[i] = parseBoolean( paramValue );
else if( paramType == int.class )
params[i] = parseInteger( paramValue );
else if( paramType == float.class )
params[i] = parseFloat( paramValue );
else if( paramType == Color.class )
params[i] = parseColorOrFunction( resolver.apply( paramValue ), resolver );
else
return null; // unsupported parameter type
}
} catch( IllegalArgumentException ex ) {
return null; // failed to parse parameter for expected parameter type
}
return params;
}
/**
* For use in LazyValue to get value for given key from UIManager and report error
* if not found. If key is prefixed by '?', then no error is reported.
@@ -1416,7 +1583,7 @@ class UIDefaultsLoader
return value;
}
private static void throwMissingParametersException( String value ) {
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
private static IllegalArgumentException newMissingParametersException( String value ) {
return new IllegalArgumentException( "missing parameters in function '" + value + "'" );
}
}

View File

@@ -53,8 +53,8 @@ public class FlatInternalFrameCloseIcon
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
float mx = width / 2;
float my = height / 2;
float mx = width / 2f;
float my = height / 2f;
float r = 3.25f;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD, 4 );

View File

@@ -94,8 +94,8 @@ public class FlatTabbedPaneCloseIcon
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
float mx = width / 2;
float my = height / 2;
float mx = width / 2f;
float my = height / 2f;
float r = ((bg != null) ? closeCrossFilledSize : closeCrossPlainSize) / 2;
// paint cross

View File

@@ -21,7 +21,6 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
@@ -30,6 +29,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* Base class for window icons.
*
* @uiDefault TitlePane.buttonSize Dimension
* @uiDefault TitlePane.buttonSymbolHeight int
* @uiDefault TitlePane.buttonHoverBackground Color
* @uiDefault TitlePane.buttonPressedBackground Color
*
@@ -38,17 +38,22 @@ import com.formdev.flatlaf.util.HiDPIUtils;
public abstract class FlatWindowAbstractIcon
extends FlatAbstractIcon
{
private final int symbolHeight;
private final Color hoverBackground;
private final Color pressedBackground;
public FlatWindowAbstractIcon() {
this( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
/** @since 3.2 */
protected FlatWindowAbstractIcon( String windowStyle ) {
this( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.buttonPressedBackground", windowStyle ) );
}
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
/** @since 3.2 */
protected FlatWindowAbstractIcon( Dimension size, int symbolHeight, Color hoverBackground, Color pressedBackground ) {
super( size.width, size.height, null );
this.symbolHeight = symbolHeight;
this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
}
@@ -80,4 +85,9 @@ public abstract class FlatWindowAbstractIcon
protected Color getForeground( Component c ) {
return c.getForeground();
}
/** @since 3.2 */
protected int getSymbolHeight() {
return symbolHeight;
}
}

View File

@@ -21,8 +21,8 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -38,18 +38,27 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatWindowCloseIcon
extends FlatWindowAbstractIcon
{
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
private final Color hoverForeground;
private final Color pressedForeground;
public FlatWindowCloseIcon() {
super( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.closeHoverBackground" ),
UIManager.getColor( "TitlePane.closePressedBackground" ) );
this( null );
}
/** @since 3.2 */
public FlatWindowCloseIcon( String windowStyle ) {
super( FlatUIUtils.getSubUIDimension( "TitlePane.buttonSize", windowStyle ),
FlatUIUtils.getSubUIInt( "TitlePane.buttonSymbolHeight", windowStyle, 10 ),
FlatUIUtils.getSubUIColor( "TitlePane.closeHoverBackground", windowStyle ),
FlatUIUtils.getSubUIColor( "TitlePane.closePressedBackground", windowStyle ) );
hoverForeground = FlatUIUtils.getSubUIColor( "TitlePane.closeHoverForeground", windowStyle );
pressedForeground = FlatUIUtils.getSubUIColor( "TitlePane.closePressedForeground", windowStyle );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int iwh = (int) (getSymbolHeight() * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int ix2 = ix + iwh - 1;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -362,6 +362,9 @@ public class FlatButtonUI
return ((FlatHelpButtonIcon)helpButtonIcon).applyStyleProperty( key, value );
}
if( "iconTextGap".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
if( borderShared == null )
borderShared = new AtomicBoolean( true );
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, b, borderShared );

View File

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

View File

@@ -24,6 +24,7 @@ import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -48,10 +49,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.CellRendererPane;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComboBox.KeySelectionManager;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
@@ -102,7 +105,6 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.maximumRowCount int
* @uiDefault ComboBox.buttonStyle String auto (default), button, mac or none
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
* @uiDefault ComboBox.focusedBackground Color optional
* @uiDefault ComboBox.disabledBackground Color
@@ -134,7 +136,6 @@ public class FlatComboBoxUI
@Styleable protected int editorColumns;
@Styleable protected String buttonStyle;
@Styleable protected String arrowType;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color editableBackground;
@@ -182,6 +183,9 @@ public class FlatComboBoxUI
private void installUIImpl( JComponent c ) {
super.installUI( c );
// install key selection manager that shows popup when Space key is pressed
comboBox.setKeySelectionManager( new FlatKeySelectionManager( comboBox.getKeySelectionManager() ) );
installStyle();
}
@@ -240,7 +244,6 @@ public class FlatComboBoxUI
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( "ComboBox.background" );
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
@@ -679,7 +682,7 @@ public class FlatComboBoxUI
return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background;
} else
return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground;
return disabledBackground;
}
protected Color getForeground( boolean enabled ) {
@@ -986,6 +989,29 @@ public class FlatComboBoxUI
}
}
// improve location of selected item in popup if list is large and scrollable
if( list.getHeight() == 0 ) {
// If popup is shown for the first time (or after a laf switch) and is scrollable,
// then BasicComboPopup scrolls the selected item to the top of the visible area.
// But for usability it would be better to have selected item somewhere
// in the middle of the visible area so that the user can see items above
// the selected item, which are usually more "important".
int selectedIndex = list.getSelectedIndex();
if( selectedIndex >= 1 ) {
int maximumRowCount = comboBox.getMaximumRowCount();
if( selectedIndex < maximumRowCount ) {
// selected item is in the first visible items --> scroll to top
list.scrollRectToVisible( new Rectangle() );
} else {
// scroll the selected item to the middle of the visible area
int firstVisibleIndex = Math.max( selectedIndex - (maximumRowCount / 2), 0 );
if( firstVisibleIndex > 0 )
list.ensureIndexIsVisible( firstVisibleIndex );
}
}
}
super.show( invoker, x, y );
}
@@ -1209,4 +1235,46 @@ public class FlatComboBoxUI
}
}
}
//---- class FlatKeySelectionManager --------------------------------------
/**
* Key selection manager that delegates to the default manager.
* Shows the popup if Space key is pressed and "typed characters" buffer is empty.
* If items contain spaces (e.g. "a b") it is still possible to select them
* by pressing keys a, Space and b.
*/
private class FlatKeySelectionManager
implements JComboBox.KeySelectionManager, UIResource
{
private final KeySelectionManager delegate;
private final long timeFactor;
private long lastTime;
FlatKeySelectionManager( JComboBox.KeySelectionManager delegate ) {
this.delegate = delegate;
Long value = (Long) UIManager.get( "ComboBox.timeFactor" );
timeFactor = (value != null) ? value : 1000;
}
@SuppressWarnings( "rawtypes" )
@Override
public int selectionForKey( char aKey, ComboBoxModel aModel ) {
long time = EventQueue.getMostRecentEventTime();
long oldLastTime = lastTime;
lastTime = time;
// SPACE key shows popup if not yet visible
if( aKey == ' ' &&
time - oldLastTime >= timeFactor &&
!comboBox.isPopupVisible() )
{
comboBox.setPopupVisible( true );
return -1;
}
return delegate.selectionForKey( aKey, aModel );
}
}
}

View File

@@ -31,6 +31,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.Caret;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -59,7 +60,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatEditorPaneUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault EditorPane.focusedBackground Color optional
*
* @author Karl Tauber
@@ -69,7 +69,6 @@ public class FlatEditorPaneUI
implements StyleableUI
{
@Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color disabledBackground;
@Styleable protected Color inactiveBackground;
@@ -101,7 +100,6 @@ public class FlatEditorPaneUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( prefix + ".background" );
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
@@ -148,6 +146,21 @@ public class FlatEditorPaneUI
focusListener = null;
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
installKeyboardActions( getComponent() );
}
static void installKeyboardActions( JTextComponent c ) {
FlatScrollPaneUI.installSmoothScrollingDelegateActions( c, false,
/* page-down */ DefaultEditorKit.pageDownAction, // PAGE_DOWN
/* page-up */ DefaultEditorKit.pageUpAction, // PAGE_UP
/* DefaultEditorKit.selectionPageDownAction */ "selection-page-down", // shift PAGE_DOWN
/* DefaultEditorKit.selectionPageUpAction */ "selection-page-up" // shift PAGE_UP
);
}
@Override
protected Caret createCaret() {
return new FlatCaret( null, false );
@@ -162,6 +175,11 @@ public class FlatEditorPaneUI
super.propertyChange( e );
propertyChange( getComponent(), e, this::installStyle );
// BasicEditorPaneUI.propertyChange() re-applied actions from editor kit,
// which removed our delegate actions
if( "editorKit".equals( propertyName ) )
installKeyboardActions( getComponent() );
}
static void propertyChange( JTextComponent c, PropertyChangeEvent e, Runnable installStyle ) {
@@ -252,11 +270,11 @@ public class FlatEditorPaneUI
@Override
protected void paintBackground( Graphics g ) {
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
paintBackground( g, getComponent(), focusedBackground );
}
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) );
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
g.setColor( FlatTextFieldUI.getBackground( c, focusedBackground ) );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
}

View File

@@ -53,6 +53,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatFileViewDirectoryIcon;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
@@ -346,12 +347,12 @@ public class FlatFileChooserUI
fileView.clearIconCache();
}
private boolean doNotUseSystemIcons() {
private static boolean doNotUseSystemIcons() {
// Java 17 32bit craches on Windows when using system icons
// fixed in Java 18+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
// fixed in Java 18+, fix backported in Java 17.0.3+ (see https://bugs.openjdk.java.net/browse/JDK-8277299)
return SystemInfo.isWindows &&
SystemInfo.isX86 &&
(SystemInfo.isJava_17_orLater && !SystemInfo.isJava_18_orLater);
(SystemInfo.isJava_17_orLater && SystemInfo.javaVersion < SystemInfo.toVersion( 17, 0, 3, 0 ));
}
//---- class FlatFileView -------------------------------------------------
@@ -526,6 +527,9 @@ public class FlatFileChooserUI
return icon;
}
if( doNotUseSystemIcons() )
return new FlatFileViewDirectoryIcon();
// Java 17+ supports getting larger system icons
try {
if( SystemInfo.isJava_17_orLater ) {

View File

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

View File

@@ -24,6 +24,7 @@ import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
@@ -179,7 +180,7 @@ public class FlatLabelUI
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
String lowerText = text.toLowerCase();
String lowerText = text.toLowerCase( Locale.ENGLISH );
int headIndex;
int styleIndex;
@@ -228,7 +229,7 @@ public class FlatLabelUI
int tagBegin = i + 1;
for( i += 2; i < textLength; i++ ) {
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
String tag = text.substring( tagBegin, i ).toLowerCase();
String tag = text.substring( tagBegin, i ).toLowerCase( Locale.ENGLISH );
if( tagsUseFontSizeSet.contains( tag ) )
return true;

View File

@@ -27,7 +27,6 @@ import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
@@ -149,7 +148,7 @@ public class FlatMenuBarUI
map = new ActionMapUIResource();
SwingUtilities.replaceUIActionMap( menuBar, map );
}
map.put( "takeFocus", new TakeFocus() );
map.put( "takeFocus", new TakeFocus( "takeFocus" ) );
}
/** @since 2 */
@@ -373,8 +372,12 @@ public class FlatMenuBarUI
* On other platforms, the popup of the first menu is shown.
*/
private static class TakeFocus
extends AbstractAction
extends FlatUIAction
{
public TakeFocus( String name ) {
super( name );
}
@Override
public void actionPerformed( ActionEvent e ) {
JMenuBar menuBar = (JMenuBar) e.getSource();

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.io.File;
import java.net.URL;
import java.security.CodeSource;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
@@ -32,6 +33,7 @@ import com.formdev.flatlaf.util.SystemInfo;
*/
class FlatNativeLibrary
{
private static boolean initialized;
private static NativeLibrary nativeLibrary;
/**
@@ -44,24 +46,41 @@ class FlatNativeLibrary
}
private static void initialize() {
if( nativeLibrary != null )
if( initialized )
return;
initialized = true;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_NATIVE_LIBRARY, true ) )
return;
String classifier;
String ext;
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
// Windows: requires Windows 10/11 (x86 or x86_64)
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64 || SystemInfo.isAARCH64) ) {
// Windows: requires Windows 10/11 (x86, x86_64 or aarch64)
if( SystemInfo.isAARCH64 )
classifier = "windows-arm64";
else if( SystemInfo.isX86_64 )
classifier = "windows-x86_64";
else
classifier = "windows-x86";
classifier = SystemInfo.isX86_64 ? "windows-x86_64" : "windows-x86";
ext = "dll";
// In Java 8, load jawt.dll (part of JRE) explicitly because it
// is not found when running application with <jdk>/bin/java.exe.
// When using <jdk>/jre/bin/java.exe, it is found.
// jawt.dll is located in <jdk>/jre/bin/.
// Java 9 and later do not have this problem,
// but load jawt.dll anyway to be on the safe side.
loadJAWT();
// Do not load jawt.dll (part of JRE) here explicitly because
// the FlatLaf native library flatlaf.dll may be loaded very early on Windows
// (e.g. from class com.formdev.flatlaf.util.SystemInfo) and before AWT is
// initialized (and awt.dll is loaded). Loading jawt.dll also loads awt.dll.
// In Java 8, loading jawt.dll before AWT is initialized may load
// a wrong version of awt.dll if a newer Java version (e.g. 19)
// is in PATH environment variable. Then Java 19 awt.dll and Java 8 awt.dll
// are loaded at same time and calling JAWT_GetAWT() crashes the application.
//
// To avoid this, flatlaf.dll is not linked to jawt.dll,
// which avoids loading jawt.dll when flatlaf.dll is loaded.
// Instead flatlaf.dll dynamically loads jawt.dll when first used,
// which is guaranteed after AWT initialization.
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
// Linux: requires x86_64
@@ -128,10 +147,15 @@ class FlatNativeLibrary
private static File findLibraryBesideJar( String classifier, String ext ) {
try {
// get location of FlatLaf jar
URL jarUrl = FlatNativeLibrary.class.getProtectionDomain().getCodeSource().getLocation();
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
URL jarUrl = (codeSource != null) ? codeSource.getLocation() : null;
if( jarUrl == null )
return null;
// if url is not a file, then we're running in a special environment (e.g. WebStart)
if( !"file".equals( jarUrl.getProtocol() ) )
return null;
File jarFile = new File( jarUrl.toURI() );
// if jarFile is a directory, then we're in a development environment

View File

@@ -66,7 +66,6 @@ import com.formdev.flatlaf.util.UIScale;
* <!-- FlatTextFieldUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault PasswordField.placeholderForeground Color
* @uiDefault PasswordField.focusedBackground Color optional
* @uiDefault PasswordField.iconTextGap int optional, default is 4

View File

@@ -391,7 +391,7 @@ public class FlatPopupFactory
//---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup
private static class NonFlashingPopup
extends Popup
{
private Popup delegate;

View File

@@ -193,27 +193,38 @@ public class FlatPopupMenuUI
@Override
public Popup getPopup( JPopupMenu popup, int x, int y ) {
Dimension popupSize = popup.getPreferredSize();
Rectangle screenBounds = getScreenBoundsAt( x, y );
// make sure that popup does not overlap any task/side bar
if( x + popupSize.width > screenBounds.x + screenBounds.width )
x = screenBounds.x + screenBounds.width - popupSize.width;
if( y + popupSize.height > screenBounds.y + screenBounds.height )
y = screenBounds.y + screenBounds.height - popupSize.height;
if( x < screenBounds.x )
x = screenBounds.x;
if( y < screenBounds.y )
y = screenBounds.y;
// do not add scroller to combobox popups or to popups that already have a scroll pane
if( popup instanceof BasicComboPopup ||
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
return super.getPopup( popup, x, y );
// do not add scroller if popup fits into screen
Dimension prefSize = popup.getPreferredSize();
int screenHeight = getScreenHeightAt( x, y );
if( prefSize.height <= screenHeight )
if( popupSize.height <= screenBounds.height )
return super.getPopup( popup, x, y );
// create scroller
FlatPopupScroller scroller = new FlatPopupScroller( popup );
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
scroller.setPreferredSize( new Dimension( popupSize.width, screenBounds.height ) );
// create popup
PopupFactory popupFactory = PopupFactory.getSharedInstance();
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
}
private int getScreenHeightAt( int x, int y ) {
private Rectangle getScreenBoundsAt( int x, int y ) {
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
GraphicsConfiguration gc = null;
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
@@ -234,7 +245,7 @@ public class FlatPopupMenuUI
Toolkit toolkit = Toolkit.getDefaultToolkit();
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
return screenBounds.height - screenInsets.top - screenInsets.bottom;
return FlatUIUtils.subtractInsets( screenBounds, screenInsets );
}
//---- class FlatPopupMenuLayout ------------------------------------------

View File

@@ -208,6 +208,9 @@ public class FlatRadioButtonUI
return ((FlatCheckBoxIcon)icon).applyStyleProperty( key, value );
}
if( "iconTextGap".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, b, key, value );
}

View File

@@ -23,6 +23,7 @@ import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.IllegalComponentStateException;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
@@ -366,6 +367,11 @@ public class FlatRootPaneUI
case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
rootPane.revalidate();
break;
case FlatClientProperties.WINDOW_STYLE:
if( rootPane.isDisplayable() )
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
break;
}
}

View File

@@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
@@ -39,10 +40,13 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -203,6 +207,16 @@ public class FlatScrollBarUI
oldStyleValues = null;
}
@Override
protected TrackListener createTrackListener() {
return new FlatTrackListener();
}
@Override
protected ScrollListener createScrollListener() {
return new FlatScrollListener();
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -431,6 +445,197 @@ public class FlatScrollBarUI
return allowsAbsolutePositioning;
}
@Override
protected void scrollByBlock( int direction ) {
runAndSetValueAnimated( () -> {
super.scrollByBlock( direction );
} );
}
@Override
protected void scrollByUnit( int direction ) {
runAndSetValueAnimated( () -> {
super.scrollByUnit( direction );
} );
}
/**
* Runs the given runnable, which should modify the scroll bar value,
* and then animate scroll bar value from old value to new value.
*/
public void runAndSetValueAnimated( Runnable r ) {
if( inRunAndSetValueAnimated || !isSmoothScrollingEnabled() ) {
r.run();
return;
}
inRunAndSetValueAnimated = true;
if( animator != null )
animator.cancel();
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// remember current scrollbar value so that we can start scroll animation from there
int oldValue = scrollbar.getValue();
// run given runnable, which computes and sets the new scrollbar value
FlatScrollPaneUI.runWithoutBlitting( scrollbar.getParent(), () ->{
// if invoked while animation is running, calculation of new value
// should start at the previous target value
if( targetValue != Integer.MIN_VALUE )
scrollbar.setValue( targetValue );
r.run();
} );
// do not use animation if started dragging thumb
if( isDragging ) {
// do not clear valueIsAdjusting here
inRunAndSetValueAnimated = false;
return;
}
int newValue = scrollbar.getValue();
if( newValue != oldValue ) {
// start scroll animation if value has changed
setValueAnimated( oldValue, newValue );
} else {
// clear valueIsAdjusting if value has not changed
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( false );
}
inRunAndSetValueAnimated = false;
}
private boolean inRunAndSetValueAnimated;
private Animator animator;
private int startValue = Integer.MIN_VALUE;
private int targetValue = Integer.MIN_VALUE;
private boolean useValueIsAdjusting = true;
int getTargetValue() {
return targetValue;
}
public void setValueAnimated( int initialValue, int value ) {
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// (always) set scrollbar value to initial value
scrollbar.setValue( initialValue );
// do some check if animation already running
if( animator != null && animator.isRunning() && targetValue != Integer.MIN_VALUE ) {
// Ignore requests if animation still running and scroll direction is the same
// and new value is within currently running animation.
// Without this check, repeating-scrolling via keyboard would become
// very slow when reaching the top/bottom/left/right of the viewport,
// because it would start a new 200ms animation to scroll a few pixels.
if( value == targetValue ||
(value > startValue && value < targetValue) || // scroll down/right
(value < startValue && value > targetValue) ) // scroll up/left
return;
}
startValue = initialValue;
targetValue = value;
// create animator
if( animator == null ) {
int duration = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.duration", 200 );
int resolution = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.resolution", 10 );
Object interpolator = UIManager.get( "ScrollPane.smoothScrolling.interpolator" );
animator = new Animator( duration, fraction -> {
if( scrollbar == null || !scrollbar.isShowing() ) {
animator.stop();
return;
}
// re-enable valueIsAdjusting if disabled while animation is running
// (e.g. in mouse released listener)
if( useValueIsAdjusting && !scrollbar.getValueIsAdjusting() )
scrollbar.setValueIsAdjusting( true );
scrollbar.setValue( startValue + Math.round( (targetValue - startValue) * fraction ) );
}, () -> {
startValue = targetValue = Integer.MIN_VALUE;
if( useValueIsAdjusting && scrollbar != null )
scrollbar.setValueIsAdjusting( false );
});
animator.setResolution( resolution );
animator.setInterpolator( (interpolator instanceof Animator.Interpolator)
? (Animator.Interpolator) interpolator
: new CubicBezierEasing( 0.5f, 0.5f, 0.5f, 1 ) );
}
// restart animator
animator.cancel();
animator.start();
}
protected boolean isSmoothScrollingEnabled() {
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( FlatSystemProperties.SMOOTH_SCROLLING, true ) )
return false;
// if scroll bar is child of scroll pane, check only client property of scroll pane
Container parent = scrollbar.getParent();
JComponent c = (parent instanceof JScrollPane) ? (JScrollPane) parent : scrollbar;
Object smoothScrolling = c.getClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING );
if( smoothScrolling instanceof Boolean )
return (Boolean) smoothScrolling;
// Note: Getting UI value "ScrollPane.smoothScrolling" here to allow
// applications to turn smooth scrolling on or off at any time
// (e.g. in application options dialog).
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
}
//---- class FlatTrackListener --------------------------------------------
protected class FlatTrackListener
extends TrackListener
{
@Override
public void mousePressed( MouseEvent e ) {
// Do not use valueIsAdjusting here (in runAndSetValueAnimated())
// for smooth scrolling because super.mousePressed() enables this itself
// and super.mouseRelease() disables it later.
// If we would disable valueIsAdjusting here (in runAndSetValueAnimated())
// and move the thumb with the mouse, then the thumb location is not updated
// if later scrolled with a key (e.g. HOME key).
useValueIsAdjusting = false;
runAndSetValueAnimated( () -> {
super.mousePressed( e );
} );
}
@Override
public void mouseReleased( MouseEvent e ) {
super.mouseReleased( e );
useValueIsAdjusting = true;
}
}
//---- class FlatScrollListener -------------------------------------------
protected class FlatScrollListener
extends ScrollListener
{
@Override
public void actionPerformed( ActionEvent e ) {
runAndSetValueAnimated( () -> {
super.actionPerformed( e );
} );
}
}
//---- class ScrollBarHoverListener ---------------------------------------
// using static field to disabling hover for other scroll bars

View File

@@ -17,10 +17,12 @@
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent;
@@ -31,6 +33,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
@@ -48,8 +52,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -135,18 +141,33 @@ public class FlatScrollPaneUI
MouseWheelListener superListener = super.createMouseWheelListener();
return e -> {
if( isSmoothScrollingEnabled() &&
scrollpane.isWheelScrollingEnabled() &&
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
scrollpane.isWheelScrollingEnabled() )
{
mouseWheelMovedSmooth( e );
if( e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
isPreciseWheelEvent( e ) )
{
// precise scrolling
mouseWheelMovedPrecise( e );
} else {
// smooth scrolling
JScrollBar scrollBar = findScrollBarToScroll( e );
if( scrollBar != null && scrollBar.getUI() instanceof FlatScrollBarUI ) {
FlatScrollBarUI ui = (FlatScrollBarUI) scrollBar.getUI();
ui.runAndSetValueAnimated( () -> {
superListener.mouseWheelMoved( e );
} );
} else
superListener.mouseWheelMoved( e );
}
} else
superListener.mouseWheelMoved( e );
};
}
protected boolean isSmoothScrollingEnabled() {
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( FlatSystemProperties.SMOOTH_SCROLLING, true ) )
return false;
Object smoothScrolling = scrollpane.getClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING );
if( smoothScrolling instanceof Boolean )
return (Boolean) smoothScrolling;
@@ -157,19 +178,40 @@ public class FlatScrollPaneUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
}
private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
private long lastPreciseWheelWhen;
private boolean isPreciseWheelEvent( MouseWheelEvent e ) {
double preciseWheelRotation = e.getPreciseWheelRotation();
if( preciseWheelRotation != 0 && preciseWheelRotation != e.getWheelRotation() ) {
// precise wheel event
lastPreciseWheelWhen = e.getWhen();
return true;
}
// If a non-precise wheel event occurs shortly after a precise wheel event,
// then it is probably still a precise wheel but the precise value
// is by chance an integer value (e.g. 1.0 or 2.0).
// Not handling this special case, would start an animation for smooth scrolling,
// which would be interrupted soon when the next precise wheel event occurs.
// This would result in jittery scrolling. E.g. on a MacBook using Trackpad or Magic Mouse.
if( e.getWhen() - lastPreciseWheelWhen < 1000 )
return true;
// non-precise wheel event
lastPreciseWheelWhen = 0;
return false;
}
private void mouseWheelMovedPrecise( MouseWheelEvent e ) {
// return if there is no viewport
JViewport viewport = scrollpane.getViewport();
if( viewport == null )
return;
// find scrollbar to scroll
JScrollBar scrollbar = scrollpane.getVerticalScrollBar();
if( scrollbar == null || !scrollbar.isVisible() || e.isShiftDown() ) {
scrollbar = scrollpane.getHorizontalScrollBar();
if( scrollbar == null || !scrollbar.isVisible() )
JScrollBar scrollbar = findScrollBarToScroll( e );
if( scrollbar == null )
return;
}
// consume event
e.consume();
@@ -262,6 +304,16 @@ public class FlatScrollPaneUI
*/
}
private JScrollBar findScrollBarToScroll( MouseWheelEvent e ) {
JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() || e.isShiftDown() ) {
scrollBar = scrollpane.getHorizontalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() )
return null;
}
return scrollBar;
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -360,8 +412,8 @@ public class FlatScrollPaneUI
protected void updateViewport( PropertyChangeEvent e ) {
super.updateViewport( e );
JViewport oldViewport = (JViewport) (e.getOldValue());
JViewport newViewport = (JViewport) (e.getNewValue());
JViewport oldViewport = (JViewport) e.getOldValue();
JViewport newViewport = (JViewport) e.getNewValue();
removeViewportListeners( oldViewport );
addViewportListeners( newViewport );
@@ -428,6 +480,119 @@ public class FlatScrollPaneUI
return false;
}
@Override
protected void syncScrollPaneWithViewport() {
// if the viewport has been scrolled by using JComponent.scrollRectToVisible()
// (e.g. by moving selection), then it is necessary to update the scroll bar values
if( isSmoothScrollingEnabled() ) {
runAndSyncScrollBarValueAnimated( scrollpane.getVerticalScrollBar(), 0, false, () -> {
runAndSyncScrollBarValueAnimated( scrollpane.getHorizontalScrollBar(), 1, false, () -> {
super.syncScrollPaneWithViewport();
} );
} );
} else
super.syncScrollPaneWithViewport();
}
/**
* Runs the given runnable, if smooth scrolling is enabled, with disabled
* viewport blitting mode and with scroll bar value set to "target" value.
* This is necessary when calculating new view position during animation.
* Otherwise calculation would use wrong view position and (repeating) scrolling
* would be much slower than without smooth scrolling.
*/
private void runWithScrollBarsTargetValues( boolean blittingOnly, Runnable r ) {
if( isSmoothScrollingEnabled() ) {
runWithoutBlitting( scrollpane, () -> {
if( blittingOnly )
r.run();
else {
runAndSyncScrollBarValueAnimated( scrollpane.getVerticalScrollBar(), 0, true, () -> {
runAndSyncScrollBarValueAnimated( scrollpane.getHorizontalScrollBar(), 1, true, r );
} );
}
} );
} else
r.run();
}
private void runAndSyncScrollBarValueAnimated( JScrollBar sb, int i, boolean useTargetValue, Runnable r ) {
if( inRunAndSyncValueAnimated[i] || sb == null || !(sb.getUI() instanceof FlatScrollBarUI) ) {
r.run();
return;
}
inRunAndSyncValueAnimated[i] = true;
int oldValue = sb.getValue();
int oldVisibleAmount = sb.getVisibleAmount();
int oldMinimum = sb.getMinimum();
int oldMaximum = sb.getMaximum();
FlatScrollBarUI ui = (FlatScrollBarUI) sb.getUI();
if( useTargetValue && ui.getTargetValue() != Integer.MIN_VALUE )
sb.setValue( ui.getTargetValue() );
r.run();
int newValue = sb.getValue();
if( newValue != oldValue &&
sb.getVisibleAmount() == oldVisibleAmount &&
sb.getMinimum() == oldMinimum &&
sb.getMaximum() == oldMaximum &&
sb.getUI() instanceof FlatScrollBarUI )
{
ui.setValueAnimated( oldValue, newValue );
}
inRunAndSyncValueAnimated[i] = false;
}
private final boolean[] inRunAndSyncValueAnimated = new boolean[2];
/**
* Runs the given runnable with disabled viewport blitting mode.
* If blitting mode is enabled, the viewport immediately repaints parts of the
* view if the view position is changed via JViewport.setViewPosition().
* This causes scrolling artifacts if smooth scrolling is enabled and the view position
* is "temporary" changed to its new target position, changed back to its old position
* and again moved animated to the target position.
*/
static void runWithoutBlitting( Container scrollPane, Runnable r ) {
// prevent the viewport to immediately repaint using blitting
JViewport viewport = (scrollPane instanceof JScrollPane) ? ((JScrollPane)scrollPane).getViewport() : null;
boolean isBlitScrollMode = (viewport != null) ? viewport.getScrollMode() == JViewport.BLIT_SCROLL_MODE : false;
if( isBlitScrollMode )
viewport.setScrollMode( JViewport.SIMPLE_SCROLL_MODE );
try {
r.run();
} finally {
if( isBlitScrollMode )
viewport.setScrollMode( JViewport.BLIT_SCROLL_MODE );
}
}
public static void installSmoothScrollingDelegateActions( JComponent c, boolean blittingOnly, String... actionKeys ) {
// get shared action map, used for all components of same type
ActionMap map = SwingUtilities.getUIActionMap( c );
if( map == null )
return;
// install actions, but only if not already installed
for( String actionKey : actionKeys )
installSmoothScrollingDelegateAction( map, blittingOnly, actionKey );
}
private static void installSmoothScrollingDelegateAction( ActionMap map, boolean blittingOnly, String actionKey ) {
Action oldAction = map.get( actionKey );
if( oldAction == null || oldAction instanceof SmoothScrollingDelegateAction )
return; // not found or already installed
map.put( actionKey, new SmoothScrollingDelegateAction( oldAction, blittingOnly ) );
}
//---- class Handler ------------------------------------------------------
/**
@@ -459,4 +624,34 @@ public class FlatScrollPaneUI
scrollpane.repaint();
}
}
//---- class SmoothScrollingDelegateAction --------------------------------
/**
* Used to run component actions with disabled blitting mode and
* with scroll bar target values.
*/
private static class SmoothScrollingDelegateAction
extends FlatUIAction
{
private final boolean blittingOnly;
private SmoothScrollingDelegateAction( Action delegate, boolean blittingOnly ) {
super( delegate );
this.blittingOnly = blittingOnly;
}
@Override
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
JScrollPane scrollPane = (source instanceof Component)
? (JScrollPane) SwingUtilities.getAncestorOfClass( JScrollPane.class, (Component) source )
: null;
if( scrollPane != null && scrollPane.getUI() instanceof FlatScrollPaneUI ) {
((FlatScrollPaneUI)scrollPane.getUI()).runWithScrollBarsTargetValues( blittingOnly,
() -> delegate.actionPerformed( e ) );
} else
delegate.actionPerformed( e );
}
}
}

View File

@@ -67,7 +67,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* @uiDefault Component.minimumWidth int
* @uiDefault Spinner.buttonStyle String button (default), mac or none
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Spinner.disabledBackground Color
* @uiDefault Spinner.disabledForeground Color
* @uiDefault Spinner.focusedBackground Color optional
@@ -92,7 +91,6 @@ public class FlatSpinnerUI
@Styleable protected int minimumWidth;
@Styleable protected String buttonStyle;
@Styleable protected String arrowType;
protected boolean isIntelliJTheme;
@Styleable protected Color disabledBackground;
@Styleable protected Color disabledForeground;
@Styleable protected Color focusedBackground;
@@ -129,7 +127,6 @@ public class FlatSpinnerUI
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
buttonStyle = UIManager.getString( "Spinner.buttonStyle" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
disabledForeground = UIManager.getColor( "Spinner.disabledForeground" );
focusedBackground = UIManager.getColor( "Spinner.focusedBackground" );
@@ -316,7 +313,7 @@ public class FlatSpinnerUI
return background;
} else
return isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground;
return disabledBackground;
}
protected Color getForeground( boolean enabled ) {

View File

@@ -87,7 +87,6 @@ public class FlatSplitPaneUI
@Styleable protected Color oneTouchHoverArrowColor;
@Styleable protected Color oneTouchPressedArrowColor;
private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
@@ -126,19 +125,9 @@ public class FlatSplitPaneUI
}
@Override
protected void installListeners() {
super.installListeners();
propertyChangeListener = FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle, null );
splitPane.addPropertyChangeListener( propertyChangeListener );
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
splitPane.removePropertyChangeListener( propertyChangeListener );
propertyChangeListener = null;
protected PropertyChangeListener createPropertyChangeListener() {
return FlatStylingSupport.createPropertyChangeListener( splitPane, this::installStyle,
super.createPropertyChangeListener() );
}
@Override

View File

@@ -100,15 +100,15 @@ public class FlatStylingSupport
/** @since 2 */
public interface StyleableUI {
Map<String, Class<?>> getStyleableInfos( JComponent c );
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key );
Map<String, Class<?>> getStyleableInfos( JComponent c ) throws IllegalArgumentException;
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key ) throws IllegalArgumentException;
}
/** @since 2 */
public interface StyleableBorder {
Object applyStyleProperty( String key, Object value );
Map<String, Class<?>> getStyleableInfos();
/** @since 2.5 */ Object getStyleableValue( String key );
Map<String, Class<?>> getStyleableInfos() throws IllegalArgumentException;
/** @since 2.5 */ Object getStyleableValue( String key ) throws IllegalArgumentException;
}
/** @since 2.5 */
@@ -135,7 +135,9 @@ public class FlatStylingSupport
return getStyle( c ) != null || getStyleClass( c ) != null;
}
public static Object getResolvedStyle( JComponent c, String type ) {
public static Object getResolvedStyle( JComponent c, String type )
throws IllegalArgumentException
{
Object style = getStyle( c );
Object styleClass = getStyleClass( c );
Object styleForClasses = getStyleForClasses( styleClass, type );
@@ -175,7 +177,9 @@ public class FlatStylingSupport
* @param type the type of the component
* @return the styles
*/
public static Object getStyleForClasses( Object styleClass, String type ) {
public static Object getStyleForClasses( Object styleClass, String type )
throws IllegalArgumentException
{
if( styleClass == null )
return null;
@@ -198,7 +202,9 @@ public class FlatStylingSupport
return null;
}
private static Object getStyleForClass( String styleClass, String type ) {
private static Object getStyleForClass( String styleClass, String type )
throws IllegalArgumentException
{
return joinStyles(
UIManager.get( "[style]." + styleClass ),
UIManager.get( "[style]" + type + '.' + styleClass ) );
@@ -218,7 +224,9 @@ public class FlatStylingSupport
* @return new joined style
*/
@SuppressWarnings( "unchecked" )
public static Object joinStyles( Object style1, Object style2 ) {
public static Object joinStyles( Object style1, Object style2 )
throws IllegalArgumentException
{
if( style1 == null )
return style2;
if( style2 == null )
@@ -278,6 +286,7 @@ public class FlatStylingSupport
* @throws IllegalArgumentException on syntax errors
* @throws ClassCastException if value type does not fit to expected type
*/
@SuppressWarnings( "ReturnValueIgnored" ) // Error Prone
public static Map<String, Object> parseAndApply( Map<String, Object> oldStyleValues,
Object style, BiFunction<String, Object, Object> applyProperty )
throws UnknownStyleException, IllegalArgumentException
@@ -379,7 +388,9 @@ public class FlatStylingSupport
return map;
}
private static Object parseValue( String key, String value ) {
private static Object parseValue( String key, String value )
throws IllegalArgumentException
{
// simple reference
if( value.startsWith( "$" ) )
return UIManager.get( value.substring( 1 ) );
@@ -474,7 +485,9 @@ public class FlatStylingSupport
}
}
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles ) {
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles )
throws IllegalArgumentException
{
checkValidField( f );
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
@@ -504,7 +517,9 @@ public class FlatStylingSupport
}
}
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles ) {
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles )
throws IllegalArgumentException
{
checkValidField( f );
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
@@ -529,7 +544,9 @@ public class FlatStylingSupport
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
}
private static void checkValidField( Field f ) {
private static void checkValidField( Field f )
throws IllegalArgumentException
{
if( !isValidField( f ) )
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
}
@@ -539,7 +556,9 @@ public class FlatStylingSupport
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
}
private static Field getStyleableField( StyleableField styleableField ) {
private static Field getStyleableField( StyleableField styleableField )
throws IllegalArgumentException
{
String fieldName = styleableField.fieldName();
if( fieldName.isEmpty() )
fieldName = styleableField.key();
@@ -647,6 +666,7 @@ public class FlatStylingSupport
static Object applyToAnnotatedObjectOrBorder( Object obj, String key, Object value,
JComponent c, AtomicBoolean borderShared )
throws IllegalArgumentException
{
try {
return applyToAnnotatedObject( obj, key, value );
@@ -695,7 +715,9 @@ public class FlatStylingSupport
};
}
static Border cloneBorder( Border border ) {
static Border cloneBorder( Border border )
throws IllegalArgumentException
{
Class<? extends Border> borderClass = border.getClass();
try {
return borderClass.getDeclaredConstructor().newInstance();
@@ -704,7 +726,9 @@ public class FlatStylingSupport
}
}
static Icon cloneIcon( Icon icon ) {
static Icon cloneIcon( Icon icon )
throws IllegalArgumentException
{
Class<? extends Icon> iconClass = icon.getClass();
try {
return iconClass.getDeclaredConstructor().newInstance();
@@ -717,11 +741,15 @@ public class FlatStylingSupport
* Returns a map of all fields annotated with {@link Styleable}.
* The key is the name of the field and the value the type of the field.
*/
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj ) {
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj )
throws IllegalArgumentException
{
return getAnnotatedStyleableInfos( obj, null );
}
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border ) {
public static Map<String, Class<?>> getAnnotatedStyleableInfos( Object obj, Border border )
throws IllegalArgumentException
{
Map<String, Class<?>> infos = new StyleableInfosMap<>();
collectAnnotatedStyleableInfos( obj, infos );
collectStyleableInfos( border, infos );
@@ -732,7 +760,9 @@ public class FlatStylingSupport
* Search for all fields annotated with {@link Styleable} and add them to the given map.
* The key is the name of the field and the value the type of the field.
*/
public static void collectAnnotatedStyleableInfos( Object obj, Map<String, Class<?>> infos ) {
public static void collectAnnotatedStyleableInfos( Object obj, Map<String, Class<?>> infos )
throws IllegalArgumentException
{
HashSet<String> processedFields = new HashSet<>();
Class<?> cls = obj.getClass();
@@ -810,7 +840,9 @@ public class FlatStylingSupport
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
}
public static Object getAnnotatedStyleableValue( Object obj, String key ) {
public static Object getAnnotatedStyleableValue( Object obj, String key )
throws IllegalArgumentException
{
String fieldName = keyToFieldName( key );
Class<?> cls = obj.getClass();
@@ -877,7 +909,9 @@ public class FlatStylingSupport
extends LinkedHashMap<K,V>
{
@Override
public V put( K key, V value ) {
public V put( K key, V value )
throws IllegalArgumentException
{
V oldValue = super.put( key, value );
if( oldValue != null )
throw new IllegalArgumentException( "duplicate key '" + key + "'" );

View File

@@ -49,6 +49,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
@@ -143,6 +144,11 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.tabHeight int
* @uiDefault TabbedPane.tabSelectionHeight int
* @uiDefault TabbedPane.cardTabSelectionHeight int
* @uiDefault TabbedPane.tabArc int
* @uiDefault TabbedPane.tabSelectionArc int
* @uiDefault TabbedPane.cardTabArc int
* @uiDefault TabbedPane.selectedInsets Insets
* @uiDefault TabbedPane.tabSelectionInsets Insets
* @uiDefault TabbedPane.contentSeparatorHeight int
* @uiDefault TabbedPane.showTabSeparators boolean
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
@@ -219,6 +225,11 @@ public class FlatTabbedPaneUI
@Styleable protected int tabHeight;
@Styleable protected int tabSelectionHeight;
/** @since 2 */ @Styleable protected int cardTabSelectionHeight;
/** @since 3.2 */ @Styleable protected int tabArc;
/** @since 3.2 */ @Styleable protected int tabSelectionArc;
/** @since 3.2 */ @Styleable protected int cardTabArc;
/** @since 3.2 */ @Styleable protected Insets selectedInsets;
/** @since 3.2 */ @Styleable protected Insets tabSelectionInsets;
@Styleable protected int contentSeparatorHeight;
@Styleable protected boolean showTabSeparators;
@Styleable protected boolean tabSeparatorsFullHeight;
@@ -344,6 +355,11 @@ public class FlatTabbedPaneUI
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
cardTabSelectionHeight = UIManager.getInt( "TabbedPane.cardTabSelectionHeight" );
tabArc = UIManager.getInt( "TabbedPane.tabArc" );
tabSelectionArc = UIManager.getInt( "TabbedPane.tabSelectionArc" );
cardTabArc = UIManager.getInt( "TabbedPane.cardTabArc" );
selectedInsets = UIManager.getInsets( "TabbedPane.selectedInsets" );
tabSelectionInsets = UIManager.getInsets( "TabbedPane.tabSelectionInsets" );
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
@@ -1184,9 +1200,40 @@ public class FlatTabbedPaneUI
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected )
{
boolean isCard = (getTabType() == TAB_TYPE_CARD);
// fill whole tab background if tab is rounded or has insets
if( (!isCard && tabArc > 0) ||
(isCard && cardTabArc > 0) ||
(!isCard && selectedInsets != null &&
(selectedInsets.top != 0 || selectedInsets.left != 0 ||
selectedInsets.bottom != 0 || selectedInsets.right != 0)) )
{
Color background = tabPane.getBackgroundAt( tabIndex );
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
g.fillRect( x, y, w, h );
}
// apply insets
if( !isCard && selectedInsets != null ) {
Insets insets = new Insets( 0, 0, 0, 0 );
rotateInsets( selectedInsets, insets, tabPane.getTabPlacement() );
x += scale( insets.left );
y += scale( insets.top );
w -= scale( insets.left + insets.right );
h -= scale( insets.top + insets.bottom );
}
// paint tab background
Color background = getTabBackground( tabPlacement, tabIndex, isSelected );
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
if( !isCard && tabArc > 0 ) {
float arc = scale( (float) tabArc ) / 2f;
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, w, h, null, arc, arc, arc, arc, 0 );
} else if( isCard && cardTabArc > 0 )
((Graphics2D)g).fill( createCardTabOuterPath( tabPlacement, x, y, w, h ) );
else
g.fillRect( x, y, w, h );
}
@@ -1241,42 +1288,38 @@ public class FlatTabbedPaneUI
protected void paintCardTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
Graphics2D g2 = (Graphics2D) g;
float borderWidth = scale( (float) contentSeparatorHeight );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( createCardTabOuterPath( tabPlacement, x, y, w, h ), false );
path.append( createCardTabInnerPath( tabPlacement, x, y, w, h ), false );
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
g2.fill( path );
}
/** @since 3.2 */
protected Shape createCardTabOuterPath( int tabPlacement, int x, int y, int w, int h ) {
float arc = scale( (float) cardTabArc ) / 2f;
switch( tabPlacement ) {
default:
case TOP:
case BOTTOM:
// paint left and right tab border
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
break;
case LEFT:
case RIGHT:
// paint top and bottom tab border
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
break;
case TOP: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, arc, arc, 0, 0 );
case BOTTOM: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, 0, 0, arc, arc );
case LEFT: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, arc, 0, arc, 0 );
case RIGHT: return FlatUIUtils.createRoundRectanglePath( x, y, w, h, 0, arc, 0, arc );
}
}
if( cardTabSelectionHeight <= 0 ) {
// if there is no tab selection indicator, paint a top border as well
/** @since 3.2 */
protected Shape createCardTabInnerPath( int tabPlacement, int x, int y, int w, int h ) {
float bw = scale( (float) contentSeparatorHeight );
float arc = (scale( (float) cardTabArc ) / 2f) - bw;
switch( tabPlacement ) {
default:
case TOP:
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
break;
case BOTTOM:
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
break;
case LEFT:
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
break;
case RIGHT:
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
break;
}
case TOP: return FlatUIUtils.createRoundRectanglePath( x + bw, y + bw, w - (bw * 2), h - bw, arc, arc, 0, 0 );
case BOTTOM: return FlatUIUtils.createRoundRectanglePath( x + bw, y, w - (bw * 2), h - bw, 0, 0, arc, arc );
case LEFT: return FlatUIUtils.createRoundRectanglePath( x + bw, y + bw, w - bw, h - (bw * 2), arc, 0, arc, 0 );
case RIGHT: return FlatUIUtils.createRoundRectanglePath( x, y + bw, w - bw, h - (bw * 2), 0, arc, 0, arc );
}
}
@@ -1324,38 +1367,62 @@ public class FlatTabbedPaneUI
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
: disabledUnderlineColor );
// paint underline selection
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
boolean isCard = (getTabType() == TAB_TYPE_CARD);
boolean atBottom = !isCard;
Insets contentInsets = atBottom
? ((!rotateTabRuns && runCount > 1 && !isScrollTabLayout() && getRunForTab( tabPane.getTabCount(), tabIndex ) > 0)
? new Insets( 0, 0, 0, 0 )
: getContentBorderInsets( tabPlacement ))
: null;
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
int sx, sy;
int tabSelectionHeight = scale( isCard ? cardTabSelectionHeight : this.tabSelectionHeight );
float arc = scale( (float) (isCard ? cardTabArc : tabSelectionArc) ) / 2f;
int sx = x, sy = y, sw = w, sh = h;
switch( tabPlacement ) {
case TOP:
default:
sy = atBottom ? (y + h + contentInsets.top - tabSelectionHeight) : y;
g.fillRect( x, sy, w, tabSelectionHeight );
sh = tabSelectionHeight;
break;
case BOTTOM:
sy = atBottom ? (y - contentInsets.bottom) : (y + h - tabSelectionHeight);
g.fillRect( x, sy, w, tabSelectionHeight );
sh = tabSelectionHeight;
break;
case LEFT:
sx = atBottom ? (x + w + contentInsets.left - tabSelectionHeight) : x;
g.fillRect( sx, y, tabSelectionHeight, h );
sw = tabSelectionHeight;
break;
case RIGHT:
sx = atBottom ? (x - contentInsets.right) : (x + w - tabSelectionHeight);
g.fillRect( sx, y, tabSelectionHeight, h );
sw = tabSelectionHeight;
break;
}
// apply insets
if( !isCard && tabSelectionInsets != null ) {
Insets insets = new Insets( 0, 0, 0, 0 );
rotateInsets( tabSelectionInsets, insets, tabPane.getTabPlacement() );
sx += scale( insets.left );
sy += scale( insets.top );
sw -= scale( insets.left + insets.right );
sh -= scale( insets.top + insets.bottom );
}
// paint underline selection
if( arc <= 0 )
g.fillRect( sx, sy, sw, sh );
else {
if( isCard ) {
Area area = new Area( createCardTabOuterPath( tabPlacement, x, y, w, h ) );
area.intersect( new Area( new Rectangle2D.Float( sx, sy, sw, sh ) ) );
((Graphics2D)g).fill( area );
} else
FlatUIUtils.paintSelection( (Graphics2D) g, sx, sy, sw, sh, null, arc, arc, arc, arc, 0 );
}
}
/** @since 2.2 */
@@ -1967,7 +2034,7 @@ public class FlatTabbedPaneUI
//---- class TabCloseButton -----------------------------------------------
private class TabCloseButton
private static class TabCloseButton
extends JButton
implements UIResource
{
@@ -1977,7 +2044,7 @@ public class FlatTabbedPaneUI
//---- class ContainerUIResource ------------------------------------------
private class ContainerUIResource
private static class ContainerUIResource
extends JPanel
implements UIResource
{
@@ -2382,7 +2449,7 @@ public class FlatTabbedPaneUI
if( isPreciseWheel &&
getScrollButtonsPlacement() == BOTH &&
getScrollButtonsPolicy() == AS_NEEDED_SINGLE &&
(isLeftToRight() || !horizontal) || // scroll buttons are hidden in right-to-left
(isLeftToRight() || !horizontal) && // scroll buttons are hidden in right-to-left
scrollBackwardButtonPrefSize != null )
{
// special cases for scrolling with touchpad or high-resolution wheel:
@@ -3051,7 +3118,7 @@ public class FlatTabbedPaneUI
break;
case CENTER:
shiftTabs( 0, (diff) / 2 );
shiftTabs( 0, diff / 2 );
topHeight += diff / 2;
bottomHeight += diff - (diff / 2);
break;
@@ -3453,10 +3520,8 @@ public class FlatTabbedPaneUI
//---- class RunWithOriginalLayoutManagerDelegateAction -------------------
private static class RunWithOriginalLayoutManagerDelegateAction
implements Action
extends FlatUIAction
{
private final Action delegate;
static void install( ActionMap map, String key ) {
Action oldAction = map.get( key );
if( oldAction == null || oldAction instanceof RunWithOriginalLayoutManagerDelegateAction )
@@ -3466,24 +3531,9 @@ public class FlatTabbedPaneUI
}
private RunWithOriginalLayoutManagerDelegateAction( Action delegate ) {
this.delegate = delegate;
super( 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();

View File

@@ -277,6 +277,9 @@ public class FlatTableUI
/** @since 2 */
protected Object applyStyleProperty( String key, Object value ) {
if( "rowHeight".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
}

View File

@@ -54,7 +54,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatTextAreaUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextArea.disabledBackground Color used if not enabled
* @uiDefault TextArea.inactiveBackground Color used if not editable
* @uiDefault TextArea.focusedBackground Color optional
@@ -66,7 +65,6 @@ public class FlatTextAreaUI
implements StyleableUI
{
@Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color disabledBackground;
@Styleable protected Color inactiveBackground;
@@ -103,7 +101,6 @@ public class FlatTextAreaUI
super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( "TextArea.background" );
disabledBackground = UIManager.getColor( "TextArea.disabledBackground" );
inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" );
@@ -144,6 +141,12 @@ public class FlatTextAreaUI
focusListener = null;
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatEditorPaneUI.installKeyboardActions( getComponent() );
}
@Override
protected Caret createCaret() {
return new FlatCaret( null, false );
@@ -227,6 +230,6 @@ public class FlatTextAreaUI
@Override
protected void paintBackground( Graphics g ) {
FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
FlatEditorPaneUI.paintBackground( g, getComponent(), focusedBackground );
}
}

View File

@@ -81,7 +81,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatTextFieldUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextField.placeholderForeground Color
* @uiDefault TextField.focusedBackground Color optional
* @uiDefault TextField.iconTextGap int optional, default is 4
@@ -95,7 +94,6 @@ public class FlatTextFieldUI
implements StyleableUI
{
@Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color disabledBackground;
@Styleable protected Color inactiveBackground;
@@ -165,7 +163,6 @@ public class FlatTextFieldUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( prefix + ".background" );
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
@@ -402,7 +399,7 @@ public class FlatTextFieldUI
@Override
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
paintBackground( g, getComponent(), focusedBackground );
paintPlaceholder( g );
if( hasLeadingIcon() || hasTrailingIcon() )
@@ -422,7 +419,7 @@ debug*/
// background is painted elsewhere
}
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
static void paintBackground( Graphics g, JTextComponent c, Color focusedBackground ) {
// do not paint background if:
// - not opaque and
// - border is not a flat border and
@@ -443,14 +440,14 @@ debug*/
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( getBackground( c, isIntelliJTheme, focusedBackground ) );
g2.setColor( getBackground( c, focusedBackground ) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
} finally {
g2.dispose();
}
}
static Color getBackground( JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) {
static Color getBackground( JTextComponent c, Color focusedBackground ) {
Color background = c.getBackground();
// always use explicitly set color
@@ -461,10 +458,6 @@ debug*/
if( focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) )
return focusedBackground;
// for compatibility with IntelliJ themes
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) )
return FlatUIUtils.getParentBackground( c );
return background;
}

View File

@@ -56,7 +56,6 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatTextPaneUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextPane.focusedBackground Color optional
*
* @author Karl Tauber
@@ -66,7 +65,6 @@ public class FlatTextPaneUI
implements StyleableUI
{
@Styleable protected int minimumWidth;
protected boolean isIntelliJTheme;
private Color background;
@Styleable protected Color disabledBackground;
@Styleable protected Color inactiveBackground;
@@ -98,7 +96,6 @@ public class FlatTextPaneUI
String prefix = getPropertyPrefix();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( prefix + ".background" );
disabledBackground = UIManager.getColor( prefix + ".disabledBackground" );
inactiveBackground = UIManager.getColor( prefix + ".inactiveBackground" );
@@ -145,6 +142,12 @@ public class FlatTextPaneUI
focusListener = null;
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatEditorPaneUI.installKeyboardActions( getComponent() );
}
@Override
protected Caret createCaret() {
return new FlatCaret( null, false );
@@ -159,6 +162,11 @@ public class FlatTextPaneUI
super.propertyChange( e );
FlatEditorPaneUI.propertyChange( getComponent(), e, this::installStyle );
// BasicEditorPaneUI.propertyChange() re-applied actions from editor kit,
// which removed our delegate actions
if( "editorKit".equals( propertyName ) )
FlatEditorPaneUI.installKeyboardActions( getComponent() );
}
/** @since 2 */
@@ -220,6 +228,6 @@ public class FlatTextPaneUI
@Override
protected void paintBackground( Graphics g ) {
FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground );
FlatEditorPaneUI.paintBackground( g, getComponent(), focusedBackground );
}
}

View File

@@ -57,6 +57,7 @@ import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
@@ -96,6 +97,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarTitleMinimumGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
@@ -109,29 +111,30 @@ public class FlatTitlePane
{
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
/** @since 2.5 */ protected final Font titleFont = UIManager.getFont( "TitlePane.font" );
protected final Color activeBackground = UIManager.getColor( "TitlePane.background" );
protected final Color inactiveBackground = UIManager.getColor( "TitlePane.inactiveBackground" );
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
/** @since 2.5 */ protected final Font titleFont;
protected final Color activeBackground;
protected final Color inactiveBackground;
protected final Color activeForeground;
protected final Color inactiveForeground;
protected final Color embeddedForeground;
protected final Color borderColor;
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
/** @since 2.5 */ protected final boolean showIconInDialogs = FlatUIUtils.getUIBoolean( "TitlePane.showIconInDialogs", true );
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
/** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 60 );
/** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 30 );
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 );
/** @since 2.4 */ protected final boolean showIconBesideTitle = UIManager.getBoolean( "TitlePane.showIconBesideTitle" );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 );
/** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 );
/** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 );
/** @since 2 */ protected final boolean showIcon;
/** @since 2.5 */ protected final boolean showIconInDialogs;
/** @since 2 */ protected final int noIconLeftGap;
protected final Dimension iconSize;
/** @since 2.4 */ protected final int titleMinimumWidth;
/** @since 2.4 */ protected final int buttonMinimumWidth;
protected final int buttonMaximizedHeight;
protected final boolean centerTitle;
protected final boolean centerTitleIfMenuBarEmbedded;
/** @since 2.4 */ protected final boolean showIconBesideTitle;
protected final int menuBarTitleGap;
/** @since 2.4 */ protected final int menuBarTitleMinimumGap;
/** @since 2.4 */ protected final int menuBarResizeHeight;
protected final JRootPane rootPane;
protected final String windowStyle;
protected JPanel leftPanel;
protected JLabel iconLabel;
@@ -150,6 +153,34 @@ public class FlatTitlePane
public FlatTitlePane( JRootPane rootPane ) {
this.rootPane = rootPane;
Window w = SwingUtilities.getWindowAncestor( rootPane );
String defaultWindowStyle = (w != null && w.getType() == Window.Type.UTILITY) ? WINDOW_STYLE_SMALL : null;
windowStyle = clientProperty( rootPane, WINDOW_STYLE, defaultWindowStyle, String.class );
titleFont = FlatUIUtils.getSubUIFont( "TitlePane.font", windowStyle );
activeBackground = FlatUIUtils.getSubUIColor( "TitlePane.background", windowStyle );
inactiveBackground = FlatUIUtils.getSubUIColor( "TitlePane.inactiveBackground", windowStyle );
activeForeground = FlatUIUtils.getSubUIColor( "TitlePane.foreground", windowStyle );
inactiveForeground = FlatUIUtils.getSubUIColor( "TitlePane.inactiveForeground", windowStyle );
embeddedForeground = FlatUIUtils.getSubUIColor( "TitlePane.embeddedForeground", windowStyle );
// not using windowStyle here because TitlePane.borderColor is also used in FlatRootPaneUI
borderColor = UIManager.getColor( "TitlePane.borderColor" );
showIcon = FlatUIUtils.getSubUIBoolean( "TitlePane.showIcon", windowStyle, true );
showIconInDialogs = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconInDialogs", windowStyle, true );
noIconLeftGap = FlatUIUtils.getSubUIInt( "TitlePane.noIconLeftGap", windowStyle, 8 );
iconSize = FlatUIUtils.getSubUIDimension( "TitlePane.iconSize", windowStyle );
titleMinimumWidth = FlatUIUtils.getSubUIInt( "TitlePane.titleMinimumWidth", windowStyle, 60 );
buttonMinimumWidth = FlatUIUtils.getSubUIInt( "TitlePane.buttonMinimumWidth", windowStyle, 30 );
buttonMaximizedHeight = FlatUIUtils.getSubUIInt( "TitlePane.buttonMaximizedHeight", windowStyle, 0 );
centerTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.centerTitle", windowStyle, false );
centerTitleIfMenuBarEmbedded = FlatUIUtils.getSubUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", windowStyle, true );
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
menuBarResizeHeight = FlatUIUtils.getSubUIInt( "TitlePane.menuBarResizeHeight", windowStyle, 4 );
handler = createHandler();
setBorder( createTitlePaneBorder() );
@@ -182,8 +213,8 @@ public class FlatTitlePane
setUI( new FlatTitleLabelUI() );
}
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
iconLabel.setBorder( new FlatEmptyBorder( FlatUIUtils.getSubUIInsets( "TitlePane.iconMargins", windowStyle ) ) );
titleLabel.setBorder( new FlatEmptyBorder( FlatUIUtils.getSubUIInsets( "TitlePane.titleMargins", windowStyle ) ) );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -310,7 +341,7 @@ public class FlatTitlePane
}
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
JButton button = new JButton( UIManager.getIcon( iconKey ) ) {
JButton button = new JButton( FlatUIUtils.getSubUIIcon( iconKey, windowStyle ) ) {
@Override
public Dimension getMinimumSize() {
// allow the button to shrink if space is rare
@@ -682,7 +713,7 @@ public class FlatTitlePane
// Seems to be a bug in sun.awt.X11.XNETProtocol.requestState(),
// which does some strange state XOR-ing...
if( (oldState & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_VERT )
newState = oldState & ~Frame.MAXIMIZED_BOTH | Frame.MAXIMIZED_HORIZ;
newState = (oldState & ~Frame.MAXIMIZED_BOTH) | Frame.MAXIMIZED_HORIZ;
}
frame.setExtendedState( newState );
@@ -952,6 +983,13 @@ public class FlatTitlePane
}
}
// allow internal frames in layered pane to be moved/resized when placed over title bar
for( Component c : rootPane.getLayeredPane().getComponents() ) {
r = (c instanceof JInternalFrame) ? getNativeHitTestSpot( (JInternalFrame) c ) : null;
if( r != null )
hitTestSpots.add( r );
}
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
Rectangle closeButtonBounds = boundsInWindow( closeButton );

View File

@@ -39,10 +39,12 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JToolBar;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.RootPaneContainer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -156,6 +158,13 @@ public class FlatToolBarUI
}
}
@Override
protected RootPaneContainer createFloatingWindow( JToolBar toolbar ) {
RootPaneContainer floatingWindow = super.createFloatingWindow( toolbar );
floatingWindow.getRootPane().putClientProperty( FlatClientProperties.WINDOW_STYLE, FlatClientProperties.WINDOW_STYLE_SMALL );
return floatingWindow;
}
@Override
protected ContainerListener createToolBarContListener() {
return new ToolBarContListener() {

View File

@@ -238,6 +238,22 @@ public class FlatTreeUI
oldStyleValues = null;
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatScrollPaneUI.installSmoothScrollingDelegateActions( tree, false,
"scrollDownChangeSelection", // PAGE_DOWN
"scrollUpChangeSelection", // PAGE_UP
"scrollDownChangeLead", // ctrl PAGE_DOWN
"scrollUpChangeLead", // ctrl PAGE_UP
"scrollDownExtendSelection", // shift PAGE_DOWN, shift ctrl PAGE_DOWN
"scrollUpExtendSelection", // shift PAGE_UP, shift ctrl PAGE_UP
"selectNextChangeLead", // ctrl DOWN
"selectPreviousChangeLead" // ctrl UP
);
}
@Override
protected void updateRenderer() {
super.updateRenderer();
@@ -420,6 +436,9 @@ public class FlatTreeUI
/** @since 2 */
protected Object applyStyleProperty( String key, Object value ) {
if( "rowHeight".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, tree, key, value );
}
@@ -575,7 +594,7 @@ public class FlatTreeUI
if( isSelected && isWideSelection() ) {
Color oldColor = g.getColor();
g.setColor( selectionInactiveBackground );
paintWideSelection( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );
paintWideSelection( g, bounds, row );
g.setColor( oldColor );
}
return;
@@ -633,7 +652,7 @@ public class FlatTreeUI
if( isWideSelection() ) {
// wide selection
paintWideSelection( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );
paintWideSelection( g, bounds, row );
} else {
// non-wide selection
paintCellBackground( g, rendererComponent, bounds, row, true );
@@ -702,9 +721,7 @@ public class FlatTreeUI
return oldColor;
}
private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
{
private void paintWideSelection( Graphics g, Rectangle bounds, int row ) {
float arcTop, arcBottom;
arcTop = arcBottom = UIScale.scale( selectionArc / 2f );

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.beans.PropertyChangeListener;
import javax.swing.Action;
/**
* Base class for UI actions used in ActionMap.
* (similar to class sun.swing.UIAction)
*
* @author Karl Tauber
* @since 3.3
*/
public abstract class FlatUIAction
implements Action
{
protected final String name;
protected final Action delegate;
protected FlatUIAction( String name ) {
this.name = name;
this.delegate = null;
}
protected FlatUIAction( Action delegate ) {
this.name = null;
this.delegate = delegate;
}
@Override
public Object getValue( String key ) {
if( key == NAME && delegate == null )
return name;
return (delegate != null) ? delegate.getValue( key ) : null;
}
@Override
public boolean isEnabled() {
return (delegate != null) ? delegate.isEnabled() : true;
}
// do nothing in following methods because this class is immutable
@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 ) {}
}

View File

@@ -46,6 +46,7 @@ import java.util.IdentityHashMap;
import java.util.WeakHashMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
@@ -166,6 +167,88 @@ public class FlatUIUtils
return defaultValue;
}
/** @since 3.2 */
public static Color getSubUIColor( String key, String subKey ) {
if( subKey != null ) {
Color value = UIManager.getColor( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getColor( key );
}
/** @since 3.2 */
public static boolean getSubUIBoolean( String key, String subKey, boolean defaultValue ) {
if( subKey != null ) {
Object value = UIManager.get( buildSubKey( key, subKey ) );
if( value instanceof Boolean )
return (Boolean) value;
}
return getUIBoolean( key, defaultValue );
}
/** @since 3.2 */
public static int getSubUIInt( String key, String subKey, int defaultValue ) {
if( subKey != null ) {
Object value = UIManager.get( buildSubKey( key, subKey ) );
if( value instanceof Integer )
return (Integer) value;
}
return getUIInt( key, defaultValue );
}
/** @since 3.2 */
public static Insets getSubUIInsets( String key, String subKey ) {
if( subKey != null ) {
Insets value = UIManager.getInsets( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getInsets( key );
}
/** @since 3.2 */
public static Dimension getSubUIDimension( String key, String subKey ) {
if( subKey != null ) {
Dimension value = UIManager.getDimension( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getDimension( key );
}
/** @since 3.2 */
public static Icon getSubUIIcon( String key, String subKey ) {
if( subKey != null ) {
Icon value = UIManager.getIcon( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getIcon( key );
}
/** @since 3.2 */
public static Font getSubUIFont( String key, String subKey ) {
if( subKey != null ) {
Font value = UIManager.getFont( buildSubKey( key, subKey ) );
if( value != null )
return value;
}
return UIManager.getFont( key );
}
/**
* Inserts {@code subKey} at last dot in {@code key}.
* <p>
* E.g. {@code buildSubKey( "TitlePane.font", "small" )} returns {@code "TitlePane.small.font"}.
*/
private static String buildSubKey( String key, String subKey ) {
int dot = key.lastIndexOf( '.' );
return (dot >= 0)
? key.substring( 0, dot ) + '.' + subKey + '.' + key.substring( dot + 1 )
: key;
}
/** @since 1.1.2 */
public static boolean getBoolean( JComponent c, String systemPropertyKey,
String clientPropertyKey, String uiKey, boolean defaultValue )
@@ -725,7 +808,7 @@ public class FlatUIUtils
{
dotSize = UIScale.scale( dotSize );
gap = UIScale.scale( gap );
int gripSize = (dotSize * dotCount) + ((gap * (dotCount - 1)));
int gripSize = (dotSize * dotCount) + (gap * (dotCount - 1));
// calculate grip position
float gx;

View File

@@ -88,10 +88,6 @@ class FlatWindowsNativeWindowBorder
if( !SystemInfo.isWindows_10_orLater )
return null;
// requires x86 architecture
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
return null;
// check whether native library was successfully loaded
if( !FlatNativeLibrary.isLoaded() )
return null;

View File

@@ -28,7 +28,6 @@ import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
@@ -368,12 +367,12 @@ public class Graphics2DProxy
}
@Override
public void setRenderingHint( Key hintKey, Object hintValue ) {
public void setRenderingHint( RenderingHints.Key hintKey, Object hintValue ) {
delegate.setRenderingHint( hintKey, hintValue );
}
@Override
public Object getRenderingHint( Key hintKey ) {
public Object getRenderingHint( RenderingHints.Key hintKey ) {
return delegate.getRenderingHint( hintKey );
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/**
/*
* @author Karl Tauber
*/
module com.formdev.flatlaf {

View File

@@ -671,7 +671,12 @@ SplitPaneDivider.gripGap = 2
TabbedPane.tabHeight = 32
TabbedPane.tabSelectionHeight = 3
TabbedPane.cardTabSelectionHeight = 2
TabbedPane.cardTabSelectionHeight = 3
TabbedPane.tabArc = 0
TabbedPane.tabSelectionArc = 0
TabbedPane.cardTabArc = 12
TabbedPane.selectedInsets = 0,0,0,0
TabbedPane.tabSelectionInsets = 0,0,0,0
TabbedPane.contentSeparatorHeight = 1
TabbedPane.showTabSeparators = false
TabbedPane.tabSeparatorsFullHeight = false
@@ -808,6 +813,7 @@ TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22
TitlePane.buttonSymbolHeight = 10
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.showIconBesideTitle = false
@@ -829,6 +835,16 @@ TitlePane.closePressedBackground = fade($TitlePane.closeHoverBackground,90%)
TitlePane.closeHoverForeground = #fff
TitlePane.closePressedForeground = #fff
# window style "small"
TitlePane.small.font = -1
TitlePane.small.showIcon = false
TitlePane.small.buttonSize = 30,20
TitlePane.small.buttonSymbolHeight = 8
TitlePane.small.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon, small
TitlePane.small.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon, small
TitlePane.small.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon, small
TitlePane.small.restoreIcon = com.formdev.flatlaf.icons.FlatWindowRestoreIcon, small
#---- ToggleButton ----

View File

@@ -112,6 +112,9 @@ ToggleButton.endBackground = $ToggleButton.background
@ijMenuCheckBackgroundL20 = lighten(@selectionBackground,20%,derived noAutoInverse)
@ijMenuCheckBackgroundD10 = darken(@selectionBackground,10%,derived noAutoInverse)
@ijTextBackgroundL3 = lighten(Panel.background,3%,lazy)
@ijTextBackgroundL4 = lighten(Panel.background,4%,lazy)
[Arc_Theme]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
@@ -135,53 +138,110 @@ ToggleButton.endBackground = $ToggleButton.background
[Arc_Theme_Dark]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark]ProgressBar.selectionForeground = #ddd
[Arc_Theme_Dark]ToolBar.separatorColor = lazy(Separator.foreground)
[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
[Arc_Theme_Dark_-_Orange]ToolBar.separatorColor = lazy(Separator.foreground)
[Carbon]Table.selectionBackground = lazy(List.selectionBackground)
[Carbon]Table.selectionInactiveForeground = lazy(List.selectionInactiveForeground)
[Carbon]TextField.background = @ijTextBackgroundL4
[Cobalt_2]Component.accentColor = lazy(Component.focusColor)
[Cobalt_2]CheckBox.icon.background = #002946
[Cobalt_2]CheckBox.icon.checkmarkColor = #002946
[Cobalt_2]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Cobalt_2]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Cobalt_2]ComboBox.background = @ijTextBackgroundL3
[Cobalt_2]ComboBox.buttonBackground = @ijTextBackgroundL3
[Cobalt_2]TextField.background = @ijTextBackgroundL3
[Cobalt_2]Table.background = lazy(List.background)
[Cobalt_2]Tree.background = lazy(List.background)
[Cyan_light]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[Cyan_light]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[Dark_Flat_Theme]*.inactiveForeground = #808080
[Dark_Flat_Theme]Component.accentColor = lazy(List.selectionBackground)
[Dark_Flat_Theme]TableHeader.background = #3B3B3B
[Dark_Flat_Theme]TextPane.foreground = lazy(TextField.foreground)
[Dark_Flat_Theme]CheckBoxMenuItem.selectionForeground = lazy(MenuItem.selectionForeground)
[Dark_Flat_Theme]List.selectionForeground = lazy(Tree.selectionForeground)
[Dark_Flat_Theme]RadioButtonMenuItem.selectionForeground = lazy(MenuItem.selectionForeground)
[Dark_Flat_Theme]Separator.foreground = lazy(ToolBar.separatorColor)
[Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived)
[Dracula---Zihan_Ma]Component.accentColor = lazy(Component.focusColor)
[Dracula---Zihan_Ma]ComboBox.selectionBackground = lazy(List.selectionBackground)
[Dracula---Zihan_Ma]ProgressBar.selectionBackground = #fff
[Dracula---Zihan_Ma]ProgressBar.selectionForeground = #fff
[Gradianto_Dark_Fuchsia]*.selectionBackground = #8452a7
[Gradianto_Dark_Fuchsia]*.selectionInactiveBackground = #562C6A
[Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gradianto_Dark_Fuchsia]TextField.background = @ijTextBackgroundL4
[Gradianto_Dark_Fuchsia]Tree.background = lazy(List.background)
[Gradianto_Dark_Fuchsia]Separator.foreground = lazy(ScrollBar.track)
[Gradianto_Dark_Fuchsia]ToolBar.separatorColor = lazy(ScrollBar.track)
[Gradianto_Dark_Fuchsia]ProgressBar.background = lazy(ScrollBar.track)
[Gradianto_Dark_Fuchsia]Slider.trackColor = lazy(ScrollBar.track)
[Gradianto_Deep_Ocean]TextField.background = @ijTextBackgroundL3
[Gradianto_Deep_Ocean]Tree.background = lazy(List.background)
[Gradianto_Midnight_Blue]ScrollBar.thumb = #533B6B
[Gradianto_Midnight_Blue]Table.selectionForeground = lazy(List.selectionForeground)
[Gradianto_Midnight_Blue]TextField.background = @ijTextBackgroundL4
[Gradianto_Midnight_Blue]Tree.background = lazy(List.background)
[Gradianto_Nature_Green]Table.selectionForeground = lazy(List.selectionForeground)
[Gradianto_Nature_Green]TextField.background = @ijTextBackgroundL4
[Gray]Separator.foreground = lazy(Slider.trackColor)
[Gray]ToolBar.separatorColor = lazy(Slider.trackColor)
[Gruvbox_Dark_Hard]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Hard]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Hard]ComboBox.background = @ijTextBackgroundL3
[Gruvbox_Dark_Hard]ComboBox.buttonBackground = @ijTextBackgroundL3
[Gruvbox_Dark_Hard]TextField.background = @ijTextBackgroundL3
[Gruvbox_Dark_Medium]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Medium]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Medium]ComboBox.background = @ijTextBackgroundL3
[Gruvbox_Dark_Medium]ComboBox.buttonBackground = @ijTextBackgroundL3
[Gruvbox_Dark_Medium]TextField.background = @ijTextBackgroundL3
[Gruvbox_Dark_Soft]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Soft]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Soft]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Soft]ComboBox.background = @ijTextBackgroundL3
[Gruvbox_Dark_Soft]ComboBox.buttonBackground = @ijTextBackgroundL3
[Gruvbox_Dark_Soft]TextField.background = @ijTextBackgroundL3
[Hiberbee_Dark]*.disabledForeground = #7F7E7D
[Hiberbee_Dark]*.disabledText = #7F7E7D
[Hiberbee_Dark]*.inactiveForeground = #7F7E7D
[Hiberbee_Dark]ProgressBar.background = lazy(Separator.foreground)
[Hiberbee_Dark]Slider.trackColor = lazy(Separator.foreground)
[Hiberbee_Dark]TabbedPane.focusColor = #5A5A5A
[Hiberbee_Dark]TabbedPane.selectedBackground = #434241
[Hiberbee_Dark]TabbedPane.selectedForeground = #70D7FF
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Hiberbee_Dark]Table.selectionInactiveBackground = lazy(List.selectionInactiveBackground)
[Hiberbee_Dark]Tree.selectionBackground = lazy(List.selectionBackground)
[Hiberbee_Dark]Tree.selectionInactiveBackground = lazy(List.selectionInactiveBackground)
[High_contrast]Component.accentColor = lazy(Component.focusColor)
[High_contrast]ToggleButton.selectedBackground = #fff
@@ -194,9 +254,20 @@ ToggleButton.endBackground = $ToggleButton.background
toolbar.selectedBackground: #fff
[High_contrast][style]ToggleButton.inTextField = $[High_contrast][style]Button.inTextField
[Light_Flat]*.disabledForeground = #8C8C8C
[Light_Flat]*.inactiveForeground = #8C8C8C
[Light_Flat]CheckBox.icon[filled].background = #fff
[Light_Flat]CheckBox.icon[filled].checkmarkColor = #fff
[Light_Flat]Component.accentColor = lazy(TabbedPane.underlineColor)
[Light_Flat]ComboBox.background = lazy(ComboBox.editableBackground)
[Light_Flat]ComboBox.buttonBackground = lazy(ComboBox.editableBackground)
[Light_Flat]Separator.foreground = lazy(ToolBar.separatorColor)
[Light_Flat]TableHeader.background = #E5E5E9
[Light_Flat]TextPane.foreground = lazy(TextField.foreground)
[Light_Flat]CheckBoxMenuItem.selectionForeground = lazy(MenuItem.selectionForeground)
[Light_Flat]RadioButtonMenuItem.selectionForeground = lazy(MenuItem.selectionForeground)
[Monocai]Button.default.foreground = #2D2A2F
[Monocai]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Monocai]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
@Monocai.acceleratorForeground = lazy(MenuItem.disabledForeground)
@@ -209,22 +280,67 @@ ToggleButton.endBackground = $ToggleButton.background
[Monocai]MenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monocai]TextField.background = @ijTextBackgroundL4
@Monocai.selectionBackground = lazy(TextField.selectionBackground)
[Monocai]ComboBox.selectionBackground = @Monocai.selectionBackground
[Monocai]List.selectionBackground = @Monocai.selectionBackground
[Monocai]Table.selectionBackground = @Monocai.selectionBackground
[Monocai]Tree.selectionBackground = @Monocai.selectionBackground
@Monocai.selectionInactiveBackground = lazy(MenuItem.selectionBackground)
[Monocai]List.selectionInactiveBackground = @Monocai.selectionInactiveBackground
[Monocai]Table.selectionInactiveBackground = @Monocai.selectionInactiveBackground
[Monocai]Tree.selectionInactiveBackground = @Monocai.selectionInactiveBackground
[Monokai_Pro]TitledBorder.titleColor = @foreground
[Monokai_Pro---Subtheme]Table.selectionInactiveForeground = lazy(List.selectionInactiveForeground)
[Monokai_Pro---Subtheme]Tree.selectionBackground = lazy(List.selectionBackground)
[Monokai_Pro---Subtheme]Separator.foreground = lazy(Slider.trackColor)
[Monokai_Pro---Subtheme]ToolBar.separatorColor = lazy(Slider.trackColor)
[Nord]*.inactiveForeground = #616E88
[Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Nord]List.selectionBackground = lazy(Tree.selectionBackground)
[Nord]List.selectionForeground = lazy(Tree.selectionForeground)
[Nord]Table.selectionBackground = lazy(Tree.selectionBackground)
[Nord]Table.selectionForeground = lazy(Tree.selectionForeground)
[Nord]TextField.selectionBackground = lazy(Tree.selectionBackground)
[Nord]TextField.selectionForeground = lazy(Tree.selectionForeground)
[Nord]Tree.selectionInactiveForeground = lazy(List.selectionInactiveForeground)
[NotReallyMDTheme]*.selectionInactiveBackground = #21384E
[NotReallyMDTheme]ToolBar.separatorColor = lazy(Separator.foreground)
[One_Dark]List.selectionInactiveForeground = lazy(Tree.selectionInactiveForeground)
[One_Dark]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[One_Dark]ProgressBar.background = lazy(Separator.foreground)
[One_Dark]Slider.trackColor = lazy(Separator.foreground)
[One_Dark]Slider.focusedColor = fade(#568af2,40%)
[One_Dark]Table.background = lazy(Tree.background)
[One_Dark]Table.selectionBackground = lazy(Tree.selectionBackground)
[One_Dark]TextField.selectionBackground = lazy(List.selectionBackground)
[One_Dark]Tree.selectionForeground = lazy(List.selectionForeground)
[Solarized_Dark---4lex4]*.inactiveForeground = #657B83
[Solarized_Dark---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[Solarized_Dark---4lex4]ComboBox.background = lazy(ComboBox.editableBackground)
[Solarized_Dark---4lex4]ComboBox.buttonBackground = lazy(ComboBox.editableBackground)
[Solarized_Dark---4lex4]Slider.focusedColor = fade($Component.focusColor,80%,derived)
[Solarized_Dark---4lex4]ToolBar.separatorColor = lazy(Separator.foreground)
[Solarized_Light---4lex4]*.inactiveForeground = #839496
[Solarized_Light---4lex4]Button.default.hoverBackground = darken($Button.default.background,3%,derived)
[Solarized_Light---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[Spacegray]ComboBox.background = @ijTextBackgroundL4
[Spacegray]ComboBox.buttonBackground = @ijTextBackgroundL4
[Spacegray]TextField.background = @ijTextBackgroundL4
[Spacegray]TextField.selectionBackground = lazy(Tree.selectionBackground)
[Spacegray]TextField.selectionForeground = lazy(Tree.selectionForeground)
[vuesion-theme]*.disabledForeground = #8C8C8C
[vuesion-theme]*.disabledText = #8C8C8C
[vuesion-theme]*.inactiveForeground = #8C8C8C
[vuesion-theme]Component.accentColor = lazy(Button.default.endBackground)
[vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
@@ -232,6 +348,12 @@ ToggleButton.endBackground = $ToggleButton.background
[vuesion-theme]Slider.trackColor = #303a45
[vuesion-theme]Slider.thumbColor = #ececee
[vuesion-theme]Slider.focusedColor = fade(#ececee,20%)
[vuesion-theme]ComboBox.background = @ijTextBackgroundL4
[vuesion-theme]ComboBox.buttonBackground = @ijTextBackgroundL4
[vuesion-theme]TextField.background = @ijTextBackgroundL4
[vuesion-theme]TextField.selectionBackground = lighten(#303A45,15%)
[Xcode-Dark]TextField.background = @ijTextBackgroundL4
# Material Theme UI Lite
@@ -241,62 +363,106 @@ ToggleButton.endBackground = $ToggleButton.background
[dark][author-Mallowigi]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[dark][author-Mallowigi]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[author-Mallowigi]Tree.selectionInactiveBackground = lazy(List.selectionInactiveBackground)
[Arc_Dark]ComboBox.selectionBackground = lazy(List.selectionBackground)
[Arc_Dark]Table.selectionBackground = lazy(List.selectionBackground)
[Atom_One_Dark]Separator.foreground = lazy(Slider.trackColor)
[Atom_One_Dark]ToolBar.separatorColor = lazy(Slider.trackColor)
[Atom_One_Light]List.selectionBackground = lazy(Table.selectionBackground)
[Atom_One_Light]Tree.selectionBackground = lazy(Table.selectionBackground)
[Atom_One_Light]TabbedPane.contentAreaColor = lazy(Separator.foreground)
[Dracula---Mallowigi]*.selectionBackground = #44475A
[Dracula---Mallowigi]List.selectionInactiveForeground = lazy(Tree.selectionInactiveForeground)
[Dracula---Mallowigi]ProgressBar.selectionBackground = #fff
[Dracula---Mallowigi]ProgressBar.selectionForeground = #fff
[Dracula_Contrast]ProgressBar.selectionBackground = #fff
[Dracula_Contrast]ProgressBar.selectionForeground = #fff
[Dracula---Mallowigi]RadioButtonMenuItem.selectionForeground = lazy(CheckBoxMenuItem.selectionForeground)
[Dracula---Mallowigi]Table.selectionForeground = lazy(List.selectionForeground)
[Dracula---Mallowigi]Separator.foreground = lazy(Slider.trackColor)
[Dracula---Mallowigi]ToolBar.separatorColor = lazy(Slider.trackColor)
[GitHub]ProgressBar.selectionBackground = #222
[GitHub]ProgressBar.selectionForeground = #222
[GitHub]TextField.background = @ijTextBackgroundL3
[GitHub]List.selectionBackground = lazy(Table.selectionBackground)
[GitHub]Tree.selectionBackground = lazy(Table.selectionBackground)
[GitHub_Contrast]ProgressBar.selectionBackground = #222
[GitHub_Contrast]ProgressBar.selectionForeground = #222
[GitHub_Dark]ComboBox.selectionBackground = lazy(Tree.selectionBackground)
[GitHub_Dark]Table.selectionBackground = lazy(Tree.selectionBackground)
[GitHub_Dark]Separator.foreground = lazy(Slider.trackColor)
[GitHub_Dark]ToolBar.separatorColor = lazy(Slider.trackColor)
[Light_Owl]CheckBoxMenuItem.selectionForeground = lazy(CheckBoxMenuItem.foreground)
[Light_Owl]ComboBox.selectionForeground = lazy(ComboBox.foreground)
[Light_Owl]List.selectionInactiveForeground = lazy(List.foreground)
[Light_Owl]Menu.selectionForeground = lazy(Menu.foreground)
[Light_Owl]MenuBar.selectionForeground = lazy(MenuBar.foreground)
[Light_Owl]MenuItem.selectionForeground = lazy(MenuItem.foreground)
[Light_Owl]ProgressBar.selectionBackground = #111
[Light_Owl]ProgressBar.selectionForeground = #fff
[Light_Owl]TabbedPane.selectedForeground = lazy(TabbedPane.foreground)
[Light_Owl]Spinner.selectionForeground = lazy(Spinner.foreground)
[Light_Owl]Table.selectionForeground = lazy(Table.foreground)
[Light_Owl]TextField.selectionForeground = lazy(TextField.foreground)
[Light_Owl]TextField.background = @ijTextBackgroundL3
[Light_Owl]List.selectionBackground = lazy(Table.selectionBackground)
[Light_Owl]Tree.selectionBackground = lazy(Table.selectionBackground)
[Light_Owl_Contrast]List.selectionInactiveForeground = lazy(List.foreground)
[Light_Owl_Contrast]ProgressBar.selectionBackground = #111
[Light_Owl_Contrast]ProgressBar.selectionForeground = #fff
[Light_Owl_Contrast]TabbedPane.selectedForeground = lazy(TabbedPane.foreground)
[Light_Owl_Contrast]Table.selectionForeground = lazy(Table.foreground)
[Material_Darker]*.selectionBackground = lighten(#2D2D2D,15%)
[Material_Darker]Separator.foreground = lazy(Slider.trackColor)
[Material_Darker]ToolBar.separatorColor = lazy(Slider.trackColor)
[Material_Deep_Ocean]*.selectionBackground = lighten(#222533,15%)
[Material_Deep_Ocean]Separator.foreground = lazy(Slider.trackColor)
[Material_Deep_Ocean]ToolBar.separatorColor = lazy(Slider.trackColor)
[Material_Lighter]List.selectionInactiveForeground = lazy(Tree.selectionInactiveForeground)
[Material_Lighter]ProgressBar.selectionBackground = #222
[Material_Lighter]ProgressBar.selectionForeground = #fff
[Material_Lighter_Contrast]ProgressBar.selectionBackground = #222
[Material_Lighter_Contrast]ProgressBar.selectionForeground = #fff
[Material_Lighter]ComboBox.selectionBackground = lazy(List.selectionBackground)
[Material_Lighter]Table.selectionBackground = lazy(List.selectionBackground)
[Material_Lighter]List.selectionForeground = lazy(Table.selectionForeground)
[Material_Lighter]RadioButtonMenuItem.selectionForeground = lazy(Table.selectionForeground)
[Material_Lighter]Tree.selectionForeground = lazy(Table.selectionForeground)
[Material_Oceanic]ProgressBar.selectionBackground = #ddd
[Material_Oceanic]ProgressBar.selectionForeground = #ddd
[Material_Oceanic_Contrast]ProgressBar.selectionBackground = #ddd
[Material_Oceanic_Contrast]ProgressBar.selectionForeground = #ddd
[Material_Oceanic]Separator.foreground = lazy(Slider.trackColor)
[Material_Oceanic]ToolBar.separatorColor = lazy(Slider.trackColor)
[Material_Palenight]ProgressBar.selectionBackground = #ddd
[Material_Palenight]ProgressBar.selectionForeground = #ddd
[Material_Palenight]List.selectionBackground = lazy(Table.selectionBackground)
[Material_Palenight]Tree.selectionBackground = lazy(Table.selectionBackground)
[Material_Palenight]Separator.foreground = lazy(Slider.trackColor)
[Material_Palenight]ToolBar.separatorColor = lazy(Slider.trackColor)
[Material_Palenight_Contrast]ProgressBar.selectionBackground = #ddd
[Material_Palenight_Contrast]ProgressBar.selectionForeground = #ddd
[Monokai_Pro---Mallowigi]List.selectionForeground = lazy(Table.selectionForeground)
[Monokai_Pro---Mallowigi]RadioButtonMenuItem.selectionForeground = lazy(Table.selectionForeground)
[Monokai_Pro---Mallowigi]Table.selectionInactiveForeground = lazy(List.selectionInactiveForeground)
[Monokai_Pro---Mallowigi]Tree.selectionForeground = lazy(Table.selectionForeground)
[Monokai_Pro---Mallowigi]Tree.selectionInactiveForeground = lazy(List.selectionInactiveForeground)
[Monokai_Pro---Mallowigi]Separator.foreground = lazy(Slider.trackColor)
[Monokai_Pro---Mallowigi]ToolBar.separatorColor = lazy(Slider.trackColor)
[Moonlight]ComboBox.selectionBackground = lazy(List.selectionBackground)
[Moonlight]Table.selectionBackground = lazy(List.selectionBackground)
[Moonlight]Separator.foreground = lazy(Slider.trackColor)
[Moonlight]ToolBar.separatorColor = lazy(Slider.trackColor)
[Night_Owl]ProgressBar.selectionBackground = #ddd
[Night_Owl]ProgressBar.selectionForeground = #ddd
[Night_Owl_Contrast]ProgressBar.selectionBackground = #ddd
[Night_Owl_Contrast]ProgressBar.selectionForeground = #ddd
[Solarized_Dark---Mallowigi]ProgressBar.selectionBackground = #ccc
[Solarized_Dark---Mallowigi]ProgressBar.selectionForeground = #ccc
[Solarized_Dark_Contrast]ProgressBar.selectionBackground = #ccc
[Solarized_Dark_Contrast]ProgressBar.selectionForeground = #ccc
[Solarized_Dark---Mallowigi]Separator.foreground = lazy(Slider.trackColor)
[Solarized_Dark---Mallowigi]ToolBar.separatorColor = lazy(Slider.trackColor)
[Solarized_Light---Mallowigi]ProgressBar.selectionBackground = #222
[Solarized_Light---Mallowigi]ProgressBar.selectionForeground = #fff
[Solarized_Light_Contrast]ProgressBar.selectionBackground = #222
[Solarized_Light_Contrast]ProgressBar.selectionForeground = #fff
[Solarized_Light---Mallowigi]ComboBox.selectionBackground = lazy(List.selectionBackground)
[Solarized_Light---Mallowigi]Table.selectionBackground = lazy(List.selectionBackground)
[Solarized_Light---Mallowigi]Separator.foreground = lazy(Slider.trackColor)
[Solarized_Light---Mallowigi]ToolBar.separatorColor = lazy(Slider.trackColor)

View File

@@ -12,42 +12,6 @@
# limitations under the License.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Look &In:
FileChooser.saveInLabelText = Save In:
FileChooser.fileNameLabel.textAndMnemonic = File &Name:
FileChooser.folderNameLabel.textAndMnemonic = Folder &name:
FileChooser.filesOfTypeLabel.textAndMnemonic = Files of &Type:
# toolbar
FileChooser.upFolderToolTipText = Up One Level
FileChooser.upFolderAccessibleName = Up
FileChooser.homeFolderToolTipText = Home
FileChooser.homeFolderAccessibleName = Home
FileChooser.newFolderToolTipText = Create New Folder
FileChooser.newFolderAccessibleName = New Folder
FileChooser.listViewButtonToolTipText = List
FileChooser.listViewButtonAccessibleName = List
FileChooser.detailsViewButtonToolTipText = Details
FileChooser.detailsViewButtonAccessibleName = Details
# details table header
FileChooser.fileNameHeaderText = Name
FileChooser.fileSizeHeaderText = Size
FileChooser.fileTypeHeaderText = Type
FileChooser.fileDateHeaderText = Modified
FileChooser.fileAttrHeaderText = Attributes
# popup menu
FileChooser.viewMenuLabelText = View
FileChooser.refreshActionLabelText = Refresh
FileChooser.newFolderActionLabelText = New Folder
FileChooser.listViewActionLabelText = List
FileChooser.detailsViewActionLabelText = Details
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Collapse Left Pane

View File

@@ -12,42 +12,6 @@
# limitations under the License.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Suchen &in:
FileChooser.saveInLabelText = Speichern in:
FileChooser.fileNameLabel.textAndMnemonic = &Dateiname:
FileChooser.folderNameLabel.textAndMnemonic = Ordner&name:
FileChooser.filesOfTypeLabel.textAndMnemonic = Datei&typ:
# toolbar
FileChooser.upFolderToolTipText = Eine Ebene h\u00F6her
FileChooser.upFolderAccessibleName = Nach oben
FileChooser.homeFolderToolTipText = Home
FileChooser.homeFolderAccessibleName = Home
FileChooser.newFolderToolTipText = Neuen Ordner erstellen
FileChooser.newFolderAccessibleName = Neuer Ordner
FileChooser.listViewButtonToolTipText = Liste
FileChooser.listViewButtonAccessibleName = Liste
FileChooser.detailsViewButtonToolTipText = Details
FileChooser.detailsViewButtonAccessibleName = Details
# details table header
FileChooser.fileNameHeaderText = Name
FileChooser.fileSizeHeaderText = Gr\u00F6\u00DFe
FileChooser.fileTypeHeaderText = Typ
FileChooser.fileDateHeaderText = \u00C4nderungsdatum
FileChooser.fileAttrHeaderText = Attribute
# popup menu
FileChooser.viewMenuLabelText = Ansicht
FileChooser.refreshActionLabelText = Aktualisieren
FileChooser.newFolderActionLabelText = Neuer Ordner
FileChooser.listViewActionLabelText = Liste
FileChooser.detailsViewActionLabelText = Details
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText = Verdeckte Tabs anzeigen

View File

@@ -12,42 +12,6 @@
# limitations under the License.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Buscar &en:
FileChooser.saveInLabelText = Guardar en:
FileChooser.fileNameLabel.textAndMnemonic = &Nombre de fichero:
FileChooser.folderNameLabel.textAndMnemonic = &Nombre de carpeta:
FileChooser.filesOfTypeLabel.textAndMnemonic = Ficheros de &Tipo:
# toolbar
FileChooser.upFolderToolTipText = Subir un nivel
FileChooser.upFolderAccessibleName = Subir
FileChooser.homeFolderToolTipText = Inicio
FileChooser.homeFolderAccessibleName = Inicio
FileChooser.newFolderToolTipText = Crear nueva carpeta
FileChooser.newFolderAccessibleName = Nueva carpeta
FileChooser.listViewButtonToolTipText = Lista
FileChooser.listViewButtonAccessibleName = Lista
FileChooser.detailsViewButtonToolTipText = Detalles
FileChooser.detailsViewButtonAccessibleName = Detalles
# details table header
FileChooser.fileNameHeaderText = Nombre
FileChooser.fileSizeHeaderText = Tama\u00F1o
FileChooser.fileTypeHeaderText = Tipo
FileChooser.fileDateHeaderText = Modificado
FileChooser.fileAttrHeaderText = Atributos
# popup menu
FileChooser.viewMenuLabelText = Ver
FileChooser.refreshActionLabelText = Refrescar
FileChooser.newFolderActionLabelText = Nueva carpeta
FileChooser.listViewActionLabelText = Lista
FileChooser.detailsViewActionLabelText = Detalles
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Contraer Panel Izquierdo

View File

@@ -12,37 +12,3 @@
# limitations under the License.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Rechercher &dans:
FileChooser.saveInLabelText = Enregistrer dans:
FileChooser.fileNameLabel.textAndMnemonic = &Nom du fichier:
FileChooser.folderNameLabel.textAndMnemonic = &Nom du dossier:
FileChooser.filesOfTypeLabel.textAndMnemonic = &Type de fichier:
# toolbar
FileChooser.upFolderToolTipText = Remonte d'un niveau
FileChooser.upFolderAccessibleName = Monter
FileChooser.homeFolderToolTipText = R\u00E9pertoire de base
FileChooser.homeFolderAccessibleName = R\u00E9pertoire de base
FileChooser.newFolderToolTipText = Cr\u00E9e un dossier
FileChooser.newFolderAccessibleName = Nouveau dossier
FileChooser.listViewButtonToolTipText = Liste
FileChooser.listViewButtonAccessibleName = Liste
FileChooser.detailsViewButtonToolTipText = D\u00E9tails
FileChooser.detailsViewButtonAccessibleName = D\u00E9tails
# details table header
FileChooser.fileNameHeaderText = Nom
FileChooser.fileSizeHeaderText = Taille
FileChooser.fileTypeHeaderText = Type
FileChooser.fileDateHeaderText = Modifi\u00E9
FileChooser.fileAttrHeaderText = Attributs
# popup menu
FileChooser.viewMenuLabelText = Affichage
FileChooser.refreshActionLabelText = Actualiser
FileChooser.newFolderActionLabelText = Nouveau dossier
FileChooser.listViewActionLabelText = Liste
FileChooser.detailsViewActionLabelText = D\u00E9tails

View File

@@ -67,11 +67,11 @@
@nsControlAccentColor = #007aff
# accent colors are:
# @nsSelectedTextBackgroundColor
# @nsSelectedContentBackgroundColor
# @nsSelectedControlColor
# @nsKeyboardFocusIndicatorColor
# @nsControlAccentColor
# @nsSelectedTextBackgroundColor for @textSelectionBackground
# @nsSelectedContentBackgroundColor for @selectionBackground
# @nsSelectedControlColor unused
# @nsKeyboardFocusIndicatorColor for @accentFocusColor
# @nsControlAccentColor for @accentColor
#---- variables ----
@@ -88,12 +88,12 @@
@menuBackground = lighten(@background,8%)
# selection
@selectionBackground = if(systemColor(accent), shade(spin(systemColor(accent),4),20%), @nsSelectedContentBackgroundColor)
@selectionBackground = shade(spin(if(systemColor(accent), systemColor(accent), @accentColor),4),20%)
@selectionForeground = @nsSelectedMenuItemTextColor
@selectionInactiveBackground = @nsUnemphasizedSelectedContentBackgroundColor
# text selection
@textSelectionBackground = systemColor(highlight,if(systemColor(accent), darken(desaturate(systemColor(accent),60%),10%), @nsSelectedTextBackgroundColor))
@textSelectionBackground = systemColor(highlight,darken(desaturate(if(systemColor(accent), systemColor(accent), @accentColor),60%),10%))
@textSelectionForeground = @nsSelectedTextColor
# menu
@@ -102,7 +102,7 @@
# accent colors (blueish)
@accentColor = systemColor(accent,@nsControlAccentColor)
@accentFocusColor = if(systemColor(accent), fade(spin(systemColor(accent),-10),50%), @nsKeyboardFocusIndicatorColor)
@accentFocusColor = fade(lighten(spin(if(systemColor(accent), systemColor(accent), @accentColor),-8),5%),50%)
#---- Button ----
@@ -261,6 +261,13 @@ Spinner.buttonPressedArrowColor = lighten($Spinner.buttonArrowColor,20%,derived
Spinner.buttonSeparatorWidth = 0
#---- TabbedPane ----
TabbedPane.tabArc = $Button.arc
TabbedPane.tabSelectionArc = 999
TabbedPane.cardTabArc = $Button.arc
#---- TextArea ---
TextArea.disabledBackground = @disabledComponentBackground

View File

@@ -67,11 +67,11 @@
@nsControlAccentColor = #007aff
# accent colors are:
# @nsSelectedTextBackgroundColor
# @nsSelectedContentBackgroundColor
# @nsSelectedControlColor
# @nsKeyboardFocusIndicatorColor
# @nsControlAccentColor
# @nsSelectedTextBackgroundColor for @textSelectionBackground
# @nsSelectedContentBackgroundColor for @selectionBackground
# @nsSelectedControlColor unused
# @nsKeyboardFocusIndicatorColor for @accentFocusColor
# @nsControlAccentColor for @accentColor
#---- variables ----
@@ -88,12 +88,12 @@
@menuBackground = darken(@background,4%)
# selection
@selectionBackground = if(systemColor(accent), shade(spin(systemColor(accent),4),10%), @nsSelectedContentBackgroundColor)
@selectionBackground = shade(spin(if(systemColor(accent), systemColor(accent), @accentColor),4),10%)
@selectionForeground = @nsSelectedMenuItemTextColor
@selectionInactiveBackground = @nsUnemphasizedSelectedContentBackgroundColor
# text selection
@textSelectionBackground = systemColor(highlight,if(systemColor(accent), tint(systemColor(accent),70%), @nsSelectedTextBackgroundColor))
@textSelectionBackground = systemColor(highlight,tint(if(systemColor(accent), systemColor(accent), @accentColor),70%))
@textSelectionForeground = @foreground
# menu
@@ -103,7 +103,7 @@
# accent colors (blueish)
@accentColor = systemColor(accent,@nsControlAccentColor)
@accentFocusColor = if(systemColor(accent), fade(spin(systemColor(accent),4),50%), @nsKeyboardFocusIndicatorColor)
@accentFocusColor = fade(spin(if(systemColor(accent), systemColor(accent), @accentColor),4),50%)
#---- Button ----
@@ -260,6 +260,13 @@ Spinner.buttonHoverArrowColor = lighten($Spinner.buttonArrowColor,20%,derived no
Spinner.buttonPressedArrowColor = lighten($Spinner.buttonArrowColor,30%,derived noAutoInverse)
#---- TabbedPane ----
TabbedPane.tabArc = $Button.arc
TabbedPane.tabSelectionArc = 999
TabbedPane.cardTabArc = $Button.arc
#---- TextArea ---
TextArea.disabledBackground = @disabledComponentBackground

View File

@@ -21,6 +21,7 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.util.Objects;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.UIDefaults.ActiveValue;
@@ -153,4 +154,103 @@ public class TestUIDefaultsLoader
new Font( name, style, size ),
((ActiveValue)UIDefaultsLoader.parseValue( "dummyFont", actualStyle, null )).createValue( null ) );
}
@Test
void parseInstance() {
String className = TestInstance.class.getName();
assertEquals( new TestInstance(), ((LazyValue)UIDefaultsLoader.parseValue( "dummyIcon", className, null )).createValue( null ) );
assertInstanceEquals( new TestInstance(), null );
assertInstanceEquals( new TestInstance( "some string" ), "some string" );
assertInstanceEquals( new TestInstance( false ), "false" );
assertInstanceEquals( new TestInstance( true ), "true" );
assertInstanceEquals( new TestInstance( 123 ), "123" );
assertInstanceEquals( new TestInstance( 123.456f ), "123.456" );
assertInstanceEquals( new TestInstance( Color.red ), "#f00" );
assertInstanceEquals( new TestInstance( "some string", true ), "some string, true" );
assertInstanceEquals( new TestInstance( "some string", true, 123 ), "some string, true, 123" );
assertInstanceEquals( new TestInstance( "some string", 123, true ), "some string, 123, true" );
assertInstanceEquals( new TestInstance( "some string", 123.456f, true ), "some string, 123.456, true" );
assertInstanceEquals( new TestInstance( 123, "some string" ), "123, some string" );
}
private void assertInstanceEquals( TestInstance expected, String params ) {
String value = TestInstance.class.getName() + (params != null ? "," + params : "");
assertEquals( expected, ((LazyValue)UIDefaultsLoader.parseValue( "dummyIcon", value, null )).createValue( null ) );
}
//---- class TestInstance -------------------------------------------------
@SuppressWarnings( "EqualsHashCode" ) // Error Prone
public static class TestInstance
{
private String s;
private boolean b;
private int i;
private float f;
private Color color;
public TestInstance() {
}
public TestInstance( String s ) {
this.s = s;
}
public TestInstance( boolean b ) {
this.b = b;
}
public TestInstance( int i ) {
this.i = i;
}
public TestInstance( float f ) {
this.f = f;
}
public TestInstance( Color color ) {
this.color = color;
}
public TestInstance( String s, boolean b ) {
this.s = s;
this.b = b;
}
public TestInstance( String s, boolean b, int i ) {
this.s = s;
this.b = b;
this.i = i;
}
public TestInstance( String s, int i, boolean b ) {
this.s = s;
this.b = b;
this.i = i;
}
public TestInstance( String s, float f, boolean b ) {
this.s = s;
this.b = b;
this.f = f;
}
protected TestInstance( int i, String s ) {
this.s = s;
this.i = i;
}
@Override
public boolean equals( Object obj ) {
if( !(obj instanceof TestInstance) )
return false;
TestInstance inst = (TestInstance) obj;
return Objects.equals( s, inst.s ) &&
b == inst.b &&
i == inst.i &&
f == inst.f &&
Objects.equals( color, inst.color );
}
}
}

View File

@@ -732,6 +732,11 @@ public class TestFlatStyleableInfo
"tabHeight", int.class,
"tabSelectionHeight", int.class,
"cardTabSelectionHeight", int.class,
"tabArc", int.class,
"tabSelectionArc", int.class,
"cardTabArc", int.class,
"selectedInsets", Insets.class,
"tabSelectionInsets", Insets.class,
"contentSeparatorHeight", int.class,
"showTabSeparators", boolean.class,
"tabSeparatorsFullHeight", boolean.class,

View File

@@ -738,6 +738,11 @@ public class TestFlatStyleableValue
testInteger( c, ui, "tabHeight", 123 );
testInteger( c, ui, "tabSelectionHeight", 123 );
testInteger( c, ui, "cardTabSelectionHeight", 123 );
testInteger( c, ui, "tabArc", 123 );
testInteger( c, ui, "tabSelectionArc", 123 );
testInteger( c, ui, "cardTabArc", 123 );
testInsets( c, ui, "selectedInsets", 1,2,3,4 );
testInsets( c, ui, "tabSelectionInsets", 1,2,3,4 );
testInteger( c, ui, "contentSeparatorHeight", 123 );
testBoolean( c, ui, "showTabSeparators", false );
testBoolean( c, ui, "tabSeparatorsFullHeight", false );
@@ -1214,6 +1219,7 @@ public class TestFlatStyleableValue
//---- class TestIcon -----------------------------------------------------
@SuppressWarnings( "EqualsHashCode" ) // Error Prone
public static class TestIcon
implements Icon
{

View File

@@ -917,6 +917,11 @@ public class TestFlatStyling
ui.applyStyle( "tabHeight: 30" );
ui.applyStyle( "tabSelectionHeight: 3" );
ui.applyStyle( "cardTabSelectionHeight: 2" );
ui.applyStyle( "tabArc: 3" );
ui.applyStyle( "tabSelectionArc: 4" );
ui.applyStyle( "cardTabArc: 5" );
ui.applyStyle( "selectedInsets: 1,2,3,4" );
ui.applyStyle( "tabSelectionInsets: 1,2,3,4" );
ui.applyStyle( "contentSeparatorHeight: 1" );
ui.applyStyle( "showTabSeparators: false" );
ui.applyStyle( "tabSeparatorsFullHeight: false" );

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.swing.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import com.formdev.flatlaf.FlatClientProperties;
/**
* @author Karl Tauber
*/
public class TestFlatStylingScale
{
private static final float[] FACTORS = { 1f, 1.5f, 2f };
@BeforeAll
static void setup() {
TestUtils.setup( false );
}
@AfterAll
static void cleanup() {
TestUtils.cleanup();
}
static float[] factors() {
return FACTORS;
}
@ParameterizedTest
@MethodSource( "factors" )
void button( float factor ) {
abstractButton( factor, new JButton() );
}
@ParameterizedTest
@MethodSource( "factors" )
void checkBox( float factor ) {
abstractButton( factor, new JCheckBox() );
}
@ParameterizedTest
@MethodSource( "factors" )
void radioButton( float factor ) {
abstractButton( factor, new JRadioButton() );
}
private void abstractButton( float factor, AbstractButton button ) {
TestUtils.scaleFont( factor );
button.putClientProperty( FlatClientProperties.STYLE, "iconTextGap: 100" );
assertEquals( (int) (100 * factor), button.getIconTextGap() );
TestUtils.resetFont();
}
@ParameterizedTest
@MethodSource( "factors" )
void tabbedPane( float factor ) {
TestUtils.scaleFont( factor );
JTabbedPane tabbedPane = new JTabbedPane();
FlatTabbedPaneUI ui = (FlatTabbedPaneUI) tabbedPane.getUI();
tabbedPane.putClientProperty( FlatClientProperties.STYLE, "textIconGap: 100" );
assertEquals( 100, ui.getStyleableValue( tabbedPane, "textIconGap" ) );
TestUtils.resetFont();
}
@ParameterizedTest
@MethodSource( "factors" )
void table( float factor ) {
TestUtils.scaleFont( factor );
JTable table = new JTable();
table.putClientProperty( FlatClientProperties.STYLE, "rowHeight: 100" );
assertEquals( (int) (100 * factor), table.getRowHeight() );
TestUtils.resetFont();
}
@ParameterizedTest
@MethodSource( "factors" )
void tree( float factor ) {
TestUtils.scaleFont( factor );
JTree tree = new JTree();
tree.putClientProperty( FlatClientProperties.STYLE, "rowHeight: 10" );
assertEquals( 10 * factor, tree.getRowHeight() );
TestUtils.resetFont();
}
}

View File

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

View File

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

View File

@@ -27,8 +27,8 @@ dependencies {
implementation( project( ":flatlaf-fonts-roboto" ) )
implementation( project( ":flatlaf-fonts-roboto-mono" ) )
implementation( project( ":flatlaf-intellij-themes" ) )
implementation( "com.miglayout:miglayout-swing:5.3" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
implementation( libs.miglayout.swing )
implementation( libs.jgoodies.forms )
// implementation( project( ":flatlaf-natives-jna" ) )
}

View File

@@ -33,6 +33,7 @@ import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*;
@@ -266,6 +267,18 @@ class DemoFrame
repaint();
}
private void animationChanged() {
boolean enabled = animationMenuItem.isSelected();
System.setProperty( FlatSystemProperties.ANIMATION, Boolean.toString( enabled ) );
smoothScrollingMenuItem.setEnabled( enabled );
animatedLafChangeMenuItem.setEnabled( enabled );
}
private void smoothScrollingChanged() {
UIManager.put( "ScrollPane.smoothScrolling", smoothScrollingMenuItem.isSelected() );
}
private void animatedLafChangeChanged() {
System.setProperty( "flatlaf.animatedLafChange", String.valueOf( animatedLafChangeMenuItem.isSelected() ) );
}
@@ -440,9 +453,9 @@ class DemoFrame
Class<? extends LookAndFeel> lafClass = UIManager.getLookAndFeel().getClass();
try {
FlatLaf.setup( lafClass.newInstance() );
FlatLaf.setup( lafClass.getDeclaredConstructor().newInstance() );
FlatLaf.updateUI();
} catch( InstantiationException | IllegalAccessException ex ) {
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
@@ -505,6 +518,8 @@ class DemoFrame
showTitleBarIconMenuItem = new JCheckBoxMenuItem();
underlineMenuSelectionMenuItem = new JCheckBoxMenuItem();
alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem();
animationMenuItem = new JCheckBoxMenuItem();
smoothScrollingMenuItem = new JCheckBoxMenuItem();
animatedLafChangeMenuItem = new JCheckBoxMenuItem();
JMenuItem showHintsMenuItem = new JMenuItem();
JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem();
@@ -591,6 +606,7 @@ class DemoFrame
undoMenuItem.setText("Undo");
undoMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
undoMenuItem.setMnemonic('U');
undoMenuItem.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/undo.svg"));
undoMenuItem.addActionListener(e -> menuItemActionPerformed(e));
editMenu.add(undoMenuItem);
@@ -598,6 +614,7 @@ class DemoFrame
redoMenuItem.setText("Redo");
redoMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
redoMenuItem.setMnemonic('R');
redoMenuItem.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/redo.svg"));
redoMenuItem.addActionListener(e -> menuItemActionPerformed(e));
editMenu.add(redoMenuItem);
editMenu.addSeparator();
@@ -606,18 +623,21 @@ class DemoFrame
cutMenuItem.setText("Cut");
cutMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
cutMenuItem.setMnemonic('C');
cutMenuItem.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
editMenu.add(cutMenuItem);
//---- copyMenuItem ----
copyMenuItem.setText("Copy");
copyMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
copyMenuItem.setMnemonic('O');
copyMenuItem.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
editMenu.add(copyMenuItem);
//---- pasteMenuItem ----
pasteMenuItem.setText("Paste");
pasteMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
pasteMenuItem.setMnemonic('P');
pasteMenuItem.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
editMenu.add(pasteMenuItem);
editMenu.addSeparator();
@@ -787,6 +807,19 @@ class DemoFrame
alwaysShowMnemonicsMenuItem.setText("Always show mnemonics");
alwaysShowMnemonicsMenuItem.addActionListener(e -> alwaysShowMnemonics());
optionsMenu.add(alwaysShowMnemonicsMenuItem);
optionsMenu.addSeparator();
//---- animationMenuItem ----
animationMenuItem.setText("Animation");
animationMenuItem.setSelected(true);
animationMenuItem.addActionListener(e -> animationChanged());
optionsMenu.add(animationMenuItem);
//---- smoothScrollingMenuItem ----
smoothScrollingMenuItem.setText("Smooth Scrolling");
smoothScrollingMenuItem.setSelected(true);
smoothScrollingMenuItem.addActionListener(e -> smoothScrollingChanged());
optionsMenu.add(smoothScrollingMenuItem);
//---- animatedLafChangeMenuItem ----
animatedLafChangeMenuItem.setText("Animated Laf Change");
@@ -827,34 +860,41 @@ class DemoFrame
//---- backButton ----
backButton.setToolTipText("Back");
backButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/back.svg"));
toolBar.add(backButton);
//---- forwardButton ----
forwardButton.setToolTipText("Forward");
forwardButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/forward.svg"));
toolBar.add(forwardButton);
toolBar.addSeparator();
//---- cutButton ----
cutButton.setToolTipText("Cut");
cutButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-cut.svg"));
toolBar.add(cutButton);
//---- copyButton ----
copyButton.setToolTipText("Copy");
copyButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/copy.svg"));
toolBar.add(copyButton);
//---- pasteButton ----
pasteButton.setToolTipText("Paste");
pasteButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/menu-paste.svg"));
toolBar.add(pasteButton);
toolBar.addSeparator();
//---- refreshButton ----
refreshButton.setToolTipText("Refresh");
refreshButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/refresh.svg"));
toolBar.add(refreshButton);
toolBar.addSeparator();
//---- showToggleButton ----
showToggleButton.setSelected(true);
showToggleButton.setToolTipText("Show Details");
showToggleButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/show.svg"));
toolBar.add(showToggleButton);
}
contentPane.add(toolBar, BorderLayout.NORTH);
@@ -901,21 +941,6 @@ class DemoFrame
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" ) );
cutMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-cut.svg" ) );
copyMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/copy.svg" ) );
pasteMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-paste.svg" ) );
backButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/back.svg" ) );
forwardButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/forward.svg" ) );
cutButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-cut.svg" ) );
copyButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/copy.svg" ) );
pasteButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-paste.svg" ) );
refreshButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/refresh.svg" ) );
showToggleButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/show.svg" ) );
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
@@ -950,6 +975,9 @@ class DemoFrame
if( SystemInfo.isMacOS )
unsupported( underlineMenuSelectionMenuItem );
if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) )
animatedLafChangeMenuItem.setSelected( false );
// remove contentPanel bottom insets
MigLayout layout = (MigLayout) contentPanel.getLayout();
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
@@ -981,6 +1009,8 @@ class DemoFrame
private JCheckBoxMenuItem showTitleBarIconMenuItem;
private JCheckBoxMenuItem underlineMenuSelectionMenuItem;
private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem;
private JCheckBoxMenuItem animationMenuItem;
private JCheckBoxMenuItem smoothScrollingMenuItem;
private JCheckBoxMenuItem animatedLafChangeMenuItem;
private JMenuItem aboutMenuItem;
private JToolBar toolBar;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.4.0.360" Java: "17" encoding: "UTF-8"
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -21,10 +21,12 @@ new FormModel {
add( new FormComponent( "javax.swing.JButton" ) {
name: "backButton"
"toolTipText": "Back"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/back.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "forwardButton"
"toolTipText": "Forward"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/forward.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator5"
@@ -32,14 +34,17 @@ new FormModel {
add( new FormComponent( "javax.swing.JButton" ) {
name: "cutButton"
"toolTipText": "Cut"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "copyButton"
"toolTipText": "Copy"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "pasteButton"
"toolTipText": "Paste"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator6"
@@ -47,6 +52,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JButton" ) {
name: "refreshButton"
"toolTipText": "Refresh"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/refresh.svg" )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator7"
@@ -55,6 +61,7 @@ new FormModel {
name: "showToggleButton"
"selected": true
"toolTipText": "Show Details"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/show.svg" )
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "North"
@@ -185,6 +192,7 @@ new FormModel {
"text": "Undo"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 90, 4226, false )
"mnemonic": 85
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/undo.svg" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
@@ -192,6 +200,7 @@ new FormModel {
"text": "Redo"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 89, 4226, false )
"mnemonic": 82
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/redo.svg" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) )
} )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
@@ -202,18 +211,21 @@ new FormModel {
"text": "Cut"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 88, 4226, false )
"mnemonic": 67
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-cut.svg" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "copyMenuItem"
"text": "Copy"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 67, 4226, false )
"mnemonic": 79
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/copy.svg" )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "pasteMenuItem"
"text": "Paste"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 86, 4226, false )
"mnemonic": 80
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/menu-paste.svg" )
} )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
name: "separator3"
@@ -406,6 +418,27 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alwaysShowMnemonics", false ) )
} )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
name: "separator9"
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "animationMenuItem"
"text": "Animation"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "animationChanged", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "smoothScrollingMenuItem"
"text": "Smooth Scrolling"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "smoothScrollingChanged", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "animatedLafChangeMenuItem"
"text": "Animated Laf Change"

View File

@@ -25,6 +25,7 @@ import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.Beans;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -32,6 +33,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
@@ -82,14 +84,13 @@ public class IJThemesPanel
private File lastDirectory;
private boolean isAdjustingThemesList;
private long lastLafChangeTime = System.currentTimeMillis();
public IJThemesPanel() {
initComponents();
saveButton.setEnabled( false );
sourceCodeButton.setEnabled( false );
saveButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/download.svg" ) );
sourceCodeButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/github.svg" ) );
// create renderer
themesList.setCellRenderer( new DefaultListCellRenderer() {
@@ -156,6 +157,9 @@ public class IJThemesPanel
}
private void updateThemesList() {
if( Beans.isDesignTime() )
return; // disable if running in GUI builder
int filterLightDark = filterComboBox.getSelectedIndex();
boolean showLight = (filterLightDark != 2);
boolean showDark = (filterLightDark != 1);
@@ -370,7 +374,10 @@ public class IJThemesPanel
if( themeInfo == null || themeInfo.resourceName == null )
return;
String themeUrl = (themeInfo.sourceCodeUrl + '/' + themeInfo.sourceCodePath).replace( " ", "%20" );
String themeUrl = themeInfo.sourceCodeUrl;
if( themeInfo.sourceCodePath != null )
themeUrl += '/' + themeInfo.sourceCodePath;
themeUrl = themeUrl.replace( " ", "%20" );
try {
Desktop.getDesktop().browse( new URI( themeUrl ) );
} catch( IOException | URISyntaxException ex ) {
@@ -409,14 +416,59 @@ public class IJThemesPanel
}
private void lafChanged( PropertyChangeEvent e ) {
if( "lookAndFeel".equals( e.getPropertyName() ) )
if( "lookAndFeel".equals( e.getPropertyName() ) ) {
selectedCurrentLookAndFeel();
lastLafChangeTime = System.currentTimeMillis();
}
}
private void windowActivated() {
// refresh themes list on window activation
if( themesManager.hasThemesFromDirectoryChanged() )
updateThemesList();
else {
// check whether core .properties files of current Laf have changed
// in development environment since last Laf change and reload theme
LookAndFeel laf = UIManager.getLookAndFeel();
if( laf instanceof FlatLaf ) {
List<Class<?>> lafClasses = new ArrayList<>();
if( laf instanceof IntelliJTheme.ThemeLaf ) {
boolean dark = ((FlatLaf)laf).isDark();
lafClasses.add( FlatLaf.class );
lafClasses.add( dark ? FlatDarkLaf.class : FlatLightLaf.class );
lafClasses.add( dark ? FlatDarculaLaf.class : FlatIntelliJLaf.class );
lafClasses.add( IntelliJTheme.ThemeLaf.class );
} else {
for( Class<?> lafClass = laf.getClass();
FlatLaf.class.isAssignableFrom( lafClass );
lafClass = lafClass.getSuperclass() )
{
lafClasses.add( 0, lafClass );
}
}
boolean reload = false;
for( Class<?> lafClass : lafClasses ) {
String propertiesName = '/' + lafClass.getName().replace( '.', '/' ) + ".properties";
URL url = lafClass.getResource( propertiesName );
if( url != null && "file".equals( url.getProtocol() ) ) {
try {
File file = new File( url.toURI() );
if( file.lastModified() > lastLafChangeTime ) {
reload = true;
break;
}
} catch( URISyntaxException ex ) {
// ignore
}
}
}
if( reload )
setTheme( themesList.getSelectedValue() );
}
}
}
private void selectedCurrentLookAndFeel() {
@@ -488,11 +540,13 @@ public class IJThemesPanel
//---- saveButton ----
saveButton.setToolTipText("Save .theme.json of selected IntelliJ theme to file.");
saveButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/download.svg"));
saveButton.addActionListener(e -> saveTheme());
toolBar.add(saveButton);
//---- sourceCodeButton ----
sourceCodeButton.setToolTipText("Opens the source code repository of selected IntelliJ theme in the browser.");
sourceCodeButton.setIcon(new FlatSVGIcon("com/formdev/flatlaf/demo/icons/github.svg"));
sourceCodeButton.addActionListener(e -> browseSourceCode());
toolBar.add(sourceCodeButton);
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "8.1.0.0.283" Java: "19.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -24,11 +24,13 @@ new FormModel {
add( new FormComponent( "javax.swing.JButton" ) {
name: "saveButton"
"toolTipText": "Save .theme.json of selected IntelliJ theme to file."
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/download.svg" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "saveTheme", false ) )
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "sourceCodeButton"
"toolTipText": "Opens the source code repository of selected IntelliJ theme in the browser."
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/github.svg" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "browseSourceCode", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {

View File

@@ -38,8 +38,10 @@ public class IJThemesUpdater
themesManager.loadBundledThemes();
for( IJThemeInfo ti : themesManager.bundledThemes ) {
if( ti.sourceCodeUrl == null || ti.sourceCodePath == null )
if( ti.sourceCodeUrl == null || ti.sourceCodePath == null ) {
System.out.println( " " + ti.name + " NOT downloaded. Needs manual update from release on JetBrains Plugin portal." );
continue;
}
String fromUrl = ti.sourceCodeUrl + "/" + ti.sourceCodePath;
if( fromUrl.contains( "github.com" ) )

View File

@@ -145,7 +145,7 @@
"license": "MIT",
"licenseFile": "Hiberbee.LICENSE.txt",
"sourceCodeUrl": "https://github.com/Hiberbee/themes",
"sourceCodePath": "blob/latest/src/main/resources/themes/HiberbeeDark.theme.json"
"sourceCodePath": "blob/latest/src/intellij/src/main/resources/themes/HiberbeeDark.theme.json"
},
"HighContrast.theme.json": {
"name": "High contrast",
@@ -249,14 +249,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Arc Dark.theme.json"
},
"material-theme-ui-lite/Arc Dark Contrast.theme.json": {
"name": "Material Theme UI Lite / Arc 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/contrast/Arc Dark Contrast.theme.json"
},
"material-theme-ui-lite/Atom One Dark.theme.json": {
"name": "Material Theme UI Lite / Atom One Dark",
"dark": true,
@@ -265,14 +257,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Atom One Dark.theme.json"
},
"material-theme-ui-lite/Atom One Dark Contrast.theme.json": {
"name": "Material Theme UI Lite / Atom One 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/contrast/Atom One Dark Contrast.theme.json"
},
"material-theme-ui-lite/Atom One Light.theme.json": {
"name": "Material Theme UI Lite / Atom One Light",
"license": "MIT",
@@ -280,13 +264,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Atom One Light.theme.json"
},
"material-theme-ui-lite/Atom One Light Contrast.theme.json": {
"name": "Material Theme UI Lite / Atom One Light Contrast",
"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/contrast/Atom One Light Contrast.theme.json"
},
"material-theme-ui-lite/Dracula.theme.json": {
"name": "Material Theme UI Lite / Dracula",
"dark": true,
@@ -295,14 +272,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Dracula.theme.json"
},
"material-theme-ui-lite/Dracula Contrast.theme.json": {
"name": "Material Theme UI Lite / Dracula 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/contrast/Dracula Contrast.theme.json"
},
"material-theme-ui-lite/GitHub.theme.json": {
"name": "Material Theme UI Lite / GitHub",
"license": "MIT",
@@ -310,13 +279,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/GitHub.theme.json"
},
"material-theme-ui-lite/GitHub Contrast.theme.json": {
"name": "Material Theme UI Lite / GitHub Contrast",
"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/contrast/GitHub Contrast.theme.json"
},
"material-theme-ui-lite/GitHub Dark.theme.json": {
"name": "Material Theme UI Lite / GitHub Dark",
"dark": true,
@@ -325,14 +287,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/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/contrast/GitHub Dark Contrast.theme.json"
},
"material-theme-ui-lite/Light Owl.theme.json": {
"name": "Material Theme UI Lite / Light Owl",
"license": "MIT",
@@ -340,13 +294,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Light Owl.theme.json"
},
"material-theme-ui-lite/Light Owl Contrast.theme.json": {
"name": "Material Theme UI Lite / Light Owl Contrast",
"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/contrast/Light Owl Contrast.theme.json"
},
"material-theme-ui-lite/Material Darker.theme.json": {
"name": "Material Theme UI Lite / Material Darker",
"dark": true,
@@ -355,14 +302,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Darker.theme.json"
},
"material-theme-ui-lite/Material Darker Contrast.theme.json": {
"name": "Material Theme UI Lite / Material Darker 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/contrast/Material Darker Contrast.theme.json"
},
"material-theme-ui-lite/Material Deep Ocean.theme.json": {
"name": "Material Theme UI Lite / Material Deep Ocean",
"dark": true,
@@ -371,14 +310,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Deep Ocean.theme.json"
},
"material-theme-ui-lite/Material Deep Ocean Contrast.theme.json": {
"name": "Material Theme UI Lite / Material Deep Ocean 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/contrast/Material Deep Ocean Contrast.theme.json"
},
"material-theme-ui-lite/Material Lighter.theme.json": {
"name": "Material Theme UI Lite / Material Lighter",
"license": "MIT",
@@ -386,13 +317,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Lighter.theme.json"
},
"material-theme-ui-lite/Material Lighter Contrast.theme.json": {
"name": "Material Theme UI Lite / Material Lighter Contrast",
"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/contrast/Material Lighter Contrast.theme.json"
},
"material-theme-ui-lite/Material Oceanic.theme.json": {
"name": "Material Theme UI Lite / Material Oceanic",
"dark": true,
@@ -401,14 +325,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Oceanic.theme.json"
},
"material-theme-ui-lite/Material Oceanic Contrast.theme.json": {
"name": "Material Theme UI Lite / Material Oceanic 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/contrast/Material Oceanic Contrast.theme.json"
},
"material-theme-ui-lite/Material Palenight.theme.json": {
"name": "Material Theme UI Lite / Material Palenight",
"dark": true,
@@ -417,14 +333,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Material Palenight.theme.json"
},
"material-theme-ui-lite/Material Palenight Contrast.theme.json": {
"name": "Material Theme UI Lite / Material Palenight 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/contrast/Material Palenight Contrast.theme.json"
},
"material-theme-ui-lite/Monokai Pro.theme.json": {
"name": "Material Theme UI Lite / Monokai Pro",
"dark": true,
@@ -433,14 +341,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Monokai Pro.theme.json"
},
"material-theme-ui-lite/Monokai Pro Contrast.theme.json": {
"name": "Material Theme UI Lite / Monokai Pro 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/contrast/Monokai Pro Contrast.theme.json"
},
"material-theme-ui-lite/Moonlight.theme.json": {
"name": "Material Theme UI Lite / Moonlight",
"dark": true,
@@ -449,14 +349,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Moonlight.theme.json"
},
"material-theme-ui-lite/Moonlight Contrast.theme.json": {
"name": "Material Theme UI Lite / Moonlight 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/contrast/Moonlight Contrast.theme.json"
},
"material-theme-ui-lite/Night Owl.theme.json": {
"name": "Material Theme UI Lite / Night Owl",
"dark": true,
@@ -465,14 +357,6 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Night Owl.theme.json"
},
"material-theme-ui-lite/Night Owl Contrast.theme.json": {
"name": "Material Theme UI Lite / Night Owl 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/contrast/Night Owl Contrast.theme.json"
},
"material-theme-ui-lite/Solarized Dark.theme.json": {
"name": "Material Theme UI Lite / Solarized Dark",
"dark": true,
@@ -481,26 +365,11 @@
"sourceCodeUrl": "https://github.com/mallowigi/material-theme-ui-lite",
"sourceCodePath": "blob/master/src/main/resources/themes/regular/Solarized Dark.theme.json"
},
"material-theme-ui-lite/Solarized Dark Contrast.theme.json": {
"name": "Material Theme UI Lite / Solarized 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/contrast/Solarized Dark Contrast.theme.json"
},
"material-theme-ui-lite/Solarized Light.theme.json": {
"name": "Material Theme UI Lite / Solarized Light",
"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/regular/Solarized Light.theme.json"
},
"material-theme-ui-lite/Solarized Light Contrast.theme.json": {
"name": "Material Theme UI Lite / Solarized Light Contrast",
"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/contrast/Solarized Light Contrast.theme.json"
}
}

View File

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

View File

@@ -4,8 +4,7 @@ FlatLaf Extras
This sub-project provides some additional components and classes:
- [FlatSVGIcon](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/FlatSVGIcon.html):
An icon that displays SVG using
[svgSalamander](https://github.com/JFormDesigner/svgSalamander).\
An icon that displays SVG using [JSVG](https://github.com/weisJ/jsvg).\
![FlatSVGIcon.png](../images/extras-FlatSVGIcon.png)
- [FlatTriStateCheckBox](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/components/FlatTriStateCheckBox.html):
A tri-state check box.\
@@ -38,9 +37,9 @@ Otherwise download `flatlaf-extras-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras)
If SVG classes are used, `svgSalamander-<version>.jar` is also required:
If SVG classes are used, `jsvg-<version>.jar` is also required:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/svgSalamander/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/svgSalamander)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.weisj/jsvg/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.github.weisj/jsvg)
Tools

View File

@@ -23,7 +23,7 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "com.formdev:svgSalamander:1.1.3" )
implementation( libs.jsvg )
}
flatlafModuleInfo {

View File

@@ -67,7 +67,7 @@ public class FlatAnimatedLafChange
* Invoke before setting new look and feel.
*/
public static void showSnapshot() {
if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
return;
// stop already running animation
@@ -138,7 +138,7 @@ public class FlatAnimatedLafChange
* Invoke after updating UI.
*/
public static void hideSnapshotWithAnimation() {
if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
return;
if( oldUIsnapshots.isEmpty() )

View File

@@ -30,8 +30,8 @@ import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
@@ -49,9 +49,9 @@ import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SoftCache;
import com.formdev.flatlaf.util.UIScale;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
import com.github.weisj.jsvg.SVGDocument;
import com.github.weisj.jsvg.geometry.size.FloatSize;
import com.github.weisj.jsvg.parser.SVGLoader;
/**
* An icon that loads and paints SVG.
@@ -62,12 +62,9 @@ public class FlatSVGIcon
extends ImageIcon
implements DisabledIconProvider
{
// cache that uses soft references for values, which allows freeing SVG diagrams if no longer used
private static final SoftCache<URI, SVGDiagram> svgCache = new SoftCache<>();
// use own SVG universe so that it can not be cleared from anywhere
private static final SVGUniverse svgUniverse = new SVGUniverse();
private static int streamNumber;
// cache that uses soft references for values, which allows freeing SVG documents if no longer used
private static final SoftCache<String, SVGDocument> svgCache = new SoftCache<>();
private static final SVGLoader svgLoader = new SVGLoader();
private final String name;
private final int width;
@@ -75,11 +72,11 @@ public class FlatSVGIcon
private final float scale;
private final boolean disabled;
private final ClassLoader classLoader;
private final URI uri;
private final URL url;
private ColorFilter colorFilter;
private SVGDiagram diagram;
private SVGDocument document;
private boolean dark;
private boolean loadFailed;
@@ -220,7 +217,7 @@ public class FlatSVGIcon
* @since 2
*/
public FlatSVGIcon( URL url ) {
this( null, -1, -1, 1, false, null, url2uri( url ) );
this( null, -1, -1, 1, false, null, url );
}
/**
@@ -236,7 +233,7 @@ public class FlatSVGIcon
* @since 2
*/
public FlatSVGIcon( URI uri ) {
this( null, -1, -1, 1, false, null, uri );
this( null, -1, -1, 1, false, null, uri2url( uri ) );
}
/**
@@ -251,7 +248,7 @@ public class FlatSVGIcon
* @since 2
*/
public FlatSVGIcon( File file ) {
this( null, -1, -1, 1, false, null, file.toURI() );
this( null, -1, -1, 1, false, null, uri2url( file.toURI() ) );
}
/**
@@ -267,19 +264,15 @@ public class FlatSVGIcon
* @since 2
*/
public FlatSVGIcon( InputStream in ) throws IOException {
this( null, -1, -1, 1, false, null, loadFromStream( in ) );
this( null, -1, -1, 1, false, null, null );
// since the input stream is already loaded and parsed,
// get diagram here and remove it from cache
update();
synchronized( FlatSVGIcon.class ) {
svgCache.remove( uri );
}
}
private static synchronized URI loadFromStream( InputStream in ) throws IOException {
try( InputStream in2 = in ) {
return svgUniverse.loadSVG( in2, "/flatlaf-stream-" + (streamNumber++) );
document = svgLoader.load( in2 );
if( document == null ) {
loadFailed = true;
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load SVG icon from input stream", null );
}
}
}
@@ -291,20 +284,22 @@ public class FlatSVGIcon
* @since 2.0.1
*/
public FlatSVGIcon( FlatSVGIcon icon ) {
this( icon.name, icon.width, icon.height, icon.scale, icon.disabled, icon.classLoader, icon.uri );
this( icon.name, icon.width, icon.height, icon.scale, icon.disabled, icon.classLoader, icon.url );
colorFilter = icon.colorFilter;
diagram = icon.diagram;
document = icon.document;
dark = icon.dark;
}
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader, URI uri ) {
protected FlatSVGIcon( String name, int width, int height, float scale,
boolean disabled, ClassLoader classLoader, URL url )
{
this.name = name;
this.width = width;
this.height = height;
this.scale = scale;
this.disabled = disabled;
this.classLoader = classLoader;
this.uri = uri;
this.url = url;
}
/**
@@ -383,9 +378,9 @@ public class FlatSVGIcon
if( width == this.width && height == this.height )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, url );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.document = document;
icon.dark = dark;
return icon;
}
@@ -402,9 +397,9 @@ public class FlatSVGIcon
if( scale == this.scale )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, url );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.document = document;
icon.dark = dark;
return icon;
}
@@ -421,9 +416,9 @@ public class FlatSVGIcon
if( disabled )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, uri );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, url );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.document = document;
icon.dark = dark;
return icon;
}
@@ -462,19 +457,19 @@ public class FlatSVGIcon
if( loadFailed )
return;
if( dark == isDarkLaf() && diagram != null )
if( dark == isDarkLaf() && document != null )
return;
dark = isDarkLaf();
// SVGs already loaded via url or input stream can not have light/dark variants
if( uri != null && diagram != null )
// SVGs already loaded via url, file or input stream can not have light/dark variants
if( document != null && name == null )
return;
URI uri = this.uri;
if( uri == null ) {
URL url = getIconURL( name, dark );
if( url == null & dark )
URL url = this.url;
if( url == null ) {
url = getIconURL( name, dark );
if( url == null && dark )
url = getIconURL( name, false );
if( url == null ) {
@@ -482,33 +477,30 @@ public class FlatSVGIcon
LoggingFacade.INSTANCE.logConfig( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
return;
}
uri = url2uri( url );
}
diagram = loadSVG( uri );
loadFailed = (diagram == null);
document = loadSVG( url );
loadFailed = (document == null);
}
static synchronized SVGDiagram loadSVG( URI uri ) {
static synchronized SVGDocument loadSVG( URL url ) {
// get from our cache
SVGDiagram diagram = svgCache.get( uri );
if( diagram != null )
return diagram;
String cacheKey = url.toString();
SVGDocument document = svgCache.get( cacheKey );
if( document != null )
return document;
// load/get SVG diagram
diagram = svgUniverse.getDiagram( uri );
// load SVG document
document = svgLoader.load( url );
if( diagram == null ) {
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + uri + "'", null );
if( document == null ) {
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + url + "'", null );
return null;
}
// add to our (soft) cache and remove from SVGUniverse (hard) cache
svgCache.put( uri, diagram );
svgUniverse.removeDocument( uri );
svgCache.put( cacheKey, document );
return diagram;
return document;
}
private URL getIconURL( String name, boolean dark ) {
@@ -528,7 +520,7 @@ public class FlatSVGIcon
*/
public boolean hasFound() {
update();
return diagram != null;
return document != null;
}
/**
@@ -540,7 +532,7 @@ public class FlatSVGIcon
return scaleSize( width );
update();
return scaleSize( (diagram != null) ? Math.round( diagram.getWidth() ) : 16 );
return scaleSize( (document != null) ? Math.round( document.size().width ) : 16 );
}
/**
@@ -552,7 +544,7 @@ public class FlatSVGIcon
return scaleSize( height );
update();
return scaleSize( (diagram != null) ? Math.round( diagram.getHeight() ) : 16 );
return scaleSize( (document != null) ? Math.round( document.size().height ) : 16 );
}
private int scaleSize( int size ) {
@@ -583,12 +575,7 @@ public class FlatSVGIcon
Graphics2D g2 = new GraphicsFilter( (Graphics2D) g.create(), colorFilter, ColorFilter.getInstance(), grayFilter );
try {
// same hints as in FlatUIUtils.setRenderingHints()
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE );
// enable better image scaling
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
setRenderingHints( g2 );
paintSvg( g2, x, y );
} finally {
@@ -597,7 +584,7 @@ public class FlatSVGIcon
}
private void paintSvg( Graphics2D g, int x, int y ) {
if( diagram == null ) {
if( document == null ) {
paintSvgError( g, x, y );
return;
}
@@ -607,19 +594,18 @@ public class FlatSVGIcon
UIScale.scaleGraphics( g );
if( width > 0 || height > 0 ) {
double sx = (width > 0) ? width / diagram.getWidth() : 1;
double sy = (height > 0) ? height / diagram.getHeight() : 1;
FloatSize svgSize = document.size();
double sx = (width > 0) ? width / svgSize.width : 1;
double sy = (height > 0) ? height / svgSize.height : 1;
if( sx != 1 || sy != 1 )
g.scale( sx, sy );
}
if( scale != 1 )
g.scale( scale, scale );
diagram.setIgnoringClipHeuristic( true );
try {
diagram.render( g );
} catch( SVGException ex ) {
document.render( null, g );
} catch( Exception ex ) {
paintSvgError( g, 0, 0 );
}
}
@@ -662,10 +648,21 @@ public class FlatSVGIcon
return MultiResolutionImageSupport.create( 0, dimensions, producer );
}
static URI url2uri( URL url ) {
static void setRenderingHints( Graphics2D g ) {
// enable anti-aliasing
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
// disable coordinate normalization for correct line rendering
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
// enable better image scaling
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
}
static URL uri2url( URI uri ) {
try {
return url.toURI();
} catch( URISyntaxException ex ) {
return uri.toURL();
} catch( MalformedURLException ex ) {
throw new IllegalArgumentException( ex );
}
}
@@ -964,6 +961,18 @@ public class FlatSVGIcon
this.grayFilter = grayFilter;
}
@Override
public Graphics create() {
return new GraphicsFilter( (Graphics2D) super.create(),
colorFilter, globalColorFilter, grayFilter );
}
@Override
public Graphics create( int x, int y, int width, int height ) {
return new GraphicsFilter( (Graphics2D) super.create( x, y, width, height ),
colorFilter, globalColorFilter, grayFilter );
}
@Override
public void setColor( Color c ) {
super.setColor( filterColor( c ) );

View File

@@ -19,7 +19,6 @@ package com.formdev.flatlaf.extras;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.Arrays;
@@ -28,8 +27,8 @@ import java.util.List;
import javax.swing.JWindow;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.github.weisj.jsvg.SVGDocument;
import com.github.weisj.jsvg.geometry.size.FloatSize;
/**
* Utility methods for SVG.
@@ -83,7 +82,7 @@ public class FlatSVGUtils
* @since 2
*/
public static List<Image> createWindowIconImages( URL svgUrl ) {
SVGDiagram diagram = loadSVG( svgUrl );
SVGDocument document = FlatSVGIcon.loadSVG( svgUrl );
if( SystemInfo.isWindows && MultiResolutionImageSupport.isAvailable() ) {
// use a multi-resolution image that creates images on demand for requested sizes
@@ -102,17 +101,17 @@ public class FlatSVGUtils
new Dimension( 48, 48 ), // 300%
new Dimension( 64, 64 ), // 400%
}, dim -> {
return svg2image( diagram, dim.width, dim.height );
return svg2image( document, 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%
svg2image( document, 16, 16 ), // 100%
svg2image( document, 20, 20 ), // 125%
svg2image( document, 24, 24 ), // 150%
svg2image( document, 28, 28 ), // 175%
svg2image( document, 32, 32 ), // 200%
svg2image( document, 48, 48 ), // 300%
svg2image( document, 64, 64 ) // 400%
);
}
}
@@ -148,7 +147,7 @@ public class FlatSVGUtils
* @since 2
*/
public static BufferedImage svg2image( URL svgUrl, int width, int height ) {
return svg2image( loadSVG( svgUrl ), width, height );
return svg2image( FlatSVGIcon.loadSVG( svgUrl ), width, height );
}
/**
@@ -180,53 +179,43 @@ public class FlatSVGUtils
* @since 2
*/
public static BufferedImage svg2image( URL svgUrl, float scaleFactor ) {
SVGDiagram diagram = loadSVG( svgUrl );
int width = (int) (diagram.getWidth() * scaleFactor);
int height = (int) (diagram.getHeight() * scaleFactor);
return svg2image( diagram, width, height );
SVGDocument document = FlatSVGIcon.loadSVG( svgUrl );
FloatSize size = document.size();
int width = (int) (size.width * scaleFactor);
int height = (int) (size.height * scaleFactor);
return svg2image( document, width, height );
}
/**
* Creates a buffered image and renders the given SVGDiagram into it.
* Creates a buffered image and renders the given SVGDocument into it.
*
* @param diagram the SVG diagram
* @param document the SVG document
* @param width the width of the image
* @param height the height of the image
* @return the image
* @throws RuntimeException if failed to render SVG file
*/
public static BufferedImage svg2image( SVGDiagram diagram, int width, int height ) {
try {
private static BufferedImage svg2image( SVGDocument document, int width, int height ) {
BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics();
try {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
FlatSVGIcon.setRenderingHints( g );
double sx = width / diagram.getWidth();
double sy = height / diagram.getHeight();
FloatSize size = document.size();
double sx = width / size.width;
double sy = height / size.height;
if( sx != 1 || sy != 1 )
g.scale( sx, sy );
diagram.setIgnoringClipHeuristic( true );
diagram.render( g );
document.render( null, g );
} finally {
g.dispose();
}
return image;
} catch( SVGException ex ) {
throw new RuntimeException( ex );
}
}
private static URL getResource( String svgName ) {
return FlatSVGUtils.class.getResource( svgName );
}
private static SVGDiagram loadSVG( URL url ) {
return FlatSVGIcon.loadSVG( FlatSVGIcon.url2uri( url ) );
}
}

View File

@@ -770,6 +770,7 @@ public class FlatUIDefaultsInspector
return String.valueOf( value );
}
@SuppressWarnings( "FormatString" ) // Error Prone
private static String color2hex( Color color ) {
int rgb = color.getRGB();
boolean hasAlpha = color.getAlpha() != 255;

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
/**
/*
* @author Karl Tauber
*/
module com.formdev.flatlaf.extras {
requires java.desktop;
requires java.prefs;
requires static com.kitfox.svg; // optional at runtime
requires static com.github.weisj.jsvg; // optional at runtime
requires com.formdev.flatlaf;
exports com.formdev.flatlaf.extras;

View File

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

View File

@@ -33,9 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
}
flatlafModuleInfo {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/**
/*
* @author Karl Tauber
*/
module com.formdev.flatlaf.fonts.inter {

View File

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

View File

@@ -33,9 +33,8 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
testImplementation( "org.junit.jupiter:junit-jupiter-api:5.7.2" )
testImplementation( "org.junit.jupiter:junit-jupiter-params" )
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
testImplementation( libs.bundles.junit )
testRuntimeOnly( libs.junit.engine )
}
flatlafModuleInfo {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/**
/*
* @author Karl Tauber
*/
module com.formdev.flatlaf.fonts.jetbrains_mono {

View File

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

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