Compare commits

...

388 Commits

Author SHA1 Message Date
Karl Tauber
c6fec0a131 release 1.0-rc1 2021-01-18 23:34:37 +01:00
Karl Tauber
fdc43fc0d3 Slider: improved thumb hover and pressed colors
Also changed auto-inverse threshold from 50% to 65% for increase and 35% for decrease, because this gives much better results for slider hover and pressed colors. This does not change other colors in core themes, but few colors in some IntelliJ themes (usually checkbox hover/pressed).
2021-01-18 23:20:25 +01:00
Karl Tauber
0b880aa335 TabbedPane: fixed scrolling tabs with touchpads and high-resolution mouse wheels 2021-01-18 18:34:21 +01:00
Karl Tauber
74f50ec992 IntelliJ Themes: fixed menu accelerator colors in Monocai theme (issue #243) 2021-01-18 12:15:12 +01:00
Karl Tauber
1bdf4532db UI defaults inspector: support wildcard matching in filter 2021-01-16 12:56:25 +01:00
Karl Tauber
f97783ddef Window decorations: RootPane.activeBorderColor and RootPane.inactiveBorderColor fixes:
- FlatDarkLaf.properties: changed darken() to lighten(), which does not change real colors due to autoInverse mechanism
- FlatLightLaf.properties: use also derived colors (to be consistent with FlatDarkLaf.properties and fix warning in UIDefaultsDump)
2021-01-16 01:01:36 +01:00
Karl Tauber
1024d6fc07 UIDefaultsDump: use DerivedColorKeys.properties to compute and dump derived colors and verify them 2021-01-16 00:39:36 +01:00
Karl Tauber
3ec59d0c58 UI defaults inspector:
- no longer show color values as decimal rgb
- use black for color value text if color is translucent
- fix derived color tooltip
- improved filter performance
2021-01-15 19:44:45 +01:00
Karl Tauber
c43249316c UI defaults inspector:
- show computed derived colors
- also show base colors and default colors
- indicate derived colors with magenta bar on right side in value column
2021-01-15 19:07:44 +01:00
Karl Tauber
ed5180ffd6 Theme Editor:
- save/restore selection when reloading file (if changed outside)
- select all text in find field when pressing Ctrl+F
- use lighter color for operators (e.g. '=')
2021-01-15 16:15:05 +01:00
Karl Tauber
e9ec769340 CHANGELOG.md: added recently merged PRs #237, #239 and #241 2021-01-15 16:00:11 +01:00
Karl T
5e16ff8dff Merge pull request #241 from ingokegel/macos_text_aa
The fix for text anti-aliasing in 50d36fe9 should only apply on macOS
2021-01-15 14:39:00 +01:00
Ingo Kegel
364b6631ea The fix for text anti-aliasing in 50d36fe9 should only apply on macOS 2021-01-15 14:31:58 +01:00
Karl T
48a18e53e3 Merge pull request #240 from ingokegel/table_header_column_borders
Made paintColumnBorders protected to help with implementing derived table header UIs
2021-01-15 14:15:27 +01:00
Karl T
bcc8282d73 Merge pull request #239 from ingokegel/macos_text_aa
Switched from sub-pixel to greyscale text anti-aliasing on macOS when running with a JetBrains JRE
2021-01-15 14:04:40 +01:00
Ingo Kegel
15017ed49c Made paintColumnBorders protected to help with implementing derived table header UIs
To implement FlatLaf UIs for CellStyleTableHeaderUI and SortableTableHeaderUI from the Jide Grids library, access to the paintColumnBorders method is required
2021-01-15 13:09:10 +01:00
Ingo Kegel
50d36fe91b Switched from sub-pixel to greyscale text anti-aliasing on macOS when running with a JetBrains JRE.
Sub-pixel anti-aliasing (VALUE_TEXT_ANTIALIAS_LCD_HRGB) causes font rendering with too much weight with a JetBrains JREs (both 8 and 11). This can be seen when comparing the text rendering of UI elements between IntelliJ IDEA and FlatLaf.

This commits aligns FlatLaf's behavior with IntelliJ IDEA which disables sub-pixel anti-aliasing on macOS for its IDE anti-aliasing setting and uses greyscale anti-aliasing by default (see com.intellij.ide.ui.AntialiasingType.canUseSubpixelAAForIDE).
2021-01-14 18:59:54 +01:00
Karl Tauber
23e67a2908 Slider: support per component custom thumb and track colors 2021-01-14 13:50:42 +01:00
Karl Tauber
0dab1b73cc JIDE: RangeSlider: fixed slider focused colors in IntelliJ themes (see commit 1fb0783808) 2021-01-14 13:36:01 +01:00
Karl T
3c086a92e2 Merge pull request #237 from ingokegel/macos_font
JetBrains Runtime 11 has support for displaying the .AppleSystemUIFont font.
2021-01-14 13:22:16 +01:00
Ingo Kegel
647d72514b JetBrains Runtime 11 has support for displaying the .AppleSystemUIFont font.
This font should be used for UI elements since macOS 10.15.
See https://youtrack.jetbrains.com/issue/JBR-1915 for more information.

Other JREs, including JetBrains Runtime 8 do not handle kerning for that font correctly.
2021-01-14 10:18:39 +01:00
Karl Tauber
15328b4fd7 ToggleButton: tab style buttons now respect explicitly set background color 2021-01-13 17:52:05 +01:00
Karl Tauber
b49a498f9c Button and ToggleButton: ToolBar buttons now respect explicitly set background color. If no background color is set, then the button background is not painted anymore (issue #191) 2021-01-13 17:22:09 +01:00
Karl Tauber
8d14d5f87c Button: disabled Button.defaultButtonFollowsFocus on Windows (as on other platforms, IntelliJ IDEA and other Lafs) 2021-01-13 10:32:15 +01:00
Karl Tauber
a6db352ecd IntelliJ Themes:
- fixed menu item check colors
- fixed MenuItem.underlineSelectionColor
- fixed List, Tree and Table selectionInactiveForeground in light Arc themes
- fixed List and Table background colors in Material UI Lite themes
2021-01-13 10:11:29 +01:00
Karl Tauber
ccbb26c176 IntelliJ Themes: added hover and pressed feedback to Button, CheckBox, RadioButton and ToggleButton (issue #176) 2021-01-12 14:15:44 +01:00
Karl Tauber
8f6af73541 CheckBox and RadioButton:
- use `CheckBox.icon.selectedBackground` as base color for derived "selected" colors
- use derived colors for `CheckBox.icon[filled].selectedHoverBackground` and `CheckBox.icon[filled].selectedPressedBackground`
- removed unnecessary `CheckBox.icon.selectedFocusedBorderColor`from FlatDarkLaf.properties
- added missing keys to FlatLafUIKeys.txt

(preparation for #176)
2021-01-12 10:43:57 +01:00
Karl Tauber
a59f17fdb2 UIDefaultsKeysDump: extend existing keys file (instead of replacing it) to allow manual adding of optional keys, which are not defined in UI defaults 2021-01-11 14:00:14 +01:00
Karl Tauber
14222e40ad TabbedPane: fixed IndexOutOfBoundsException when using tooltip text on close buttons and closing last/rightmost tab (issue #235) 2021-01-10 18:28:30 +01:00
Karl Tauber
7d48bf06fe Button and ToggleButton: Threat Unicode surrogate character pair as single character and make button square (issue #234) 2021-01-09 23:46:56 +01:00
Karl Tauber
1d06a2c2e8 IntelliJ Themes: updated "Material Theme UI Lite" themes; added "Material Theme UI Lite / Moonlight" theme 2021-01-09 17:55:09 +01:00
Karl Tauber
cf141f0e55 IntelliJ Themes: updated "Dracula" and "Gradianto" themes 2021-01-09 17:35:13 +01:00
Karl Tauber
9113c31612 UI defaults inspector: support copy key/value to clipboard 2021-01-09 11:13:17 +01:00
Karl Tauber
00b4e0a6fd UI defaults inspector: support embedding into any window 2021-01-09 00:38:46 +01:00
Karl Tauber
e3cac95d37 UI defaults:
- moved some common properties from FlatLightLaf.properties and FlatDarkLaf.properties to FlatLaf.properties
- use color functions for more properties
2021-01-08 18:24:39 +01:00
Karl Tauber
64d850c583 build.gradle.kts: added more information to pom 2021-01-08 11:47:48 +01:00
Karl Tauber
2fe1b9e726 ScrollPane: smooth scrolling:
- scroll at least one pixel to avoid "hanging"
- limit scroll increment to visible width/height
- no longer use block increment because had width/height of view (IOW was too large and had no effect)

(issue #27)
2021-01-08 11:20:55 +01:00
Karl Tauber
1315d847b9 removed dummy pom.xml for GitHub dependency graph 2021-01-07 14:09:55 +01:00
Karl Tauber
b5954102b6 README.md: added maven-central badge 2021-01-05 15:09:52 +01:00
Karl Tauber
1c8ba0c538 added dummy root pom.xml for GitHub dependency graph 2021-01-05 11:58:02 +01:00
Karl Tauber
be18317a6d moved flatlaf-extras/pom.xml (for GitHub dependency graph) to another folder to check whether GitHub recognizes it there 2021-01-05 11:48:50 +01:00
Karl Tauber
88d2b8266e README.md: reordered chapters 2021-01-03 11:59:44 +01:00
Karl Tauber
949ca5ddff JIDE: auto-initialize JIDE extensions (issue #230) 2021-01-03 11:55:09 +01:00
Karl Tauber
3eb53b9648 Theme Editor: save/restore window size (basic implementation; ignoring maximized state and screen number) 2021-01-02 15:44:37 +01:00
Karl Tauber
e4a03ede1f added dummy pom.xml for GitHub dependency graph for flatlaf-extras 2021-01-02 14:05:10 +01:00
Karl Tauber
cb65dc0e9d added dummy pom.xml for GitHub dependency graph 2021-01-02 13:09:31 +01:00
Karl Tauber
8ec907050e Theme Editor:
- "Open Directory" action added
- remember recently opened directories
- remember recently selected file
2021-01-01 17:43:05 +01:00
Karl Tauber
15ba00a902 Theme Editor: use selected text in editor for searching when pressing Ctrl+F 2021-01-01 12:56:29 +01:00
Karl Tauber
89d0c301c2 Theme Editor: "replace" and "replace all" added; focus editor with F12 key 2020-12-31 23:22:45 +01:00
Karl Tauber
2f47466f3b Theme Editor:
- fixed broken (mouse-wheel) scrolling caused by the additional JPanel
- fixed broken slide-in animation of "find bar"
2020-12-31 22:29:09 +01:00
Karl Tauber
d70eca9774 Theme Editor: added "error strip" to right side; removed scroll pane border 2020-12-31 18:15:20 +01:00
Karl Tauber
95ce92fa18 Theme Editor: find previous/next with UP/DOWN keys 2020-12-31 17:34:16 +01:00
Karl Tauber
b3db52b2ed Theme Editor: mark occurrence while typing; disable previous/next occurrence buttons if searchFor is empty 2020-12-31 16:23:04 +01:00
Karl Tauber
c40912013d Theme Editor: use markAll() (instead of find()) to avoid that selection jumps to next occurrence when showing find bar or when changing options 2020-12-31 16:11:22 +01:00
Karl Tauber
1c08e98c1c Theme Editor: show/hide highlighted matches when showing/hiding "find bar" 2020-12-31 15:55:22 +01:00
Karl Tauber
3f202a7cdc Theme Editor: transfer focus to editor when hiding "find bar" 2020-12-31 15:24:32 +01:00
Karl Tauber
6f3aea8fc1 Theme Editor: basic "find bar" added 2020-12-31 15:08:14 +01:00
Karl Tauber
0896143838 Theme Editor: support navigating to next/previous editor with Ctrl+Tab/Ctrl+Shift+Tab 2020-12-30 14:03:41 +01:00
Karl Tauber
ea94899a28 Extras: added missing export of package com.formdev.flatlaf.extras.components to Java 9 module descriptor (issue #117) 2020-12-30 11:23:51 +01:00
Karl Tauber
d2109cef86 Theme Editor: update open tabs when .properties files were added or removed to directory (on window activation) 2020-12-29 23:12:23 +01:00
Karl Tauber
cda146366c Theme Editor: auto-reload .properties files on window activation, if modified outside 2020-12-29 18:30:52 +01:00
Karl Tauber
678b879a01 Theme Editor:
- open all .properties files in passed directory in tabs
- basic menu bar added (Save, Exit)
- auto-save files on window deactivation and app exit
2020-12-28 20:38:48 +01:00
Karl Tauber
4c885c5e7b CHANGELOG.md: added PR #229 2020-12-23 12:31:16 +01:00
Karl Tauber
d5002b1c33 Merge pull request #229
TextField Placeholder now honors the right inset
2020-12-23 12:18:33 +01:00
Karl Tauber
4f8b6d6b28 UIDefaultsLoader:
- changed "globals" to "wildcard replacements"
- strict checking for background/foreground keys
2020-12-23 11:14:26 +01:00
Karl Tauber
66dab41539 properties: added spaces around '=' for easier reading 2020-12-23 10:52:42 +01:00
Niklas
9e4940228d TextField now honours right component inset
If the placeholder can't be drawn fully, we clip it by adding an
ellipse.
2020-12-23 09:26:30 +01:00
Karl Tauber
cbb11ebb03 ComboBox, Spinner and SplitPaneDivider: support "pressed" feedback on arrow buttons 2020-12-23 00:02:58 +01:00
Karl Tauber
073a25f381 release 0.46 2020-12-20 18:42:23 +01:00
Karl Tauber
40592ab876 FlatUIUtils: fixed javadoc warnings 2020-12-20 18:34:13 +01:00
Karl Tauber
bbfe624b51 Merge pull request #222 into master
AnimatedIcon
2020-12-20 18:26:09 +01:00
Karl Tauber
a2af9e4c65 JIDE: RangeSlider: clicking on track now immediately moves the thumb to mouse location and starts dragging the thumb 2020-12-20 18:24:40 +01:00
Karl Tauber
0123a8895f JIDE: updated UI defaults dumps for commit ef065d31a0 (support TristateCheckBox) 2020-12-20 17:33:42 +01:00
Karl Tauber
53854a4d13 Slider: snap to ticks is now done while dragging the thumb 2020-12-20 17:32:01 +01:00
Karl Tauber
4fdd44858f Slider: clicking on track now immediately moves the thumb to mouse location and starts dragging the thumb 2020-12-20 13:32:10 +01:00
Karl Tauber
3c58879ce5 Slider: fixed painting of colored track if JSlider.inverted is true 2020-12-19 17:01:34 +01:00
Karl Tauber
a7c6a881b3 Extras: FlatTriStateCheckBox reworked 2020-12-19 16:13:12 +01:00
Karl Tauber
ef065d31a0 JIDE: support TristateCheckBox 2020-12-19 13:34:53 +01:00
Karl Tauber
d059d6b448 README.md: new projects using FlatLaf:
- jEnTunnel
- JPass
- Linotte
- MEKA
- Shutter Encoder
- ThunderFocus
- lectureStudio
2020-12-18 16:05:56 +01:00
Karl Tauber
2d0a6f1bec README.md: new projects using FlatLaf:
- JOSM
- Novel-Grabber
- Android Tool
2020-12-18 16:04:43 +01:00
Karl Tauber
a3cc5a1938 README.md: added descriptions to projects using FlatLaf 2020-12-18 14:34:40 +01:00
Karl Tauber
435068515a always reset our graphics rendering hints
(this is usually not necessary because each component gets its own instance of Graphics when painting, but resetting may avoid side effects if our paint methods are invoked directly)
2020-12-18 13:35:17 +01:00
Karl Tauber
956001dbd7 avoid painting text with our rendering hints enabled to avoid antialiased text in some components if text antialiasing is disabled in system (issue #227) 2020-12-18 12:22:27 +01:00
Karl Tauber
460f0d9dee UIScale: fixed NPE in getSystemScaleFactor(Graphics2D) when using Batik SVGGraphics2D (issue #226) 2020-12-15 11:25:00 +01:00
Karl Tauber
5155ec93c9 ToolTip: fixed drop shadow for wide tooltips (issue #224; regression since fixed issue #142) 2020-12-15 11:19:30 +01:00
Karl Tauber
8bb8883e22 IntelliJ Themes: added flag whether a theme is dark to FlatAllIJThemes.INFOS. (issue #221) 2020-12-12 18:54:42 +01:00
Karl Tauber
ffb7a6dfbb README.md:
- added demo download section
- added link to javadoc of extras components
2020-12-12 14:45:08 +01:00
Karl Tauber
176de6f245 README.md: simplified download sections of subprojects 2020-12-12 14:21:07 +01:00
Karl Tauber
11f9740dbf Extras: added support for JComponent.outline client property (issue #117) 2020-12-12 13:59:58 +01:00
Karl Tauber
42a91ba26c Extras: renamed SVG utility class from com.formdev.flatlaf.extras.SVGUtils to com.formdev.flatlaf.extras.FlatSVGUtils 2020-12-12 12:21:48 +01:00
Karl Tauber
234003e2b1 Extras: Renamed tri-state check box class from
`com.formdev.flatlaf.extras.TriStateCheckBox` to
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`
2020-12-12 00:33:51 +01:00
Karl Tauber
534384438b Extras: added extension class for JTabbedPane (issue #117) 2020-12-11 23:44:52 +01:00
Karl Tauber
ab51f35d5d Extras: added extension classes for JEditorPane, JSpinner, JTextArea and JTextPane; added minimumWidth and roundRect properties (issue #117) 2020-12-11 18:05:58 +01:00
Karl Tauber
511a4044d7 Extras: added extension classes for JButton and JToggleButton (issue #117) 2020-12-11 17:18:35 +01:00
Karl Tauber
821efaff40 Extras: removed duplicate enums in text components (issue #117) 2020-12-11 14:01:42 +01:00
Karl Tauber
91bc994532 Extras: made enums in text components public (issue #117) 2020-12-11 13:39:51 +01:00
Karl Tauber
1323b46ac7 Extras: added extension class for JProgressBar (issue #117) 2020-12-11 13:28:55 +01:00
Karl Tauber
3a8b30ca8e Extras: removed extension interfaces and moved methods to components classes because:
- Javadoc for components that implement extension interfaces are useless because they do not include default methods from the extension interface
- GUI builders do not recognize default methods from the extension interface and it is not possible to edit extension properties in GUI builder
- the idea of adding the extension interface to own components can be also achieved by changing superclass of own component

(issue #117)
2020-12-11 13:24:14 +01:00
Karl Tauber
923d58519f Extras: added extension interfaces and classes for JComboBox, JFormattedTextField, JPasswordField, JScrollBar, JScrollPane and JTextField (issue #117) 2020-12-10 20:30:27 +01:00
Karl Tauber
eabb1f84f6 Table and TableHeader: fixed missing right vertical grid line if using table as row header in scroll pane (issues #152 and #46) 2020-12-09 23:04:04 +01:00
Karl Tauber
cfbe44b946 TableHeader: fixed position of column separators in right-to-left component orientation; do not paint anything if column count is zero 2020-12-09 00:33:01 +01:00
Karl Tauber
81c35eab46 SwingX: fixed striping background highlighting color (e.g. alternating table rows) in dark themes
Table: made grid lines slightly darker/lighter
2020-12-07 12:28:31 +01:00
Karl Tauber
a1c7c29113 FlatComponents2Test: added SwingX JXTable and JXTreeTable to test extended/customized tables 2020-12-07 12:21:34 +01:00
Karl Tauber
1293e2a074 AnimatedIcon added (for future animations) (issue #66) 2020-12-05 17:57:06 +01:00
Karl Tauber
b5deca7f22 release 0.45 2020-12-05 14:32:02 +01:00
Karl Tauber
604ba236c0 Merge pull request #217 into master
MenuBar.underlineSelectionColor
2020-12-05 12:00:50 +01:00
Karl Tauber
14df490b2a MenuBar: support different underline menu selection style UI defaults for MenuBar and MenuItem. (PR #217; issue #216) 2020-12-05 11:56:38 +01:00
Karl Tauber
dd2f73e8ad Merge pull request #214 into master
Slider redesign
2020-12-04 22:43:05 +01:00
Karl Tauber
56bfdc8ef9 Slider: updated CHANGELOG.md 2020-12-04 22:29:32 +01:00
Karl Tauber
91dbf1e144 Sider: text baseline layout in FlatComponentsTest 2020-12-04 21:08:12 +01:00
Karl Tauber
e07ae90d09 TabbedPane: no longer add (internal) tab close button component as child to JTabbedPane (issue #219) 2020-11-29 01:32:38 +01:00
Karl Tauber
5ef0c9aae1 Table: fixed unstable grid line thickness when scaled on HiDPI screens (issue #152) 2020-11-28 23:20:58 +01:00
Karl Tauber
aefed7c481 Table: do not paint last vertical grid line if auto-resize mode is not off (issue #46) 2020-11-28 23:15:37 +01:00
Karl Tauber
0d66d9f9a3 FlatCheckBoxIcon:
- added parameter `Component c` to all paint methods so that subclasses can access component states
- extracted methods to get colors and selected/indeterminate state
2020-11-28 12:29:13 +01:00
Karl Tauber
d0ffc4f979 TabbedPane: support hiding tab area if it contains only one tab 2020-11-28 11:21:46 +01:00
mmatessi
f149d2b7cd MenuBar.underlineSelectionColor 2020-11-27 19:14:28 +01:00
Karl Tauber
21a12b8dd4 added Flat*Laf.installLafInfo() methods to add a Laf to the set of available Lafs
uses `UIManager.installLookAndFeel( new UIManager.LookAndFeelInfo(...) )`
2020-11-23 22:14:42 +01:00
Karl Tauber
6c8b8e8949 Popup: allow forcing to heavy weight popup windows (issue #189) 2020-11-23 18:09:44 +01:00
Karl Tauber
539737d1c5 ScrollBar: fixed NPE in NetBeans GUI builder when using JCalendar component (issue #194) 2020-11-23 17:19:04 +01:00
Karl Tauber
33ff5828da IntelliJ Themes:
- added "Gradianto Nature Green" theme
- updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox" and "One Dark" themes
2020-11-22 17:10:11 +01:00
Karl Tauber
1fb0783808 Slider: fixed slider colors in IntelliJ themes 2020-11-21 18:18:06 +01:00
Karl Tauber
b5e7aa8553 Slider: fixed painting issues:
- needle of directional thumb was not painted while dragging
- artifacts on HiDPI screen while dragging
- cut off focus indicator on HiDPI screen
2020-11-21 18:18:06 +01:00
Karl Tauber
1d3ce76b27 Slider: replaced Slider.thumbWidth with Slider.thumbSize to support non-square sized thumbs (as used in Windows 10) 2020-11-21 18:18:06 +01:00
Karl Tauber
0101171159 UIDefaultsLoader: added fadein(), fadeout(), fade() and spin() color functions (inspired by Less CSS) 2020-11-21 18:18:06 +01:00
Karl Tauber
8b8ed0b9ff Slider:
- compute useful baseline for horizontal orientation so that the track is vertically centered
- no baseline for vertical orientation
2020-11-21 18:18:06 +01:00
Karl Tauber
413b60e630 Slider:
- changed default color to bluish
- made track thinner (2px, was 3px)
- made thumb larger (12px, was 11px)
- added thumb outline focus indicator (4px wide)
- slider component height increased from 11px to 20px
- support painting thumb border
- support different colors for thumb background and colored track
2020-11-21 18:18:06 +01:00
Karl Tauber
10b2a94c70 JIDE: RangeSlider: avoid that middle track is painted over first thumb 2020-11-21 18:18:06 +01:00
Karl Tauber
e337e5bbd8 JIDE: RangeSlider:
- updated with latest changes from FlatSliderUI
- use static FlatSliderUI methods for thumb painting
- hover/pressed feedback on single thumb
- hover/pressed feedback on middle track and both thumbs
- added JSlider components to FlatRangeSliderTest for easier testing/comparing
2020-11-21 18:18:06 +01:00
Karl Tauber
6e55e0a183 Slider:
- hover feedback only when mouse is over thumb
- pressed feedback added
- separate disabled colors for track and thumb
- made private fields protected
2020-11-21 18:18:06 +01:00
Karl Tauber
8ee1d26935 Merge branch into master 2020-11-21 17:53:17 +01:00
Karl Tauber
80bdf69eaf GitHub Actions: build on all branches; produce snapshots only on master branch; disable Travis CI 2020-11-21 17:31:52 +01:00
Karl Tauber
18e838bffd GitHub Actions: exclude javadoc and sources from build artifacts 2020-11-21 15:21:33 +01:00
Karl Tauber
d95b1b0ec4 GitHub Actions: upload build artifacts 2020-11-21 15:08:07 +01:00
Karl Tauber
d16a3c117b GitHub Actions: 3rd attempt to test release job without publishing 2020-11-21 14:45:02 +01:00
Karl Tauber
d04ec982ab GitHub Actions: 2nd attempt to test release job without publishing 2020-11-21 14:42:09 +01:00
Karl Tauber
cce99c803e GitHub Actions: test release job without publishing 2020-11-21 14:32:36 +01:00
Karl Tauber
19ed538573 GitHub Actions: added secrets for snapshot and release jobs 2020-11-21 14:24:56 +01:00
Karl Tauber
a1f78345e6 GitHub Actions: use separate jobs for snapshots and releases to be sure that build succeeded for all Java versions 2020-11-21 14:04:32 +01:00
Karl Tauber
f8c7ccf064 GitHub Actions: run if tags are pushed 2020-11-21 13:38:21 +01:00
Karl Tauber
4d5242cd61 GitHub Actions: fixed typo in snapshot step condition 2020-11-21 12:06:18 +01:00
Karl Tauber
7ad176f98d GitHub Actions: info step added 2020-11-21 12:02:08 +01:00
Karl Tauber
57df7d28b5 GitHub Actions: added steps for snapshots and releases 2020-11-21 11:51:41 +01:00
Karl Tauber
f784ff2c84 GitHub Actions: test also against Java 9 2020-11-21 01:57:37 +01:00
Karl Tauber
a0f6affb68 GitHub Actions: cache gradle wrapper; fixed key for caching gradle cache 2020-11-21 01:37:28 +01:00
Karl Tauber
0c679167fa GitHub Actions: cache gradle dependencies 2020-11-21 00:48:27 +01:00
Karl Tauber
4fe707e519 GitHub Actions: initial commit 2020-11-21 00:19:46 +01:00
Karl Tauber
d83704b7cb FlatPaintingTest: added test case for circular components 2020-11-20 11:57:24 +01:00
Karl Tauber
2177ee45cc FlatUIUtils: replaced quadratic curves with bezier curves in createRoundRectanglePath() to get perfect circle when using large arcs
(currently only used for SwingX)
2020-11-20 11:50:03 +01:00
Karl Tauber
ccd4f99aea Window decorations: removed 1px window border if window is in full-screen mode (issue #212) 2020-11-20 10:12:28 +01:00
Karl Tauber
cd6b55c846 Demo: Alt+UP and Alt+DOWN now switch to previous/next theme 2020-11-20 00:40:10 +01:00
Karl Tauber
d923c8df81 Window decorations: title bar was not hidden if window is in full-screen mode (issue #212) 2020-11-18 23:31:04 +01:00
Karl Tauber
59879f493e FlatTestFrame: fixed exception when using FlatPropertiesLaf and changing scale factor, which re-sets the current Laf 2020-11-18 18:45:13 +01:00
Karl Tauber
06cab0d4b5 updated svgSalamander to version 1.1.2.4 2020-11-18 18:34:12 +01:00
Karl Tauber
a16db38a6f Testing: FlatBaselineTest added 2020-11-18 18:32:08 +01:00
Karl Tauber
de93e19a80 JIDE: RangeSlider: updated UI defaults dumps 2020-11-17 12:13:01 +01:00
Karl Tauber
47bb7d0de7 JIDE: RangeSlider: added to CHANGELOG.md and README.md 2020-11-16 22:26:49 +01:00
Karl Tauber
896e808db4 JIDE: RangeSlider: removed nested panel from FlatRangeSliderTest 2020-11-16 22:19:09 +01:00
Karl Tauber
6fe6d1ffa0 JIDE: RangeSlider: reordered methods and slightly changed formatting to make it easier to compare with FlatRangeSliderUI 2020-11-16 22:04:08 +01:00
Karl Tauber
4c6f7a66e2 Merge pull request #209 into master
Add RangeSlider support
2020-11-16 21:25:50 +01:00
Karl Tauber
4b5646ec88 release 0.44 2020-11-15 11:29:07 +01:00
Karl Tauber
66a5f350da Merge pull request #211 into master
TabbedPane: scroll buttons on both sides of the tab area
2020-11-15 11:20:43 +01:00
Karl Tauber
f9e34cbab7 TabbedPane: support specifying default tab layout policy for all tabbed panes via UI value TabbedPane.tabLayoutPolicy 2020-11-14 18:46:39 +01:00
Karl Tauber
634f7b5ba3 CHANGELOG.md: added/updated latest TabbedPane changes 2020-11-14 00:48:37 +01:00
Karl Tauber
7dbc6ff8a3 TabbedPane: fixes
- avoid that tab area "jump" to the right/top when backward button becomes hidden
- scroll arrow buttons were not always hidden in right-to-left horizontal layout
2020-11-14 00:24:38 +01:00
Karl Tauber
afccdc4749 Demo: "Tabs" tab: improved demo of leading and trailing tab area components by using toolbars 2020-11-13 23:25:19 +01:00
Karl Tauber
c98ec041d4 Demo: "Tabs" tab: added "Scroll buttons policy", "Scroll buttons placement" and "Tabs popup policy" configuration for PR #211 2020-11-13 22:26:14 +01:00
Karl Tauber
9e0c62092e TabbedPane: updated UI defaults dumps for previous checkins 2020-11-13 22:23:28 +01:00
Karl Tauber
9aea006f50 TabbedPane: fixed typo in previous commit 2020-11-13 18:20:00 +01:00
Karl Tauber
c16c3759cf TabbedPane:
- support forward/backward scroll buttons on both sides of the tab area (new default)
- optionally: not applicable scroll buttons are hidden (new default)
- changed configuration
  - removed TabbedPane.hiddenTabsNavigation
  - added TabbedPane.tabsPopupPolicy, TabbedPane.scrollButtonsPolicy and TabbedPane.scrollButtonsPlacement
- made scroll arrows larger

(issue #40; replaces PR #195)
2020-11-13 17:34:46 +01:00
Karl Tauber
cbc1fe27ef TabbedPane: more fallbacks to find tab name for "more tabs" popup in case that tab title is not set (issue #207; PR #190) 2020-11-13 11:48:03 +01:00
mmatessi
f57dbf94c8 FlatJideOssDefaultsAddon reformat 2020-11-13 09:47:32 +01:00
mmatessi
c0f15d2e6f FlatRangeSliderUI fix change label foreground 2020-11-13 09:42:26 +01:00
mmatessi
cb525fafb6 FlatSliderUI extends BasicSliderUI 2020-11-12 13:02:16 +01:00
mmatessi
5cae3a8141 add RangeSlider support 2020-11-11 16:57:40 +01:00
Karl Tauber
8594e78287 TabbedPane: search for label or text component in custom tab component and use its text in "more tabs" popup (issue #207; PR #190) 2020-11-10 15:28:25 +01:00
Karl Tauber
5b8f922273 FlatSVGIcon: getImage() now returns a multi-resolution image (on Java 9+) for HiDPI disabled icons in other LaFs that support multi-resolution images when producing disabled icons in LookAndFeel.getDisabledIcon() (e.g. Windows or Nimbus Laf) (issue #205) 2020-11-10 11:56:59 +01:00
Karl Tauber
847b41752c FlatSVGIcon: icons were not painted in disabled labels and disabled tabs (issue #205) 2020-11-10 11:22:34 +01:00
Karl Tauber
7c08489cb3 UIDefaultsLoader: minor optimization (avoid String.substring() and avoid double searching for '.') 2020-11-06 23:47:06 +01:00
Karl Tauber
605c77ecbc IntelliJ Themes: added getName() method to all InttelliJ Laf classes so that they return same name as defined in class FlatAllIJThemes (issue #201) 2020-11-06 19:13:11 +01:00
Karl Tauber
fd0c2a5cd1 IntelliJ Themes: added suffix "(Material)" to names of all Material UI Lite themes to avoid duplicate theme names (issue #201) 2020-11-06 18:38:21 +01:00
Karl Tauber
a80790fc8e TabbedPane:
- use rounded rectangles for buttons in tab area
- "pressed" background for buttons in tab area
- fill background of buttons in tab area
- use derived colors for hover and pressed
- fixed missing arrow in "more tabs" button at larger scaling
2020-11-06 17:30:29 +01:00
Karl Tauber
206d449d0d FlatLaf.properties: added "allowed values" as comments 2020-11-05 19:19:13 +01:00
Karl Tauber
2323dc099f TabbedPane: always use chevron arrows (even in IntelliJ and Darcula themes) 2020-11-05 18:59:28 +01:00
Karl Tauber
642583479f default arrow type changed from "triangle" to "chevron" (does not change any theme) 2020-11-05 18:55:08 +01:00
Karl Tauber
082e5842d0 removed FlatClientProperties.clientPropertyChoice() 2020-11-05 18:35:36 +01:00
Karl Tauber
c67ba02839 UI defaults dumps updated for PR #202 2020-11-05 17:12:21 +01:00
Karl Tauber
4c6cb7618f Merge pull request #202 into master
TextField: allow select all on mouse click
2020-11-05 17:07:29 +01:00
basix86
c15100f129 Update FlatLaf.properties
fixed previous behavior selectAllOnMouseClick
2020-11-05 10:47:47 +01:00
basix86
6dfb3cc84e Update FlatCaret.java
missing space
2020-11-05 10:46:12 +01:00
Karl Tauber
18d8c7d086 SplitPane: added grip to divider (issue #179) 2020-11-04 11:52:50 +01:00
mmatessi
ab3adf4ae3 selectAllOnMouseClick 2020-11-04 10:16:23 +01:00
basix86
7e6619af00 Merge pull request #1 from JFormDesigner/master
update fork
2020-11-04 09:48:40 +01:00
Karl Tauber
a7e2a10403 TabbedPane: support horizontal alignment of tab title and icon 2020-11-03 22:20:55 +01:00
Karl Tauber
3a784375d0 SplitPane: support tooltip texts in splitpane client properties (issue #198) 2020-11-02 15:07:57 +01:00
Karl Tauber
b8c9433259 SplitPane: added tooltips to expand/collapse buttons (issue #198) 2020-11-02 12:13:06 +01:00
Karl Tauber
815d9d6012 SplitPane: hide not applicable expand/collapse buttons (issue #198) 2020-11-02 11:51:51 +01:00
Karl Tauber
feb91aa056 Demo: re-designed "Tabs" tab to show features added in PRs #187, #190, #192, #193 and #199 2020-11-01 21:36:46 +01:00
Karl Tauber
cd264586ca TabbedPane: fixed missing arrow in "more tabs" button when changing tabPlacement to left/right and back to top/bottom (PR #190) 2020-11-01 17:19:12 +01:00
Karl Tauber
c6d561f2df Demo: moved split panes from "SplitPane & Tabs" tab to "More Components" tab (to make room for more tabbed pane features) 2020-11-01 16:45:05 +01:00
Karl Tauber
6167c5f855 TabbedPane: calculate correct preferred size for tabbed panes without any content 2020-11-01 16:38:28 +01:00
Karl Tauber
1a31cb96b8 TabbedPane: disable wheel scrolling if application has added its own mouse wheel listener (PR #187) 2020-11-01 16:22:05 +01:00
Karl Tauber
9b8df64c35 FlatContainerTest: always use for-loop to modify all tabbed panes 2020-11-01 15:59:47 +01:00
Karl Tauber
a47565afec Merge pull request #199 into master
TabbedPane: tab area alignment; min/max tab widths; tab icon placement; tab width mode
2020-11-01 15:56:10 +01:00
Karl Tauber
c2ee815cbe TabbedPane: fixed clipping when painting tab selection in scroll layout 2020-10-31 14:20:35 +01:00
Karl Tauber
e45a2df6b6 FlatContainerTest: test disabled tab icons 2020-10-31 11:30:06 +01:00
Karl Tauber
a19979c233 FlatContainerTest: test HTML tab titles 2020-10-30 10:50:17 +01:00
Karl Tauber
e2a297fa40 TabbedPane: support left, right, top and bottom tab icon placement 2020-10-30 01:47:14 +01:00
Karl Tauber
df13b338b2 TabbedPane: support specifying tab area insets via client property 2020-10-29 22:42:41 +01:00
Karl Tauber
da9d7a0dee TabbedPane: support equal and compact tab width modes 2020-10-29 19:26:09 +01:00
Karl Tauber
0374c65159 TabbedPane: support alignment of tab area (leading, trailing, center or fill) 2020-10-29 16:11:30 +01:00
Karl Tauber
71b1e07ba6 TabbedPane: support minimum and maximum tab widths 2020-10-29 16:10:27 +01:00
Karl Tauber
c3781dc4b5 CHANGELOG.md: added PR references 2020-10-27 17:16:25 +01:00
Karl Tauber
dc92d0913c TabbedPane: trailing component now fills all available horizontal space (PR #192) 2020-10-27 16:29:58 +01:00
Karl Tauber
a5adf29001 FlatContainerTest:
- reorganized tabbed pane control panel
- added "tab area insets" checkbox
- made text of leading component shorter
2020-10-27 14:07:14 +01:00
Karl Tauber
8861bfe4fa FlatContainerTest:
- replaced "more tabs" checkbox and spinner with "tab count" spinner
- avoid right-to-left for tabbed pane control panel
- use other color for trailing component
2020-10-27 10:44:14 +01:00
Karl Tauber
c8d280f418 TabbedPane: improved/fixed placement of tab close button on smaller tab insets or smaller tab height (PR #193) 2020-10-26 23:37:02 +01:00
Karl Tauber
09c98359af fixed javadoc warnings and errors 2020-10-26 15:16:37 +01:00
Karl Tauber
6f8a7471c2 SVGUtils: support creating window images from SVG files that are not 16x16 (issue #196) 2020-10-26 15:13:37 +01:00
Karl Tauber
4c141fe47c FlatSVGIcon now allows specifying icon width and height in constructors (issue #196) 2020-10-26 13:53:25 +01:00
Karl T
b37ff348fb Merge pull request #197 from kingthorin/patch-1
Minor correction
2020-10-26 11:17:54 +01:00
kingthorin
09798d33b0 Minor correction
“not be” vs “be not”.

Just noticed while browsing around.
2020-10-24 16:22:21 -04:00
Karl Tauber
717ab95fbe Merge pull request #193 into master
TabbedPane closable tabs
2020-10-22 22:40:30 +02:00
Karl Tauber
3f616e3608 TabbedPane: for right-to-left always use "more tabs" button for horizontal scrolling because methods scrollForward() and scrollBackward() in class BasicTabbedPaneUI.ScrollableTabSupport do not work for right-to-left 2020-10-22 11:07:42 +02:00
Karl Tauber
c590157561 TabbedPane: support specifying tooltip text for tab close buttons via client property 2020-10-21 23:13:01 +02:00
Karl Tauber
2b50431081 TabbedPane: fixed scaling of client property "JTabbedPane.tabHeight"; avoid storing scaled values in UI delegate 2020-10-21 11:24:06 +02:00
Karl Tauber
6d38e44f91 TabbedPane: support specifying tab insets via client property 2020-10-21 01:14:26 +02:00
Karl Tauber
9bc656a5c5 TabbedPane: fixed NPE in scroll layout when removing last tab 2020-10-20 10:37:31 +02:00
Karl Tauber
700bb9b567 TabbedPane: support closable tabs (issue #40) 2020-10-20 09:37:28 +02:00
Karl Tauber
8ccda81d9a Merge pull request #192 into master
TabbedPane custom components on left and right sides of tabs area
2020-10-19 23:35:17 +02:00
Karl Tauber
3818790ced TabbedPane: support adding custom components to left and right sides of tabs area if wrap layout is used (issue #40) 2020-10-17 18:17:45 +02:00
Karl Tauber
c34ce389a4 TabbedPane: do not include preferred/minimum size of leading/trailing components in calculating preferred/minimum size of tabbed pane, because the largest tab content determines the size 2020-10-17 16:46:56 +02:00
Karl Tauber
15718cdb46 TabbedPane: support adding custom components to left and right sides of tabs area if scroll backward/foreward buttons are used (issue #40)
this also fixes some minor layout issues when using tabAreaInsets and arrow buttons
2020-10-17 15:19:39 +02:00
Karl Tauber
10746a454a TabbedPane: support adding custom components to left and right sides of tabs area if "more tabs" button is used (issue #40) 2020-10-17 11:30:04 +02:00
Karl Tauber
f0fd02e81f Merge pull request #190 into master
Tabbedpane "Show Hidden Tabs" button
2020-10-16 23:10:49 +02:00
Karl Tauber
bfaac6d164 TabbedPane: fixed: content separator was painted at wrong position if using TabbedPane.tabAreaInsets (regression since changing TabbedPane.tabsOverlapBorder to false in commit c58f5a6ca7)
exit paintContentBorder() early if content separator is not painted
2020-10-16 21:13:06 +02:00
Karl Tauber
a909f1012a TabbedPane: finally get rid of the cropped edge (issue #40) 2020-10-16 12:25:04 +02:00
Karl Tauber
201581a07c TabbedPane: support right-to-left if "more tabs" button is used (issue #40) 2020-10-16 00:24:02 +02:00
Karl Tauber
8cef5ecf7e popups using JToolTip components did not respect their location (fixes #188; regression in 0.42 in fix for #164) 2020-10-15 17:49:34 +02:00
Karl Tauber
2c1075f471 TabbedPane: do not clip title on left tabs when scrolled 2020-10-15 14:53:42 +02:00
Karl Tauber
1f5e08fdc6 TabbedPane: fixed clipping title if "more tabs" button is used (issue #40) 2020-10-15 13:16:21 +02:00
Karl Tauber
c0408045ef TabbedPane: support specifying hiddenTabsNavigation type per tabbedpane via client property (issue #40) 2020-10-15 10:41:45 +02:00
Karl Tauber
c58f5a6ca7 TabbedPane: replaced forward/backward scrolling arrow buttons with "Show Hidden Tabs" button (issue #40) 2020-10-15 00:10:07 +02:00
Karl Tauber
ae445c9343 Merge pull request #187 into master
TabbedPane wheel scrolling
2020-10-14 22:43:02 +02:00
Karl Tauber
ad7ff2ba0b support painting separator line between window title and content (issue #184) 2020-10-14 22:08:20 +02:00
Karl Tauber
4b7ef6e853 FlatWindowDecorationsTest: added "menu bar visible" checkbox (for testing previous commit) 2020-10-14 13:37:28 +02:00
Karl Tauber
87f2acc2d9 Window decorations: not visible menu bar is now ignored in layout 2020-10-14 13:05:39 +02:00
Karl Tauber
ec2fef02ed Demo: if disabling window decoration ("Options > Window decorations") , which changes the main window, also invoke JDialog.setDefaultLookAndFeelDecorated() to disable window decorations for dialogs 2020-10-14 10:50:54 +02:00
Karl Tauber
ebe0d74dbe FlatInspector: make sure that glass pane is not opaque, which is not the case in all Lafs 2020-10-14 10:36:29 +02:00
Karl Tauber
029dc51f8b Testing: updated 3rd party Lafs 2020-10-14 10:30:44 +02:00
Karl Tauber
3fc85cd7b2 TabbedPane: support precise scrolling tabs with trackpad (issue #40) 2020-10-12 00:33:23 +02:00
Karl Tauber
a46bdef079 Animator: reuse timer instance
(cherry picked from commit 0888fd8fb5d18c36886bf958ac5a5e44bf75618d)
2020-10-11 22:51:33 +02:00
Karl Tauber
3de489f693 TabbedPane:
- fixed jittery animated scrolling tabs
- support disabling animated scrolling with "ScrollPane.smoothScrolling=false"
2020-10-09 16:27:52 +02:00
Karl Tauber
eddb9eee46 TabbedPane: make sure that tab stays hover highlighted when mouse is moved to custom tab component that handles mouse events (e.g. a close button)
refactored PropertyChangeListener to class Handler
2020-10-09 10:19:17 +02:00
Karl Tauber
5b0c96cd6d TabbedPane: avoid scrolling selected tab back into visible area (after wheel scrolling) if the mouse is over a custom tab component that handles mouse events (e.g. a close button) 2020-10-08 23:46:43 +02:00
Karl Tauber
15ac77107f TabbedPane: increased size of scroll arrow buttons (issue #40) 2020-10-07 19:09:19 +02:00
Karl Tauber
a7c906091c TabbedPane: use animation for scrolling tabs with mouse wheel (issue #40) 2020-10-07 17:54:12 +02:00
Karl Tauber
de870c546c TabbedPane: repeat scrolling as long as arrow buttons are pressed (issue #40) 2020-10-07 16:05:26 +02:00
Karl Tauber
2f3427e6ad TabbedPane: scroll selected tab into visible area (500ms delayed) if mouse exits scroll viewport after wheel scrolling (issue #40) 2020-10-07 13:29:15 +02:00
Karl Tauber
203426bd55 TabbedPane: support scrolling tabs with mouse wheel (if tabLayoutPolicy is SCROLL_TAB_LAYOUT) (issue #40) 2020-10-07 12:25:46 +02:00
Karl Tauber
16242080e0 README.md: screenshots of dark themes updated 2020-10-06 13:37:37 +02:00
Karl Tauber
57655d8859 release 0.43 2020-10-05 14:36:54 +02:00
Karl Tauber
62ffd57108 Windows: made scaling compatible with Windows OS scaling, which distinguish between "screen scaling" and "text scaling" (issue #175) 2020-10-05 13:14:44 +02:00
Karl Tauber
8db05f47b5 FlatChooserTest: grow file chooser when resizing window 2020-10-04 23:59:28 +02:00
Karl Tauber
c684761eef ComboBox: limit popup width to screen width for very long items (issue #182) 2020-10-04 18:59:54 +02:00
Karl Tauber
0a8ece8c9c no longer use static fields for shared instances of UI delegates because this makes problems in GUI builders that support Laf switching and use more than one FlatLaf theme at the same time 2020-10-04 14:21:00 +02:00
Karl Tauber
01058bde1b UI defaults inspector: fixed key rendering for Nimbus Laf 2020-10-04 14:03:39 +02:00
Karl Tauber
9c2c03cddb Spinner: fixed NullPointerException in case that arrow buttons were removed to create button-less spinner (issue #181) 2020-10-04 13:58:23 +02:00
Karl Tauber
f0778a83a0 CheckBoxMenuItem and RadioButtonMenuItem: improved checkmark background colors of selected menu items that have also an icon 2020-09-25 00:07:25 +02:00
Karl Tauber
b86ae1f122 FileChooser: fixed localizing special Windows folders (e.g. "Documents") and enabled hiding known file extensions (if enabled in Windows Explorer) (issue #178) 2020-09-24 22:27:10 +02:00
Karl Tauber
dfd6831b02 ComboBox: if using own JTextField as editor, default text field border is now removed to avoid duplicate border 2020-09-24 22:17:10 +02:00
Karl Tauber
a4ddc13c1a TabbedPane: added some missing UI defaults 2020-09-24 22:05:07 +02:00
Karl Tauber
fd63a1b7c2 TabbedPane: support hiding separator between tabs and content area via client property 2020-09-24 22:03:39 +02:00
Karl Tauber
d83c3689d0 TabbedPane:
- made tabs separator color lighter in dark themes so that it is easier to recognize the tabbed pane
- added top and bottom tab insets to avoid that large tab icons are painted over active tab underline
2020-09-24 15:18:45 +02:00
Karl Tauber
d52bf9d318 FlatScreenInfo: output warning if screens intersect (issue #177) 2020-09-23 21:44:00 +02:00
Karl Tauber
80f56dec15 travis: added openjdk15 2020-09-23 19:03:53 +02:00
Karl Tauber
358c226b96 update to Gradle 6.6.1
./gradlew wrapper --gradle-version=6.6.1
2020-09-23 18:42:12 +02:00
Karl Tauber
9de9983416 laf.dark flag added to UI defaults 2020-09-20 10:59:50 +02:00
Karl Tauber
c9da4fcaf1 UI defaults: dumps moved out of resources folder to dumps/uidefaults folder 2020-09-18 14:41:15 +02:00
Karl Tauber
932ca6f9d4 FlatDesktopPropertiesDump tool added
developed to find out whether it is possible to detect Windows screen scaling and text scaling factors in Java 8, which seems to be possible by using "win.defaultGUI.font" desktop property

issue #175
2020-09-18 14:24:30 +02:00
Karl Tauber
4487c9985c release 0.42 2020-09-17 15:51:27 +02:00
Karl Tauber
a53ce99977 PasswordField: support disabling Caps Lock warning icon (issue #172) 2020-09-17 15:34:57 +02:00
Karl Tauber
5444719895 Extras: added screenshots to README.md and instructions for using UI inspectors 2020-09-17 14:20:59 +02:00
Karl Tauber
b66139281d FlatHtmlTest: fixed labels and added HTML tooltips 2020-09-17 13:50:56 +02:00
Karl Tauber
8925c27eb9 ToolTip: avoid that tooltip hides owner component (issue #164) 2020-09-17 13:32:28 +02:00
Karl Tauber
99be346387 FlatWindowDecorationsTest: disable "add/remove/change menu" buttons if shown in dialog, which does not have a menubar 2020-09-17 13:28:02 +02:00
Karl Tauber
81d46ba8ee Demo: show simple dialog for "File > New"
(used to test previous commit)
2020-09-17 13:26:45 +02:00
Karl Tauber
ef4c467b20 fixed occasional wrong positioning of heavy weight popups when using multiple screens with different scaling factors (issue #166)
workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
2020-09-17 11:43:20 +02:00
Karl Tauber
44d196fb8c Demo: menu item "Options > Window decorations" did exit Demo
(regression in commit ee6a1da709)
2020-09-16 22:52:19 +02:00
Karl Tauber
867c4fff58 fixed compiling flatlaf-extras on Java 9+ 2020-09-15 18:06:56 +02:00
Karl Tauber
5643546117 UI defaults inspector:
- add placeholder text to filter field
- fixed menu item text in Demo
2020-09-15 17:56:20 +02:00
Karl Tauber
549832ba96 UI defaults inspector:
- fixed: indicate when a LaF UI value was overridden with UIManager.put(key,value)
- auto-refresh if UIManager.put(key,value) was invoked
2020-09-15 17:30:13 +02:00
Karl Tauber
a8744b2bb4 made disabled text color slightly lighter in dark themes for better readability (issue #174) 2020-09-15 15:47:12 +02:00
Karl Tauber
e292d3444c UI defaults inspector: avoid that restored window bounds are outside of screens 2020-09-15 15:09:03 +02:00
Karl Tauber
ee6a1da709 Demo: exit even if UI defaults inspector window is shown 2020-09-15 13:44:00 +02:00
Karl Tauber
8c15bc746b UI defaults inspector: render values of type Border, GrayFilter, Object[] and int[]; paint icons with light gray background 2020-09-15 13:16:01 +02:00
Karl Tauber
aebb083180 UI defaults inspector: indicate when a LaF UI value was overridden with UIManager.put(key,value) 2020-09-15 12:02:51 +02:00
Karl Tauber
5438549b6d UI defaults inspector: horizontally align rgb() and hsl() in color values 2020-09-15 10:56:28 +02:00
Karl Tauber
0077708235 UI defaults inspector: install it in FlatTestFrame and FlatThemeFileEditor 2020-09-15 00:32:04 +02:00
Karl Tauber
2fd99ec9f3 UI defaults inspector: support sorting 2020-09-15 00:16:57 +02:00
Karl Tauber
0d266c4990 UI defaults inspector: use short format for hex colors if possible; use uppercase hex 2020-09-14 23:53:54 +02:00
Karl Tauber
0982675b5f UI defaults inspector: support filter by value
this is also a preparation to support sort by value
2020-09-14 23:13:44 +02:00
Karl Tauber
3bac5d3c80 UI defaults inspector:
- update table if LaF was switched or F5 key pressed
- added LaF name to window title
- close window with ESC key
2020-09-14 21:18:52 +02:00
Karl Tauber
58338f4848 UI defaults inspector: scroll with Up, Down, PageUp and PageDown keys if filter field is focused 2020-09-14 20:35:41 +02:00
Karl Tauber
9c261d3a3f UI defaults inspector: support filter by key and by value type 2020-09-14 18:17:05 +02:00
Karl Tauber
5441ac6640 UI defaults inspector: added separator between component groups and draw component name with lighter color 2020-09-14 15:18:10 +02:00
Karl Tauber
015b04a29a UI defaults inspector: initial commit with basic functionality 2020-09-14 15:16:16 +02:00
Karl Tauber
12ec0abf54 UI defaults: moved some common properties from FlatLightLaf.properties and FlatDarkLaf.properties to FlatLaf.properties 2020-09-12 22:00:17 +02:00
Karl Tauber
c8d461cdee UI defaults: moved "globals" from FlatLightLaf.properties and FlatDarkLaf.properties to FlatLaf.properties 2020-09-12 20:53:23 +02:00
Karl Tauber
faecffeadd TextComponents: fixed text color of disabled text components in dark themes (issue #174) 2020-09-12 18:45:40 +02:00
Karl Tauber
b3c76c21b4 UIDefaultsLoader: moved some code to where it belongs (for previous commit) 2020-09-12 18:38:35 +02:00
Karl Tauber
1697735162 UIDefaultsLoader: changed processing of "globals" so that they are first added to the properties table (instead of directly modifying defaults table), which is then parsed and copied to defaults table
this has the advantage that they can be referenced in other values, which did not work before (because they only existed in `defaults` table)

used for Tree.textForeground
verified with UIDefaultsDump that there are no side effects
2020-09-12 18:13:34 +02:00
Karl Tauber
ecb94bac6d use short color format #RGB (instead of #RRGGBB) where possible 2020-09-11 21:24:00 +02:00
Karl Tauber
7ebeacf16e UIDefaultsDump: dump FlatTestLaf 2020-09-11 21:08:07 +02:00
Karl Tauber
d0079ab66b UIDefaultsLoader: use class loader from FlatLaf.registerCustomDefaultsSource(String, ClassLoader) also for instantiating classes specified in values
see commit b208017117
2020-09-11 17:58:12 +02:00
Karl Tauber
147e400bd6 FlatInspector: limit parent level to real depth at mouse location (issue #169) 2020-09-11 17:37:40 +02:00
Karl Tauber
c44905ea5e InternalFrame: support draggable border for resizing frame inside of the visible frame border (issue #121) 2020-09-04 22:59:09 +02:00
Karl Tauber
98b9df06fe Window decorations: fixed wrong window bounds when resizing window to another screen with different scaling factor (issue #166) 2020-09-04 09:46:12 +02:00
Karl Tauber
02473080a5 Window decorations: fixed wrong window placement when moving window to another screen with different scaling factor (issue #166) 2020-09-03 19:26:52 +02:00
Karl Tauber
c6beb9dc0a Demo: menu items "File > Open" and "File > Save As" now show file choosers 2020-09-03 18:16:28 +02:00
Karl Tauber
dcce14b122 FlatScreenInfo tool added 2020-09-03 15:55:12 +02:00
Karl Tauber
a2ac24ac74 Demo: "SplitPane & Tabs" tab improved 2020-09-03 15:09:28 +02:00
Karl Tauber
600f812f45 Demo: removed too large gap between content panel and control bar 2020-09-03 12:01:49 +02:00
Karl Tauber
e945f46f25 Demo: "Data components" tab: added checkboxes to control table grid and selection 2020-09-03 11:53:50 +02:00
Karl Tauber
c78c653b0a FlatComponents2Test: moved table checkboxes into tableOptionsPanel 2020-09-03 11:26:16 +02:00
Karl Tauber
e0b3663239 FlatComponents2Test: support testing large amount of list/tree/table rows 2020-09-03 11:12:00 +02:00
Karl Tauber
3cc9c98040 Demo:
- "Data components" tab: increase component height if frame is made larger
- "SplitPane & Tabs" tab: increased some gaps and renamed TabbedPane option checkboxes
- "Option Pane" and "Extras" tabs: minor layout improvements
2020-09-02 19:08:24 +02:00
Karl Tauber
ec8213b891 release 0.41 2020-09-02 11:23:43 +02:00
Karl Tauber
ae61383742 README.md: screenshots updated; removed unused screenshots 2020-09-01 18:50:26 +02:00
Karl Tauber
cc90a2ad75 Demo: reworked "More Components" tab and added screenshot mode 2020-09-01 17:24:26 +02:00
Karl Tauber
28634cda56 README.md: screenshots updated 2020-09-01 12:20:40 +02:00
Karl Tauber
3b71fcd690 Demo: fixed too large gap between themes list and control bar 2020-08-31 18:25:01 +02:00
Karl Tauber
5923ac65df smoother transition from old to new theme, independent of UI complexity, when using animated theme change 2020-08-31 18:10:54 +02:00
Karl Tauber
faffc9393d fixed sub-pixel text rendering in animated theme change; use weak hash map for static map to avoid memory leak for the case that something went wrong 2020-08-31 18:07:37 +02:00
Karl Tauber
6da220f36c IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2020-08-27 00:05:29 +02:00
Karl Tauber
21d78671d6 Demo: show hint popups to guide users to some features of the FlatLaf Demo application; added "Options > Show hints" menu item 2020-08-26 23:17:55 +02:00
Karl Tauber
af5a0ec0b7 Window decorations: fixed title pane background color in IntelliJ themes if window is inactive 2020-08-26 16:13:44 +02:00
Karl Tauber
ff214455a3 Window decorations: fixed iconify, maximize and close icon colors if window is inactive 2020-08-26 15:03:26 +02:00
Karl Tauber
3e941e3e42 Demo: fixed restoring last used theme on startup (regression in 0.39 since commit a8f4c8e843) 2020-08-26 12:35:26 +02:00
Karl Tauber
2f876d553f List and Table: fixed possible NPE in unusual cases 2020-08-26 12:16:11 +02:00
Karl Tauber
b208017117 added API to register packages or folders where FlatLaf searches for application specific properties files with custom UI defaults 2020-08-26 12:07:00 +02:00
Karl Tauber
a1dab94a61 TextArea: update background color property if enabled or editable state changes in the same way as Swing does it for all other text components (issue #147) 2020-08-25 19:15:53 +02:00
Karl Tauber
e55b2afd60 Button: show "selected" state (issue #161) 2020-08-25 16:41:40 +02:00
Karl Tauber
535c3ddf6c FlatSVGIcon now allows specifying ClassLoader that is used to load SVG file (issue #163) 2020-08-24 23:31:18 +02:00
Karl Tauber
3008d99fcd updated svgSalamander to version 1.1.2.3 2020-08-24 22:45:35 +02:00
Karl Tauber
fd37339e2f TableHeader: fixed NPE for the (unusual) case that JTableHeader is used without JTable 2020-08-13 17:07:44 +02:00
Karl Tauber
e29eca203c Theme Editor: build fat jar (includes all dependencies) (issue #160) 2020-08-12 14:02:04 +02:00
Karl Tauber
f1fd6dcdd2 release 0.40 2020-08-11 11:32:05 +02:00
Karl Tauber
2975ed2eae FlatComponents2Test: added checkboxes to enable/configure table grid lines 2020-08-07 22:46:26 +02:00
Karl Tauber
5a27d03faa IntelliJ Themes: fixed NPE in Solarized themes on scroll bar hover 2020-08-07 17:34:23 +02:00
Karl Tauber
8bcf9dbcaf - Table: detect whether component is used in cell editor and automatically disable round border style and reduce cell editor outer border width (used for focus indicator) to zero
- ComboBox, Spinner and TextField: support disabling round border style per component, if globally enabled
(issue #148)
2020-08-07 11:27:27 +02:00
Karl Tauber
56ebd26361 Window decorations: make embedded menu bar make smaller if horizontal space is rare to avoid that embedded menu bar overlaps buttons 2020-08-06 23:10:54 +02:00
Karl Tauber
b0426b81a7 Window decorations: embedded menu bar did not always respond to mouse events after adding menus and when running in JetBrains Runtime (issue #151) 2020-08-06 11:45:47 +02:00
Karl Tauber
368fbcdeb0 release 0.39 2020-08-03 16:20:57 +02:00
Karl Tauber
30747b7776 UIScale: added system property "flatlaf.uiScale.enabled" (replaces "hidpi" property) to disable user scaling mode 2020-08-02 14:08:18 +02:00
Karl Tauber
4eb4ddf5d8 FlatTestFrame: do not use sun.java2d.uiScale for user scale factor 2020-08-02 11:43:46 +02:00
Karl Tauber
b1d24680b2 ToolTip: fixed truncated text in HTML formatted tooltip on HiDPI displays (issue #142) 2020-08-01 22:53:09 +02:00
Karl Tauber
ef38f3805e IntelliJ Themes: fixed text colors in ProgressBar (issue #138) 2020-08-01 00:31:20 +02:00
Karl Tauber
2f5ca20ca4 fixed compile error caused by previous checkin (issue #143) 2020-07-31 19:28:58 +02:00
Karl Tauber
f29d3d84d4 FileChooser: fixed too small text field when renaming a file/directory in Flat IntelliJ/Darcula themes (issue #143) 2020-07-31 19:17:49 +02:00
Karl Tauber
02132c5fcd MenuItem on macOS: removed plus characters from accelerator text and made modifier key order conform with macOS standard (issue #141) 2020-07-31 13:02:01 +02:00
Karl Tauber
7057e3c6ad IntelliJ Themes: added "Carbon" and "Cobalt 2" themes 2020-07-30 23:11:37 +02:00
Karl Tauber
a8f4c8e843 Demo: added combo box above themes list to show only light or dark themes 2020-07-30 19:41:56 +02:00
Karl Tauber
a2b6e66a13 CHANGELOG.md: split change log of last version into "New features" and "Fixed bugs" sections 2020-07-30 19:26:50 +02:00
Karl Tauber
e3b3cc2896 IntelliJ Themes: replaced "Solarized" themes with much better ones from 4lex4 2020-07-30 16:30:56 +02:00
Karl Tauber
a5b2c50f24 IntelliJ Themes:
- added "Arc Dark" and "Arc Dark - Orange" themes
- updated themes to newest versions (used IJThemesUpdater)
2020-07-30 15:00:31 +02:00
Karl Tauber
5ebdf64d30 ComboBox: fixed width of popup, which was too small if popup is wider than combo box and vertical scroll bar is visible (issue #137) 2020-07-30 13:30:50 +02:00
Karl Tauber
2640ab2e8b ComboBox: changed maximum row count of popup list to 15 (was 20) (issue #124) 2020-07-30 12:11:15 +02:00
Karl Tauber
e29436da04 Button: support specifying button border width 2020-07-28 23:51:02 +02:00
Karl Tauber
7b35325f9a Flat IntelliJ theme: use color functions for selected checkbox/radio button hover/pressed background 2020-07-28 22:14:08 +02:00
Karl Tauber
f2ab7fafcf ToolTip: do not show empty tooltip component if tooltip text is an empty string (issue #134) 2020-07-28 11:10:34 +02:00
Karl Tauber
e3cda9905a Table: allow disabling swapped behavior of Home/End and Ctrl+Home/End with Table.consistentHomeEndKeyBehavior=false (issue #95) 2020-07-27 17:55:31 +02:00
Karl Tauber
a8423f7741 ScrollBar: increased minimum thumb size on macOS and Linux to 18px and on Windows to 10px; also include ScrollBar.thumbInsets in minimum size calculation (issue #131) 2020-07-27 14:41:01 +02:00
Karl Tauber
5a9e620c17 Animator: added constructor that allows passing a runnable that is invoked at the end of the animation, which allows using lambdas in most cases 2020-07-25 10:53:06 +02:00
Karl Tauber
9f41ec3986 ScrollPane: support disabling smooth scrolling per component via client property "JScrollPane.smoothScrolling" 2020-07-25 10:27:06 +02:00
Karl Tauber
5a2c0672d4 Window decorations: avoid possible endless restore/maximize in WindowStateListener in case of behavior changes in Java (issue #129) 2020-07-23 10:43:24 +02:00
Karl Tauber
38d853b5b2 Window decorations: fixed maximized window bounds with Java 11.0.8 and 13.0.4, which has fixes backported from Java 15 (issue #129) 2020-07-22 23:23:46 +02:00
Karl Tauber
5166d4bb0f SystemInfo:
- renamed public fields from upper-case to mixed-case
- added public fields for osVersion and javaVersion
- fixed Mac -> MacOS
- added orLater to Mojave
2020-07-22 22:01:19 +02:00
Karl Tauber
2ffd5437a9 animated Laf changing added to flatlaf-extras, used in Demo 2020-07-22 12:56:42 +02:00
Karl Tauber
797830ff96 InternalFrame: title pane height was too small when iconify, maximize and close buttons are hidden (issue #132) 2020-07-21 18:23:57 +02:00
Karl Tauber
008ecabd21 animator and cubic bezier easing classes added (for future animations) (issue #66) 2020-07-21 17:53:53 +02:00
Karl Tauber
2cdcde8a5e Window decorations: fixed maximized window bounds when programmatically maximizing window before showing window (issue #129) 2020-07-18 14:21:19 +02:00
Karl Tauber
e7ec3988e2 Window decorations: fixed maximized window bounds when programmatically maximizing window (issue #129) 2020-07-17 00:08:21 +02:00
Karl Tauber
093dd9f3ef README.md: added jAlbum to list of projects that use FlatLaf 2020-07-15 19:37:47 +02:00
Karl Tauber
b491202ec7 UIDefaultsLoader: fixed NPE on syntax error in color function 2020-07-15 11:57:40 +02:00
Karl Tauber
8603ca827e Theme Editor: auto-completion improvements:
- include reference completions in value completions (if already entered text is empty)
- order completions: 1st color functions, 2nd @refs, 3rd $refs
- exclude platform specific keys from reference provider
2020-07-11 13:35:59 +02:00
Karl Tauber
6b148a59da Theme Editor: added auto-completion for "amount" and "options" parameters of color functions 2020-07-11 13:01:59 +02:00
Karl Tauber
de6d45fee6 Theme Editor: fixed NPE in FlatCompletionProvider.isAutoActivateOkay() 2020-07-10 16:10:43 +02:00
Karl Tauber
65e2071937 CHANGELOG.md: added regression note 2020-07-10 15:58:04 +02:00
452 changed files with 44756 additions and 10692 deletions

142
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,142 @@
# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: CI
on:
push:
branches:
- '*'
tags:
- '[0-9]*'
pull_request:
branches:
- '*'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
# test against
# - Java 1.8 (minimum requirement)
# - Java 9 (first version with JPMS)
# - Java LTS versions (11, 17, ...)
# - lastest Java version(s)
java:
- 1.8
- 9
- 11 # LTS
- 14
- 15
steps:
- uses: actions/checkout@v2
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- name: Cache Gradle wrapper
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Build with Gradle
run: ./gradlew build
- name: Upload artifacts
uses: actions/upload-artifact@v2
if: matrix.java == '11'
with:
name: FlatLaf-build-artifacts
path: |
flatlaf-core/build/libs
flatlaf-demo/build/libs
flatlaf-extras/build/libs
flatlaf-intellij-themes/build/libs
flatlaf-jide-oss/build/libs
flatlaf-swingx/build/libs
!**/*-javadoc.jar
!**/*-sources.jar
snapshot:
runs-on: ubuntu-latest
needs: build
if: |
github.event_name == 'push' &&
github.ref == 'refs/heads/master' &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v2
- name: Setup Java 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache Gradle wrapper
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Publish snapshot to oss.jfrog.org
run: ./gradlew artifactoryPublish
env:
BINTRAY_USER: ${{ secrets.BINTRAY_USER }}
BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
release:
runs-on: ubuntu-latest
needs: build
if: |
github.event_name == 'push' &&
startsWith( github.ref, 'refs/tags/' ) &&
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v2
- name: Setup Java 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache Gradle wrapper
uses: actions/cache@v1
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: ${{ runner.os }}-gradle
- name: Release a new stable version to bintray
run: ./gradlew bintrayUpload -Drelease=true
env:
BINTRAY_USER: ${{ secrets.BINTRAY_USER }}
BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}

View File

@@ -1,39 +0,0 @@
language: java
sudo: false
jdk:
- openjdk8
- openjdk9
- openjdk11
- openjdk14
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
before_install:
- ./gradlew --version
- java -version
stages:
- name: test
- name: snapshot
if: branch = master AND type IN (push) AND tag IS blank
- name: release
if: type IN (push) AND tag IS present
jobs:
include:
# publish snapshot to oss.jfrog.org
- stage: snapshot
jdk: openjdk11
script: ./gradlew artifactoryPublish
# release a new stable version to bintray
- stage: release
jdk: openjdk11
script: ./gradlew bintrayUpload -Drelease=true

View File

@@ -1,6 +1,339 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 1.0-rc1
#### New features and improvements
- Button: Disabled `Button.defaultButtonFollowsFocus` on Windows (as on other
platforms). If you like to keep the old behavior in your application, use:
`if(SystemInfo.isWindows)
UIManager.put("Button.defaultButtonFollowsFocus",true);`.
- ComboBox, Spinner and SplitPaneDivider: Added pressed feedback to arrow
buttons.
- Slider: Support per component custom thumb and track colors via
`JSlider.setForeground(Color)` and `JSlider.setBackground(Color)`.
- Slider: Improved thumb hover and pressed colors.
- TextComponent: Clip placeholder text if it does not fit into visible area. (PR
#229)
- macOS: Improved font rendering on macOS when using JetBrains Runtime. (PRs
#237, #239 and #241)
- Extras: UI defaults inspector:
- Support embedding UI defaults inspector panel into any window. See
`FlatUIDefaultsInspector.createInspectorPanel()`.
- Copy selected keys and values into clipboard via context menu.
- Support wildcard matching in filter (`*` matches any number of characters,
`?` matches a single character, `^` beginning of line, `$` end of line).
- IntelliJ Themes:
- Added hover and pressed feedback to Button, CheckBox, RadioButton and
ToggleButton. (issue #176)
- Added "Material Theme UI Lite / Moonlight" theme.
- Updated "Dracula", "Gradianto" and "Material Theme UI Lite" themes.
#### Fixed bugs
- Button and ToggleButton: Threat Unicode surrogate character pair as single
character and make button square. (issue #234)
- Button and ToggleButton: ToolBar buttons now respect explicitly set background
color. If no background color is set, then the button background is not
painted anymore. (issue #191)
- ToggleButton: Tab style buttons (client property `JButton.buttonType` is
`tab`) now respect explicitly set background color.
- TabbedPane: Fixed `IndexOutOfBoundsException` when using tooltip text on close
buttons and closing last/rightmost tab. (issue #235)
- TabbedPane: Fixed scrolling tabs with touchpads and high-resolution mouse
wheels.
- Extras: Added missing export of package
`com.formdev.flatlaf.extras.components` to Java 9 module descriptor.
- JIDE Common Layer:
- Invoke `LookAndFeelFactory.installJideExtension()` when using FlatLaf UI
delegates. (issue #230)
- RangeSlider: Fixed slider focused colors in IntelliJ themes.
- IntelliJ Themes:
- Fixed menu item check colors.
- Fixed `MenuItem.underlineSelectionColor`.
- Fixed List, Tree and Table `selectionInactiveForeground` in light Arc
themes.
- Fixed List and Table background colors in Material UI Lite themes.
- Fixed menu accelerator colors in Monocai theme. (issue #243)
## 0.46
#### New features and improvements
- Slider and JIDE RangeSlider: Clicking on track now immediately moves the thumb
to mouse location and starts dragging the thumb. Use `UIManager.put(
"Slider.scrollOnTrackClick", true )` to enable old behavior that scrolls the
thumb when clicking on track.
- Slider: Snap to ticks is now done while dragging the thumb. Use
`UIManager.put( "Slider.snapToTicksOnReleased", true )` to enable old behavior
that snaps to ticks on mouse released.
- Extras: Added standard component extension classes that provides easy access
to FlatLaf specific client properties (see package
`com.formdev.flatlaf.extras.components`).
- Extras: Renamed tri-state check box class from
`com.formdev.flatlaf.extras.TriStateCheckBox` to
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`. Also
changed/improved API and added javadoc.
- Extras: Renamed SVG utility class from `com.formdev.flatlaf.extras.SVGUtils`
to `com.formdev.flatlaf.extras.FlatSVGUtils`.
- IntelliJ Themes: Added flag whether a theme is dark to
`FlatAllIJThemes.INFOS`. (issue #221)
- JIDE Common Layer: Support `TristateCheckBox`.
#### Fixed bugs
- Slider: Fixed painting of colored track if `JSlider.inverted` is `true`.
- Table and TableHeader: Fixed missing right vertical grid line if using table
as row header in scroll pane. (issues #152 and #46)
- TableHeader: Fixed position of column separators in right-to-left component
orientation.
- ToolTip: Fixed drop shadow for wide tooltips on Windows and Java 9+. (issue
#224)
- SwingX: Fixed striping background highlighting color (e.g. alternating table
rows) in dark themes.
- Fixed: If text antialiasing is disabled (in OS system settings or via
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
TabbedPane and multiline ToolTip). (issue #227)
## 0.45
#### New features and improvements
- Slider: New design, added hover and pressed feedback and improved customizing.
(PR #214)
- JIDE Common Layer: Support `RangeSlider`. (PR #209)
- IntelliJ Themes:
- Added "Gradianto Nature Green" theme.
- Updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox"
and "One Dark" themes.
- TabbedPane: Support hiding tab area if it contains only one tab. (set client
property `JTabbedPane.hideTabAreaWithOneTab` to `true`)
- MenuBar: Support different underline menu selection style UI defaults for
`MenuBar` and `MenuItem`. (PR #217; issue #216)
#### Fixed bugs
- Table: Do not paint last vertical grid line if auto-resize mode is not off.
(issue #46)
- Table: Fixed unstable grid line thickness when scaled on HiDPI screens. (issue
#152)
- TabbedPane: No longer add (internal) tab close button component as child to
`JTabbedPane`. (issue #219)
- Custom window decorations: Title bar was not hidden if window is in
full-screen mode. (issue #212)
## 0.44
#### New features and improvements
- TabbedPane: In scroll tab layout, added "Show Hidden Tabs" button to trailing
side of tab area. If pressed, it shows a popup menu that contains (partly)
hidden tabs and selecting one activates that tab. (PR #190; issue #40)
- TabbedPane: Support forward/backward scroll arrow buttons on both sides of tab
area. Backward button on left side, forward button on right side. Not
applicable scroll buttons are hidden. (PR #211; issue #40)
- TabbedPane: Support specifying default tab layout policy for all tabbed panes
in the application via UI value `TabbedPane.tabLayoutPolicy`. E.g. invoke
`UIManager.put( "TabbedPane.tabLayoutPolicy", "scroll" );` to use scroll
layout.
- TabbedPane: Support tab scrolling with mouse wheel (in scroll tab layout). (PR
#187; issue #40)
- TabbedPane: Repeat scrolling as long as scroll arrow buttons are pressed. (PR
#187; issue #40)
- TabbedPane: Support adding custom components to left and right sides of tab
area. (set client property `JTabbedPane.leadingComponent` or
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (PR #192; issue
#40)
- TabbedPane: Support closable tabs. (PR #193; issues #31 and #40)
- TabbedPane: Support minimum or maximum tab widths. (set client property
`JTabbedPane.minimumTabWidth` or `JTabbedPane.maximumTabWidth` to an integer)
(PR #199)
- TabbedPane: Support alignment of tab area. (set client property
`JTabbedPane.tabAreaAlignment` to `"leading"`, `"trailing"`, `"center"` or
`"fill"`) (PR #199)
- TabbedPane: Support horizontal alignment of tab title and icon. (set client
property `JTabbedPane.tabAlignment` to `SwingConstants.LEADING`,
`SwingConstants.TRAILING` or `SwingConstants.CENTER`)
- TabbedPane: Support equal and compact tab width modes. (set client property
`JTabbedPane.tabWidthMode` to `"preferred"`, `"equal"` or `"compact"`) (PR
#199)
- TabbedPane: Support left, right, top and bottom tab icon placement. (set
client property `JTabbedPane.tabIconPlacement` to `SwingConstants.LEADING`,
`SwingConstants.TRAILING`, `SwingConstants.TOP` or `SwingConstants.BOTTOM`)
(PR #199)
- Support painting separator line between window title and content (use UI value
`TitlePane.borderColor`). (issue #184)
- Extras: `FlatSVGIcon` now allows specifying icon width and height in
constructors. (issue #196)
- SplitPane: Hide not applicable expand/collapse buttons. Added tooltips to
expand/collapse buttons. (issue #198)
- SplitPane: Added grip to divider. Can be disabled with `UIManager.put(
"SplitPaneDivider.style", "plain" )`. (issue #179)
#### Fixed bugs
- Custom window decorations: Not visible menu bar is now ignored in layout.
- Popups using `JToolTip` components did not respect their location. (issue
#188; regression in 0.42 in fix for #164)
- IntelliJ Themes: Added suffix "(Material)" to names of all Material UI Lite
themes to avoid duplicate theme names. (issue #201)
- Extras: `FlatSVGIcon` icons were not painted in disabled labels and disabled
tabs. (issue #205)
## 0.43
#### New features and improvements
- TabbedPane: Made tabs separator color lighter in dark themes so that it is
easier to recognize the tabbed pane.
- TabbedPane: Added top and bottom tab insets to avoid that large tab icons are
painted over active tab underline.
- TabbedPane: Support hiding separator between tabs and content area (set client
property `JTabbedPane.showContentSeparator` to `false`).
- CheckBoxMenuItem and RadioButtonMenuItem: Improved checkmark background colors
of selected menu items that have also an icon. This makes it is easier to
recognize selected menu items.
- Windows: Made scaling compatible with Windows OS scaling, which distinguish
between "screen scaling" and "text scaling". (issue #175)
#### Fixed bugs
- ComboBox: If using own `JTextField` as editor, default text field border is
now removed to avoid duplicate border.
- ComboBox: Limit popup width to screen width for very long items. (issue #182)
- FileChooser: Fixed localizing special Windows folders (e.g. "Documents") and
enabled hiding known file extensions (if enabled in Windows Explorer). (issue
#178)
- Spinner: Fixed `NullPointerException` in case that arrow buttons were removed
to create button-less spinner. (issue #181)
## 0.42
#### New features and improvements
- Demo: Improved "SplitPane & Tabs" and "Data Components" tabs.
- Demo: Menu items "File > Open" and "File > Save As" now show file choosers.
- InternalFrame: Support draggable border for resizing frame inside of the
visible frame border. (issue #121)
- `FlatUIDefaultsInspector` added (see [FlatLaf Extras](flatlaf-extras)). A
simple UI defaults inspector that shows a window with all UI defaults used in
current theme (look and feel).
- Made disabled text color slightly lighter in dark themes for better
readability. (issue #174)
- PasswordField: Support disabling Caps Lock warning icon. (issue #172)
#### Fixed bugs
- TextComponents: Fixed text color of disabled text components in dark themes.
- Custom window decorations: Fixed wrong window placement when moving window to
another screen with different scaling factor. (issue #166)
- Custom window decorations: Fixed wrong window bounds when resizing window to
another screen with different scaling factor. (issue #166)
- Fixed occasional wrong positioning of heavy weight popups when using multiple
screens with different scaling factors. (issue #166)
- ToolTip: Avoid that tooltip hides owner component. (issue #164)
## 0.41
#### New features and improvements
- Added API to register packages or folders where FlatLaf searches for
application specific properties files with custom UI defaults (see
`FlatLaf.registerCustomDefaultsSource(...)` methods).
- Demo: Show hint popups to guide users to some features of the FlatLaf Demo
application.
- Extras: `FlatSVGIcon` now allows specifying `ClassLoader` that is used to load
SVG file. (issue #163)
- Smoother transition from old to new theme, independent of UI complexity, when
using animated theme change (see [FlatLaf Extras](flatlaf-extras)).
#### Fixed bugs
- Button: "selected" state was not shown. (issue #161)
- TextArea: Update background color property if enabled or editable state
changes in the same way as Swing does it for all other text components. (issue
#147)
- Demo: Fixed restoring last used theme on startup. (regression in 0.39)
- Custom window decorations: Fixed iconify, maximize and close icon colors if
window is inactive.
- Custom window decorations: Fixed title pane background color in IntelliJ
themes if window is inactive.
- Fixed sub-pixel text rendering in animated theme change (see
[FlatLaf Extras](flatlaf-extras)).
#### Other Changes
- Extras: Updated dependency
[svgSalamander](https://github.com/JFormDesigner/svgSalamander) to version
1.1.2.3.
## 0.40
#### New features
- Table: Detect whether component is used in cell editor and automatically
disable round border style and reduce cell editor outer border width (used for
focus indicator) to zero. (issue #148)
- ComboBox, Spinner and TextField: Support disabling round border style per
component, if globally enabled (set client property `JComponent.roundRect` to
`false`). (issue #148)
#### Fixed bugs
- Custom window decorations: Embedded menu bar did not always respond to mouse
events after adding menus and when running in JetBrains Runtime. (issue #151)
- IntelliJ Themes: Fixed NPE in Solarized themes on scroll bar hover.
## 0.39
#### New features
- Animated theme change (see [FlatLaf Extras](flatlaf-extras)). Used in Demo.
- Demo: Added combo box above themes list to show only light or dark themes.
- IntelliJ Themes:
- Added "Arc Dark", "Arc Dark - Orange", "Carbon" and "Cobalt 2" themes.
- Replaced "Solarized" themes with much better ones from 4lex4.
- Updated "Arc", "One Dark" and "Vuesion" themes.
- ScrollPane: Enable/disable smooth scrolling per component if client property
"JScrollPane.smoothScrolling" is set to a `Boolean` on `JScrollPane`.
- ScrollBar: Increased minimum thumb size on macOS and Linux from 8 to 18
pixels. On Windows, it is now 10 pixels. (issue #131)
- Button: Support specifying button border width.
- ComboBox: Changed maximum row count of popup list to 15 (was 20). Set UI value
`ComboBox.maximumRowCount` to any integer to use a different value.
#### Fixed bugs
- Custom window decorations: Fixed maximized window bounds when programmatically
maximizing window. E.g. restoring window state at startup. (issue #129)
- InternalFrame: Title pane height was too small when iconify, maximize and
close buttons are hidden. (issue #132)
- ToolTip: Do not show empty tooltip component if tooltip text is an empty
string. (issue #134)
- ToolTip: Fixed truncated text in HTML formatted tooltip on HiDPI displays.
(issue #142)
- ComboBox: Fixed width of popup, which was too small if popup is wider than
combo box and vertical scroll bar is visible. (issue #137)
- MenuItem on macOS: Removed plus characters from accelerator text and made
modifier key order conform with macOS standard. (issue #141)
- FileChooser: Fixed too small text field when renaming a file/directory in Flat
IntelliJ/Darcula themes. (issue #143)
- IntelliJ Themes: Fixed text colors in ProgressBar. (issue #138)
## 0.38 ## 0.38
- Hide focus indicator when window is inactive. - Hide focus indicator when window is inactive.
@@ -9,7 +342,8 @@ FlatLaf Change Log
- Custom window decorations: Center title if menu bar is embedded. - Custom window decorations: Center title if menu bar is embedded.
- Custom window decorations: Cursor of components (e.g. TextField) was not - Custom window decorations: Cursor of components (e.g. TextField) was not
changed. (issue #125) changed. (issue #125)
- CheckBox: Fixed colors in light IntelliJ themes. (issue #126) - CheckBox: Fixed colors in light IntelliJ themes. (issue #126; regression in
0.37)
- InternalFrame: Use default icon in internal frames. (issue #122) - InternalFrame: Use default icon in internal frames. (issue #122)

121
README.md
View File

@@ -11,9 +11,9 @@ scales on **HiDPI** displays and runs on Java 8 or newer.
The look is heavily inspired by **Darcula** and **IntelliJ** themes from The look is heavily inspired by **Darcula** and **IntelliJ** themes from
IntelliJ IDEA 2019.2+ and uses almost the same colors and icons. IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
![Flat Light Demo](images/FlatLightDemo.png) ![Flat Light](images/flat_light.png)
![Flat Dark Demo](images/FlatDarkDemo.png) ![Flat Dark](images/flat_dark.png)
IntelliJ Platform Themes IntelliJ Platform Themes
@@ -22,9 +22,7 @@ IntelliJ Platform Themes
FlatLaf can use 3rd party themes created for IntelliJ Platform (see FlatLaf can use 3rd party themes created for IntelliJ Platform (see
[IntelliJ Themes Pack](flatlaf-intellij-themes)): [IntelliJ Themes Pack](flatlaf-intellij-themes)):
![Cyan Light Demo](images/CyanLightDemo.png) ![IntelliJ Platform Themes](images/intellij_platform_themes.png)
![Dark Purple Demo](images/DarkPurpleDemo.png)
Demo Demo
@@ -75,39 +73,11 @@ Addons
- [JIDE Common Layer](flatlaf-jide-oss) - [JIDE Common Layer](flatlaf-jide-oss)
Projects using FlatLaf Documentation
---------------------- -------------
- [NetBeans](https://netbeans.apache.org/) 11.3 For more information and documentation visit
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5 [FlatLaf Home](https://www.formdev.com/flatlaf/)
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (commercial)
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial)
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org)
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
0.13.b024
- [Rest Suite](https://github.com/supanadit/restsuite)
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy)
- [SpringRemote](https://github.com/HaleyWang/SpringRemote)
- [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)
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2
- [lsfusion platform](https://github.com/lsfusion/platform)
- [Jes - Die Java-EÜR](https://www.jes-eur.de)
- [Mapton](https://mapton.org/) 2.0
([source code](https://github.com/trixon/mapton)) based on NetBeans platform
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE)
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis)
- [RemoteLight](https://github.com/Drumber/RemoteLight) - Multifunctional LED
Control Software
- and more...
Buzz Buzz
@@ -117,8 +87,77 @@ Buzz
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/) - [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
Documentation Projects using FlatLaf
------------- ----------------------
For more information and documentation visit - [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
[FlatLaf Home](https://www.formdev.com/flatlaf/) and much more
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- ![New](images/new.svg) [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds
most widely used web app scanner
- ![New](images/new.svg) [JOSM](https://josm.openstreetmap.de/) - an extensible
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
JOSM plugin)
- [jAlbum](https://jalbum.net/) 21 (commercial) - creates photo album websites
- [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) v0.47.4 and
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
game
- [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
- ![New](images/new.svg) [jEnTunnel](https://github.com/ggrandes/jentunnel) -
manage SSH Tunnels made easy
- [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)
- [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
- ![New](images/new.svg) [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
- ![New](images/new.svg) [Linotte](https://github.com/cpc6128/LangageLinotte)
3.1 - French programming language created to learn programming
- ![New](images/new.svg) [MEKA](https://github.com/Waikato/meka) 1.9.3 -
multi-label classifiers and evaluation procedures using the Weka machine
learning framework
- ![New](images/new.svg) [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
- ![New](images/new.svg)
[ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
Arduino-based telescope focuser
- ![New](images/new.svg)
[Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
from any webnovel and lightnovel site
- ![New](images/new.svg) [lectureStudio](https://www.lecturestudio.org/)
4.3.1060 - digitize your lectures with ease
- ![New](images/new.svg)
[Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
and fastboot commands easier to use
- and more...

View File

@@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
val releaseVersion = "0.38" val releaseVersion = "1.0-rc1"
val developmentVersion = "0.39-SNAPSHOT" val developmentVersion = "1.0-rc2-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
@@ -48,7 +48,7 @@ extra["bintray.dryRun"] = false
// if true, uploaded artifacts are visible to all // if true, uploaded artifacts are visible to all
// if false, only visible to owner when logged into bintray // if false, only visible to owner when logged into bintray
extra["bintray.publish"] = true extra["bintray.publish"] = false
allprojects { allprojects {
@@ -64,7 +64,7 @@ allprojects {
// manifest for all created JARs // manifest for all created JARs
manifest.attributes(mapOf( manifest.attributes(mapOf(
"Implementation-Vendor" to "FormDev Software GmbH", "Implementation-Vendor" to "FormDev Software GmbH",
"Implementation-Copyright" to "Copyright (C) ${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.", "Implementation-Copyright" to "Copyright (C) 2019-${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
"Implementation-Version" to project.version)) "Implementation-Version" to project.version))
// add META-INF/LICENSE to all created JARs // add META-INF/LICENSE to all created JARs

View File

@@ -63,8 +63,14 @@ publishing {
} }
scm { scm {
connection.set( "scm:git:git://github.com/JFormDesigner/FlatLaf.git" )
url.set( "https://github.com/JFormDesigner/FlatLaf" ) url.set( "https://github.com/JFormDesigner/FlatLaf" )
} }
issueManagement {
system.set( "GitHub" )
url.set( "https://github.com/JFormDesigner/FlatLaf/issues" )
}
} }
} }
} }

View File

@@ -19,19 +19,26 @@ package com.formdev.flatlaf;
import java.awt.Color; import java.awt.Color;
import java.util.Objects; import java.util.Objects;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.SwingConstants;
/** /**
* @author Karl Tauber * @author Karl Tauber
*/ */
public interface FlatClientProperties public interface FlatClientProperties
{ {
//---- JButton ------------------------------------------------------------
/** /**
* Specifies type of a button. * Specifies type of a button.
* <p> * <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br> * <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong> {@link #BUTTON_TYPE_SQUARE}, {@link #BUTTON_TYPE_ROUND_RECT}, * <strong>Allowed Values</strong>
* {@link #BUTTON_TYPE_TAB}, {@link #BUTTON_TYPE_HELP} and {@link BUTTON_TYPE_TOOLBAR_BUTTON} * {@link #BUTTON_TYPE_SQUARE},
* {@link #BUTTON_TYPE_ROUND_RECT},
* {@link #BUTTON_TYPE_TAB},
* {@link #BUTTON_TYPE_HELP} or
* {@link BUTTON_TYPE_TOOLBAR_BUTTON}
*/ */
String BUTTON_TYPE = "JButton.buttonType"; String BUTTON_TYPE = "JButton.buttonType";
@@ -99,17 +106,19 @@ public interface FlatClientProperties
/** /**
* Specifies whether the button preferred size will be made square (quadratically). * Specifies whether the button preferred size will be made square (quadratically).
* <p> * <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton} * <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Boolean} * <strong>Value type</strong> {@link java.lang.Boolean}
*/ */
String SQUARE_SIZE = "JButton.squareSize"; String SQUARE_SIZE = "JButton.squareSize";
//---- JComponent ---------------------------------------------------------
/** /**
* Specifies minimum width of a component. * Specifies minimum width of a component.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton}, * <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton},
* {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br> * {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br> * <strong>Value type</strong> {@link java.lang.Integer}
*/ */
String MINIMUM_WIDTH = "JComponent.minimumWidth"; String MINIMUM_WIDTH = "JComponent.minimumWidth";
@@ -117,10 +126,19 @@ public interface FlatClientProperties
* Specifies minimum height of a component. * Specifies minimum height of a component.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br> * <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br> * <strong>Value type</strong> {@link java.lang.Integer}
*/ */
String MINIMUM_HEIGHT = "JComponent.minimumHeight"; String MINIMUM_HEIGHT = "JComponent.minimumHeight";
/**
* Paint the component with round edges.
* <p>
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
/** /**
* Specifies the outline color of the component border. * Specifies the outline color of the component border.
* <p> * <p>
@@ -129,9 +147,12 @@ public interface FlatClientProperties
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner}, * {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
* {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br> * {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br> * <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br>
* <strong>Allowed Values</strong> {@link #OUTLINE_ERROR}, {@link #OUTLINE_WARNING}, * <strong>Allowed Values</strong>
* any color (type {@link java.awt.Color}) or an array of two colors (type {@link java.awt.Color}[2]) * {@link #OUTLINE_ERROR},
* where the first color is for focused state and the second for unfocused state * {@link #OUTLINE_WARNING},
* any color (type {@link java.awt.Color}) or
* an array of two colors (type {@link java.awt.Color}[2]) where the first color
* is for focused state and the second for unfocused state
*/ */
String OUTLINE = "JComponent.outline"; String OUTLINE = "JComponent.outline";
@@ -149,14 +170,7 @@ public interface FlatClientProperties
*/ */
String OUTLINE_WARNING = "warning"; String OUTLINE_WARNING = "warning";
/** //---- Popup --------------------------------------------------------------
* Paint the component with round edges.
* <p>
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
/** /**
* Specifies whether a drop shadow is painted if the component is shown in a popup * Specifies whether a drop shadow is painted if the component is shown in a popup
@@ -167,6 +181,17 @@ public interface FlatClientProperties
*/ */
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"; String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
/**
* Specifies whether a heavy weight window should be used if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
* <p>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight";
//---- JProgressBar -------------------------------------------------------
/** /**
* Specifies whether the progress bar has always the larger height even if no string is painted. * Specifies whether the progress bar has always the larger height even if no string is painted.
* <p> * <p>
@@ -183,6 +208,8 @@ public interface FlatClientProperties
*/ */
String PROGRESS_BAR_SQUARE = "JProgressBar.square"; String PROGRESS_BAR_SQUARE = "JProgressBar.square";
//---- JRootPane ----------------------------------------------------------
/** /**
* Specifies whether the menu bar is embedded into the title pane if custom * Specifies whether the menu bar is embedded into the title pane if custom
* window decorations are enabled. Default is {@code true}. * window decorations are enabled. Default is {@code true}.
@@ -192,6 +219,8 @@ public interface FlatClientProperties
*/ */
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"; String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
//---- JScrollBar / JScrollPane -------------------------------------------
/** /**
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown. * Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
* <p> * <p>
@@ -200,6 +229,16 @@ public interface FlatClientProperties
*/ */
String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"; String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons";
/**
* Specifies whether the scroll pane uses smooth scrolling.
* <p>
* <strong>Component</strong> {{@link javax.swing.JScrollPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
//---- JTabbedPane --------------------------------------------------------
/** /**
* Specifies whether separators are shown between tabs. * Specifies whether separators are shown between tabs.
* <p> * <p>
@@ -208,6 +247,14 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators"; String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators";
/**
* Specifies whether the separator between tabs area and content area should be shown.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String TABBED_PANE_SHOW_CONTENT_SEPARATOR = "JTabbedPane.showContentSeparator";
/** /**
* Specifies whether a full border is painted around a tabbed pane. * Specifies whether a full border is painted around a tabbed pane.
* <p> * <p>
@@ -216,6 +263,35 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder"; String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
/**
* Specifies whether the tab area should be hidden if it contains only one tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB = "JTabbedPane.hideTabAreaWithOneTab";
/**
* Specifies the minimum width of a tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.Integer}
*/
String TABBED_PANE_MINIMUM_TAB_WIDTH = "JTabbedPane.minimumTabWidth";
/**
* Specifies the maximum width of a tab.
* <p>
* Applied only if tab does not have a custom tab component
* (see {@link javax.swing.JTabbedPane#setTabComponentAt(int, java.awt.Component)}).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.Integer}
*/
String TABBED_PANE_MAXIMUM_TAB_WIDTH = "JTabbedPane.maximumTabWidth";
/** /**
* Specifies the height of a tab. * Specifies the height of a tab.
* <p> * <p>
@@ -224,14 +300,316 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"; String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
/**
* Specifies the insets of a tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.awt.Insets}
*/
String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets";
/**
* Specifies the insets of the tab area.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.awt.Insets}
*/
String TABBED_PANE_TAB_AREA_INSETS = "JTabbedPane.tabAreaInsets";
/**
* Specifies whether tabs are closable.
* If set to {@code true} on a tabbed pane component, all tabs in that tabbed pane are closable.
* To make individual tabs closable, set it to {@code true} on a tab content component.
* <p>
* Note that you have to specify a callback (see {@link #TABBED_PANE_TAB_CLOSABLE})
* that is invoked when the user clicks a tab close button.
* The callback is responsible for closing the tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @see #TABBED_PANE_TAB_CLOSE_CALLBACK
*/
String TABBED_PANE_TAB_CLOSABLE = "JTabbedPane.tabClosable";
/**
* Specifies the tooltip text used for tab close buttons.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.String}
*
* @see #TABBED_PANE_TAB_CLOSABLE
*/
String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JTabbedPane.tabCloseToolTipText";
/**
* Specifies the callback that is invoked when a tab close button is clicked.
* The callback is responsible for closing the tab.
* <p>
* Either use a {@link java.util.function.IntConsumer} that receives the tab index as parameter:
* <pre>{@code
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
* (IntConsumer) tabIndex -> {
* // close tab here
* } );
* }</pre>
* Or use a {@link java.util.function.BiConsumer}&lt;javax.swing.JTabbedPane, Integer&gt;
* that receives the tabbed pane and the tab index as parameters:
* <pre>{@code
* myTabbedPane.putClientProperty( "JTabbedPane.tabCloseCallback",
* (BiConsumer<JTabbedPane, Integer>) (tabbedPane, tabIndex) -> {
* // close tab here
* } );
* }</pre>
* If you need to check whether a modifier key (e.g. Alt or Shift) was pressed
* while the user clicked the tab close button, use {@link java.awt.EventQueue#getCurrentEvent}
* to get current event, check whether it is a {@link java.awt.event.MouseEvent}
* and invoke its methods. E.g.
* <pre>{@code
* AWTEvent e = EventQueue.getCurrentEvent();
* boolean shift = (e instanceof MouseEvent) ? ((MouseEvent)e).isShiftDown() : false;
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.util.function.IntConsumer}
* or {@link java.util.function.BiConsumer}&lt;javax.swing.JTabbedPane, Integer&gt;
*
* @see #TABBED_PANE_TAB_CLOSABLE
*/
String TABBED_PANE_TAB_CLOSE_CALLBACK = "JTabbedPane.tabCloseCallback";
/**
* Specifies the display policy for the "more tabs" button,
* which shows a popup menu with the (partly) hidden tabs.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #TABBED_PANE_POLICY_NEVER} or
* {@link #TABBED_PANE_POLICY_AS_NEEDED}
*/
String TABBED_PANE_TABS_POPUP_POLICY = "JTabbedPane.tabsPopupPolicy";
/**
* Specifies the display policy for the forward/backward scroll arrow buttons.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #TABBED_PANE_POLICY_NEVER},
* {@link #TABBED_PANE_POLICY_AS_NEEDED} or
* {@link #TABBED_PANE_POLICY_AS_NEEDED_SINGLE}
*/
String TABBED_PANE_SCROLL_BUTTONS_POLICY = "JTabbedPane.scrollButtonsPolicy";
/**
* Display never.
*
* @see #TABBED_PANE_TABS_POPUP_POLICY
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
*/
String TABBED_PANE_POLICY_NEVER = "never";
/**
* Display only when needed.
* <p>
* If used for {@link #TABBED_PANE_SCROLL_BUTTONS_POLICY}, both scroll arrow buttons
* are either shown or hidden. Buttons are disabled if scrolling in that
* direction is not applicable.
*
* @see #TABBED_PANE_TABS_POPUP_POLICY
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
*/
String TABBED_PANE_POLICY_AS_NEEDED = "asNeeded";
/**
* Display single button only when needed.
* <p>
* If scroll button placement is trailing, then this option is ignored
* and both buttons are shown or hidden as needed.
*
* @see #TABBED_PANE_SCROLL_BUTTONS_POLICY
*/
String TABBED_PANE_POLICY_AS_NEEDED_SINGLE = "asNeededSingle";
/**
* Specifies the placement of the forward/backward scroll arrow buttons.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #TABBED_PANE_PLACEMENT_BOTH} or
* {@link #TABBED_PANE_PLACEMENT_TRAILING}
*/
String TABBED_PANE_SCROLL_BUTTONS_PLACEMENT = "JTabbedPane.scrollButtonsPlacement";
/**
* The forward/backward scroll arrow buttons are placed on both sides of the tab area.
* The backward scroll button at the left/top side.
* The forward scroll button at the right/bottom side.
*
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
*/
String TABBED_PANE_PLACEMENT_BOTH = "both";
/**
* The forward/backward scroll arrow buttons are placed on the trailing side of the tab area.
*
* @see #TABBED_PANE_SCROLL_BUTTONS_PLACEMENT
*/
String TABBED_PANE_PLACEMENT_TRAILING = "trailing";
/**
* Specifies the alignment of the tab area.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING} (default)
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#CENTER},
* {@link #TABBED_PANE_ALIGN_LEADING} (default),
* {@link #TABBED_PANE_ALIGN_TRAILING},
* {@link #TABBED_PANE_ALIGN_CENTER} or
* {@link #TABBED_PANE_ALIGN_FILL}
*/
String TABBED_PANE_TAB_AREA_ALIGNMENT = "JTabbedPane.tabAreaAlignment";
/**
* Specifies the horizontal alignment of the tab title and icon.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}
* or tab content components (see {@link javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)})<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING},
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#CENTER} (default),
* {@link #TABBED_PANE_ALIGN_LEADING},
* {@link #TABBED_PANE_ALIGN_TRAILING} or
* {@link #TABBED_PANE_ALIGN_CENTER} (default)
*/
String TABBED_PANE_TAB_ALIGNMENT = "JTabbedPane.tabAlignment";
/**
* Align to the leading edge.
*
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
* @see #TABBED_PANE_TAB_ALIGNMENT
*/
String TABBED_PANE_ALIGN_LEADING = "leading";
/**
* Align to the trailing edge.
*
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
* @see #TABBED_PANE_TAB_ALIGNMENT
*/
String TABBED_PANE_ALIGN_TRAILING = "trailing";
/**
* Align to center.
*
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
* @see #TABBED_PANE_TAB_ALIGNMENT
*/
String TABBED_PANE_ALIGN_CENTER = "center";
/**
* Stretch to fill all available space.
*
* @see #TABBED_PANE_TAB_AREA_ALIGNMENT
*/
String TABBED_PANE_ALIGN_FILL = "fill";
/**
* Specifies how the tabs should be sized.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #TABBED_PANE_TAB_WIDTH_MODE_PREFERRED} (default),
* {@link #TABBED_PANE_TAB_WIDTH_MODE_EQUAL} or
* {@link #TABBED_PANE_TAB_WIDTH_MODE_COMPACT}
*/
String TABBED_PANE_TAB_WIDTH_MODE = "JTabbedPane.tabWidthMode";
/**
* Tab width is adjusted to tab icon and title.
*
* @see #TABBED_PANE_TAB_WIDTH_MODE
*/
String TABBED_PANE_TAB_WIDTH_MODE_PREFERRED = "preferred";
/**
* All tabs in a tabbed pane has same width.
*
* @see #TABBED_PANE_TAB_WIDTH_MODE
*/
String TABBED_PANE_TAB_WIDTH_MODE_EQUAL = "equal";
/**
* Unselected tabs are smaller because they show only the tab icon, but no tab title.
* Selected tabs show both.
*
* @see #TABBED_PANE_TAB_WIDTH_MODE
*/
String TABBED_PANE_TAB_WIDTH_MODE_COMPACT = "compact";
/**
* Specifies the tab icon placement (relative to tab title).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEADING} (default),
* {@link SwingConstants#TRAILING},
* {@link SwingConstants#TOP} or
* {@link SwingConstants#BOTTOM}
*/
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
/**
* Specifies a component that will be placed at the leading edge of the tabs area.
* <p>
* For top and bottom tab placement, the layed out component size will be
* the preferred component width and the tab area height.<br>
* For left and right tab placement, the layed out component size will be
* the tab area width and the preferred component height.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.awt.Component}
*/
String TABBED_PANE_LEADING_COMPONENT = "JTabbedPane.leadingComponent";
/**
* Specifies a component that will be placed at the trailing edge of the tabs area.
* <p>
* For top and bottom tab placement, the layed out component size will be
* the available horizontal space (minimum is preferred component width) and the tab area height.<br>
* For left and right tab placement, the layed out component size will be
* the tab area width and the available vertical space (minimum is preferred component height).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.awt.Component}
*/
String TABBED_PANE_TRAILING_COMPONENT = "JTabbedPane.trailingComponent";
//---- JTextField ---------------------------------------------------------
/** /**
* Specifies whether all text is selected when the text component gains focus. * Specifies whether all text is selected when the text component gains focus.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br> * <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong> {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER}, * <strong>Allowed Values</strong>
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or * {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS} * {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
*/ */
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy"; String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
@@ -266,6 +644,8 @@ public interface FlatClientProperties
*/ */
String PLACEHOLDER_TEXT = "JTextField.placeholderText"; String PLACEHOLDER_TEXT = "JTextField.placeholderText";
//---- JToggleButton ------------------------------------------------------
/** /**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}. * Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p> * <p>
@@ -290,6 +670,8 @@ public interface FlatClientProperties
*/ */
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"; String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
//---- helper methods -----------------------------------------------------
/** /**
* Checks whether a client property of a component has the given value. * Checks whether a client property of a component has the given value.
*/ */
@@ -306,6 +688,15 @@ public interface FlatClientProperties
return (value instanceof Boolean) ? (boolean) value : defaultValue; return (value instanceof Boolean) ? (boolean) value : defaultValue;
} }
/**
* Checks whether a client property of a component is a {@link Boolean} and returns its value.
* If the client property is not set, or not a {@link Boolean}, defaultValue is returned.
*/
static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) {
Object value = c.getClientProperty( key );
return (value instanceof Boolean) ? (Boolean) value : defaultValue;
}
/** /**
* Checks whether a client property of a component is an integer and returns its value. * Checks whether a client property of a component is an integer and returns its value.
* If the client property is not set, or not an integer, defaultValue is returned. * If the client property is not set, or not an integer, defaultValue is returned.
@@ -323,14 +714,4 @@ public interface FlatClientProperties
Object value = c.getClientProperty( key ); Object value = c.getClientProperty( key );
return (value instanceof Color) ? (Color) value : defaultValue; return (value instanceof Color) ? (Color) value : defaultValue;
} }
static int clientPropertyChoice( JComponent c, String key, String... choices ) {
Object value = c.getClientProperty( key );
for( int i = 0; i < choices.length; i++ ) {
if( choices[i].equals( value ) )
return i;
}
return -1;
}
} }

View File

@@ -18,21 +18,28 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a dark color scheme and looks like Darcula LaF. * A Flat LaF that has a dark color scheme and looks like Darcula LaF.
* * <p>
* The UI defaults are loaded from FlatDarculaLaf.properties, FlatDarkLaf.properties and FlatLaf.properties * The UI defaults are loaded from {@code FlatDarculaLaf.properties},
* {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarculaLaf public class FlatDarculaLaf
extends FlatDarkLaf extends FlatDarkLaf
{ {
public static boolean install( ) { public static final String NAME = "FlatLaf Darcula";
public static boolean install() {
return install( new FlatDarculaLaf() ); return install( new FlatDarculaLaf() );
} }
public static void installLafInfo() {
installLafInfo( NAME, FlatDarculaLaf.class );
}
@Override @Override
public String getName() { public String getName() {
return "FlatLaf Darcula"; return NAME;
} }
@Override @Override

View File

@@ -16,23 +16,41 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import javax.swing.UIManager;
/** /**
* A Flat LaF that has a dark color scheme. * A Flat LaF that has a dark color scheme.
* * <p>
* The UI defaults are loaded from FlatDarkLaf.properties and FlatLaf.properties * The UI defaults are loaded from {@code FlatDarkLaf.properties} and {@code FlatLaf.properties}.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarkLaf public class FlatDarkLaf
extends FlatLaf extends FlatLaf
{ {
public static boolean install( ) { public static final String NAME = "FlatLaf Dark";
/**
* Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean install() {
return install( new FlatDarkLaf() ); return install( new FlatDarkLaf() );
} }
/**
* Adds this look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo() {
installLafInfo( NAME, FlatDarkLaf.class );
}
@Override @Override
public String getName() { public String getName() {
return "FlatLaf Dark"; return NAME;
} }
@Override @Override

View File

@@ -22,9 +22,11 @@ import javax.swing.KeyStroke;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.UIDefaults; import javax.swing.UIDefaults;
import javax.swing.UIDefaults.LazyValue; import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIManager;
import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.InputMapUIResource;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import static javax.swing.text.DefaultEditorKit.*; import static javax.swing.text.DefaultEditorKit.*;
import java.util.function.BooleanSupplier;
/** /**
* @author Karl Tauber * @author Karl Tauber
@@ -35,7 +37,7 @@ class FlatInputMaps
initBasicInputMaps( defaults ); initBasicInputMaps( defaults );
initTextComponentInputMaps( defaults ); initTextComponentInputMaps( defaults );
if( SystemInfo.IS_MAC ) if( SystemInfo.isMacOS )
initMacInputMaps( defaults ); initMacInputMaps( defaults );
} }
@@ -59,7 +61,7 @@ class FlatInputMaps
mac( "alt KP_DOWN", null ), "togglePopup" mac( "alt KP_DOWN", null ), "togglePopup"
); );
if( !SystemInfo.IS_MAC ) { if( !SystemInfo.isMacOS ) {
modifyInputMap( defaults, "FileChooser.ancestorInputMap", modifyInputMap( defaults, "FileChooser.ancestorInputMap",
"F2", "editFileName", "F2", "editFileName",
"BACK_SPACE", "Go Up" "BACK_SPACE", "Go Up"
@@ -81,8 +83,11 @@ class FlatInputMaps
"shift ctrl TAB", "navigatePrevious" "shift ctrl TAB", "navigatePrevious"
); );
modifyInputMap( defaults, "Table.ancestorInputMap", // swap Home/End with Ctrl+Home/End to make it consistent with List and Tree
// swap to make it consistent with List and Tree modifyInputMap( () -> {
return UIManager.getBoolean( "Table.consistentHomeEndKeyBehavior" );
},
defaults, "Table.ancestorInputMap",
"HOME", "selectFirstRow", "HOME", "selectFirstRow",
"END", "selectLastRow", "END", "selectLastRow",
"shift HOME", "selectFirstRowExtendSelection", "shift HOME", "selectFirstRowExtendSelection",
@@ -93,7 +98,7 @@ class FlatInputMaps
mac( "shift ctrl END", null ), "selectLastColumnExtendSelection" mac( "shift ctrl END", null ), "selectLastColumnExtendSelection"
); );
if( !SystemInfo.IS_MAC ) { if( !SystemInfo.isMacOS ) {
modifyInputMap( defaults, "Tree.focusInputMap", modifyInputMap( defaults, "Tree.focusInputMap",
"ADD", "expand", "ADD", "expand",
"SUBTRACT", "collapse" "SUBTRACT", "collapse"
@@ -164,7 +169,7 @@ class FlatInputMaps
"control shift O", "toggle-componentOrientation", // DefaultEditorKit.toggleComponentOrientation "control shift O", "toggle-componentOrientation", // DefaultEditorKit.toggleComponentOrientation
}; };
Object[] macCommonTextComponentBindings = SystemInfo.IS_MAC ? new Object[] { Object[] macCommonTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret one character (without selecting text) // move caret one character (without selecting text)
"ctrl B", backwardAction, "ctrl B", backwardAction,
"ctrl F", forwardAction, "ctrl F", forwardAction,
@@ -211,7 +216,7 @@ class FlatInputMaps
"ENTER", JTextField.notifyAction, "ENTER", JTextField.notifyAction,
}; };
Object[] macSingleLineTextComponentBindings = SystemInfo.IS_MAC ? new Object[] { Object[] macSingleLineTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret to line begin/end (without selecting text) // move caret to line begin/end (without selecting text)
"UP", beginLineAction, "UP", beginLineAction,
"DOWN", endLineAction, "DOWN", endLineAction,
@@ -289,7 +294,7 @@ class FlatInputMaps
mac( "ctrl SPACE", "meta SPACE" ), "activate-link-action", mac( "ctrl SPACE", "meta SPACE" ), "activate-link-action",
}; };
Object[] macMultiLineTextComponentBindings = SystemInfo.IS_MAC ? new Object[] { Object[] macMultiLineTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret one line (without selecting text) // move caret one line (without selecting text)
"ctrl N", downAction, "ctrl N", downAction,
"ctrl P", upAction, "ctrl P", upAction,
@@ -574,12 +579,16 @@ class FlatInputMaps
} }
private static void modifyInputMap( UIDefaults defaults, String key, Object... bindings ) { private static void modifyInputMap( UIDefaults defaults, String key, Object... bindings ) {
// Note: not using `defaults.get(key)` here because this would resolve the lazy value modifyInputMap( null, defaults, key, bindings );
defaults.put( key, new LazyModifyInputMap( defaults.remove( key ), bindings ) ); }
private static void modifyInputMap( BooleanSupplier condition, UIDefaults defaults, String key, Object... bindings ) {
// Note: not using `defaults.get(key)` here because this would resolve a lazy value
defaults.put( key, new LazyModifyInputMap( condition, defaults.remove( key ), bindings ) );
} }
private static <T> T mac( T value, T macValue ) { private static <T> T mac( T value, T macValue ) {
return SystemInfo.IS_MAC ? macValue : value; return SystemInfo.isMacOS ? macValue : value;
} }
//---- class LazyInputMapEx ----------------------------------------------- //---- class LazyInputMapEx -----------------------------------------------
@@ -614,10 +623,12 @@ class FlatInputMaps
private static class LazyModifyInputMap private static class LazyModifyInputMap
implements LazyValue implements LazyValue
{ {
private final BooleanSupplier condition;
private final Object baseInputMap; private final Object baseInputMap;
private final Object[] bindings; private final Object[] bindings;
LazyModifyInputMap( Object baseInputMap, Object[] bindings ) { LazyModifyInputMap( BooleanSupplier condition, Object baseInputMap, Object[] bindings ) {
this.condition = condition;
this.baseInputMap = baseInputMap; this.baseInputMap = baseInputMap;
this.bindings = bindings; this.bindings = bindings;
} }
@@ -629,6 +640,9 @@ class FlatInputMaps
? (InputMap) ((LazyValue)baseInputMap).createValue( table ) ? (InputMap) ((LazyValue)baseInputMap).createValue( table )
: (InputMap) baseInputMap; : (InputMap) baseInputMap;
if( condition != null && !condition.getAsBoolean() )
return inputMap;
// modify input map (replace or remove) // modify input map (replace or remove)
for( int i = 0; i < bindings.length; i += 2 ) { for( int i = 0; i < bindings.length; i += 2 ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( (String) bindings[i] ); KeyStroke keyStroke = KeyStroke.getKeyStroke( (String) bindings[i] );

View File

@@ -18,21 +18,28 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF. * A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
* * <p>
* The UI defaults are loaded from FlatIntelliJLaf.properties, FlatLightLaf.properties and FlatLaf.properties * The UI defaults are loaded from {@code FlatIntelliJLaf.properties},
* {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatIntelliJLaf public class FlatIntelliJLaf
extends FlatLightLaf extends FlatLightLaf
{ {
public static boolean install( ) { public static final String NAME = "FlatLaf IntelliJ";
public static boolean install() {
return install( new FlatIntelliJLaf() ); return install( new FlatIntelliJLaf() );
} }
public static void installLafInfo() {
installLafInfo( NAME, FlatIntelliJLaf.class );
}
@Override @Override
public String getName() { public String getName() {
return "FlatLaf IntelliJ"; return NAME;
} }
@Override @Override

View File

@@ -29,6 +29,7 @@ import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer; import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -49,9 +50,9 @@ import javax.swing.LookAndFeel;
import javax.swing.PopupFactory; import javax.swing.PopupFactory;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIDefaults; import javax.swing.UIDefaults;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException; import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource; import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -76,6 +77,8 @@ public abstract class FlatLaf
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() ); static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
private static final String DESKTOPFONTHINTS = "awt.font.desktophints"; private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
private static List<Object> customDefaultsSources;
private String desktopPropertyName; private String desktopPropertyName;
private String desktopPropertyName2; private String desktopPropertyName2;
private PropertyChangeListener desktopPropertyListener; private PropertyChangeListener desktopPropertyListener;
@@ -91,6 +94,10 @@ public abstract class FlatLaf
private Boolean oldFrameWindowDecorated; private Boolean oldFrameWindowDecorated;
private Boolean oldDialogWindowDecorated; private Boolean oldDialogWindowDecorated;
/**
* Sets the application look and feel to the given LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean install( LookAndFeel newLookAndFeel ) { public static boolean install( LookAndFeel newLookAndFeel ) {
try { try {
UIManager.setLookAndFeel( newLookAndFeel ); UIManager.setLookAndFeel( newLookAndFeel );
@@ -101,6 +108,16 @@ public abstract class FlatLaf
} }
} }
/**
* Adds the given look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo( String lafName, Class<? extends LookAndFeel> lafClass ) {
UIManager.installLookAndFeel( new UIManager.LookAndFeelInfo( lafName, lafClass.getName() ) );
}
/** /**
* Returns the look and feel identifier. * Returns the look and feel identifier.
* <p> * <p>
@@ -147,12 +164,12 @@ public abstract class FlatLaf
*/ */
@Override @Override
public boolean getSupportsWindowDecorations() { public boolean getSupportsWindowDecorations() {
if( SystemInfo.IS_JETBRAINS_JVM_11_OR_LATER && if( SystemInfo.isJetBrainsJVM_11_orLater &&
SystemInfo.IS_WINDOWS_10_OR_LATER && SystemInfo.isWindows_10_orLater &&
JBRCustomDecorations.isSupported() ) JBRCustomDecorations.isSupported() )
return false; return false;
return SystemInfo.IS_WINDOWS_10_OR_LATER; return SystemInfo.isWindows_10_orLater;
} }
@Override @Override
@@ -167,6 +184,9 @@ public abstract class FlatLaf
@Override @Override
public Icon getDisabledIcon( JComponent component, Icon icon ) { public Icon getDisabledIcon( JComponent component, Icon icon ) {
if( icon instanceof DisabledIconProvider )
return ((DisabledIconProvider)icon).getDisabledIcon();
if( icon instanceof ImageIcon ) { if( icon instanceof ImageIcon ) {
Object grayFilter = UIManager.get( "Component.grayFilter" ); Object grayFilter = UIManager.get( "Component.grayFilter" );
ImageFilter filter = (grayFilter instanceof ImageFilter) ImageFilter filter = (grayFilter instanceof ImageFilter)
@@ -187,7 +207,7 @@ public abstract class FlatLaf
@Override @Override
public void initialize() { public void initialize() {
if( SystemInfo.IS_MAC ) if( SystemInfo.isMacOS )
initializeAqua(); initializeAqua();
super.initialize(); super.initialize();
@@ -201,11 +221,11 @@ public abstract class FlatLaf
mnemonicHandler.install(); mnemonicHandler.install();
// listen to desktop property changes to update UI if system font or scaling changes // listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.IS_WINDOWS ) { if( SystemInfo.isWindows ) {
// Windows 10 allows increasing font size independent of scaling: // Windows 10 allows increasing font size independent of scaling:
// Settings > Ease of Access > Display > Make text bigger (100% - 225%) // Settings > Ease of Access > Display > Make text bigger (100% - 225%)
desktopPropertyName = "win.messagebox.font"; desktopPropertyName = "win.messagebox.font";
} else if( SystemInfo.IS_LINUX ) { } else if( SystemInfo.isLinux ) {
// Linux/Gnome allows changing font in "Tweaks" app // Linux/Gnome allows changing font in "Tweaks" app
desktopPropertyName = "gnome.Gtk/FontName"; desktopPropertyName = "gnome.Gtk/FontName";
@@ -315,7 +335,7 @@ public abstract class FlatLaf
String aquaLafClassName = "com.apple.laf.AquaLookAndFeel"; String aquaLafClassName = "com.apple.laf.AquaLookAndFeel";
BasicLookAndFeel aquaLaf; BasicLookAndFeel aquaLaf;
try { try {
if( SystemInfo.IS_JAVA_9_OR_LATER ) { if( SystemInfo.isJava_9_orLater ) {
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class ); Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" ); aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
} else } else
@@ -341,6 +361,10 @@ public abstract class FlatLaf
public UIDefaults getDefaults() { public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults(); UIDefaults defaults = super.getDefaults();
// add flag that indicates whether the LaF is light or dark
// (can be queried without using FlatLaf API)
defaults.put( "laf.dark", isDark() );
// add resource bundle for localized texts // add resource bundle for localized texts
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" ); defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
@@ -391,7 +415,7 @@ public abstract class FlatLaf
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults ); UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
// use Aqua MenuBarUI if Mac screen menubar is enabled // use Aqua MenuBarUI if Mac screen menubar is enabled
if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) { if( SystemInfo.isMacOS && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" ); defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
// add defaults necessary for AquaMenuBarUI // add defaults necessary for AquaMenuBarUI
@@ -435,17 +459,22 @@ public abstract class FlatLaf
private void initFonts( UIDefaults defaults ) { private void initFonts( UIDefaults defaults ) {
FontUIResource uiFont = null; FontUIResource uiFont = null;
if( SystemInfo.IS_WINDOWS ) { if( SystemInfo.isWindows ) {
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" ); Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
if( winFont != null ) if( winFont != null )
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() ); uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
} else if( SystemInfo.IS_MAC ) { } else if( SystemInfo.isMacOS ) {
String fontName; String fontName;
if( SystemInfo.IS_MAC_OS_10_15_CATALINA_OR_LATER ) { if( SystemInfo.isMacOS_10_15_Catalina_orLater ) {
// use Helvetica Neue font if (SystemInfo.isJetBrainsJVM_11_orLater) {
fontName = "Helvetica Neue"; // See https://youtrack.jetbrains.com/issue/JBR-1915
} else if( SystemInfo.IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER ) { fontName = ".AppleSystemUIFont";
} else {
// use Helvetica Neue font
fontName = "Helvetica Neue";
}
} else if( SystemInfo.isMacOS_10_11_ElCapitan_orLater ) {
// use San Francisco Text font // use San Francisco Text font
fontName = ".SF NS Text"; fontName = ".SF NS Text";
} else { } else {
@@ -455,14 +484,16 @@ public abstract class FlatLaf
uiFont = createCompositeFont( fontName, Font.PLAIN, 13 ); uiFont = createCompositeFont( fontName, Font.PLAIN, 13 );
} else if( SystemInfo.IS_LINUX ) { } else if( SystemInfo.isLinux ) {
Font font = LinuxFontPolicy.getFont(); Font font = LinuxFontPolicy.getFont();
uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font ); uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
} }
// fallback
if( uiFont == null ) if( uiFont == null )
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 ); uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
// increase font size if system property "flatlaf.uiScale" is set
uiFont = UIScale.applyCustomScaleFactor( uiFont ); uiFont = UIScale.applyCustomScaleFactor( uiFont );
// use active value for all fonts to allow changing fonts in all components // use active value for all fonts to allow changing fonts in all components
@@ -515,7 +546,12 @@ public abstract class FlatLaf
} }
private void putAATextInfo( UIDefaults defaults ) { private void putAATextInfo( UIDefaults defaults ) {
if( SystemInfo.IS_JAVA_9_OR_LATER ) { if ( SystemInfo.isMacOS && SystemInfo.isJetBrainsJVM ) {
// The awt.font.desktophints property suggests sub-pixel anti-aliasing
// which renders text with too much weight on macOS in the JetBrains JRE.
// Use greyscale anti-aliasing instead.
defaults.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
} else if( SystemInfo.isJava_9_orLater ) {
Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS ); Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS );
if( desktopHints instanceof Map ) { if( desktopHints instanceof Map ) {
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
@@ -552,6 +588,87 @@ public abstract class FlatLaf
defaults.put( key, value ); defaults.put( key, value );
} }
static List<Object> getCustomDefaultsSources() {
return customDefaultsSources;
}
/**
* Registers a package where FlatLaf searches for properties files with custom UI defaults.
* <p>
* This can be used to specify application specific UI defaults that override UI values
* of existing themes or to define own UI values used in custom controls.
* <p>
* There may be multiple properties files in that package for multiple themes.
* The properties file name must match the used theme class names.
* E.g. {@code FlatLightLaf.properties} for class {@link FlatLightLaf}
* or {@code FlatDarkLaf.properties} for class {@link FlatDarkLaf}.
* {@code FlatLaf.properties} is loaded first for all themes.
* <p>
* These properties files are loaded after theme and addon properties files
* and can therefore override all UI defaults.
* <p>
* Invoke this method before setting the look and feel.
*
* @param packageName a package name (e.g. "com.myapp.resources")
*/
public static void registerCustomDefaultsSource( String packageName ) {
registerCustomDefaultsSource( packageName, null );
}
public static void unregisterCustomDefaultsSource( String packageName ) {
unregisterCustomDefaultsSource( packageName, null );
}
/**
* Registers a package where FlatLaf searches for properties files with custom UI defaults.
* <p>
* See {@link #registerCustomDefaultsSource(String)} for details.
*
* @param packageName a package name (e.g. "com.myapp.resources")
* @param classLoader a class loader used to find resources, or {@code null}
*/
public static void registerCustomDefaultsSource( String packageName, ClassLoader classLoader ) {
if( customDefaultsSources == null )
customDefaultsSources = new ArrayList<>();
customDefaultsSources.add( packageName );
customDefaultsSources.add( classLoader );
}
public static void unregisterCustomDefaultsSource( String packageName, ClassLoader classLoader ) {
if( customDefaultsSources == null )
return;
int size = customDefaultsSources.size();
for( int i = 0; i < size - 1; i++ ) {
Object source = customDefaultsSources.get( i );
if( packageName.equals( source ) && customDefaultsSources.get( i + 1 ) == classLoader ) {
customDefaultsSources.remove( i + 1 );
customDefaultsSources.remove( i );
break;
}
}
}
/**
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
* <p>
* See {@link #registerCustomDefaultsSource(String)} for details.
*
* @param folder a folder
*/
public static void registerCustomDefaultsSource( File folder ) {
if( customDefaultsSources == null )
customDefaultsSources = new ArrayList<>();
customDefaultsSources.add( folder );
}
public static void unregisterCustomDefaultsSource( File folder ) {
if( customDefaultsSources == null )
return;
customDefaultsSources.remove( folder );
}
private static void reSetLookAndFeel() { private static void reSetLookAndFeel() {
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
LookAndFeel lookAndFeel = UIManager.getLookAndFeel(); LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
@@ -612,6 +729,18 @@ public abstract class FlatLaf
MnemonicHandler.showMnemonics( false, null ); MnemonicHandler.showMnemonics( false, null );
} }
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
@Override
public final boolean equals( Object obj ) {
return super.equals( obj );
}
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
@Override
public final int hashCode() {
return super.hashCode();
}
//---- class ActiveFont --------------------------------------------------- //---- class ActiveFont ---------------------------------------------------
private static class ActiveFont private static class ActiveFont
@@ -660,4 +789,24 @@ public abstract class FlatLaf
super( image ); super( image );
} }
} }
//---- interface DisabledIconProvider -------------------------------------
/**
* A provider for disabled icons.
* <p>
* This is intended to be implemented by {@link javax.swing.Icon} implementations
* that provide the ability to paint disabled state.
* <p>
* Used in {@link FlatLaf#getDisabledIcon(JComponent, Icon)} to create a disabled icon from an enabled icon.
*/
public interface DisabledIconProvider
{
/**
* Returns an icon with a disabled appearance.
*
* @return a disabled icon
*/
Icon getDisabledIcon();
}
} }

View File

@@ -16,23 +16,41 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import javax.swing.UIManager;
/** /**
* A Flat LaF that has a light color scheme. * A Flat LaF that has a light color scheme.
* * <p>
* The UI defaults are loaded from FlatLightLaf.properties and FlatLaf.properties * The UI defaults are loaded from {@code FlatLightLaf.properties} and {@code FlatLaf.properties}.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatLightLaf public class FlatLightLaf
extends FlatLaf extends FlatLaf
{ {
public static boolean install( ) { public static final String NAME = "FlatLaf Light";
/**
* Sets the application look and feel to this LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean install() {
return install( new FlatLightLaf() ); return install( new FlatLightLaf() );
} }
/**
* Adds this look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo() {
installLafInfo( NAME, FlatLightLaf.class );
}
@Override @Override
public String getName() { public String getName() {
return "FlatLaf Light"; return NAME;
} }
@Override @Override

View File

@@ -88,6 +88,10 @@ public class FlatPropertiesLaf
return dark; return dark;
} }
public Properties getProperties() {
return properties;
}
@Override @Override
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() { protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>(); ArrayList<Class<?>> lafClasses = new ArrayList<>();

View File

@@ -39,6 +39,14 @@ public interface FlatSystemProperties
*/ */
String UI_SCALE = "flatlaf.uiScale"; String UI_SCALE = "flatlaf.uiScale";
/**
* Specifies whether user scaling mode is enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled";
/** /**
* Specifies whether Ubuntu font should be used on Ubuntu Linux. * Specifies whether Ubuntu font should be used on Ubuntu Linux.
* By default, if not running in a JetBrains Runtime, the Liberation Sans font * By default, if not running in a JetBrains Runtime, the Liberation Sans font
@@ -84,6 +92,14 @@ public interface FlatSystemProperties
*/ */
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded"; String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
/**
* Specifies whether animations are enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String ANIMATION = "flatlaf.animation";
/** /**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens. * Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p> * <p>

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import java.awt.Color;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -56,6 +57,8 @@ public class IntelliJTheme
public final boolean dark; public final boolean dark;
public final String author; public final String author;
private final boolean isMaterialUILite;
private final Map<String, String> colors; private final Map<String, String> colors;
private final Map<String, Object> ui; private final Map<String, Object> ui;
private final Map<String, Object> icons; private final Map<String, Object> icons;
@@ -119,6 +122,8 @@ public class IntelliJTheme
dark = Boolean.parseBoolean( (String) json.get( "dark" ) ); dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
author = (String) json.get( "author" ); author = (String) json.get( "author" );
isMaterialUILite = author.equals( "Mallowigi" );
colors = (Map<String, String>) json.get( "colors" ); colors = (Map<String, String>) json.get( "colors" );
ui = (Map<String, Object>) json.get( "ui" ); ui = (Map<String, Object>) json.get( "ui" );
icons = (Map<String, Object>) json.get( "icons" ); icons = (Map<String, Object>) json.get( "icons" );
@@ -147,11 +152,20 @@ public class IntelliJTheme
applyColorPalette( defaults ); applyColorPalette( defaults );
applyCheckBoxColors( defaults ); applyCheckBoxColors( defaults );
// copy values
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
// IDEA does not paint button background if disabled, but FlatLaf does // IDEA does not paint button background if disabled, but FlatLaf does
Object panelBackground = defaults.get( "Panel.background" ); Object panelBackground = defaults.get( "Panel.background" );
defaults.put( "Button.disabledBackground", panelBackground ); defaults.put( "Button.disabledBackground", panelBackground );
defaults.put( "ToggleButton.disabledBackground", panelBackground ); defaults.put( "ToggleButton.disabledBackground", panelBackground );
// fix Button borders
copyIfNotSet( defaults, "Button.focusedBorderColor", "Component.focusedBorderColor", uiKeys );
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground // IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
Object helpButtonBackground = defaults.get( "Button.startBackground" ); Object helpButtonBackground = defaults.get( "Button.startBackground" );
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" ); Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
@@ -201,6 +215,12 @@ public class IntelliJTheme
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) ) if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) ); defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
// fix List and Table background colors in Material UI Lite themes
if( isMaterialUILite ) {
defaults.put( "List.background", defaults.get( "Tree.background" ) );
defaults.put( "Table.background", defaults.get( "Tree.background" ) );
}
// limit tree row height // limit tree row height
int rowHeight = defaults.getInt( "Tree.rowHeight" ); int rowHeight = defaults.getInt( "Tree.rowHeight" );
if( rowHeight > 22 ) if( rowHeight > 22 )
@@ -221,10 +241,17 @@ public class IntelliJTheme
// remove theme specific UI defaults and remember only those for current theme // remove theme specific UI defaults and remember only those for current theme
Map<Object, Object> themeSpecificDefaults = new HashMap<>(); Map<Object, Object> themeSpecificDefaults = new HashMap<>();
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']'; String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
String currentAuthorPrefix = "[author-" + author.replace( ' ', '_' ) + ']';
String allThemesPrefix = "[*]";
String[] prefixes = { currentThemePrefix, currentAuthorPrefix, allThemesPrefix };
for( String key : themeSpecificKeys ) { for( String key : themeSpecificKeys ) {
Object value = defaults.remove( key ); Object value = defaults.remove( key );
if( key.startsWith( currentThemePrefix ) ) for( String prefix : prefixes ) {
themeSpecificDefaults.put( key.substring( currentThemePrefix.length() ), value ); if( key.startsWith( prefix ) ) {
themeSpecificDefaults.put( key.substring( prefix.length() ), value );
break;
}
}
} }
return themeSpecificDefaults; return themeSpecificDefaults;
@@ -259,10 +286,12 @@ public class IntelliJTheme
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() ) for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys ); apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
} else { } else {
if( "".equals( value ) )
return; // ignore empty value
uiKeys.add( key ); uiKeys.add( key );
// fix ComboBox size and Spinner border in all Material UI Lite themes // fix ComboBox size and Spinner border in all Material UI Lite themes
boolean isMaterialUILite = author.equals( "Mallowigi" );
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) ) if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
return; // ignore return; // ignore
@@ -374,7 +403,7 @@ public class IntelliJTheme
} }
/** /**
* Because IDEA uses SVGs for check boxes and radio buttons the colors for * Because IDEA uses SVGs for check boxes and radio buttons, the colors for
* this two components are specified in "icons > ColorPalette". * this two components are specified in "icons > ColorPalette".
* FlatLaf uses vector icons and expects colors for the two components in UI defaults. * FlatLaf uses vector icons and expects colors for the two components in UI defaults.
*/ */
@@ -446,33 +475,47 @@ public class IntelliJTheme
} }
} }
// remove hover and pressed colors // update hover, pressed and focused colors
if( checkboxModified ) { if( checkboxModified ) {
// for non-filled checkbox/radiobutton used in dark themes
defaults.remove( "CheckBox.icon.focusWidth" ); defaults.remove( "CheckBox.icon.focusWidth" );
defaults.remove( "CheckBox.icon.hoverBorderColor" ); defaults.put( "CheckBox.icon.hoverBorderColor", defaults.get( "CheckBox.icon.focusedBorderColor" ) );
defaults.remove( "CheckBox.icon.focusedBackground" );
defaults.remove( "CheckBox.icon.hoverBackground" );
defaults.remove( "CheckBox.icon.pressedBackground" );
defaults.remove( "CheckBox.icon.selectedFocusedBackground" );
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
// for filled checkbox/radiobutton used in light themes
defaults.remove( "CheckBox.icon[filled].focusWidth" ); defaults.remove( "CheckBox.icon[filled].focusWidth" );
defaults.remove( "CheckBox.icon[filled].hoverBorderColor" ); defaults.put( "CheckBox.icon[filled].hoverBorderColor", defaults.get( "CheckBox.icon[filled].focusedBorderColor" ) );
defaults.remove( "CheckBox.icon[filled].focusedBackground" ); defaults.put( "CheckBox.icon[filled].selectedFocusedBackground", defaults.get( "CheckBox.icon[filled].selectedBackground" ) );
defaults.remove( "CheckBox.icon[filled].hoverBackground" );
defaults.remove( "CheckBox.icon[filled].pressedBackground" );
defaults.remove( "CheckBox.icon[filled].selectedFocusedBackground" );
defaults.remove( "CheckBox.icon[filled].selectedHoverBackground" );
defaults.remove( "CheckBox.icon[filled].selectedPressedBackground" );
}
// copy values if( dark ) {
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) // IDEA Darcula checkBoxFocused.svg, checkBoxSelectedFocused.svg,
defaults.put( e.getKey(), defaults.get( e.getValue() ) ); // radioFocused.svg and radioSelectedFocused.svg
// use opacity=".65" for the border
// --> add alpha to focused border colors
String[] focusedBorderColorKeys = new String[] {
"CheckBox.icon.focusedBorderColor",
"CheckBox.icon.selectedFocusedBorderColor",
"CheckBox.icon[filled].focusedBorderColor",
"CheckBox.icon[filled].selectedFocusedBorderColor",
};
for( String key : focusedBorderColorKeys ) {
Color color = defaults.getColor( key );
if( color != null ) {
defaults.put( key, new ColorUIResource( new Color(
(color.getRGB() & 0xffffff) | 0xa6000000, true ) ) );
}
}
}
}
} }
private void copyIfNotSet( UIDefaults defaults, String destKey, String srcKey, Set<String> uiKeys ) {
if( !uiKeys.contains( destKey ) )
defaults.put( destKey, defaults.get( srcKey ) );
}
/** Rename UI default keys (key --> value). */
private static Map<String, String> uiKeyMapping = new HashMap<>(); private static Map<String, String> uiKeyMapping = new HashMap<>();
/** Copy UI default keys (value --> key). */
private static Map<String, String> uiKeyCopying = new HashMap<>(); private static Map<String, String> uiKeyCopying = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>(); private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>(); private static Map<String, String> checkboxKeyMapping = new HashMap<>();
@@ -502,6 +545,7 @@ public class IntelliJTheme
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" ); uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
uiKeyCopying.put( "RadioButtonMenuItem.margin", "MenuItem.margin" ); uiKeyCopying.put( "RadioButtonMenuItem.margin", "MenuItem.margin" );
uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" ); uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" );
uiKeyCopying.put( "MenuItem.underlineSelectionColor", "TabbedPane.underlineColor" );
// IDEA uses List.selectionBackground also for menu selection // IDEA uses List.selectionBackground also for menu selection
uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" ); uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" );
@@ -514,6 +558,8 @@ public class IntelliJTheme
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" ); uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" );
uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" ); uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" );
uiKeyCopying.put( "ProgressBar.selectionForeground", "ProgressBar.background" );
uiKeyCopying.put( "ProgressBar.selectionBackground", "ProgressBar.foreground" );
// ScrollBar // ScrollBar
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" ); uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
@@ -524,8 +570,12 @@ public class IntelliJTheme
// Slider // Slider
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite) uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
uiKeyCopying.put( "Slider.trackValueColor", "ProgressBar.foreground" );
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
// TitlePane // TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" ); uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
uiKeyMapping.put( "TitlePane.inactiveInfoForeground", "TitlePane.inactiveForeground" ); uiKeyMapping.put( "TitlePane.inactiveInfoForeground", "TitlePane.inactiveForeground" );
@@ -577,7 +627,7 @@ public class IntelliJTheme
@Override @Override
public String getDescription() { public String getDescription() {
return theme.name; return getName();
} }
@Override @Override

View File

@@ -39,7 +39,7 @@ import com.formdev.flatlaf.util.UIScale;
class LinuxFontPolicy class LinuxFontPolicy
{ {
static Font getFont() { static Font getFont() {
return SystemInfo.IS_KDE ? getKDEFont() : getGnomeFont(); return SystemInfo.isKDE ? getKDEFont() : getGnomeFont();
} }
/** /**
@@ -77,7 +77,7 @@ class LinuxFontPolicy
// Ubuntu font is rendered poorly (except if running in JetBrains VM) // Ubuntu font is rendered poorly (except if running in JetBrains VM)
// --> use Liberation Sans font // --> use Liberation Sans font
if( family.startsWith( "Ubuntu" ) && if( family.startsWith( "Ubuntu" ) &&
!SystemInfo.IS_JETBRAINS_JVM && !SystemInfo.isJetBrainsJVM &&
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_UBUNTU_FONT, false ) ) !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_UBUNTU_FONT, false ) )
family = "Liberation Sans"; family = "Liberation Sans";

View File

@@ -71,13 +71,13 @@ class MnemonicHandler
@Override @Override
public boolean postProcessKeyEvent( KeyEvent e ) { public boolean postProcessKeyEvent( KeyEvent e ) {
int keyCode = e.getKeyCode(); int keyCode = e.getKeyCode();
if( SystemInfo.IS_MAC ) { if( SystemInfo.isMacOS ) {
// Ctrl+Alt keys must be pressed on Mac // Ctrl+Alt keys must be pressed on Mac
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT ) if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT )
showMnemonics( shouldShowMnemonics( e ) && e.isControlDown() && e.isAltDown(), e.getComponent() ); showMnemonics( shouldShowMnemonics( e ) && e.isControlDown() && e.isAltDown(), e.getComponent() );
} else { } else {
// Alt key must be pressed on Windows and Linux // Alt key must be pressed on Windows and Linux
if( SystemInfo.IS_WINDOWS ) if( SystemInfo.isWindows )
return processKeyEventOnWindows( e ); return processKeyEventOnWindows( e );
if( keyCode == KeyEvent.VK_ALT ) if( keyCode == KeyEvent.VK_ALT )

View File

@@ -19,14 +19,18 @@ package com.formdev.flatlaf;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Insets; import java.awt.Insets;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
@@ -66,7 +70,7 @@ class UIDefaultsLoader
private static final String VARIABLE_PREFIX = "@"; private static final String VARIABLE_PREFIX = "@";
private static final String PROPERTY_PREFIX = "$"; private static final String PROPERTY_PREFIX = "$";
private static final String OPTIONAL_PREFIX = "?"; private static final String OPTIONAL_PREFIX = "?";
private static final String GLOBAL_PREFIX = "*."; private static final String WILDCARD_PREFIX = "*.";
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons, static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
Properties additionalDefaults, boolean dark, UIDefaults defaults ) Properties additionalDefaults, boolean dark, UIDefaults defaults )
@@ -115,6 +119,46 @@ class UIDefaultsLoader
addonClassLoaders.add( addonClassLoader ); addonClassLoaders.add( addonClassLoader );
} }
// load custom properties files (usually provides by applications)
List<Object> customDefaultsSources = FlatLaf.getCustomDefaultsSources();
int size = (customDefaultsSources != null) ? customDefaultsSources.size() : 0;
for( int i = 0; i < size; i++ ) {
Object source = customDefaultsSources.get( i );
if( source instanceof String && i + 1 < size ) {
// load from package in classloader
String packageName = (String) source;
ClassLoader classLoader = (ClassLoader) customDefaultsSources.get( ++i );
// use class loader also for instantiating classes specified in values
if( classLoader != null && !addonClassLoaders.contains( classLoader ) )
addonClassLoaders.add( classLoader );
packageName = packageName.replace( '.', '/' );
if( classLoader == null )
classLoader = FlatLaf.class.getClassLoader();
for( Class<?> lafClass : lafClasses ) {
String propertiesName = packageName + '/' + lafClass.getSimpleName() + ".properties";
try( InputStream in = classLoader.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
}
}
} else if( source instanceof File ) {
// load from folder
File folder = (File) source;
for( Class<?> lafClass : lafClasses ) {
File propertiesFile = new File( folder, lafClass.getSimpleName() + ".properties" );
if( !propertiesFile.isFile() )
continue;
try( InputStream in = new FileInputStream( propertiesFile ) ) {
properties.load( in );
}
}
}
}
// add additional defaults // add additional defaults
if( additionalDefaults != null ) if( additionalDefaults != null )
properties.putAll( additionalDefaults ); properties.putAll( additionalDefaults );
@@ -144,9 +188,9 @@ class UIDefaultsLoader
// handle platform specific properties // handle platform specific properties
String platformPrefix = String platformPrefix =
SystemInfo.IS_WINDOWS ? "[win]" : SystemInfo.isWindows ? "[win]" :
SystemInfo.IS_MAC ? "[mac]" : SystemInfo.isMacOS ? "[mac]" :
SystemInfo.IS_LINUX ? "[linux]" : "[unknown]"; SystemInfo.isLinux ? "[linux]" : "[unknown]";
for( String key : platformSpecificKeys ) { for( String key : platformSpecificKeys ) {
Object value = properties.remove( key ); Object value = properties.remove( key );
if( key.startsWith( platformPrefix ) ) if( key.startsWith( platformPrefix ) )
@@ -154,6 +198,32 @@ class UIDefaultsLoader
} }
} }
// get (and remove) wildcard replacements, which override all other defaults that end with same suffix
HashMap<String, String> wildcards = new HashMap<>();
Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
while( it.hasNext() ) {
Entry<Object, Object> e = it.next();
String key = (String) e.getKey();
if( key.startsWith( WILDCARD_PREFIX ) ) {
wildcards.put( key.substring( WILDCARD_PREFIX.length() ), (String) e.getValue() );
it.remove();
}
}
// override UI defaults with wildcard replacements
for( Object key : defaults.keySet() ) {
int dot;
if( !(key instanceof String) ||
properties.containsKey( key ) ||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
continue;
String wildcardKey = ((String)key).substring( dot + 1 );
String wildcardValue = wildcards.get( wildcardKey );
if( wildcardValue != null )
properties.put( key, wildcardValue );
}
Function<String, String> propertiesGetter = key -> { Function<String, String> propertiesGetter = key -> {
return properties.getProperty( key ); return properties.getProperty( key );
}; };
@@ -161,37 +231,10 @@ class UIDefaultsLoader
return resolveValue( value, propertiesGetter ); return resolveValue( value, propertiesGetter );
}; };
// get globals, which override all other defaults that end with same suffix // parse and add properties to UI defaults
HashMap<String, Object> globals = new HashMap<>();
for( Map.Entry<Object, Object> e : properties.entrySet() ) { for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey(); String key = (String) e.getKey();
if( !key.startsWith( GLOBAL_PREFIX ) ) if( key.startsWith( VARIABLE_PREFIX ) )
continue;
String value = resolveValue( (String) e.getValue(), propertiesGetter );
try {
globals.put( key.substring( GLOBAL_PREFIX.length() ),
parseValue( key, value, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( Level.SEVERE, key, value, ex );
}
}
// override UI defaults with globals
for( Object key : defaults.keySet() ) {
if( key instanceof String && ((String)key).contains( "." ) ) {
String skey = (String) key;
String globalKey = skey.substring( skey.lastIndexOf( '.' ) + 1 );
Object globalValue = globals.get( globalKey );
if( globalValue != null )
defaults.put( key, globalValue );
}
}
// add non-global properties to UI defaults
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( key.startsWith( VARIABLE_PREFIX ) || key.startsWith( GLOBAL_PREFIX ) )
continue; continue;
String value = resolveValue( (String) e.getValue(), propertiesGetter ); String value = resolveValue( (String) e.getValue(), propertiesGetter );
@@ -298,7 +341,12 @@ class UIDefaultsLoader
// determine value type from key // determine value type from key
if( valueType == ValueType.UNKNOWN ) { if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( "ground" ) || key.endsWith( "Color" ) ) if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
else if( key.endsWith( "Color" ) ||
(key.endsWith( "ground" ) &&
(key.endsWith( ".background" ) || key.endsWith( "Background" ) ||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ))) )
valueType = ValueType.COLOR; valueType = ValueType.COLOR;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) ) else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER; valueType = ValueType.BORDER;
@@ -313,8 +361,6 @@ class UIDefaultsLoader
valueType = ValueType.INTEGER; valueType = ValueType.INTEGER;
else if( key.endsWith( "Char" ) ) else if( key.endsWith( "Char" ) )
valueType = ValueType.CHARACTER; valueType = ValueType.CHARACTER;
else if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
else if( key.endsWith( "grayFilter" ) ) else if( key.endsWith( "grayFilter" ) )
valueType = ValueType.GRAYFILTER; valueType = ValueType.GRAYFILTER;
} }
@@ -543,13 +589,17 @@ class UIDefaultsLoader
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError ); case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError ); case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError ); case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
case "fade": return parseColorFade( params, resolver, reportError );
case "spin": return parseColorSpin( params, resolver, reportError );
} }
throw new IllegalArgumentException( "unknown color function '" + value + "'" ); throw new IllegalArgumentException( "unknown color function '" + value + "'" );
} }
/** /**
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha) * Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
* - red: an integer 0-255 or a percentage 0-100% * - red: an integer 0-255 or a percentage 0-100%
* - green: an integer 0-255 or a percentage 0-100% * - green: an integer 0-255 or a percentage 0-100%
* - blue: an integer 0-255 or a percentage 0-100% * - blue: an integer 0-255 or a percentage 0-100%
@@ -560,6 +610,8 @@ class UIDefaultsLoader
{ {
if( hasAlpha && params.size() == 2 ) { if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color // syntax rgba(color,alpha), which allows adding alpha to any color
// NOTE: this syntax is deprecated
// use fade(color,alpha) instead
String colorStr = params.get( 0 ); String colorStr = params.get( 0 );
int alpha = parseInteger( params.get( 1 ), 0, 255, true ); int alpha = parseInteger( params.get( 1 ), 0, 255, true );
@@ -596,7 +648,8 @@ class UIDefaultsLoader
/** /**
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or * Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
* saturate(color,amount[,options]) or desaturate(color,amount[,options]) * saturate(color,amount[,options]) or desaturate(color,amount[,options]) or
* fadein(color,amount[,options]) or fadeout(color,amount[,options])
* - color: a color (e.g. #f00) or a color function * - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100% * - amount: percentage 0-100%
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived] * - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
@@ -636,9 +689,64 @@ class UIDefaultsLoader
}; };
} }
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
/**
* Syntax: fade(color,amount[,options])
* - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100%
* - options: [derived]
*/
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
boolean derived = false;
if( params.size() > 2 ) {
String options = params.get( 2 );
derived = options.contains( "derived" );
}
// create function
ColorFunction function = new ColorFunctions.Fade( amount );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
/**
* Syntax: spin(color,angle[,options])
* - color: a color (e.g. #f00) or a color function
* - angle: number of degrees to rotate
* - options: [derived]
*/
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) {
String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ), true );
boolean derived = false;
if( params.size() > 2 ) {
String options = params.get( 2 );
derived = options.contains( "derived" );
}
// create function
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError )
{
// parse base color // parse base color
String resolvedColorStr = resolver.apply( colorStr ); String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError ); ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
if( baseColor == null )
return null;
// apply this function to base color // apply this function to base color
Color newColor = ColorFunctions.applyFunctions( baseColor, function ); Color newColor = ColorFunctions.applyFunctions( baseColor, function );

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import com.formdev.flatlaf.util.AnimatedIcon;
/**
* Base class for animated icons that scales width and height, creates and initializes
* a scaled graphics context for icon painting.
* <p>
* Subclasses do not need to scale icon painting.
* <p>
* This class does not store any state information (needed for animation) in its instance.
* Instead a client property is set on the painted component.
* This makes it possible to use a share icon instance for multiple components.
*
* @author Karl Tauber
*/
public abstract class FlatAnimatedIcon
extends FlatAbstractIcon
implements AnimatedIcon
{
public FlatAnimatedIcon( int width, int height, Color color ) {
super( width, height, color );
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
super.paintIcon( c, g, x, y );
AnimatedIcon.AnimationSupport.saveIconLocation( this, c, x, y );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
AnimatedIcon.AnimationSupport.paintIcon( this, c, g, 0, 0 );
}
}

View File

@@ -27,7 +27,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "ascendingSort" icon for {@link javax.swing.table.JTableHeader}. * "ascendingSort" icon for {@link javax.swing.table.JTableHeader}.
* *
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Table.sortIconColor Color * @uiDefault Table.sortIconColor Color
* *
* @author Karl Tauber * @author Karl Tauber
@@ -35,7 +35,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatAscendingSortIcon public class FlatAscendingSortIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) ); protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" ); protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
public FlatAscendingSortIcon() { public FlatAscendingSortIcon() {

View File

@@ -49,15 +49,16 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @uiDefault CheckBox.icon.disabledBorderColor Color * @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.disabledBackground Color * @uiDefault CheckBox.icon.disabledBackground Color
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color * @uiDefault CheckBox.icon.disabledCheckmarkColor Color
* @uiDefault CheckBox.icon.focusedBorderColor Color * @uiDefault CheckBox.icon.focusedBorderColor Color optional
* @uiDefault CheckBox.icon.focusedBackground Color optional * @uiDefault CheckBox.icon.focusedBackground Color optional
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional * @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional; CheckBox.icon.focusedBorderColor is used if not specified
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional * @uiDefault CheckBox.icon.selectedFocusedBackground Color optional; CheckBox.icon.focusedBackground is used if not specified
* @uiDefault CheckBox.icon.selectedFocusedCheckmarkColor Color optional; CheckBox.icon.checkmarkColor is used if not specified
* @uiDefault CheckBox.icon.hoverBorderColor Color optional * @uiDefault CheckBox.icon.hoverBorderColor Color optional
* @uiDefault CheckBox.icon.hoverBackground Color optional * @uiDefault CheckBox.icon.hoverBackground Color optional
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional * @uiDefault CheckBox.icon.selectedHoverBackground Color optional; CheckBox.icon.hoverBackground is used if not specified
* @uiDefault CheckBox.icon.pressedBackground Color optional * @uiDefault CheckBox.icon.pressedBackground Color optional
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional * @uiDefault CheckBox.icon.selectedPressedBackground Color optional; CheckBox.icon.pressedBackground is used if not specified
* @uiDefault CheckBox.arc int * @uiDefault CheckBox.arc int
* *
* @author Karl Tauber * @author Karl Tauber
@@ -129,78 +130,102 @@ public class FlatCheckBoxIcon
} }
@Override @Override
protected void paintIcon( Component c, Graphics2D g2 ) { protected void paintIcon( Component c, Graphics2D g ) {
boolean indeterminate = c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE ); boolean indeterminate = isIndeterminate( c );
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected()); boolean selected = indeterminate || isSelected( c );
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c ); boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
// paint focused border // paint focused border
if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) { if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
g2.setColor( focusColor ); g.setColor( getFocusColor( c ) );
paintFocusBorder( g2 ); paintFocusBorder( c, g );
} }
// paint border // paint border
g2.setColor( FlatButtonUI.buttonStateColor( c, g.setColor( getBorderColor( c, selected ) );
selected ? selectedBorderColor : borderColor, paintBorder( c, g );
disabledBorderColor,
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
hoverBorderColor,
null ) );
paintBorder( g2 );
// paint background // paint background
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c, g.setColor( FlatUIUtils.deriveColor( getBackground( c, selected ),
selected ? selectedBackground : background, selected ? selectedBackground : background ) );
disabledBackground, paintBackground( c, g );
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground ),
background ) );
paintBackground( g2 );
// paint checkmark // paint checkmark
if( selected || indeterminate ) { if( selected || indeterminate ) {
g2.setColor( c.isEnabled() g.setColor( getCheckmarkColor( c, selected, isFocused ) );
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
? selectedFocusedCheckmarkColor
: checkmarkColor)
: disabledCheckmarkColor );
if( indeterminate ) if( indeterminate )
paintIndeterminate( g2 ); paintIndeterminate( c, g );
else else
paintCheckmark( g2 ); paintCheckmark( c, g );
} }
} }
protected void paintFocusBorder( Graphics2D g2 ) { protected void paintFocusBorder( Component c, Graphics2D g ) {
// the outline focus border is painted outside of the icon // the outline focus border is painted outside of the icon
int wh = ICON_SIZE - 1 + (focusWidth * 2); int wh = ICON_SIZE - 1 + (focusWidth * 2);
int arcwh = arc + (focusWidth * 2); int arcwh = arc + (focusWidth * 2);
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh ); g.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
} }
protected void paintBorder( Graphics2D g2 ) { protected void paintBorder( Component c, Graphics2D g ) {
int arcwh = arc; int arcwh = arc;
g2.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh ); g.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
} }
protected void paintBackground( Graphics2D g2 ) { protected void paintBackground( Component c, Graphics2D g ) {
int arcwh = arc - 1; int arcwh = arc - 1;
g2.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh ); g.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
} }
protected void paintCheckmark( Graphics2D g2 ) { protected void paintCheckmark( Component c, Graphics2D g ) {
Path2D.Float path = new Path2D.Float(); Path2D.Float path = new Path2D.Float();
path.moveTo( 4.5f, 7.5f ); path.moveTo( 4.5f, 7.5f );
path.lineTo( 6.6f, 10f ); path.lineTo( 6.6f, 10f );
path.lineTo( 11.25f, 3.5f ); path.lineTo( 11.25f, 3.5f );
g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); g.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g2.draw( path ); g.draw( path );
} }
protected void paintIndeterminate( Graphics2D g2 ) { protected void paintIndeterminate( Component c, Graphics2D g ) {
g2.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) ); g.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
}
protected boolean isIndeterminate( Component c ) {
return c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
}
protected boolean isSelected( Component c ) {
return c instanceof AbstractButton && ((AbstractButton)c).isSelected();
}
protected Color getFocusColor( Component c ) {
return focusColor;
}
protected Color getBorderColor( Component c, boolean selected ) {
return FlatButtonUI.buttonStateColor( c,
selected ? selectedBorderColor : borderColor,
disabledBorderColor,
selected && selectedFocusedBorderColor != null ? selectedFocusedBorderColor : focusedBorderColor,
hoverBorderColor,
null );
}
protected Color getBackground( Component c, boolean selected ) {
return FlatButtonUI.buttonStateColor( c,
selected ? selectedBackground : background,
disabledBackground,
(selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
(selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
(selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground );
}
protected Color getCheckmarkColor( Component c, boolean selected, boolean isFocused ) {
return c.isEnabled()
? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
? selectedFocusedCheckmarkColor
: checkmarkColor)
: disabledCheckmarkColor;
} }
} }

View File

@@ -27,7 +27,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "descendingSort" icon for {@link javax.swing.table.JTableHeader}. * "descendingSort" icon for {@link javax.swing.table.JTableHeader}.
* *
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Table.sortIconColor Color * @uiDefault Table.sortIconColor Color
* *
* @author Karl Tauber * @author Karl Tauber
@@ -35,7 +35,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatDescendingSortIcon public class FlatDescendingSortIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) ); protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" ); protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
public FlatDescendingSortIcon() { public FlatDescendingSortIcon() {

View File

@@ -28,7 +28,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "arrow" icon for {@link javax.swing.JMenu}. * "arrow" icon for {@link javax.swing.JMenu}.
* *
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Menu.icon.arrowColor Color * @uiDefault Menu.icon.arrowColor Color
* @uiDefault Menu.icon.disabledArrowColor Color * @uiDefault Menu.icon.disabledArrowColor Color
* @uiDefault Menu.selectionForeground Color * @uiDefault Menu.selectionForeground Color
@@ -39,7 +39,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatMenuArrowIcon public class FlatMenuArrowIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final boolean chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) ); protected final boolean chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" ); protected final Color arrowColor = UIManager.getColor( "Menu.icon.arrowColor" );
protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" ); protected final Color disabledArrowColor = UIManager.getColor( "Menu.icon.disabledArrowColor" );
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" ); protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
@@ -36,25 +37,25 @@ public class FlatRadioButtonIcon
protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style ); protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
@Override @Override
protected void paintFocusBorder( Graphics2D g2 ) { protected void paintFocusBorder( Component c, Graphics2D g ) {
// the outline focus border is painted outside of the icon // the outline focus border is painted outside of the icon
int wh = ICON_SIZE + (focusWidth * 2); int wh = ICON_SIZE + (focusWidth * 2);
g2.fillOval( -focusWidth, -focusWidth, wh, wh ); g.fillOval( -focusWidth, -focusWidth, wh, wh );
} }
@Override @Override
protected void paintBorder( Graphics2D g2 ) { protected void paintBorder( Component c, Graphics2D g ) {
g2.fillOval( 0, 0, 15, 15 ); g.fillOval( 0, 0, 15, 15 );
} }
@Override @Override
protected void paintBackground( Graphics2D g2 ) { protected void paintBackground( Component c, Graphics2D g ) {
g2.fillOval( 1, 1, 13, 13 ); g.fillOval( 1, 1, 13, 13 );
} }
@Override @Override
protected void paintCheckmark( Graphics2D g2 ) { protected void paintCheckmark( Component c, Graphics2D g ) {
float xy = (ICON_SIZE - centerDiameter) / 2f; float xy = (ICON_SIZE - centerDiameter) / 2f;
g2.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) ); g.fill( new Ellipse2D.Float( xy, xy, centerDiameter, centerDiameter ) );
} }
} }

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "close" icon for closable tabs in {@link javax.swing.JTabbedPane}.
*
* @uiDefault TabbedPane.closeSize Dimension
* @uiDefault TabbedPane.closeArc int
* @uiDefault TabbedPane.closeCrossPlainSize float
* @uiDefault TabbedPane.closeCrossFilledSize float
* @uiDefault TabbedPane.closeCrossLineWidth float
* @uiDefault TabbedPane.closeBackground Color
* @uiDefault TabbedPane.closeForeground Color
* @uiDefault TabbedPane.closeHoverBackground Color
* @uiDefault TabbedPane.closeHoverForeground Color
* @uiDefault TabbedPane.closePressedBackground Color
* @uiDefault TabbedPane.closePressedForeground Color
*
* @author Karl Tauber
*/
public class FlatTabbedPaneCloseIcon
extends FlatAbstractIcon
{
protected final Dimension size = UIManager.getDimension( "TabbedPane.closeSize" );
protected final int arc = UIManager.getInt( "TabbedPane.closeArc" );
protected final float crossPlainSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossPlainSize", 7.5f );
protected final float crossFilledSize = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossFilledSize", crossPlainSize );
protected final float closeCrossLineWidth = FlatUIUtils.getUIFloat( "TabbedPane.closeCrossLineWidth", 1f );
protected final Color background = UIManager.getColor( "TabbedPane.closeBackground" );
protected final Color foreground = UIManager.getColor( "TabbedPane.closeForeground" );
protected final Color hoverBackground = UIManager.getColor( "TabbedPane.closeHoverBackground" );
protected final Color hoverForeground = UIManager.getColor( "TabbedPane.closeHoverForeground" );
protected final Color pressedBackground = UIManager.getColor( "TabbedPane.closePressedBackground" );
protected final Color pressedForeground = UIManager.getColor( "TabbedPane.closePressedForeground" );
public FlatTabbedPaneCloseIcon() {
super( 16, 16, null );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
// paint background
Color bg = FlatButtonUI.buttonStateColor( c, background, null, null, hoverBackground, pressedBackground );
if( bg != null ) {
g.setColor( FlatUIUtils.deriveColor( bg, c.getBackground() ) );
g.fillRoundRect( (width - size.width) / 2, (height - size.height) / 2,
size.width, size.height, arc, arc );
}
// set cross color
Color fg = FlatButtonUI.buttonStateColor( c, foreground, null, null, hoverForeground, pressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );
float mx = width / 2;
float my = height / 2;
float r = ((bg != null) ? crossFilledSize : crossPlainSize) / 2;
// paint cross
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
g.setStroke( new BasicStroke( closeCrossLineWidth ) );
g.draw( path );
}
}

View File

@@ -25,7 +25,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "collapsed" icon for {@link javax.swing.JTree}. * "collapsed" icon for {@link javax.swing.JTree}.
* *
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Tree.icon.collapsedColor Color * @uiDefault Tree.icon.collapsedColor Color
* *
* @author Karl Tauber * @author Karl Tauber
@@ -41,7 +41,7 @@ public class FlatTreeCollapsedIcon
FlatTreeCollapsedIcon( Color color ) { FlatTreeCollapsedIcon( Color color ) {
super( 11, 11, color ); super( 11, 11, color );
chevron = "chevron".equals( UIManager.getString( "Component.arrowType" ) ); chevron = FlatUIUtils.isChevron( UIManager.getString( "Component.arrowType" ) );
} }
@Override @Override

View File

@@ -42,12 +42,13 @@ public class FlatArrowButton
{ {
public static final int DEFAULT_ARROW_WIDTH = 8; public static final int DEFAULT_ARROW_WIDTH = 8;
private final boolean chevron; protected final boolean chevron;
private final Color foreground; protected final Color foreground;
private final Color disabledForeground; protected final Color disabledForeground;
private final Color hoverForeground; protected final Color hoverForeground;
private final Color hoverBackground; protected final Color hoverBackground;
private final Color pressedBackground; protected final Color pressedForeground;
protected final Color pressedBackground;
private int arrowWidth = DEFAULT_ARROW_WIDTH; private int arrowWidth = DEFAULT_ARROW_WIDTH;
private int xOffset = 0; private int xOffset = 0;
@@ -57,27 +58,24 @@ public class FlatArrowButton
private boolean pressed; private boolean pressed;
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground, public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
Color hoverForeground, Color hoverBackground ) Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
{
this( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, null );
}
public FlatArrowButton( int direction, String type, Color foreground, Color disabledForeground,
Color hoverForeground, Color hoverBackground, Color pressedBackground )
{ {
super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE ); super( direction, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE );
this.chevron = "chevron".equals( type ); this.chevron = FlatUIUtils.isChevron( type );
this.foreground = foreground; this.foreground = foreground;
this.disabledForeground = disabledForeground; this.disabledForeground = disabledForeground;
this.hoverForeground = hoverForeground; this.hoverForeground = hoverForeground;
this.hoverBackground = hoverBackground; this.hoverBackground = hoverBackground;
this.pressedForeground = pressedForeground;
this.pressedBackground = pressedBackground; this.pressedBackground = pressedBackground;
setOpaque( false ); setOpaque( false );
setBorder( null ); setBorder( null );
if( hoverForeground != null || hoverBackground != null || pressedBackground != null ) { if( hoverForeground != null || hoverBackground != null ||
pressedForeground != null || pressedBackground != null )
{
addMouseListener( new MouseAdapter() { addMouseListener( new MouseAdapter() {
@Override @Override
public void mouseEntered( MouseEvent e ) { public void mouseEntered( MouseEvent e ) {
@@ -142,6 +140,10 @@ public class FlatArrowButton
return background; return background;
} }
protected Color deriveForeground( Color foreground ) {
return FlatUIUtils.deriveColor( foreground, this.foreground );
}
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
return scale( super.getPreferredSize() ); return scale( super.getPreferredSize() );
@@ -154,27 +156,40 @@ public class FlatArrowButton
@Override @Override
public void paint( Graphics g ) { public void paint( Graphics g ) {
Graphics2D g2 = (Graphics2D)g; Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.setRenderingHints( g2 );
int width = getWidth();
int height = getHeight();
boolean enabled = isEnabled();
// paint hover or pressed background // paint hover or pressed background
if( enabled ) { if( isEnabled() ) {
Color background = (pressedBackground != null && isPressed()) Color background = (pressedBackground != null && isPressed())
? deriveBackground( pressedBackground ) ? pressedBackground
: ((hoverBackground != null && isHover()) : (hoverBackground != null && isHover()
? deriveBackground( hoverBackground ) ? hoverBackground
: null); : null);
if( background != null ) { if( background != null ) {
g.setColor( background ); g.setColor( deriveBackground( background ) );
g.fillRect( 0, 0, width, height ); paintBackground( (Graphics2D) g );
} }
} }
// paint arrow
g.setColor( deriveForeground( isEnabled()
? (pressedForeground != null && isPressed()
? pressedForeground
: (hoverForeground != null && isHover()
? hoverForeground
: foreground))
: disabledForeground ) );
paintArrow( (Graphics2D) g );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
protected void paintBackground( Graphics2D g ) {
g.fillRect( 0, 0, getWidth(), getHeight() );
}
protected void paintArrow( Graphics2D g ) {
int direction = getDirection(); int direction = getDirection();
boolean vert = (direction == NORTH || direction == SOUTH); boolean vert = (direction == NORTH || direction == SOUTH);
@@ -193,8 +208,8 @@ public class FlatArrowButton
rh++; rh++;
} }
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) ); int x = Math.round( (getWidth() - rw) / 2f + scale( (float) xOffset ) );
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) ); int y = Math.round( (getHeight() - rh) / 2f + scale( (float) yOffset ) );
// move arrow for round borders // move arrow for round borders
Container parent = getParent(); Container parent = getParent();
@@ -202,20 +217,17 @@ public class FlatArrowButton
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 ); x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
// paint arrow // paint arrow
g.setColor( enabled
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
: disabledForeground );
g.translate( x, y ); g.translate( x, y );
/*debug /*debug
debugPaint( g2, vert, rw, rh ); debugPaint( g, vert, rw, rh );
debug*/ debug*/
Shape arrowShape = createArrowShape( direction, chevron, w, h ); Shape arrowShape = createArrowShape( direction, chevron, w, h );
if( chevron ) { if( chevron ) {
g2.setStroke( new BasicStroke( scale( 1f ) ) ); g.setStroke( new BasicStroke( scale( 1f ) ) );
g2.draw( arrowShape ); g.draw( arrowShape );
} else { } else {
// triangle // triangle
g2.fill( arrowShape ); g.fill( arrowShape );
} }
g.translate( -x, -y ); g.translate( -x, -y );
} }

View File

@@ -43,24 +43,24 @@ import com.formdev.flatlaf.util.DerivedColor;
* Border for various components (e.g. {@link javax.swing.JTextField}). * Border for various components (e.g. {@link javax.swing.JTextField}).
* *
* There is empty space around the component border, if Component.focusWidth is greater than zero, * There is empty space around the component border, if Component.focusWidth is greater than zero,
* which is used to paint focus border. * which is used to paint outer focus border.
* *
* Because there is empty space (if focus border is not painted), * Because there is empty space (if outer focus border is not painted),
* UI delegates that use this border (or subclasses) must invoke * UI delegates that use this border (or subclasses) must invoke
* {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly. * {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly.
* *
* @uiDefault Component.focusWidth int * @uiDefault Component.focusWidth int
* @uiDefault Component.innerFocusWidth int or float * @uiDefault Component.innerFocusWidth int or float
* @uiDefault Component.focusColor Color * @uiDefault Component.focusColor Color
* @uiDefault Component.borderColor Color * @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color * @uiDefault Component.disabledBorderColor Color
* @uiDefault Component.focusedBorderColor Color * @uiDefault Component.focusedBorderColor Color
* *
* @uiDefault Component.error.borderColor Color * @uiDefault Component.error.borderColor Color
* @uiDefault Component.error.focusedBorderColor Color * @uiDefault Component.error.focusedBorderColor Color
* @uiDefault Component.warning.borderColor Color * @uiDefault Component.warning.borderColor Color
* @uiDefault Component.warning.focusedBorderColor Color * @uiDefault Component.warning.focusedBorderColor Color
* @uiDefault Component.custom.borderColor Color * @uiDefault Component.custom.borderColor Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -87,22 +87,23 @@ public class FlatBorder
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
boolean isCellEditor = isTableCellEditor( c ); float focusWidth = scale( (float) getFocusWidth( c ) );
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
float borderWidth = scale( (float) getBorderWidth( c ) ); float borderWidth = scale( (float) getBorderWidth( c ) );
float arc = isCellEditor ? 0 : scale( (float) getArc( c ) ); float arc = scale( (float) getArc( c ) );
Color outlineColor = getOutlineColor( c ); Color outlineColor = getOutlineColor( c );
// paint outer border
if( outlineColor != null || isFocused( c ) ) { if( outlineColor != null || isFocused( c ) ) {
float innerFocusWidth = !(c instanceof JScrollPane) float innerWidth = !isCellEditor( c ) && !(c instanceof JScrollPane)
? (outlineColor != null ? innerOutlineWidth : this.innerFocusWidth) ? (outlineColor != null ? innerOutlineWidth : innerFocusWidth)
: 0; : 0;
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) ); g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height, focusWidth, FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
scale( (float) getLineWidth( c ) ) + scale( innerFocusWidth ), arc ); focusWidth, borderWidth + scale( innerWidth ), arc );
} }
// paint border
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) ); g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc ); FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
} finally { } finally {
@@ -110,6 +111,10 @@ public class FlatBorder
} }
} }
/**
* Returns the outline color of the component border specified in client property
* {@link FlatClientProperties#OUTLINE}.
*/
protected Color getOutlineColor( Component c ) { protected Color getOutlineColor( Component c ) {
if( !(c instanceof JComponent) ) if( !(c instanceof JComponent) )
return null; return null;
@@ -192,14 +197,13 @@ public class FlatBorder
return FlatUIUtils.isPermanentFocusOwner( c ); return FlatUIUtils.isPermanentFocusOwner( c );
} }
protected boolean isTableCellEditor( Component c ) { protected boolean isCellEditor( Component c ) {
return FlatUIUtils.isTableCellEditor( c ); return FlatUIUtils.isCellEditor( c );
} }
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
boolean isCellEditor = isTableCellEditor( c ); float focusWidth = scale( (float) getFocusWidth( c ) );
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
float ow = focusWidth + scale( (float) getLineWidth( c ) ); float ow = focusWidth + scale( (float) getLineWidth( c ) );
insets = super.getBorderInsets( c, insets ); insets = super.getBorderInsets( c, insets );
@@ -207,6 +211,18 @@ public class FlatBorder
insets.left = Math.round( scale( (float) insets.left ) + ow ); insets.left = Math.round( scale( (float) insets.left ) + ow );
insets.bottom = Math.round( scale( (float) insets.bottom ) + ow ); insets.bottom = Math.round( scale( (float) insets.bottom ) + ow );
insets.right = Math.round( scale( (float) insets.right ) + ow ); insets.right = Math.round( scale( (float) insets.right ) + ow );
if( isCellEditor( c ) ) {
// remove top and bottom insets if used as cell editor
insets.top = insets.bottom = 0;
// remove right/left insets to avoid that text is truncated (e.g. in file chooser)
if( c.getComponentOrientation().isLeftToRight() )
insets.right = 0;
else
insets.left = 0;
}
return insets; return insets;
} }
@@ -214,6 +230,9 @@ public class FlatBorder
* Returns the (unscaled) thickness of the outer focus border. * Returns the (unscaled) thickness of the outer focus border.
*/ */
protected int getFocusWidth( Component c ) { protected int getFocusWidth( Component c ) {
if( isCellEditor( c ) )
return 0;
return focusWidth; return focusWidth;
} }

View File

@@ -42,6 +42,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.hoverBorderColor Color optional * @uiDefault Button.default.hoverBorderColor Color optional
* @uiDefault Button.default.focusedBorderColor Color * @uiDefault Button.default.focusedBorderColor Color
* @uiDefault Button.default.focusColor Color * @uiDefault Button.default.focusColor Color
* @uiDefault Button.borderWidth int
* @uiDefault Button.default.borderWidth int * @uiDefault Button.default.borderWidth int
* @uiDefault Button.toolbar.margin Insets * @uiDefault Button.toolbar.margin Insets
* @uiDefault Button.toolbar.spacingInsets Insets * @uiDefault Button.toolbar.spacingInsets Insets
@@ -62,6 +63,7 @@ public class FlatButtonBorder
protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" ); protected final Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" ); protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" ); protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
protected final int borderWidth = UIManager.getInt( "Button.borderWidth" );
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" ); protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" ); protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" ); protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
@@ -134,11 +136,14 @@ public class FlatButtonBorder
@Override @Override
protected int getBorderWidth( Component c ) { protected int getBorderWidth( Component c ) {
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : super.getBorderWidth( c ); return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
} }
@Override @Override
protected int getArc( Component c ) { protected int getArc( Component c ) {
if( isCellEditor( c ) )
return 0;
switch( FlatButtonUI.getButtonType( c ) ) { switch( FlatButtonUI.getButtonType( c ) ) {
case FlatButtonUI.TYPE_SQUARE: return 0; case FlatButtonUI.TYPE_SQUARE: return 0;
case FlatButtonUI.TYPE_ROUND_RECT: return Short.MAX_VALUE; case FlatButtonUI.TYPE_ROUND_RECT: return Short.MAX_VALUE;

View File

@@ -67,8 +67,11 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.focusedBackground Color optional * @uiDefault Button.focusedBackground Color optional
* @uiDefault Button.hoverBackground Color optional * @uiDefault Button.hoverBackground Color optional
* @uiDefault Button.pressedBackground Color optional * @uiDefault Button.pressedBackground Color optional
* @uiDefault Button.selectedBackground Color
* @uiDefault Button.selectedForeground Color
* @uiDefault Button.disabledBackground Color optional * @uiDefault Button.disabledBackground Color optional
* @uiDefault Button.disabledText Color * @uiDefault Button.disabledText Color
* @uiDefault Button.disabledSelectedBackground Color
* @uiDefault Button.default.background Color * @uiDefault Button.default.background Color
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored * @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used * @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
@@ -84,6 +87,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.toolbar.spacingInsets Insets * @uiDefault Button.toolbar.spacingInsets Insets
* @uiDefault Button.toolbar.hoverBackground Color * @uiDefault Button.toolbar.hoverBackground Color
* @uiDefault Button.toolbar.pressedBackground Color * @uiDefault Button.toolbar.pressedBackground Color
* @uiDefault Button.toolbar.selectedBackground Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -101,8 +105,11 @@ public class FlatButtonUI
protected Color focusedBackground; protected Color focusedBackground;
protected Color hoverBackground; protected Color hoverBackground;
protected Color pressedBackground; protected Color pressedBackground;
protected Color selectedBackground;
protected Color selectedForeground;
protected Color disabledBackground; protected Color disabledBackground;
protected Color disabledText; protected Color disabledText;
protected Color disabledSelectedBackground;
protected Color defaultBackground; protected Color defaultBackground;
protected Color defaultEndBackground; protected Color defaultEndBackground;
@@ -119,17 +126,14 @@ public class FlatButtonUI
protected Insets toolbarSpacingInsets; protected Insets toolbarSpacingInsets;
protected Color toolbarHoverBackground; protected Color toolbarHoverBackground;
protected Color toolbarPressedBackground; protected Color toolbarPressedBackground;
protected Color toolbarSelectedBackground;
private Icon helpButtonIcon; private Icon helpButtonIcon;
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
instance = new FlatButtonUI();
return instance;
} }
@Override @Override
@@ -150,8 +154,11 @@ public class FlatButtonUI
focusedBackground = UIManager.getColor( prefix + "focusedBackground" ); focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
hoverBackground = UIManager.getColor( prefix + "hoverBackground" ); hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
pressedBackground = UIManager.getColor( prefix + "pressedBackground" ); pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
disabledBackground = UIManager.getColor( prefix + "disabledBackground" ); disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
disabledText = UIManager.getColor( prefix + "disabledText" ); disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
if( UIManager.getBoolean( "Button.paintShadow" ) ) { if( UIManager.getBoolean( "Button.paintShadow" ) ) {
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 ); shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
@@ -174,6 +181,7 @@ public class FlatButtonUI
toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" ); toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" ); toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" ); toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" ); helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
@@ -243,18 +251,29 @@ public class FlatButtonUI
Icon icon = ((AbstractButton)c).getIcon(); Icon icon = ((AbstractButton)c).getIcon();
String text = ((AbstractButton)c).getText(); String text = ((AbstractButton)c).getText();
return (icon != null && (text == null || text.isEmpty())) || return (icon != null && (text == null || text.isEmpty())) ||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1)); (icon == null && text != null &&
("...".equals( text ) ||
text.length() == 1 ||
(text.length() == 2 && Character.isSurrogatePair( text.charAt( 0 ), text.charAt( 1 ) ))));
} }
// same indices as in parameters to clientPropertyChoice()
static final int TYPE_OTHER = -1; static final int TYPE_OTHER = -1;
static final int TYPE_SQUARE = 0; static final int TYPE_SQUARE = 0;
static final int TYPE_ROUND_RECT = 1; static final int TYPE_ROUND_RECT = 1;
static int getButtonType( Component c ) { static int getButtonType( Component c ) {
return (c instanceof AbstractButton) if( !(c instanceof AbstractButton) )
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT ) return TYPE_OTHER;
: TYPE_OTHER;
Object value = ((AbstractButton)c).getClientProperty( BUTTON_TYPE );
if( !(value instanceof String) )
return TYPE_OTHER;
switch( (String) value ) {
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
default: return TYPE_OTHER;
}
} }
static boolean isHelpButton( Component c ) { static boolean isHelpButton( Component c ) {
@@ -369,6 +388,17 @@ public class FlatButtonUI
} }
protected Color getBackground( JComponent c ) { protected Color getBackground( JComponent c ) {
if( ((AbstractButton)c).isSelected() ) {
// in toolbar use same colors for disabled and enabled because
// we assume that toolbar icon is shown disabled
boolean toolBarButton = isToolBarButton( c );
return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
null, null,
toolBarButton ? toolbarPressedBackground : pressedBackground );
}
if( !c.isEnabled() ) if( !c.isEnabled() )
return disabledBackground; return disabledBackground;
@@ -380,8 +410,13 @@ public class FlatButtonUI
if( model.isRollover() ) if( model.isRollover() )
return toolbarHoverBackground; return toolbarHoverBackground;
// use background of toolbar // use component background if explicitly set
return c.getParent().getBackground(); Color bg = c.getBackground();
if( isCustomBackground( bg ) )
return bg;
// do not paint background
return null;
} }
boolean def = isDefaultButton( c ); boolean def = isDefaultButton( c );
@@ -430,6 +465,9 @@ public class FlatButtonUI
if( !c.isEnabled() ) if( !c.isEnabled() )
return disabledText; return disabledText;
if( ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
return selectedForeground;
// use component foreground if explicitly set // use component foreground if explicitly set
Color fg = c.getForeground(); Color fg = c.getForeground();
if( isCustomForeground( fg ) ) if( isCustomForeground( fg ) )

View File

@@ -36,13 +36,15 @@ public class FlatCaret
implements UIResource implements UIResource
{ {
private final String selectAllOnFocusPolicy; private final String selectAllOnFocusPolicy;
private final boolean selectAllOnMouseClick;
private boolean wasFocused; private boolean wasFocused;
private boolean wasTemporaryLost; private boolean wasTemporaryLost;
private boolean isMousePressed; private boolean isMousePressed;
public FlatCaret( String selectAllOnFocusPolicy ) { public FlatCaret( String selectAllOnFocusPolicy, boolean selectAllOnMouseClick ) {
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy; this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
this.selectAllOnMouseClick = selectAllOnMouseClick;
} }
@Override @Override
@@ -61,7 +63,7 @@ public class FlatCaret
@Override @Override
public void focusGained( FocusEvent e ) { public void focusGained( FocusEvent e ) {
if( !wasTemporaryLost && !isMousePressed ) if( !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
selectAllOnFocusGained(); selectAllOnFocusGained();
wasTemporaryLost = false; wasTemporaryLost = false;
wasFocused = true; wasFocused = true;

View File

@@ -42,12 +42,8 @@ import javax.swing.plaf.ComponentUI;
public class FlatCheckBoxUI public class FlatCheckBoxUI
extends FlatRadioButtonUI extends FlatRadioButtonUI
{ {
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, FlatCheckBoxUI::new );
instance = new FlatCheckBoxUI();
return instance;
} }
@Override @Override

View File

@@ -24,14 +24,18 @@ import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Insets; import java.awt.Insets;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
@@ -47,6 +51,7 @@ import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
@@ -82,7 +87,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ComboBox.editorColumns int * @uiDefault ComboBox.editorColumns int
* @uiDefault ComboBox.maximumRowCount int * @uiDefault ComboBox.maximumRowCount int
* @uiDefault ComboBox.buttonStyle String auto (default), button or none * @uiDefault ComboBox.buttonStyle String auto (default), button or none
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color * @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color * @uiDefault Component.disabledBorderColor Color
@@ -94,6 +99,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ComboBox.buttonArrowColor Color * @uiDefault ComboBox.buttonArrowColor Color
* @uiDefault ComboBox.buttonDisabledArrowColor Color * @uiDefault ComboBox.buttonDisabledArrowColor Color
* @uiDefault ComboBox.buttonHoverArrowColor Color * @uiDefault ComboBox.buttonHoverArrowColor Color
* @uiDefault ComboBox.buttonPressedArrowColor Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -117,9 +123,11 @@ public class FlatComboBoxUI
protected Color buttonArrowColor; protected Color buttonArrowColor;
protected Color buttonDisabledArrowColor; protected Color buttonDisabledArrowColor;
protected Color buttonHoverArrowColor; protected Color buttonHoverArrowColor;
protected Color buttonPressedArrowColor;
private MouseListener hoverListener; private MouseListener hoverListener;
protected boolean hover; protected boolean hover;
protected boolean pressed;
private WeakReference<Component> lastRendererComponent; private WeakReference<Component> lastRendererComponent;
@@ -131,13 +139,36 @@ public class FlatComboBoxUI
protected void installListeners() { protected void installListeners() {
super.installListeners(); super.installListeners();
hoverListener = new FlatUIUtils.HoverListener( null, h -> { hoverListener = new MouseAdapter() {
if( !comboBox.isEditable() ) { @Override
hover = h; public void mouseEntered( MouseEvent e ) {
if( arrowButton != null ) hover = true;
repaintArrowButton();
}
@Override
public void mouseExited( MouseEvent e ) {
hover = false;
repaintArrowButton();
}
@Override
public void mousePressed( MouseEvent e ) {
pressed = true;
repaintArrowButton();
}
@Override
public void mouseReleased( MouseEvent e ) {
pressed = false;
repaintArrowButton();
}
private void repaintArrowButton() {
if( arrowButton != null && !comboBox.isEditable() )
arrowButton.repaint(); arrowButton.repaint();
} }
} ); };
comboBox.addMouseListener( hoverListener ); comboBox.addMouseListener( hoverListener );
} }
@@ -172,6 +203,7 @@ public class FlatComboBoxUI
buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" ); buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" );
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" ); buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" );
// set maximumRowCount // set maximumRowCount
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" ); int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
@@ -200,6 +232,7 @@ public class FlatComboBoxUI
buttonArrowColor = null; buttonArrowColor = null;
buttonDisabledArrowColor = null; buttonDisabledArrowColor = null;
buttonHoverArrowColor = null; buttonHoverArrowColor = null;
buttonPressedArrowColor = null;
MigLayoutVisualPadding.uninstall( comboBox ); MigLayoutVisualPadding.uninstall( comboBox );
} }
@@ -297,6 +330,10 @@ public class FlatComboBoxUI
protected void configureEditor() { protected void configureEditor() {
super.configureEditor(); super.configureEditor();
// remove default text field border from editor
if( editor instanceof JTextField && ((JTextField)editor).getBorder() instanceof FlatTextBorder )
((JTextField)editor).setBorder( BorderFactory.createEmptyBorder() );
// explicitly make non-opaque // explicitly make non-opaque
if( editor instanceof JComponent ) if( editor instanceof JComponent )
((JComponent)editor).setOpaque( false ); ((JComponent)editor).setOpaque( false );
@@ -306,7 +343,7 @@ public class FlatComboBoxUI
updateEditorColors(); updateEditorColors();
// macOS // macOS
if( SystemInfo.IS_MAC && editor instanceof JTextComponent ) { if( SystemInfo.isMacOS && editor instanceof JTextComponent ) {
// delegate actions from editor text field to combobox, which is necessary // delegate actions from editor text field to combobox, which is necessary
// because text field on macOS already handle those keys // because text field on macOS already handle those keys
InputMap inputMap = ((JTextComponent)editor).getInputMap(); InputMap inputMap = ((JTextComponent)editor).getInputMap();
@@ -345,7 +382,7 @@ public class FlatComboBoxUI
FlatUIUtils.paintParentBackground( g, c ); FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
@@ -379,6 +416,9 @@ public class FlatComboBoxUI
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) ); g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
} }
// avoid that the "current value" renderer is invoked with enabled antialiasing
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
paint( g, c ); paint( g, c );
} }
@@ -506,19 +546,26 @@ public class FlatComboBoxUI
extends FlatArrowButton extends FlatArrowButton
{ {
protected FlatComboBoxButton() { protected FlatComboBoxButton() {
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor, buttonHoverArrowColor, null, null ); this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor,
buttonHoverArrowColor, null, buttonPressedArrowColor, null );
} }
protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground, protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
Color hoverForeground, Color hoverBackground, Color pressedBackground ) Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
{ {
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground ); super( direction, type, foreground, disabledForeground,
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
} }
@Override @Override
protected boolean isHover() { protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false); return super.isHover() || (!comboBox.isEditable() ? hover : false);
} }
@Override
protected boolean isPressed() {
return super.isPressed() || (!comboBox.isEditable() ? pressed : false);
}
} }
//---- class FlatComboPopup ----------------------------------------------- //---- class FlatComboPopup -----------------------------------------------
@@ -544,13 +591,37 @@ public class FlatComboBoxUI
@Override @Override
protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) { protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
// get maximum display size of all items // get maximum display width of all items
Dimension displaySize = getDisplaySize(); int displayWidth = getDisplaySize().width;
// add border insets
for( Border border : new Border[] { scroller.getViewportBorder(), scroller.getBorder() } ) {
if( border != null ) {
Insets borderInsets = border.getBorderInsets( null );
displayWidth += borderInsets.left + borderInsets.right;
}
}
// add width of vertical scroll bar
JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
if( verticalScrollBar != null )
displayWidth += verticalScrollBar.getPreferredSize().width;
// make popup wider if necessary // make popup wider if necessary
if( displaySize.width > pw ) { if( displayWidth > pw ) {
int diff = displaySize.width - pw; // limit popup width to screen width
pw = displaySize.width; GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
if( gc != null ) {
Rectangle screenBounds = gc.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
} else {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
displayWidth = Math.min( displayWidth, screenSize.width );
}
int diff = displayWidth - pw;
pw = displayWidth;
if( !comboBox.getComponentOrientation().isLeftToRight() ) if( !comboBox.getComponentOrientation().isLeftToRight() )
px -= diff; px -= diff;

View File

@@ -57,4 +57,8 @@ public class FlatEmptyBorder
insets.bottom = scale( bottom ); insets.bottom = scale( bottom );
return insets; return insets;
} }
public Insets getUnscaledBorderInsets() {
return super.getBorderInsets();
}
} }

View File

@@ -45,6 +45,7 @@ import javax.swing.plaf.ComponentUI;
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault FormattedTextField.placeholderForeground Color * @uiDefault FormattedTextField.placeholderForeground Color
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* @uiDefault TextComponent.selectAllOnMouseClick boolean
* *
* @author Karl Tauber * @author Karl Tauber
*/ */

View File

@@ -92,7 +92,21 @@ public class FlatInternalFrameTitlePane
updateFrameIcon(); updateFrameIcon();
updateColors(); updateColors();
buttonPanel = new JPanel(); buttonPanel = new JPanel() {
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
int height = size.height;
// use height of invisible buttons to always have same title pane height
if( !iconButton.isVisible() )
height = Math.max( height, iconButton.getPreferredSize().height );
if( !maxButton.isVisible() )
height = Math.max( height, maxButton.getPreferredSize().height );
if( !closeButton.isVisible() )
height = Math.max( height, closeButton.getPreferredSize().height );
return new Dimension( size.width, height );
}
};
buttonPanel.setLayout( new BoxLayout( buttonPanel, BoxLayout.LINE_AXIS ) ); buttonPanel.setLayout( new BoxLayout( buttonPanel, BoxLayout.LINE_AXIS ) );
buttonPanel.setOpaque( false ); buttonPanel.setOpaque( false );

View File

@@ -84,6 +84,8 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
public class FlatInternalFrameUI public class FlatInternalFrameUI
extends BasicInternalFrameUI extends BasicInternalFrameUI
{ {
protected FlatWindowResizer windowResizer;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatInternalFrameUI( (JInternalFrame) c ); return new FlatInternalFrameUI( (JInternalFrame) c );
} }
@@ -97,6 +99,18 @@ public class FlatInternalFrameUI
super.installUI( c ); super.installUI( c );
LookAndFeel.installProperty( frame, "opaque", false ); LookAndFeel.installProperty( frame, "opaque", false );
windowResizer = createWindowResizer();
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
if( windowResizer != null ) {
windowResizer.uninstall();
windowResizer = null;
}
} }
@Override @Override
@@ -104,6 +118,10 @@ public class FlatInternalFrameUI
return new FlatInternalFrameTitlePane( w ); return new FlatInternalFrameTitlePane( w );
} }
protected FlatWindowResizer createWindowResizer() {
return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager );
}
//---- class FlatInternalFrameBorder -------------------------------------- //---- class FlatInternalFrameBorder --------------------------------------
public static class FlatInternalFrameBorder public static class FlatInternalFrameBorder

View File

@@ -56,12 +56,8 @@ public class FlatLabelUI
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatLabelUI.class, FlatLabelUI::new );
instance = new FlatLabelUI();
return instance;
} }
@Override @Override

View File

@@ -126,6 +126,9 @@ public class FlatListUI
* or the application has to be changed to extend a FlatLaf renderer. * or the application has to be changed to extend a FlatLaf renderer.
*/ */
private void toggleSelectionColors() { private void toggleSelectionColors() {
if( list == null )
return;
if( FlatUIUtils.isPermanentFocusOwner( list ) ) { if( FlatUIUtils.isPermanentFocusOwner( list ) ) {
if( list.getSelectionBackground() == selectionInactiveBackground ) if( list.getSelectionBackground() == selectionInactiveBackground )
list.setSelectionBackground( selectionBackground ); list.setSelectionBackground( selectionBackground );

View File

@@ -20,9 +20,7 @@ import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -40,16 +38,8 @@ public class FlatMenuBarBorder
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create(); float lineHeight = scale( (float) 1 );
try { FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
float lineHeight = scale( (float) 1 );
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( borderColor );
g2.fill( new Rectangle2D.Float( x, y + height - lineHeight, width, lineHeight ) );
} finally {
g2.dispose();
}
} }
@Override @Override

View File

@@ -82,7 +82,7 @@ public class FlatMenuBarUI
JMenuBar menuBar = (JMenuBar) e.getSource(); JMenuBar menuBar = (JMenuBar) e.getSource();
JMenu menu = menuBar.getMenu( 0 ); JMenu menu = menuBar.getMenu( 0 );
if( menu != null ) { if( menu != null ) {
MenuSelectionManager.defaultManager().setSelectedPath( SystemInfo.IS_WINDOWS MenuSelectionManager.defaultManager().setSelectedPath( SystemInfo.isWindows
? new MenuElement[] { menuBar, menu } ? new MenuElement[] { menuBar, menu }
: new MenuElement[] { menuBar, menu, menu.getPopupMenu() } ); : new MenuElement[] { menuBar, menu, menu.getPopupMenu() } );

View File

@@ -39,8 +39,10 @@ import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View; import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* Renderer for menu items. * Renderer for menu items.
@@ -54,7 +56,8 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* @uiDefault MenuItem.underlineSelectionBackground Color * @uiDefault MenuItem.underlineSelectionBackground Color
* @uiDefault MenuItem.underlineSelectionCheckBackground Color * @uiDefault MenuItem.underlineSelectionCheckBackground Color
* @uiDefault MenuItem.underlineSelectionColor Color * @uiDefault MenuItem.underlineSelectionColor Color
* @uiDefault MenuItem.underlineSelectionHeight Color * @uiDefault MenuItem.underlineSelectionHeight int
* @uiDefault MenuItem.selectionBackground Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -80,6 +83,8 @@ public class FlatMenuItemRenderer
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" ); protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" ); protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
protected final Color selectionBackground = UIManager.getColor( "MenuItem.selectionBackground" );
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon, protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter ) Font acceleratorFont, String acceleratorDelimiter )
{ {
@@ -245,8 +250,11 @@ public class FlatMenuItemRenderer
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 ); g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
debug*/ debug*/
paintBackground( g, selectionBackground ); boolean underlineSelection = isUnderlineSelection();
paintIcon( g, iconRect, getIconForPainting() ); paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) )
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground ); paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground ); paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) ) if( !isTopLevelMenu( menuItem ) )
@@ -256,36 +264,36 @@ debug*/
protected void paintBackground( Graphics g, Color selectionBackground ) { protected void paintBackground( Graphics g, Color selectionBackground ) {
boolean armedOrSelected = isArmedOrSelected( menuItem ); boolean armedOrSelected = isArmedOrSelected( menuItem );
if( menuItem.isOpaque() || armedOrSelected ) { if( menuItem.isOpaque() || armedOrSelected ) {
int width = menuItem.getWidth();
int height = menuItem.getHeight();
// paint background // paint background
g.setColor( armedOrSelected g.setColor( armedOrSelected
? (isUnderlineSelection() ? deriveBackground( selectionBackground )
? deriveBackground( underlineSelectionBackground )
: selectionBackground)
: menuItem.getBackground() ); : menuItem.getBackground() );
g.fillRect( 0, 0, width, height ); g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
}
}
// paint underline protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
if( armedOrSelected && isUnderlineSelection() ) { int width = menuItem.getWidth();
int underlineHeight = scale( underlineSelectionHeight ); int height = menuItem.getHeight();
g.setColor( underlineSelectionColor );
if( isTopLevelMenu( menuItem ) ) { int underlineHeight = scale( underlineSelectionHeight );
// paint underline at bottom g.setColor( underlineSelectionColor );
g.fillRect( 0, height - underlineHeight, width, underlineHeight ); if( isTopLevelMenu( menuItem ) ) {
} else if( menuItem.getComponentOrientation().isLeftToRight() ) { // paint underline at bottom
// paint underline at left side g.fillRect( 0, height - underlineHeight, width, underlineHeight );
g.fillRect( 0, 0, underlineHeight, height ); } else if( menuItem.getComponentOrientation().isLeftToRight() ) {
} else { // paint underline at left side
// paint underline at right side g.fillRect( 0, 0, underlineHeight, height );
g.fillRect( width - underlineHeight, 0, underlineHeight, height ); } else {
} // paint underline at right side
} g.fillRect( width - underlineHeight, 0, underlineHeight, height );
} }
} }
protected Color deriveBackground( Color background ) { protected Color deriveBackground( Color background ) {
if( !(background instanceof DerivedColor) )
return background;
Color baseColor = menuItem.isOpaque() Color baseColor = menuItem.isOpaque()
? menuItem.getBackground() ? menuItem.getBackground()
: FlatUIUtils.getParentBackground( menuItem ); : FlatUIUtils.getParentBackground( menuItem );
@@ -293,12 +301,12 @@ debug*/
return FlatUIUtils.deriveColor( background, baseColor ); return FlatUIUtils.deriveColor( background, baseColor );
} }
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) { protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
// if checkbox/radiobutton menu item is selected and also has a custom icon, // if checkbox/radiobutton menu item is selected and also has a custom icon,
// then use filled icon background to indicate selection (instead of using checkIcon) // then use filled icon background to indicate selection (instead of using checkIcon)
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) { if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) ); Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
g.setColor( deriveBackground( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground ) ); g.setColor( FlatUIUtils.deriveColor( checkBackground, selectionBackground ) );
g.fillRect( r.x, r.y, r.width, r.height ); g.fillRect( r.x, r.y, r.width, r.height );
} }
@@ -418,36 +426,78 @@ debug*/
private KeyStroke cachedAccelerator; private KeyStroke cachedAccelerator;
private String cachedAcceleratorText; private String cachedAcceleratorText;
private boolean cachedAcceleratorLeftToRight;
private String getAcceleratorText() { private String getAcceleratorText() {
KeyStroke accelerator = menuItem.getAccelerator(); KeyStroke accelerator = menuItem.getAccelerator();
if( accelerator == null ) if( accelerator == null )
return null; return null;
if( accelerator == cachedAccelerator ) boolean leftToRight = menuItem.getComponentOrientation().isLeftToRight();
if( accelerator == cachedAccelerator && leftToRight == cachedAcceleratorLeftToRight )
return cachedAcceleratorText; return cachedAcceleratorText;
cachedAccelerator = accelerator; cachedAccelerator = accelerator;
cachedAcceleratorText = getTextForAccelerator( accelerator ); cachedAcceleratorText = getTextForAccelerator( accelerator );
cachedAcceleratorLeftToRight = leftToRight;
return cachedAcceleratorText; return cachedAcceleratorText;
} }
protected String getTextForAccelerator( KeyStroke accelerator ) { protected String getTextForAccelerator( KeyStroke accelerator ) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
int modifiers = accelerator.getModifiers(); boolean leftToRight = menuItem.getComponentOrientation().isLeftToRight();
if( modifiers != 0 )
buf.append( InputEvent.getModifiersExText( modifiers ) ).append( acceleratorDelimiter );
// modifiers
int modifiers = accelerator.getModifiers();
if( modifiers != 0 ) {
if( SystemInfo.isMacOS ) {
if( leftToRight )
buf.append( getMacOSModifiersExText( modifiers, leftToRight ) );
} else
buf.append( InputEvent.getModifiersExText( modifiers ) ).append( acceleratorDelimiter );
}
// key
int keyCode = accelerator.getKeyCode(); int keyCode = accelerator.getKeyCode();
if( keyCode != 0 ) if( keyCode != 0 )
buf.append( KeyEvent.getKeyText( keyCode ) ); buf.append( KeyEvent.getKeyText( keyCode ) );
else else
buf.append( accelerator.getKeyChar() ); buf.append( accelerator.getKeyChar() );
// modifiers if right-to-left on macOS
if( modifiers != 0 && !leftToRight && SystemInfo.isMacOS )
buf.append( getMacOSModifiersExText( modifiers, leftToRight ) );
return buf.toString(); return buf.toString();
} }
protected String getMacOSModifiersExText( int modifiers, boolean leftToRight ) {
StringBuilder buf = new StringBuilder();
if( (modifiers & InputEvent.CTRL_DOWN_MASK) != 0 )
buf.append( controlGlyph );
if( (modifiers & (InputEvent.ALT_DOWN_MASK | InputEvent.ALT_GRAPH_DOWN_MASK)) != 0 )
buf.append( optionGlyph );
if( (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 )
buf.append( shiftGlyph );
if( (modifiers & InputEvent.META_DOWN_MASK) != 0 )
buf.append( commandGlyph );
// reverse order for right-to-left
if( !leftToRight )
buf.reverse();
return buf.toString();
}
private static final char
controlGlyph = 0x2303,
optionGlyph = 0x2325,
shiftGlyph = 0x21E7,
commandGlyph = 0x2318;
//---- class MinSizeIcon -------------------------------------------------- //---- class MinSizeIcon --------------------------------------------------
private class MinSizeIcon private class MinSizeIcon

View File

@@ -62,6 +62,12 @@ import javax.swing.plaf.basic.BasicMenuUI;
* @uiDefault MenuItem.iconTextGap int * @uiDefault MenuItem.iconTextGap int
* @uiDefault MenuBar.hoverBackground Color * @uiDefault MenuBar.hoverBackground Color
* *
* <!-- FlatMenuRenderer -->
*
* @uiDefault MenuBar.underlineSelectionBackground Color
* @uiDefault MenuBar.underlineSelectionColor Color
* @uiDefault MenuBar.underlineSelectionHeight int
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatMenuUI public class FlatMenuUI
@@ -123,6 +129,14 @@ public class FlatMenuUI
}; };
} }
@Override
public Dimension getMinimumSize( JComponent c ) {
// avoid that top-level menus (in menu bar) are made smaller if horizontal space is rare
// same code is in BasicMenuUI since Java 10
// see https://bugs.openjdk.java.net/browse/JDK-8178430
return ((JMenu)menuItem).isTopLevelMenu() ? c.getPreferredSize() : null;
}
@Override @Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) { protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize(); return renderer.getPreferredMenuItemSize();
@@ -139,6 +153,10 @@ public class FlatMenuUI
protected class FlatMenuRenderer protected class FlatMenuRenderer
extends FlatMenuItemRenderer extends FlatMenuItemRenderer
{ {
protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon, protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter ) Font acceleratorFont, String acceleratorDelimiter )
{ {
@@ -147,6 +165,9 @@ public class FlatMenuUI
@Override @Override
protected void paintBackground( Graphics g, Color selectionBackground ) { protected void paintBackground( Graphics g, Color selectionBackground ) {
if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
selectionBackground = menuBarUnderlineSelectionBackground;
ButtonModel model = menuItem.getModel(); ButtonModel model = menuItem.getModel();
if( model.isRollover() && !model.isArmed() && !model.isSelected() && if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() ) model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
@@ -156,5 +177,15 @@ public class FlatMenuUI
} else } else
super.paintBackground( g, selectionBackground ); super.paintBackground( g, selectionBackground );
} }
@Override
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
underlineSelectionColor = menuBarUnderlineSelectionColor;
underlineSelectionHeight = menuBarUnderlineSelectionHeight;
}
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
}
} }
} }

View File

@@ -35,11 +35,7 @@ import javax.swing.plaf.basic.BasicPanelUI;
public class FlatPanelUI public class FlatPanelUI
extends BasicPanelUI extends BasicPanelUI
{ {
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatPanelUI.class, FlatPanelUI::new );
instance = new FlatPanelUI();
return instance;
} }
} }

View File

@@ -60,8 +60,10 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* @uiDefault Component.minimumWidth int * @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault PasswordField.placeholderForeground Color * @uiDefault PasswordField.placeholderForeground Color
* @uiDefault PasswordField.showCapsLock boolean
* @uiDefault PasswordField.capsLockIcon Icon * @uiDefault PasswordField.capsLockIcon Icon
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* @uiDefault TextComponent.selectAllOnMouseClick boolean
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -71,6 +73,7 @@ public class FlatPasswordFieldUI
protected int minimumWidth; protected int minimumWidth;
protected boolean isIntelliJTheme; protected boolean isIntelliJTheme;
protected Color placeholderForeground; protected Color placeholderForeground;
protected boolean showCapsLock;
protected Icon capsLockIcon; protected Icon capsLockIcon;
private FocusListener focusListener; private FocusListener focusListener;
@@ -88,6 +91,7 @@ public class FlatPasswordFieldUI
minimumWidth = UIManager.getInt( "Component.minimumWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" ); placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" ); capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
LookAndFeel.installProperty( getComponent(), "opaque", false ); LookAndFeel.installProperty( getComponent(), "opaque", false );
@@ -141,7 +145,8 @@ public class FlatPasswordFieldUI
@Override @Override
protected Caret createCaret() { protected Caret createCaret() {
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) ); return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ),
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
} }
@Override @Override
@@ -160,6 +165,9 @@ public class FlatPasswordFieldUI
} }
protected void paintCapsLock( Graphics g ) { protected void paintCapsLock( Graphics g ) {
if( !showCapsLock )
return;
JTextComponent c = getComponent(); JTextComponent c = getComponent();
if( !FlatUIUtils.isPermanentFocusOwner( c ) || if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) ) !Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )

View File

@@ -21,6 +21,7 @@ import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Insets; import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Panel; import java.awt.Panel;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
@@ -32,6 +33,8 @@ import java.lang.reflect.Method;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.Popup; import javax.swing.Popup;
import javax.swing.PopupFactory; import javax.swing.PopupFactory;
import javax.swing.RootPaneContainer; import javax.swing.RootPaneContainer;
@@ -40,6 +43,7 @@ import javax.swing.UIManager;
import javax.swing.border.Border; import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* A popup factory that adds drop shadows to popups on Windows. * A popup factory that adds drop shadows to popups on Windows.
@@ -58,39 +62,111 @@ public class FlatPopupFactory
public Popup getPopup( Component owner, Component contents, int x, int y ) public Popup getPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException throws IllegalArgumentException
{ {
if( !isDropShadowPainted( owner, contents ) ) Point pt = fixToolTipLocation( owner, contents, x, y );
return new NonFlashingPopup( super.getPopup( owner, contents, x, y ), contents ); if( pt != null ) {
x = pt.x;
// macOS and Linux adds drop shadow to heavy weight popups y = pt.y;
if( SystemInfo.IS_MAC || SystemInfo.IS_LINUX ) {
Popup popup = getHeavyWeightPopup( owner, contents, x, y );
if( popup == null )
popup = super.getPopup( owner, contents, x, y );
return new NonFlashingPopup( popup, contents );
} }
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
// create drop shadow popup // create drop shadow popup
return new DropShadowPopup( super.getPopup( owner, contents, x, y ), owner, contents ); return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
} }
private boolean isDropShadowPainted( Component owner, Component contents ) { /**
Boolean b = isDropShadowPainted( owner ); * Creates a popup for the screen that the owner component is on.
if( b != null ) * <p>
return b; * PopupFactory caches heavy weight popup windows and reuses them.
* On a dual screen setup, if the popup owner has moved from one screen to the other one,
* then the cached heavy weight popup window may be connected to the wrong screen.
* If the two screens use different scaling factors, then the popup location and size
* is scaled when the popup becomes visible, which shows the popup in the wrong location
* (or on wrong screen). The re-scaling is done in WWindowPeer.setBounds() (Java 9+).
* <p>
* To fix this, dispose popup windows that are on wrong screen and get new popup.
* <p>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/
private Popup getPopupForScreenOfOwner( Component owner, Component contents, int x, int y, boolean forceHeavyWeight )
throws IllegalArgumentException
{
int count = 0;
b = isDropShadowPainted( contents ); for(;;) {
if( b != null ) // create new or get cached popup
return b; Popup popup = forceHeavyWeight
? getHeavyWeightPopup( owner, contents, x, y )
: super.getPopup( owner, contents, x, y );
return UIManager.getBoolean( "Popup.dropShadowPainted" ); // get heavy weight popup window; is null for non-heavy weight popup
Window popupWindow = SwingUtilities.windowForComponent( contents );
// check whether heavy weight popup window is on same screen as owner component
if( popupWindow == null ||
popupWindow.getGraphicsConfiguration() == owner.getGraphicsConfiguration() )
return popup;
// remove contents component from popup window
if( popupWindow instanceof JWindow )
((JWindow)popupWindow).getContentPane().removeAll();
// dispose unused popup
// (do not invoke popup.hide() because this would cache the popup window)
popupWindow.dispose();
// avoid endless loop (should newer happen; PopupFactory cache size is 5)
if( ++count > 10 )
return popup;
}
} }
private Boolean isDropShadowPainted( Component c ) { /**
if( !(c instanceof JComponent) ) * Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
return null; * <p>
* On a dual screen setup, where screens use different scale factors, it may happen
* that the window location changes when showing a heavy weight popup window.
* E.g. when opening an dialog on the secondary screen and making combobox popup visible.
* <p>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
if( popupWindow != null ) {
// remember location of heavy weight popup window
int x = popupWindow.getX();
int y = popupWindow.getY();
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED ); popup.show();
return (value instanceof Boolean ) ? (Boolean) value : null;
// restore popup window location if it has changed
// (probably scaled when screens use different scale factors)
if( popupWindow.getX() != x || popupWindow.getY() != y )
popupWindow.setLocation( x, y );
} else
popup.show();
}
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
if( owner instanceof JComponent ) {
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) owner, clientKey, null );
if( b != null )
return b;
}
if( contents instanceof JComponent ) {
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) contents, clientKey, null );
if( b != null )
return b;
}
return UIManager.getBoolean( uiKey );
} }
/** /**
@@ -105,7 +181,7 @@ public class FlatPopupFactory
throws IllegalArgumentException throws IllegalArgumentException
{ {
try { try {
if( SystemInfo.IS_JAVA_9_OR_LATER ) { if( SystemInfo.isJava_9_orLater ) {
if( java9getPopupMethod == null ) { if( java9getPopupMethod == null ) {
java9getPopupMethod = PopupFactory.class.getDeclaredMethod( java9getPopupMethod = PopupFactory.class.getDeclaredMethod(
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class ); "getPopup", Component.class, Component.class, int.class, int.class, boolean.class );
@@ -126,12 +202,48 @@ public class FlatPopupFactory
} }
} }
/**
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
* In case that the tooltip would be partly outside of the screen,
* ToolTipManagerthe changes the location so that the entire tooltip fits on screen.
* But this can place the tooltip under the mouse location and hide the owner component.
* <p>
* This method checks whether the current mouse location is within tooltip bounds
* and corrects the y-location so that the tooltip is placed above the mouse location.
*/
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
return null;
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
Dimension tipSize = contents.getPreferredSize();
// check whether mouse location is within tooltip bounds
Rectangle tipBounds = new Rectangle( x, y, tipSize.width, tipSize.height );
if( !tipBounds.contains( mouseLocation ) )
return null;
// place tooltip above mouse location
return new Point( x, mouseLocation.y - tipSize.height - UIScale.scale( 20 ) );
}
private boolean wasInvokedFromToolTipManager() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for( StackTraceElement stackTraceElement : stackTrace ) {
if( "javax.swing.ToolTipManager".equals( stackTraceElement.getClassName() ) &&
"showTipWindow".equals( stackTraceElement.getMethodName() ) )
return true;
}
return false;
}
//---- class NonFlashingPopup --------------------------------------------- //---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup private class NonFlashingPopup
extends Popup extends Popup
{ {
private Popup delegate; private Popup delegate;
private Component contents;
// heavy weight // heavy weight
protected Window popupWindow; protected Window popupWindow;
@@ -139,6 +251,7 @@ public class FlatPopupFactory
NonFlashingPopup( Popup delegate, Component contents ) { NonFlashingPopup( Popup delegate, Component contents ) {
this.delegate = delegate; this.delegate = delegate;
this.contents = contents;
popupWindow = SwingUtilities.windowForComponent( contents ); popupWindow = SwingUtilities.windowForComponent( contents );
if( popupWindow != null ) { if( popupWindow != null ) {
@@ -153,8 +266,26 @@ public class FlatPopupFactory
@Override @Override
public void show() { public void show() {
if( delegate != null ) if( delegate != null ) {
delegate.show(); showPopupAndFixLocation( delegate, popupWindow );
// increase tooltip size if necessary because it may be too small on HiDPI screens
// https://bugs.openjdk.java.net/browse/JDK-8213535
if( contents instanceof JToolTip && popupWindow == null ) {
Container parent = contents.getParent();
if( parent instanceof JPanel ) {
Dimension prefSize = parent.getPreferredSize();
if( !prefSize.equals( parent.getSize() ) ) {
Container mediumWeightPanel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
Container c = (mediumWeightPanel != null)
? mediumWeightPanel // medium weight popup
: parent; // light weight popup
c.setSize( prefSize );
c.validate();
}
}
}
}
} }
@Override @Override
@@ -162,6 +293,7 @@ public class FlatPopupFactory
if( delegate != null ) { if( delegate != null ) {
delegate.hide(); delegate.hide();
delegate = null; delegate = null;
contents = null;
} }
if( popupWindow != null ) { if( popupWindow != null ) {
@@ -228,7 +360,7 @@ public class FlatPopupFactory
// create heavy weight popup for drop shadow // create heavy weight popup for drop shadow
int x = popupWindow.getX() - insets.left; int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top; int y = popupWindow.getY() - insets.top;
dropShadowDelegate = getHeavyWeightPopup( owner, dropShadowPanel, x, y ); dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
// make drop shadow popup window translucent // make drop shadow popup window translucent
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel ); dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
@@ -270,7 +402,7 @@ public class FlatPopupFactory
@Override @Override
public void show() { public void show() {
if( dropShadowDelegate != null ) if( dropShadowDelegate != null )
dropShadowDelegate.show(); showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
if( mediumWeightPanel != null ) if( mediumWeightPanel != null )
showMediumWeightDropShadow(); showMediumWeightDropShadow();

View File

@@ -38,12 +38,8 @@ import javax.swing.plaf.ComponentUI;
public class FlatPopupMenuSeparatorUI public class FlatPopupMenuSeparatorUI
extends FlatSeparatorUI extends FlatSeparatorUI
{ {
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatPopupMenuSeparatorUI.class, FlatPopupMenuSeparatorUI::new );
instance = new FlatPopupMenuSeparatorUI();
return instance;
} }
@Override @Override

View File

@@ -155,7 +155,7 @@ public class FlatProgressBarUI
? 0 ? 0
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width ); : Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
FlatUIUtils.setRenderingHints( (Graphics2D) g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
// paint track // paint track
RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc ); RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float( x, y, width, height, arc, arc );
@@ -163,6 +163,7 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( trackShape ); ((Graphics2D)g).fill( trackShape );
// paint progress // paint progress
int amountFull = 0;
if( progressBar.isIndeterminate() ) { if( progressBar.isIndeterminate() ) {
boxRect = getBox( boxRect ); boxRect = getBox( boxRect );
if( boxRect != null ) { if( boxRect != null ) {
@@ -170,11 +171,8 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y, ((Graphics2D)g).fill( new RoundRectangle2D.Float( boxRect.x, boxRect.y,
boxRect.width, boxRect.height, arc, arc ) ); boxRect.width, boxRect.height, arc, arc ) );
} }
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, 0, insets );
} else { } else {
int amountFull = getAmountFull( insets, width, height ); amountFull = getAmountFull( insets, width, height );
RoundRectangle2D.Float progressShape = horizontal RoundRectangle2D.Float progressShape = horizontal
? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull), ? new RoundRectangle2D.Float( c.getComponentOrientation().isLeftToRight() ? x : x + (width - amountFull),
@@ -189,10 +187,12 @@ public class FlatProgressBarUI
((Graphics2D)g).fill( area ); ((Graphics2D)g).fill( area );
} else } else
((Graphics2D)g).fill( progressShape ); ((Graphics2D)g).fill( progressShape );
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, amountFull, insets );
} }
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
if( progressBar.isStringPainted() )
paintString( g, x, y, width, height, amountFull, insets );
} }
@Override @Override

View File

@@ -60,12 +60,8 @@ public class FlatRadioButtonUI
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, FlatRadioButtonUI::new );
instance = new FlatRadioButtonUI();
return instance;
} }
@Override @Override

View File

@@ -37,6 +37,7 @@ import javax.swing.JMenuBar;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -45,6 +46,7 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
@@ -54,6 +56,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault RootPane.border Border * @uiDefault RootPane.border Border
* @uiDefault RootPane.activeBorderColor Color * @uiDefault RootPane.activeBorderColor Color
* @uiDefault RootPane.inactiveBorderColor Color * @uiDefault RootPane.inactiveBorderColor Color
* @uiDefault TitlePane.borderColor Color optional
* *
* <!-- FlatWindowResizer --> * <!-- FlatWindowResizer -->
* *
@@ -69,7 +72,9 @@ public class FlatRootPaneUI
{ {
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class // check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
static final boolean canUseJBRCustomDecorations static final boolean canUseJBRCustomDecorations
= SystemInfo.IS_JETBRAINS_JVM_11_OR_LATER && SystemInfo.IS_WINDOWS_10_OR_LATER; = SystemInfo.isJetBrainsJVM_11_orLater && SystemInfo.isWindows_10_orLater;
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected JRootPane rootPane; protected JRootPane rootPane;
protected FlatTitlePane titlePane; protected FlatTitlePane titlePane;
@@ -89,11 +94,21 @@ public class FlatRootPaneUI
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
installClientDecorations(); installClientDecorations();
else
installBorder();
if( canUseJBRCustomDecorations ) if( canUseJBRCustomDecorations )
JBRCustomDecorations.install( rootPane ); JBRCustomDecorations.install( rootPane );
} }
protected void installBorder() {
if( borderColor != null ) {
Border b = rootPane.getBorder();
if( b == null || b instanceof UIResource )
rootPane.setBorder( new FlatWindowTitleBorder( borderColor ) );
}
}
@Override @Override
public void uninstallUI( JComponent c ) { public void uninstallUI( JComponent c ) {
super.uninstallUI( c ); super.uninstallUI( c );
@@ -119,11 +134,8 @@ public class FlatRootPaneUI
} }
// enable dark window appearance on macOS when running in JetBrains Runtime // enable dark window appearance on macOS when running in JetBrains Runtime
if( SystemInfo.IS_JETBRAINS_JVM && SystemInfo.IS_MAC_OS_10_14_MOJAVE ) { if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
LookAndFeel laf = UIManager.getLookAndFeel(); c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
boolean isDark = laf instanceof FlatLaf && ((FlatLaf)laf).isDark();
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", isDark );
}
} }
protected void installClientDecorations() { protected void installClientDecorations() {
@@ -172,7 +184,7 @@ public class FlatRootPaneUI
} }
protected FlatWindowResizer createWindowResizer() { protected FlatWindowResizer createWindowResizer() {
return new FlatWindowResizer( rootPane ); return new FlatWindowResizer.WindowResizer( rootPane );
} }
protected FlatTitlePane createTitlePane() { protected FlatTitlePane createTitlePane() {
@@ -203,6 +215,8 @@ public class FlatRootPaneUI
uninstallClientDecorations(); uninstallClientDecorations();
if( rootPane.getWindowDecorationStyle() != JRootPane.NONE ) if( rootPane.getWindowDecorationStyle() != JRootPane.NONE )
installClientDecorations(); installClientDecorations();
else
installBorder();
break; break;
case FlatClientProperties.MENU_BAR_EMBEDDED: case FlatClientProperties.MENU_BAR_EMBEDDED:
@@ -252,8 +266,9 @@ public class FlatRootPaneUI
int width = Math.max( titlePaneSize.width, contentSize.width ); int width = Math.max( titlePaneSize.width, contentSize.width );
int height = titlePaneSize.height + contentSize.height; int height = titlePaneSize.height + contentSize.height;
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) { if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
Dimension menuBarSize = (rootPane.getJMenuBar() != null) JMenuBar menuBar = rootPane.getJMenuBar();
? getSizeFunc.apply( rootPane.getJMenuBar() ) Dimension menuBarSize = (menuBar != null && menuBar.isVisible())
? getSizeFunc.apply( menuBar )
: new Dimension(); : new Dimension();
width = Math.max( width, menuBarSize.width ); width = Math.max( width, menuBarSize.width );
@@ -270,6 +285,7 @@ public class FlatRootPaneUI
@Override @Override
public void layoutContainer( Container parent ) { public void layoutContainer( Container parent ) {
JRootPane rootPane = (JRootPane) parent; JRootPane rootPane = (JRootPane) parent;
boolean isFullScreen = FlatUIUtils.isFullScreen( rootPane );
Insets insets = rootPane.getInsets(); Insets insets = rootPane.getInsets();
int x = insets.left; int x = insets.left;
@@ -283,15 +299,15 @@ public class FlatRootPaneUI
rootPane.getGlassPane().setBounds( x, y, width, height ); rootPane.getGlassPane().setBounds( x, y, width, height );
int nextY = 0; int nextY = 0;
if( titlePane != null ) { if( !isFullScreen && titlePane != null ) {
Dimension prefSize = titlePane.getPreferredSize(); Dimension prefSize = titlePane.getPreferredSize();
titlePane.setBounds( 0, 0, width, prefSize.height ); titlePane.setBounds( 0, 0, width, prefSize.height );
nextY += prefSize.height; nextY += prefSize.height;
} }
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
if( menuBar != null ) { if( menuBar != null && menuBar.isVisible() ) {
if( titlePane != null && titlePane.isMenuBarEmbedded() ) { if( !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded() ) {
titlePane.validate(); titlePane.validate();
menuBar.setBounds( titlePane.getMenuBarBounds() ); menuBar.setBounds( titlePane.getMenuBarBounds() );
} else { } else {
@@ -304,6 +320,9 @@ public class FlatRootPaneUI
Container contentPane = rootPane.getContentPane(); Container contentPane = rootPane.getContentPane();
if( contentPane != null ) if( contentPane != null )
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) ); contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );
if( titlePane != null )
titlePane.menuBarLayouted();
} }
@Override @Override
@@ -338,7 +357,7 @@ public class FlatRootPaneUI
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
if( isWindowMaximized( c ) ) { if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) ) {
// hide border if window is maximized // hide border if window is maximized
insets.top = insets.left = insets.bottom = insets.right = 0; insets.top = insets.left = insets.bottom = insets.right = 0;
return insets; return insets;
@@ -348,7 +367,7 @@ public class FlatRootPaneUI
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( isWindowMaximized( c ) ) if( isWindowMaximized( c ) || FlatUIUtils.isFullScreen( c ) )
return; return;
Container parent = c.getParent(); Container parent = c.getParent();
@@ -369,4 +388,40 @@ public class FlatRootPaneUI
: false; : false;
} }
} }
//---- class FlatWindowTitleBorder ----------------------------------------
private static class FlatWindowTitleBorder
extends BorderUIResource.EmptyBorderUIResource
{
private final Color borderColor;
FlatWindowTitleBorder( Color borderColor ) {
super( 0, 0, 0, 0 );
this.borderColor = borderColor;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( showBorder( c ) ) {
float lineHeight = UIScale.scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y, width, lineHeight );
}
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets.set( showBorder( c ) ? 1 : 0, 0, 0, 0 );
return insets;
}
private boolean showBorder( Component c ) {
Container parent = c.getParent();
return
(parent instanceof JFrame &&
(((JFrame)parent).getJMenuBar() == null ||
!((JFrame)parent).getJMenuBar().isVisible())) ||
parent instanceof JDialog;
}
}
} }

View File

@@ -33,6 +33,10 @@ public class FlatRoundBorder
@Override @Override
protected int getArc( Component c ) { protected int getArc( Component c ) {
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc; if( isCellEditor( c ) )
return 0;
Boolean roundRect = FlatUIUtils.isRoundRect( c );
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
} }
} }

View File

@@ -19,7 +19,6 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
@@ -65,7 +64,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ScrollBar.pressedTrackColor Color optional * @uiDefault ScrollBar.pressedTrackColor Color optional
* @uiDefault ScrollBar.pressedThumbColor Color optional * @uiDefault ScrollBar.pressedThumbColor Color optional
* @uiDefault ScrollBar.pressedThumbWithTrack boolean * @uiDefault ScrollBar.pressedThumbWithTrack boolean
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault ScrollBar.showButtons boolean * @uiDefault ScrollBar.showButtons boolean
* @uiDefault ScrollBar.buttonArrowColor Color * @uiDefault ScrollBar.buttonArrowColor Color
* @uiDefault ScrollBar.buttonDisabledArrowColor Color * @uiDefault ScrollBar.buttonDisabledArrowColor Color
@@ -142,6 +141,12 @@ public class FlatScrollBarUI
buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" );
hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" ); hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" );
pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" ); pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" );
// fallback (e.g. when used in NetBeans GUI builder)
if( trackInsets == null )
trackInsets = new Insets( 0, 0, 0, 0 );
if( thumbInsets == null )
thumbInsets = new Insets( 0, 0, 0, 0 );
} }
@Override @Override
@@ -215,8 +220,9 @@ public class FlatScrollBarUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.paint( g, c ); super.paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
@Override @Override
@@ -284,12 +290,12 @@ public class FlatScrollBarUI
@Override @Override
protected Dimension getMinimumThumbSize() { protected Dimension getMinimumThumbSize() {
return UIScale.scale( super.getMinimumThumbSize() ); return UIScale.scale( FlatUIUtils.addInsets( super.getMinimumThumbSize(), thumbInsets ) );
} }
@Override @Override
protected Dimension getMaximumThumbSize() { protected Dimension getMaximumThumbSize() {
return UIScale.scale( super.getMaximumThumbSize() ); return UIScale.scale( FlatUIUtils.addInsets( super.getMaximumThumbSize(), thumbInsets ) );
} }
//---- class ScrollBarHoverListener --------------------------------------- //---- class ScrollBarHoverListener ---------------------------------------
@@ -351,13 +357,14 @@ public class FlatScrollBarUI
{ {
protected FlatScrollBarButton( int direction ) { protected FlatScrollBarButton( int direction ) {
this( direction, arrowType, buttonArrowColor, buttonDisabledArrowColor, this( direction, arrowType, buttonArrowColor, buttonDisabledArrowColor,
null, hoverButtonBackground, pressedButtonBackground ); null, hoverButtonBackground, null, pressedButtonBackground );
} }
protected FlatScrollBarButton( int direction, String type, Color foreground, Color disabledForeground, protected FlatScrollBarButton( int direction, String type, Color foreground, Color disabledForeground,
Color hoverForeground, Color hoverBackground, Color pressedBackground ) Color hoverForeground, Color hoverBackground, Color pressedForeground, Color pressedBackground )
{ {
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground ); super( direction, type, foreground, disabledForeground,
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 ); setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
setFocusable( false ); setFocusable( false );

View File

@@ -14,12 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
/*
* Smooth scrolling code partly based on code from IntelliJ IDEA Community Edition,
* which is licensed under the Apache 2.0 license. Copyright 2000-2016 JetBrains s.r.o.
* See: https://github.com/JetBrains/intellij-community/blob/31e1b5a8e43219b9571951bab6457cfb3012e3ef/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java#L141-L185
*
*/
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
@@ -114,10 +108,7 @@ public class FlatScrollPaneUI
return new BasicScrollPaneUI.MouseWheelHandler() { return new BasicScrollPaneUI.MouseWheelHandler() {
@Override @Override
public void mouseWheelMoved( MouseWheelEvent e ) { public void mouseWheelMoved( MouseWheelEvent e ) {
// Note: Getting UI value "ScrollPane.smoothScrolling" here to allow if( isSmoothScrollingEnabled() &&
// applications to turn smooth scrolling on or off at any time
// (e.g. in application options dialog).
if( UIManager.getBoolean( "ScrollPane.smoothScrolling" ) &&
scrollpane.isWheelScrollingEnabled() && scrollpane.isWheelScrollingEnabled() &&
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL && e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 && e.getPreciseWheelRotation() != 0 &&
@@ -130,7 +121,16 @@ public class FlatScrollPaneUI
}; };
} }
private static final double EPSILON = 1e-5d; protected boolean isSmoothScrollingEnabled() {
Object smoothScrolling = scrollpane.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" );
}
private void mouseWheelMovedSmooth( MouseWheelEvent e ) { private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
// return if there is no viewport // return if there is no viewport
@@ -152,24 +152,22 @@ public class FlatScrollPaneUI
// get precise wheel rotation // get precise wheel rotation
double rotation = e.getPreciseWheelRotation(); double rotation = e.getPreciseWheelRotation();
// get unit and block increment // get unit increment
int unitIncrement; int unitIncrement;
int blockIncrement;
int orientation = scrollbar.getOrientation(); int orientation = scrollbar.getOrientation();
Component view = viewport.getView(); Component view = viewport.getView();
if( view instanceof Scrollable ) { if( view instanceof Scrollable ) {
Scrollable scrollable = (Scrollable) view; Scrollable scrollable = (Scrollable) view;
// Use (0, 0) view position to obtain constant unit increment of first item // Use (0, 0) view position to obtain a constant unit increment of first item.
// (which might otherwise be variable on smaller-than-unit scrolling). // Unit increment may be different for each item.
Rectangle visibleRect = new Rectangle( viewport.getViewSize() ); Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 ); unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
blockIncrement = scrollable.getScrollableBlockIncrement( visibleRect, orientation, 1 );
if( unitIncrement > 0 ) { if( unitIncrement > 0 ) {
// For the case that the first item (e.g. in a list) is larger // For the case that the first item (e.g. in a list) is larger
// than the other items, get the unit increment of the second item // than the other items (e.g. themes list in FlatLaf Demo),
// and use the smaller one. // get the unit increment of the second item and use the smaller one.
if( orientation == SwingConstants.VERTICAL ) { if( orientation == SwingConstants.VERTICAL ) {
visibleRect.y += unitIncrement; visibleRect.y += unitIncrement;
visibleRect.height -= unitIncrement; visibleRect.height -= unitIncrement;
@@ -184,52 +182,58 @@ public class FlatScrollPaneUI
} else { } else {
int direction = rotation < 0 ? -1 : 1; int direction = rotation < 0 ? -1 : 1;
unitIncrement = scrollbar.getUnitIncrement( direction ); unitIncrement = scrollbar.getUnitIncrement( direction );
blockIncrement = scrollbar.getBlockIncrement( direction );
} }
// limit scroll amount (number of units to scroll) for small viewports // get viewport width/height (the visible width/height)
// (e.g. vertical scrolling in file chooser)
int scrollAmount = e.getScrollAmount();
int viewportWH = (orientation == SwingConstants.VERTICAL) int viewportWH = (orientation == SwingConstants.VERTICAL)
? viewport.getHeight() ? viewport.getHeight()
: viewport.getWidth(); : viewport.getWidth();
if( unitIncrement * scrollAmount > viewportWH )
scrollAmount = Math.max( viewportWH / unitIncrement, 1 ); // limit scroll increment to viewport width/height
// - if scroll amount is set to a large value in OS settings
// - for large unit increments in small viewports (e.g. horizontal scrolling in file chooser)
int scrollIncrement = Math.min( unitIncrement * e.getScrollAmount(), viewportWH );
// compute relative delta // compute relative delta
double delta = rotation * scrollAmount * unitIncrement; double delta = rotation * scrollIncrement;
boolean adjustDelta = Math.abs( rotation ) < (1.0 + EPSILON); int idelta = (int) Math.round( delta );
double adjustedDelta = adjustDelta
? Math.max( -blockIncrement, Math.min( delta, blockIncrement ) ) // scroll at least one pixel to avoid "hanging"
: delta; // - for "super-low-speed" scrolling (move fingers very slowly on trackpad)
// - if unit increment is very small (e.g. 1 if scroll view does not implement
// javax.swing.Scrollable interface)
if( idelta == 0 ) {
if( rotation > 0 )
idelta = 1;
else if( rotation < 0 )
idelta = -1;
}
// compute new value // compute new value
int value = scrollbar.getValue(); int value = scrollbar.getValue();
double minDelta = scrollbar.getMinimum() - value; int minValue = scrollbar.getMinimum();
double maxDelta = scrollbar.getMaximum() - scrollbar.getModel().getExtent() - value; int maxValue = scrollbar.getMaximum() - scrollbar.getModel().getExtent();
double boundedDelta = Math.max( minDelta, Math.min( adjustedDelta, maxDelta ) ); int newValue = Math.max( minValue, Math.min( value + idelta, maxValue ) );
int newValue = value + (int) Math.round( boundedDelta );
// set new value // set new value
if( newValue != value ) if( newValue != value )
scrollbar.setValue( newValue ); scrollbar.setValue( newValue );
/*debug /*debug
System.out.println( String.format( "%4d %9f / %4d %4d / %12f %5s %12f / %4d %4d %4d / %12f %12f %12f / %4d", System.out.println( String.format( "%s %4d %9f / %3d * %d = %3d [%3d] / %8.2f %5d / %4d --> %4d [%d, %d]",
(orientation == SwingConstants.VERTICAL) ? "V" : "H",
e.getWheelRotation(), e.getWheelRotation(),
e.getPreciseWheelRotation(), e.getPreciseWheelRotation(),
unitIncrement, unitIncrement,
blockIncrement, e.getScrollAmount(),
scrollIncrement,
viewportWH,
delta, delta,
adjustDelta, idelta,
adjustedDelta,
value, value,
scrollbar.getMinimum(), newValue,
scrollbar.getMaximum(), minValue,
minDelta, maxValue ) );
maxDelta,
boundedDelta,
newValue ) );
*/ */
} }

View File

@@ -52,12 +52,8 @@ public class FlatSeparatorUI
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatSeparatorUI.class, FlatSeparatorUI::new );
instance = new FlatSeparatorUI();
return instance;
} }
@Override @Override

View File

@@ -18,17 +18,23 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.event.MouseListener; import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JSlider; import javax.swing.JSlider;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI; import javax.swing.plaf.basic.BasicSliderUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -49,29 +55,49 @@ import com.formdev.flatlaf.util.UIScale;
* <!-- FlatSliderUI --> * <!-- FlatSliderUI -->
* *
* @uiDefault Slider.trackWidth int * @uiDefault Slider.trackWidth int
* @uiDefault Slider.thumbWidth int * @uiDefault Slider.thumbSize Dimension
* @uiDefault Slider.focusWidth int
* @uiDefault Slider.trackValueColor Color optional; defaults to Slider.thumbColor
* @uiDefault Slider.trackColor Color * @uiDefault Slider.trackColor Color
* @uiDefault Slider.thumbColor Color * @uiDefault Slider.thumbColor Color
* @uiDefault Slider.thumbBorderColor Color optional; if null, no border is painted
* @uiDefault Slider.focusedColor Color optional; defaults to Component.focusColor * @uiDefault Slider.focusedColor Color optional; defaults to Component.focusColor
* @uiDefault Slider.hoverColor Color optional; defaults to Slider.focusedColor * @uiDefault Slider.focusedThumbBorderColor Color optional; defaults to Component.focusedBorderColor
* @uiDefault Slider.disabledForeground Color used for track and thumb is disabled * @uiDefault Slider.hoverThumbColor Color optional
* @uiDefault Slider.pressedThumbColor Color optional
* @uiDefault Slider.disabledTrackColor Color
* @uiDefault Slider.disabledThumbColor Color
* @uiDefault Slider.disabledThumbBorderColor Color optional; defaults to Component.disabledBorderColor
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatSliderUI public class FlatSliderUI
extends BasicSliderUI extends BasicSliderUI
{ {
private int trackWidth; protected int trackWidth;
private int thumbWidth; protected Dimension thumbSize;
protected int focusWidth;
private Color trackColor; protected Color trackValueColor;
private Color thumbColor; protected Color trackColor;
private Color focusColor; protected Color thumbColor;
private Color hoverColor; protected Color thumbBorderColor;
private Color disabledForeground; protected Color focusBaseColor;
protected Color focusedColor;
protected Color focusedThumbBorderColor;
protected Color hoverThumbColor;
protected Color pressedThumbColor;
protected Color disabledTrackColor;
protected Color disabledThumbColor;
protected Color disabledThumbBorderColor;
private MouseListener hoverListener; private Color defaultBackground;
private boolean hover; private Color defaultForeground;
protected boolean thumbHover;
protected boolean thumbPressed;
private Object[] oldRenderingHints;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatSliderUI(); return new FlatSliderUI();
@@ -81,24 +107,6 @@ public class FlatSliderUI
super( null ); super( null );
} }
@Override
protected void installListeners( JSlider slider ) {
super.installListeners( slider );
hoverListener = new FlatUIUtils.HoverListener( slider, h -> {
hover = h;
} );
slider.addMouseListener( hoverListener );
}
@Override
protected void uninstallListeners( JSlider slider ) {
super.uninstallListeners( slider );
slider.removeMouseListener( hoverListener );
hoverListener = null;
}
@Override @Override
protected void installDefaults( JSlider slider ) { protected void installDefaults( JSlider slider ) {
super.installDefaults( slider ); super.installDefaults( slider );
@@ -106,24 +114,71 @@ public class FlatSliderUI
LookAndFeel.installProperty( slider, "opaque", false ); LookAndFeel.installProperty( slider, "opaque", false );
trackWidth = UIManager.getInt( "Slider.trackWidth" ); trackWidth = UIManager.getInt( "Slider.trackWidth" );
thumbWidth = UIManager.getInt( "Slider.thumbWidth" ); thumbSize = UIManager.getDimension( "Slider.thumbSize" );
if( thumbSize == null ) {
// fallback for compatibility with old versions
int thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
thumbSize = new Dimension( thumbWidth, thumbWidth );
}
focusWidth = FlatUIUtils.getUIInt( "Slider.focusWidth", 4 );
trackValueColor = FlatUIUtils.getUIColor( "Slider.trackValueColor", "Slider.thumbColor" );
trackColor = UIManager.getColor( "Slider.trackColor" ); trackColor = UIManager.getColor( "Slider.trackColor" );
thumbColor = UIManager.getColor( "Slider.thumbColor" ); thumbColor = UIManager.getColor( "Slider.thumbColor" );
focusColor = FlatUIUtils.getUIColor( "Slider.focusedColor", "Component.focusColor" ); thumbBorderColor = UIManager.getColor( "Slider.thumbBorderColor" );
hoverColor = FlatUIUtils.getUIColor( "Slider.hoverColor", focusColor ); focusBaseColor = UIManager.getColor( "Component.focusColor" );
disabledForeground = UIManager.getColor( "Slider.disabledForeground" ); focusedColor = FlatUIUtils.getUIColor( "Slider.focusedColor", focusBaseColor );
focusedThumbBorderColor = FlatUIUtils.getUIColor( "Slider.focusedThumbBorderColor", "Component.focusedBorderColor" );
hoverThumbColor = UIManager.getColor( "Slider.hoverThumbColor" );
pressedThumbColor = UIManager.getColor( "Slider.pressedThumbColor" );
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
defaultBackground = UIManager.getColor( "Slider.background" );
defaultForeground = UIManager.getColor( "Slider.foreground" );
} }
@Override @Override
protected void uninstallDefaults( JSlider slider ) { protected void uninstallDefaults( JSlider slider ) {
super.uninstallDefaults( slider ); super.uninstallDefaults( slider );
trackValueColor = null;
trackColor = null; trackColor = null;
thumbColor = null; thumbColor = null;
focusColor = null; thumbBorderColor = null;
hoverColor = null; focusBaseColor = null;
disabledForeground = null; focusedColor = null;
focusedThumbBorderColor = null;
hoverThumbColor = null;
pressedThumbColor = null;
disabledTrackColor = null;
disabledThumbColor = null;
disabledThumbBorderColor = null;
defaultBackground = null;
defaultForeground = null;
}
@Override
protected TrackListener createTrackListener( JSlider slider ) {
return new FlatTrackListener();
}
@Override
public int getBaseline( JComponent c, int width, int height ) {
if( c == null )
throw new NullPointerException();
if( width < 0 || height < 0 )
throw new IllegalArgumentException();
// no baseline for vertical orientation
if( slider.getOrientation() == JSlider.VERTICAL )
return -1;
// compute a baseline so that the track is vertically centered
FontMetrics fm = slider.getFontMetrics( slider.getFont() );
return trackRect.y + Math.round( (trackRect.height - fm.getHeight()) / 2f ) + fm.getAscent() - 1;
} }
@Override @Override
@@ -153,14 +208,50 @@ public class FlatSliderUI
@Override @Override
protected Dimension getThumbSize() { protected Dimension getThumbSize() {
return new Dimension( UIScale.scale( thumbWidth ), UIScale.scale( thumbWidth ) ); return calcThumbSize( slider, thumbSize, focusWidth );
}
public static Dimension calcThumbSize( JSlider slider, Dimension thumbSize, int focusWidth ) {
int fw = UIScale.scale( focusWidth );
int w = UIScale.scale( thumbSize.width ) + fw + fw;
int h = UIScale.scale( thumbSize.height ) + fw + fw;
return (slider.getOrientation() == JSlider.HORIZONTAL)
? new Dimension( w, h )
: new Dimension( h, w );
} }
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g ); oldRenderingHints = FlatUIUtils.setRenderingHints( g );
/*debug
g.setColor( Color.gray );
g.drawRect( 0, 0, c.getWidth() - 1, c.getHeight() - 1 );
g.setColor( Color.orange );
g.drawRect( focusRect.x, focusRect.y, focusRect.width - 1, focusRect.height - 1 );
g.setColor( Color.magenta );
g.drawRect( contentRect.x, contentRect.y, contentRect.width - 1, contentRect.height - 1 );
g.setColor( Color.blue );
g.drawRect( trackRect.x, trackRect.y, trackRect.width - 1, trackRect.height - 1 );
g.setColor( Color.red );
g.drawRect( thumbRect.x, thumbRect.y, thumbRect.width - 1, thumbRect.height - 1 );
g.setColor( Color.green );
g.drawRect( tickRect.x, tickRect.y, tickRect.width - 1, tickRect.height - 1 );
g.setColor( Color.red );
g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 );
debug*/
super.paint( g, c ); super.paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
oldRenderingHints = null;
}
@Override
public void paintLabels( Graphics g ) {
FlatUIUtils.runWithoutRenderingHints( g, oldRenderingHints, () -> {
super.paintLabels( g );
} );
} }
@Override @Override
@@ -201,50 +292,326 @@ public class FlatSliderUI
} }
if( coloredTrack != null ) { if( coloredTrack != null ) {
g.setColor( FlatUIUtils.deriveColor( FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor ) ); if( slider.getInverted() ) {
RoundRectangle2D temp = track;
track = coloredTrack;
coloredTrack = temp;
}
g.setColor( getTrackValueColor() );
((Graphics2D)g).fill( coloredTrack ); ((Graphics2D)g).fill( coloredTrack );
} }
g.setColor( enabled ? trackColor : disabledForeground ); g.setColor( enabled ? getTrackColor() : disabledTrackColor );
((Graphics2D)g).fill( track ); ((Graphics2D)g).fill( track );
} }
@Override @Override
public void paintThumb( Graphics g ) { public void paintThumb( Graphics g ) {
g.setColor( FlatUIUtils.deriveColor( slider.isEnabled() Color thumbColor = getThumbColor();
? (FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor)) Color color = stateColor( slider, thumbHover, thumbPressed,
: disabledForeground, thumbColor, disabledThumbColor, null, hoverThumbColor, pressedThumbColor );
thumbColor ) ); color = FlatUIUtils.deriveColor( color, thumbColor );
if( isRoundThumb() ) Color foreground = slider.getForeground();
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height ); Color borderColor = (thumbBorderColor != null && foreground == defaultForeground)
else { ? stateColor( slider, false, false, thumbBorderColor, disabledThumbBorderColor, focusedThumbBorderColor, null, null )
double w = thumbRect.width; : null;
double h = thumbRect.height;
double wh = w / 2;
Path2D thumb = FlatUIUtils.createPath( 0,0, w,0, w,(h - wh), wh,h, 0,(h - wh) ); Color focusedColor = FlatUIUtils.deriveColor( this.focusedColor,
(foreground != defaultForeground) ? foreground : focusBaseColor );
paintThumb( g, slider, thumbRect, isRoundThumb(), color, borderColor, focusedColor, focusWidth );
}
public static void paintThumb( Graphics g, JSlider slider, Rectangle thumbRect, boolean roundThumb,
Color thumbColor, Color thumbBorderColor, Color focusedColor, int focusWidth )
{
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintThumbImpl( g, slider, x2, y2, width2, height2,
roundThumb, thumbColor, thumbBorderColor, focusedColor,
(float) (focusWidth * scaleFactor) );
} );
return;
}
paintThumbImpl( g, slider, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
roundThumb, thumbColor, thumbBorderColor, focusedColor, focusWidth );
}
private static void paintThumbImpl( Graphics g, JSlider slider, int x, int y, int width, int height,
boolean roundThumb, Color thumbColor, Color thumbBorderColor, Color focusedColor, float focusWidth )
{
int fw = Math.round( UIScale.scale( focusWidth ) );
int tx = x + fw;
int ty = y + fw;
int tw = width - fw - fw;
int th = height - fw - fw;
boolean focused = FlatUIUtils.isPermanentFocusOwner( slider );
if( roundThumb ) {
// paint thumb focus border
if( focused ) {
g.setColor( focusedColor );
((Graphics2D)g).fill( createRoundThumbShape( x, y, width, height ) );
}
if( thumbBorderColor != null ) {
// paint thumb border
g.setColor( thumbBorderColor );
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
// paint thumb background
float lw = UIScale.scale( 1f );
g.setColor( thumbColor );
((Graphics2D)g).fill( createRoundThumbShape( tx + lw, ty + lw,
tw - lw - lw, th - lw - lw ) );
} else {
// paint thumb background
g.setColor( thumbColor );
((Graphics2D)g).fill( createRoundThumbShape( tx, ty, tw, th ) );
}
} else {
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
g2.translate( thumbRect.x, thumbRect.y ); g2.translate( x, y );
if( slider.getOrientation() == JSlider.VERTICAL ) { if( slider.getOrientation() == JSlider.VERTICAL ) {
if( slider.getComponentOrientation().isLeftToRight() ) { if( slider.getComponentOrientation().isLeftToRight() ) {
g2.translate( 0, thumbRect.height ); g2.translate( 0, height );
g2.rotate( Math.toRadians( 270 ) ); g2.rotate( Math.toRadians( 270 ) );
} else { } else {
g2.translate( thumbRect.width, 0 ); g2.translate( width, 0 );
g2.rotate( Math.toRadians( 90 ) ); g2.rotate( Math.toRadians( 90 ) );
} }
// rotate thumb width/height
int temp = tw;
tw = th;
th = temp;
}
// paint thumb focus border
if( focused ) {
g2.setColor( focusedColor );
g2.fill( createDirectionalThumbShape( 0, 0,
tw + fw + fw, th + fw + fw + (fw * 0.4142f), fw ) );
}
if( thumbBorderColor != null ) {
// paint thumb border
g2.setColor( thumbBorderColor );
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
// paint thumb background
float lw = UIScale.scale( 1f );
g2.setColor( thumbColor );
g2.fill( createDirectionalThumbShape( fw + lw, fw + lw,
tw - lw - lw, th - lw - lw - (lw * 0.4142f), 0 ) );
} else {
// paint thumb background
g2.setColor( thumbColor );
g2.fill( createDirectionalThumbShape( fw, fw, tw, th, 0 ) );
} }
g2.fill( thumb );
} finally { } finally {
g2.dispose(); g2.dispose();
} }
} }
} }
private boolean isRoundThumb() { public static Shape createRoundThumbShape( float x, float y, float w, float h ) {
if( w == h )
return new Ellipse2D.Float( x, y, w, h );
else {
float arc = Math.min( w, h );
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
}
}
public static Shape createDirectionalThumbShape( float x, float y, float w, float h, float arc ) {
float wh = w / 2;
Path2D path = new Path2D.Float();
path.moveTo( x + wh, y + h );
path.lineTo( x, y + (h - wh) );
path.lineTo( x, y + arc );
path.quadTo( x, y, x + arc, y );
path.lineTo( x + (w - arc), y );
path.quadTo( x + w, y, x + w, y + arc );
path.lineTo( x + w, y + (h - wh) );
path.closePath();
return path;
}
protected Color getTrackValueColor() {
Color foreground = slider.getForeground();
return (foreground != defaultForeground) ? foreground : trackValueColor;
}
protected Color getTrackColor() {
Color backround = slider.getBackground();
return (backround != defaultBackground) ? backround : trackColor;
}
protected Color getThumbColor() {
Color foreground = slider.getForeground();
return (foreground != defaultForeground) ? foreground : thumbColor;
}
public static Color stateColor( JSlider slider, boolean hover, boolean pressed,
Color enabledColor, Color disabledColor, Color focusedColor, Color hoverColor, Color pressedColor )
{
if( disabledColor != null && !slider.isEnabled() )
return disabledColor;
if( pressedColor != null && pressed )
return pressedColor;
if( hoverColor != null && hover )
return hoverColor;
if( focusedColor != null && FlatUIUtils.isPermanentFocusOwner( slider ) )
return focusedColor;
return enabledColor;
}
protected boolean isRoundThumb() {
return !slider.getPaintTicks() && !slider.getPaintLabels(); return !slider.getPaintTicks() && !slider.getPaintLabels();
} }
@Override
public void setThumbLocation( int x, int y ) {
if( !isRoundThumb() ) {
// the needle of the directional thumb is painted outside of thumbRect
// --> must increase repaint rectangle
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
// increase union rectangle for repaint
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
if( slider.getOrientation() == JSlider.HORIZONTAL )
r.height += extra;
else {
r.width += extra;
if( !slider.getComponentOrientation().isLeftToRight() )
r.x -= extra;
}
slider.repaint( r );
} else
super.setThumbLocation( x, y );
}
//---- class FlatTrackListener --------------------------------------------
protected class FlatTrackListener
extends TrackListener
{
@Override
public void mouseEntered( MouseEvent e ) {
setThumbHover( isOverThumb( e ) );
super.mouseEntered( e );
}
@Override
public void mouseExited( MouseEvent e ) {
setThumbHover( false );
super.mouseExited( e );
}
@Override
public void mouseMoved( MouseEvent e ) {
setThumbHover( isOverThumb( e ) );
super.mouseMoved( e );
}
@Override
public void mousePressed( MouseEvent e ) {
setThumbPressed( isOverThumb( e ) );
if( !slider.isEnabled() )
return;
// use "old" behavior when clicking on track
if( UIManager.getBoolean( "Slider.scrollOnTrackClick" ) ) {
super.mousePressed( e );
return;
}
// "new" behavior set thumb to mouse location when clicking on track
int x = e.getX();
int y = e.getY();
// clicked on thumb --> let super class do the work
calculateGeometry();
if( thumbRect.contains( x, y ) ) {
super.mousePressed( e );
return;
}
if( UIManager.getBoolean( "Slider.onlyLeftMouseButtonDrag" ) &&
!SwingUtilities.isLeftMouseButton( e ) )
return;
// move the mouse event coordinates to the center of the thumb
int tx = thumbRect.x + (thumbRect.width / 2) - x;
int ty = thumbRect.y + (thumbRect.height / 2) - y;
e.translatePoint( tx, ty );
// invoke super mousePressed() to start dragging thumb
super.mousePressed( e );
// move the mouse event coordinates back to current mouse location
e.translatePoint( -tx, -ty );
// invoke mouseDragged() to update thumb location
mouseDragged( e );
setThumbPressed( true );
}
@Override
public void mouseReleased( MouseEvent e ) {
setThumbPressed( false );
super.mouseReleased( e );
}
@Override
public void mouseDragged( MouseEvent e ) {
super.mouseDragged( e );
if( isDragging() &&
slider.getSnapToTicks() &&
slider.isEnabled() &&
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
{
calculateThumbLocation();
slider.repaint();
}
}
protected void setThumbHover( boolean hover ) {
if( hover != thumbHover ) {
thumbHover = hover;
slider.repaint( thumbRect );
}
}
protected void setThumbPressed( boolean pressed ) {
if( pressed != thumbPressed ) {
thumbPressed = pressed;
slider.repaint( thumbRect );
}
}
protected boolean isOverThumb( MouseEvent e ) {
return e != null && slider.isEnabled() && thumbRect.contains( e.getX(), e.getY() );
}
}
} }

View File

@@ -59,7 +59,7 @@ import com.formdev.flatlaf.FlatClientProperties;
* *
* @uiDefault Component.minimumWidth int * @uiDefault Component.minimumWidth int
* @uiDefault Spinner.buttonStyle String button (default) or none * @uiDefault Spinner.buttonStyle String button (default) or none
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color * @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color * @uiDefault Component.disabledBorderColor Color
@@ -69,6 +69,7 @@ import com.formdev.flatlaf.FlatClientProperties;
* @uiDefault Spinner.buttonArrowColor Color * @uiDefault Spinner.buttonArrowColor Color
* @uiDefault Spinner.buttonDisabledArrowColor Color * @uiDefault Spinner.buttonDisabledArrowColor Color
* @uiDefault Spinner.buttonHoverArrowColor Color * @uiDefault Spinner.buttonHoverArrowColor Color
* @uiDefault Spinner.buttonPressedArrowColor Color
* @uiDefault Spinner.padding Insets * @uiDefault Spinner.padding Insets
* *
* @author Karl Tauber * @author Karl Tauber
@@ -90,6 +91,7 @@ public class FlatSpinnerUI
protected Color buttonArrowColor; protected Color buttonArrowColor;
protected Color buttonDisabledArrowColor; protected Color buttonDisabledArrowColor;
protected Color buttonHoverArrowColor; protected Color buttonHoverArrowColor;
protected Color buttonPressedArrowColor;
protected Insets padding; protected Insets padding;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
@@ -114,6 +116,7 @@ public class FlatSpinnerUI
buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" ); buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" );
buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" );
buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" ); buttonHoverArrowColor = UIManager.getColor( "Spinner.buttonHoverArrowColor" );
buttonPressedArrowColor = UIManager.getColor( "Spinner.buttonPressedArrowColor" );
padding = UIManager.getInsets( "Spinner.padding" ); padding = UIManager.getInsets( "Spinner.padding" );
// scale // scale
@@ -134,6 +137,7 @@ public class FlatSpinnerUI
buttonArrowColor = null; buttonArrowColor = null;
buttonDisabledArrowColor = null; buttonDisabledArrowColor = null;
buttonHoverArrowColor = null; buttonHoverArrowColor = null;
buttonPressedArrowColor = null;
padding = null; padding = null;
MigLayoutVisualPadding.uninstall( spinner ); MigLayoutVisualPadding.uninstall( spinner );
@@ -244,7 +248,7 @@ public class FlatSpinnerUI
private Component createArrowButton( int direction, String name ) { private Component createArrowButton( int direction, String name ) {
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor, FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
buttonDisabledArrowColor, buttonHoverArrowColor, null ); buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
button.setName( name ); button.setName( name );
button.setYOffset( (direction == SwingConstants.NORTH) ? 1 : -1 ); button.setYOffset( (direction == SwingConstants.NORTH) ? 1 : -1 );
if( direction == SwingConstants.NORTH ) if( direction == SwingConstants.NORTH )
@@ -264,35 +268,38 @@ public class FlatSpinnerUI
FlatUIUtils.paintParentBackground( g, c ); FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
Component nextButton = getHandler().nextButton;
int arrowX = nextButton.getX();
int arrowWidth = nextButton.getWidth();
boolean paintButton = !"none".equals( buttonStyle );
boolean enabled = spinner.isEnabled(); boolean enabled = spinner.isEnabled();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
// paint background // paint background
g2.setColor( getBackground( enabled ) ); g2.setColor( getBackground( enabled ) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc ); FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
// paint arrow buttons background // paint button background and separator
if( paintButton && enabled ) { boolean paintButton = !"none".equals( buttonStyle );
g2.setColor( buttonBackground ); Handler handler = getHandler();
Shape oldClip = g2.getClip(); if( paintButton && (handler.nextButton != null || handler.previousButton != null) ) {
if( isLeftToRight ) Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
g2.clipRect( arrowX, 0, width - arrowX, height ); int arrowX = button.getX();
else int arrowWidth = button.getWidth();
g2.clipRect( 0, 0, arrowX + arrowWidth, height ); boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint vertical line between value and arrow buttons // paint arrow buttons background
if( paintButton ) { if( enabled ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint vertical line between value and arrow buttons
g2.setColor( enabled ? borderColor : disabledBorderColor ); g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f ); float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw; float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
@@ -300,6 +307,8 @@ public class FlatSpinnerUI
} }
paint( g, c ); paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
//---- class Handler ------------------------------------------------------ //---- class Handler ------------------------------------------------------
@@ -359,7 +368,7 @@ public class FlatSpinnerUI
if( nextButton == null && previousButton == null ) { if( nextButton == null && previousButton == null ) {
if( editor != null ) if( editor != null )
editor.setBounds( r ); editor.setBounds( FlatUIUtils.subtractInsets( r, padding ) );
return; return;
} }

View File

@@ -17,11 +17,17 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.ToolTipManager;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneDivider;
@@ -42,10 +48,16 @@ import com.formdev.flatlaf.util.UIScale;
* *
* <!-- FlatSplitPaneUI --> * <!-- FlatSplitPaneUI -->
* *
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault SplitPane.continuousLayout boolean * @uiDefault SplitPane.continuousLayout boolean
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color * @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color * @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
* @uiDefault SplitPaneDivider.style String grip (default) or plain
* @uiDefault SplitPaneDivider.gripColor Color
* @uiDefault SplitPaneDivider.gripDotCount int
* @uiDefault SplitPaneDivider.gripDotSize int
* @uiDefault SplitPaneDivider.gripGap int
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -54,8 +66,9 @@ public class FlatSplitPaneUI
{ {
protected String arrowType; protected String arrowType;
private Boolean continuousLayout; private Boolean continuousLayout;
private Color oneTouchArrowColor; protected Color oneTouchArrowColor;
private Color oneTouchHoverArrowColor; protected Color oneTouchHoverArrowColor;
protected Color oneTouchPressedArrowColor;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatSplitPaneUI(); return new FlatSplitPaneUI();
@@ -69,12 +82,22 @@ public class FlatSplitPaneUI
// used in there on LaF switching // used in there on LaF switching
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" ); oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
oneTouchHoverArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchHoverArrowColor" ); oneTouchHoverArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchHoverArrowColor" );
oneTouchPressedArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchPressedArrowColor" );
super.installDefaults(); super.installDefaults();
continuousLayout = (Boolean) UIManager.get( "SplitPane.continuousLayout" ); continuousLayout = (Boolean) UIManager.get( "SplitPane.continuousLayout" );
} }
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
oneTouchArrowColor = null;
oneTouchHoverArrowColor = null;
oneTouchPressedArrowColor = null;
}
@Override @Override
public boolean isContinuousLayout() { public boolean isContinuousLayout() {
return super.isContinuousLayout() || (continuousLayout != null && Boolean.TRUE.equals( continuousLayout )); return super.isContinuousLayout() || (continuousLayout != null && Boolean.TRUE.equals( continuousLayout ));
@@ -90,8 +113,16 @@ public class FlatSplitPaneUI
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider
extends BasicSplitPaneDivider extends BasicSplitPaneDivider
{ {
protected final String style = UIManager.getString( "SplitPaneDivider.style" );
protected final Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
protected final int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
protected final int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
protected final int gripGap = FlatUIUtils.getUIInt( "SplitPaneDivider.gripGap", 2 );
protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) { protected FlatSplitPaneDivider( BasicSplitPaneUI ui ) {
super( ui ); super( ui );
setLayout( new FlatDividerLayout() );
} }
@Override @Override
@@ -109,16 +140,67 @@ public class FlatSplitPaneUI
return new FlatOneTouchButton( false ); return new FlatOneTouchButton( false );
} }
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
switch( e.getPropertyName() ) {
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
// necessary to show/hide one-touch buttons on expand/collapse
revalidate();
break;
}
}
@Override
public void paint( Graphics g ) {
super.paint( g );
if( "plain".equals( style ) )
return;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( gripColor );
paintGrip( g, 0, 0, getWidth(), getHeight() );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
protected void paintGrip( Graphics g, int x, int y, int width, int height ) {
FlatUIUtils.paintGrip( g, x, y, width, height,
splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT,
gripDotCount, gripDotSize, gripGap, true );
}
protected boolean isLeftCollapsed() {
int location = splitPane.getDividerLocation();
Insets insets = splitPane.getInsets();
return (orientation == JSplitPane.VERTICAL_SPLIT)
? location == insets.top
: location == insets.left;
}
protected boolean isRightCollapsed() {
int location = splitPane.getDividerLocation();
Insets insets = splitPane.getInsets();
return (orientation == JSplitPane.VERTICAL_SPLIT)
? location == (splitPane.getHeight() - getHeight() - insets.bottom)
: location == (splitPane.getWidth() - getWidth() - insets.right);
}
//---- class FlatOneTouchButton --------------------------------------- //---- class FlatOneTouchButton ---------------------------------------
private class FlatOneTouchButton protected class FlatOneTouchButton
extends FlatArrowButton extends FlatArrowButton
{ {
private final boolean left; protected final boolean left;
public FlatOneTouchButton( boolean left ) { protected FlatOneTouchButton( boolean left ) {
super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null, oneTouchHoverArrowColor, null ); super( SwingConstants.NORTH, arrowType, oneTouchArrowColor, null,
oneTouchHoverArrowColor, null, oneTouchPressedArrowColor, null );
setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) );
ToolTipManager.sharedInstance().registerComponent( this );
this.left = left; this.left = left;
} }
@@ -129,7 +211,67 @@ public class FlatSplitPaneUI
? (left ? SwingConstants.NORTH : SwingConstants.SOUTH) ? (left ? SwingConstants.NORTH : SwingConstants.SOUTH)
: (left ? SwingConstants.WEST : SwingConstants.EAST); : (left ? SwingConstants.WEST : SwingConstants.EAST);
} }
@Override
public String getToolTipText( MouseEvent e ) {
String key = (orientation == JSplitPane.VERTICAL_SPLIT)
? (left
? (isRightCollapsed()
? "SplitPaneDivider.expandBottomToolTipText"
: "SplitPaneDivider.collapseTopToolTipText")
: (isLeftCollapsed()
? "SplitPaneDivider.expandTopToolTipText"
: "SplitPaneDivider.collapseBottomToolTipText"))
: (left
? (isRightCollapsed()
? "SplitPaneDivider.expandRightToolTipText"
: "SplitPaneDivider.collapseLeftToolTipText")
: (isLeftCollapsed()
? "SplitPaneDivider.expandLeftToolTipText"
: "SplitPaneDivider.collapseRightToolTipText"));
// get text from client property
Object value = splitPane.getClientProperty( key );
if( value instanceof String )
return (String) value;
// get text from bundle
return UIManager.getString( key, getLocale() );
}
}
//---- class FlatDividerLayout ----------------------------------------
protected class FlatDividerLayout
extends DividerLayout
{
@Override
public void layoutContainer( Container c ) {
super.layoutContainer( c );
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
return;
// increase side of buttons, which makes them easier to hit by the user
// and avoids cut arrows at small divider sizes
int extraSize = UIScale.scale( 4 );
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
leftButton.setSize( leftButton.getWidth() + extraSize, leftButton.getHeight() );
rightButton.setBounds( leftButton.getX() + leftButton.getWidth(), rightButton.getY(),
rightButton.getWidth() + extraSize, rightButton.getHeight() );
} else {
leftButton.setSize( leftButton.getWidth(), leftButton.getHeight() + extraSize );
rightButton.setBounds( rightButton.getX(), leftButton.getY() + leftButton.getHeight(),
rightButton.getWidth(), rightButton.getHeight() + extraSize );
}
// hide buttons if not applicable
boolean leftCollapsed = isLeftCollapsed();
if( leftCollapsed )
rightButton.setLocation( leftButton.getLocation() );
leftButton.setVisible( !leftCollapsed );
rightButton.setVisible( !isRightCollapsed() );
}
} }
} }
} }

View File

@@ -31,6 +31,7 @@ import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border; import javax.swing.border.Border;
@@ -98,10 +99,13 @@ public class FlatTableHeaderUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
if( header.getColumnModel().getColumnCount() <= 0 )
return;
// do not paint borders if JTableHeader.setDefaultRenderer() was used // do not paint borders if JTableHeader.setDefaultRenderer() was used
TableCellRenderer defaultRenderer = header.getDefaultRenderer(); TableCellRenderer defaultRenderer = header.getDefaultRenderer();
boolean paintBorders = isSystemDefaultRenderer( defaultRenderer ); boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
if( !paintBorders && header.getColumnModel().getColumnCount() > 0 ) { if( !paintBorders ) {
// check whether the renderer delegates to the system default renderer // check whether the renderer delegates to the system default renderer
Component rendererComponent = defaultRenderer.getTableCellRendererComponent( Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
header.getTable(), "", false, false, -1, 0 ); header.getTable(), "", false, false, -1, 0 );
@@ -137,7 +141,7 @@ public class FlatTableHeaderUI
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" ); rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
} }
private void paintColumnBorders( Graphics g, JComponent c ) { protected void paintColumnBorders( Graphics g, JComponent c ) {
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
float lineWidth = UIScale.scale( 1f ); float lineWidth = UIScale.scale( 1f );
@@ -145,6 +149,9 @@ public class FlatTableHeaderUI
float bottomLineIndent = lineWidth * 3; float bottomLineIndent = lineWidth * 3;
TableColumnModel columnModel = header.getColumnModel(); TableColumnModel columnModel = header.getColumnModel();
int columnCount = columnModel.getColumnCount(); int columnCount = columnModel.getColumnCount();
int sepCount = columnCount;
if( hideLastVerticalLine() )
sepCount--;
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
@@ -157,23 +164,30 @@ public class FlatTableHeaderUI
// paint column separator lines // paint column separator lines
g2.setColor( separatorColor ); g2.setColor( separatorColor );
int sepCount = columnCount; float y = topLineIndent;
if( header.getTable().getAutoResizeMode() != JTable.AUTO_RESIZE_OFF && !isVerticalScrollBarVisible() ) float h = height - bottomLineIndent;
sepCount--;
if( header.getComponentOrientation().isLeftToRight() ) { if( header.getComponentOrientation().isLeftToRight() ) {
int x = 0; int x = 0;
for( int i = 0; i < sepCount; i++ ) { for( int i = 0; i < sepCount; i++ ) {
x += columnModel.getColumn( i ).getWidth(); x += columnModel.getColumn( i ).getWidth();
g2.fill( new Rectangle2D.Float( x - lineWidth, topLineIndent, lineWidth, height - bottomLineIndent ) ); g2.fill( new Rectangle2D.Float( x - lineWidth, y, lineWidth, h ) );
} }
// paint trailing separator (on right side)
if( !hideTrailingVerticalLine() )
g2.fill( new Rectangle2D.Float( header.getWidth() - lineWidth, y, lineWidth, h ) );
} else { } else {
int x = width; Rectangle cellRect = header.getHeaderRect( 0 );
int x = cellRect.x + cellRect.width;
for( int i = 0; i < sepCount; i++ ) { for( int i = 0; i < sepCount; i++ ) {
x -= columnModel.getColumn( i ).getWidth(); x -= columnModel.getColumn( i ).getWidth();
g2.fill( new Rectangle2D.Float( x - (i < sepCount - 1 ? lineWidth : 0), g2.fill( new Rectangle2D.Float( x - (i < sepCount - 1 ? lineWidth : 0), y, lineWidth, h ) );
topLineIndent, lineWidth, height - bottomLineIndent ) );
} }
// paint trailing separator (on left side)
if( !hideTrailingVerticalLine() )
g2.fill( new Rectangle2D.Float( 0, y, lineWidth, h ) );
} }
} finally { } finally {
g2.dispose(); g2.dispose();
@@ -230,20 +244,30 @@ public class FlatTableHeaderUI
return size; return size;
} }
private boolean isVerticalScrollBarVisible() { protected boolean hideLastVerticalLine() {
JScrollPane scrollPane = getScrollPane(); Container viewport = header.getParent();
return (scrollPane != null && scrollPane.getVerticalScrollBar() != null) Container viewportParent = (viewport != null) ? viewport.getParent() : null;
? scrollPane.getVerticalScrollBar().isVisible() if( !(viewportParent instanceof JScrollPane) )
: false; return false;
Rectangle cellRect = header.getHeaderRect( header.getColumnModel().getColumnCount() - 1 );
// using component orientation of scroll pane here because it is also used in FlatTableUI
JScrollPane scrollPane = (JScrollPane) viewportParent;
return scrollPane.getComponentOrientation().isLeftToRight()
? cellRect.x + cellRect.width >= viewport.getWidth()
: cellRect.x <= 0;
} }
private JScrollPane getScrollPane() { protected boolean hideTrailingVerticalLine() {
Container parent = header.getParent(); Container viewport = header.getParent();
if( parent == null ) Container viewportParent = (viewport != null) ? viewport.getParent() : null;
return null; if( !(viewportParent instanceof JScrollPane) )
return false;
parent = parent.getParent(); JScrollPane scrollPane = (JScrollPane) viewportParent;
return (parent instanceof JScrollPane) ? (JScrollPane) parent : null; return viewport == scrollPane.getColumnHeader() &&
scrollPane.getCorner( ScrollPaneConstants.UPPER_TRAILING_CORNER ) == null;
} }
//---- class FlatTableCellHeaderRenderer ---------------------------------- //---- class FlatTableCellHeaderRenderer ----------------------------------

View File

@@ -17,17 +17,25 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI; import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -71,6 +79,10 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.cellFocusColor Color * @uiDefault Table.cellFocusColor Color
* @uiDefault Table.showCellFocusIndicator boolean * @uiDefault Table.showCellFocusIndicator boolean
* *
* <!-- FlatInputMaps -->
*
* @uiDefault Table.consistentHomeEndKeyBehavior boolean
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatTableUI public class FlatTableUI
@@ -93,16 +105,6 @@ public class FlatTableUI
return new FlatTableUI(); return new FlatTableUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
@@ -194,6 +196,9 @@ public class FlatTableUI
* or the application has to be changed to extend a FlatLaf renderer. * or the application has to be changed to extend a FlatLaf renderer.
*/ */
private void toggleSelectionColors() { private void toggleSelectionColors() {
if( table == null )
return;
if( FlatUIUtils.isPermanentFocusOwner( table ) ) { if( FlatUIUtils.isPermanentFocusOwner( table ) ) {
if( table.getSelectionBackground() == selectionInactiveBackground ) if( table.getSelectionBackground() == selectionInactiveBackground )
table.setSelectionBackground( selectionBackground ); table.setSelectionBackground( selectionBackground );
@@ -206,4 +211,94 @@ public class FlatTableUI
table.setSelectionForeground( selectionInactiveForeground ); table.setSelectionForeground( selectionInactiveForeground );
} }
} }
@Override
public void paint( Graphics g, JComponent c ) {
boolean horizontalLines = table.getShowHorizontalLines();
boolean verticalLines = table.getShowVerticalLines();
if( horizontalLines || verticalLines ) {
// fix grid painting issues in BasicTableUI
// - do not paint last vertical grid line if line is on right edge of scroll pane
// - fix unstable grid line thickness when scaled at 125%, 150%, 175%, 225%, ...
// which paints either 1px or 2px lines depending on location
boolean hideLastVerticalLine = hideLastVerticalLine();
int tableWidth = table.getWidth();
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
// Java 8 uses drawLine() to paint grid lines
// Java 9+ uses fillRect() to paint grid lines
g = new Graphics2DProxy( (Graphics2D) g ) {
@Override
public void drawLine( int x1, int y1, int x2, int y2 ) {
// do not paint last vertical line
if( hideLastVerticalLine && verticalLines &&
x1 == x2 && y1 == 0 && x1 == tableWidth - 1 &&
wasInvokedFromPaintGrid() )
return;
super.drawLine( x1, y1, x2, y2 );
}
@Override
public void fillRect( int x, int y, int width, int height ) {
// do not paint last vertical line
if( hideLastVerticalLine && verticalLines &&
width == 1 && y == 0 && x == tableWidth - 1 &&
wasInvokedFromPaintGrid() )
return;
// reduce line thickness to avoid unstable painted line thickness
if( lineThickness != 1 ) {
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
return;
}
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
return;
}
}
super.fillRect( x, y, width, height );
}
private boolean wasInvokedFromPaintGrid() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for( int i = 0; i < 10 || i < stackTrace.length; i++ ) {
if( "javax.swing.plaf.basic.BasicTableUI".equals( stackTrace[i].getClassName() ) &&
"paintGrid".equals( stackTrace[i].getMethodName() ) )
return true;
}
return false;
}
};
}
super.paint( g, c );
}
protected boolean hideLastVerticalLine() {
Container viewport = SwingUtilities.getUnwrappedParent( table );
Container viewportParent = (viewport != null) ? viewport.getParent() : null;
if( !(viewportParent instanceof JScrollPane) )
return false;
// do not hide last vertical line if table is smaller than viewport
if( table.getX() + table.getWidth() < viewport.getWidth() )
return false;
// in left-to-right:
// - do not hide last vertical line if table used as row header in scroll pane
// in right-to-left:
// - hide last vertical line if table used as row header in scroll pane
// - do not hide last vertical line if table is in center and scroll pane has row header
JScrollPane scrollPane = (JScrollPane) viewportParent;
JViewport rowHeader = scrollPane.getRowHeader();
return scrollPane.getComponentOrientation().isLeftToRight()
? (viewport != rowHeader)
: (viewport == rowHeader || rowHeader == null);
}
} }

View File

@@ -60,6 +60,7 @@ public class FlatTextAreaUI
{ {
protected int minimumWidth; protected int minimumWidth;
protected boolean isIntelliJTheme; protected boolean isIntelliJTheme;
protected Color background;
protected Color disabledBackground; protected Color disabledBackground;
protected Color inactiveBackground; protected Color inactiveBackground;
@@ -67,12 +68,20 @@ public class FlatTextAreaUI
return new FlatTextAreaUI(); return new FlatTextAreaUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
updateBackground();
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
background = UIManager.getColor( "TextArea.background" );
disabledBackground = UIManager.getColor( "TextArea.disabledBackground" ); disabledBackground = UIManager.getColor( "TextArea.disabledBackground" );
inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" ); inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" );
} }
@@ -81,6 +90,7 @@ public class FlatTextAreaUI
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
background = null;
disabledBackground = null; disabledBackground = null;
inactiveBackground = null; inactiveBackground = null;
} }
@@ -89,26 +99,36 @@ public class FlatTextAreaUI
protected void propertyChange( PropertyChangeEvent e ) { protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e ); super.propertyChange( e );
FlatEditorPaneUI.propertyChange( getComponent(), e ); FlatEditorPaneUI.propertyChange( getComponent(), e );
switch( e.getPropertyName() ) {
case "editable":
case "enabled":
updateBackground();
break;
}
} }
@Override private void updateBackground() {
protected void paintSafely( Graphics g ) {
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent(); JTextComponent c = getComponent();
Color background = c.getBackground(); Color background = c.getBackground();
g.setColor( !(background instanceof UIResource) if( !(background instanceof UIResource) )
? background return;
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
? FlatUIUtils.getParentBackground( c ) // do not update background if it currently has a unknown color (assigned from outside)
: (!c.isEnabled() if( background != this.background &&
? disabledBackground background != disabledBackground &&
: (!c.isEditable() ? inactiveBackground : background))) ); background != inactiveBackground )
g.fillRect( 0, 0, c.getWidth(), c.getHeight() ); return;
Color newBackground = !c.isEnabled()
? disabledBackground
: (!c.isEditable()
? inactiveBackground
: this.background);
if( newBackground != background )
c.setBackground( newBackground );
} }
@Override @Override
@@ -128,4 +148,22 @@ public class FlatTextAreaUI
return FlatEditorPaneUI.applyMinimumWidth( c, size, minimumWidth ); return FlatEditorPaneUI.applyMinimumWidth( c, size, minimumWidth );
} }
@Override
protected void paintSafely( Graphics g ) {
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
// for compatibility with IntelliJ themes
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
FlatUIUtils.paintParentBackground( g, c );
return;
}
super.paintBackground( g );
}
} }

View File

@@ -33,6 +33,10 @@ public class FlatTextBorder
@Override @Override
protected int getArc( Component c ) { protected int getArc( Component c ) {
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc; if( isCellEditor( c ) )
return 0;
Boolean roundRect = FlatUIUtils.isRoundRect( c );
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
} }
} }

View File

@@ -39,6 +39,7 @@ import javax.swing.text.Caret;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}. * Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
@@ -64,6 +65,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* @uiDefault Component.isIntelliJTheme boolean * @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextField.placeholderForeground Color * @uiDefault TextField.placeholderForeground Color
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* @uiDefault TextComponent.selectAllOnMouseClick boolean
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -121,7 +123,8 @@ public class FlatTextFieldUI
@Override @Override
protected Caret createCaret() { protected Caret createCaret() {
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) ); return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy"),
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
} }
@Override @Override
@@ -211,7 +214,9 @@ public class FlatTextFieldUI
// paint placeholder // paint placeholder
g.setColor( placeholderForeground ); g.setColor( placeholderForeground );
FlatUIUtils.drawString( c, g, (String) placeholder, x, y ); String clippedPlaceholder = JavaCompatibility.getClippedString( jc, fm,
(String) placeholder, c.getWidth() - insets.left - insets.right );
FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y );
} }
@Override @Override

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog; import java.awt.Dialog;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
@@ -75,6 +76,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.foreground Color * @uiDefault TitlePane.foreground Color
* @uiDefault TitlePane.inactiveForeground Color * @uiDefault TitlePane.inactiveForeground Color
* @uiDefault TitlePane.embeddedForeground Color * @uiDefault TitlePane.embeddedForeground Color
* @uiDefault TitlePane.borderColor Color optional
* @uiDefault TitlePane.iconSize Dimension * @uiDefault TitlePane.iconSize Dimension
* @uiDefault TitlePane.iconMargins Insets * @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.titleMargins Insets
@@ -96,6 +98,7 @@ public class FlatTitlePane
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" ); protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" ); protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" ); protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" ); protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
@@ -148,10 +151,6 @@ public class FlatTitlePane
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) ); iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) ); titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
//TODO
// titleLabel.setHorizontalAlignment( JLabel.CENTER );
// titleLabel.setHorizontalAlignment( JLabel.RIGHT );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) ); leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false ); leftPanel.setOpaque( false );
leftPanel.add( iconLabel ); leftPanel.add( iconLabel );
@@ -160,7 +159,7 @@ public class FlatTitlePane
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && isMenuBarEmbedded()) return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded())
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) ) ? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
: new Dimension(); : new Dimension();
} }
@@ -169,7 +168,25 @@ public class FlatTitlePane
createButtons(); createButtons();
setLayout( new BorderLayout() ); setLayout( new BorderLayout() {
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// make left panel (with embedded menu bar) smaller if horizontal space is rare
// to avoid that embedded menu bar overlaps button bar
Insets insets = target.getInsets();
int width = target.getWidth() - insets.left - insets.right;
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) {
int oldWidth = leftPanel.getWidth();
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 );
leftPanel.setSize( newWidth, leftPanel.getHeight() );
if( !getComponentOrientation().isLeftToRight() )
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
}
}
} );
add( leftPanel, BorderLayout.LINE_START ); add( leftPanel, BorderLayout.LINE_START );
add( titleLabel, BorderLayout.CENTER ); add( titleLabel, BorderLayout.CENTER );
add( buttonPanel, BorderLayout.LINE_END ); add( buttonPanel, BorderLayout.LINE_END );
@@ -223,14 +240,17 @@ public class FlatTitlePane
} }
protected void activeChanged( boolean active ) { protected void activeChanged( boolean active ) {
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && isMenuBarEmbedded(); boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && rootPane.getJMenuBar().isVisible() && isMenuBarEmbedded();
Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground ); Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
Color foreground = FlatUIUtils.nonUIResource( active Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
? (hasEmbeddedMenuBar ? embeddedForeground : activeForeground) Color titleForeground = (hasEmbeddedMenuBar && active) ? FlatUIUtils.nonUIResource( embeddedForeground ) : foreground;
: inactiveForeground );
setBackground( background ); setBackground( background );
titleLabel.setForeground( foreground ); titleLabel.setForeground( titleForeground );
iconifyButton.setForeground( foreground );
maximizeButton.setForeground( foreground );
restoreButton.setForeground( foreground );
closeButton.setForeground( foreground );
titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING ); titleLabel.setHorizontalAlignment( hasEmbeddedMenuBar ? SwingConstants.CENTER : SwingConstants.LEADING );
@@ -253,6 +273,26 @@ public class FlatTitlePane
iconifyButton.setVisible( true ); iconifyButton.setVisible( true );
maximizeButton.setVisible( resizable && !maximized ); maximizeButton.setVisible( resizable && !maximized );
restoreButton.setVisible( resizable && maximized ); restoreButton.setVisible( resizable && maximized );
if( maximized &&
rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null )
{
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", null );
// In case that frame was maximized from custom code (e.g. when restoring
// window state on application startup), then maximized bounds is not set
// and the window would overlap Windows task bar.
// To avoid this, update maximized bounds here and if it has changed
// re-maximize windows so that maximized bounds are used.
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
updateMaximizedBounds();
Rectangle newMaximizedBounds = frame.getMaximizedBounds();
if( newMaximizedBounds != null && !newMaximizedBounds.equals( oldMaximizedBounds ) ) {
int oldExtendedState = frame.getExtendedState();
frame.setExtendedState( oldExtendedState & ~Frame.MAXIMIZED_BOTH );
frame.setExtendedState( oldExtendedState );
}
}
} else { } else {
// hide buttons because they are only supported in frames // hide buttons because they are only supported in frames
iconifyButton.setVisible( false ); iconifyButton.setVisible( false );
@@ -375,21 +415,46 @@ public class FlatTitlePane
return FlatUIUtils.subtractInsets( bounds, UIScale.scale( getMenuBarMargins() ) ); return FlatUIUtils.subtractInsets( bounds, UIScale.scale( getMenuBarMargins() ) );
} }
protected Insets getMenuBarMargins() {
return getComponentOrientation().isLeftToRight()
? menuBarMargins
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
}
protected void menuBarChanged() { protected void menuBarChanged() {
menuBarPlaceholder.invalidate(); menuBarPlaceholder.invalidate();
// necessary for the case that an embedded menu bar is made invisible
// and a border color is specified
repaint();
// update title foreground color // update title foreground color
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
activeChanged( window == null || window.isActive() ); activeChanged( window == null || window.isActive() );
} ); } );
} }
protected Insets getMenuBarMargins() { protected void menuBarLayouted() {
return getComponentOrientation().isLeftToRight() updateJBRHitTestSpotsAndTitleBarHeightLater();
? menuBarMargins
: new Insets( menuBarMargins.top, menuBarMargins.right, menuBarMargins.bottom, menuBarMargins.left );
} }
/*debug
@Override
public void paint( Graphics g ) {
super.paint( g );
if( debugTitleBarHeight > 0 ) {
g.setColor( Color.green );
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
}
if( debugHitTestSpots != null ) {
g.setColor( Color.blue );
for( Rectangle r : debugHitTestSpots )
g.drawRect( r.x, r.y, r.width, r.height );
}
}
debug*/
@Override @Override
protected void paintComponent( Graphics g ) { protected void paintComponent( Graphics g ) {
g.setColor( getBackground() ); g.setColor( getBackground() );
@@ -425,11 +490,24 @@ public class FlatTitlePane
Frame frame = (Frame) window; Frame frame = (Frame) window;
updateMaximizedBounds();
// let our WindowStateListener know that the maximized bounds are up-to-date
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
// maximize window
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH );
}
protected void updateMaximizedBounds() {
Frame frame = (Frame) window;
// set maximized bounds to avoid that maximized window overlaps Windows task bar // set maximized bounds to avoid that maximized window overlaps Windows task bar
// (if not running in JBR and if not modified from the application) // (if not running in JBR and if not modified from the application)
Rectangle oldMaximizedBounds = frame.getMaximizedBounds();
if( !hasJBRCustomDecoration() && if( !hasJBRCustomDecoration() &&
(frame.getMaximizedBounds() == null || (oldMaximizedBounds == null ||
Objects.equals( frame.getMaximizedBounds(), rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) ) Objects.equals( oldMaximizedBounds, rootPane.getClientProperty( "_flatlaf.maximizedBounds" ) )) )
{ {
GraphicsConfiguration gc = window.getGraphicsConfiguration(); GraphicsConfiguration gc = window.getGraphicsConfiguration();
@@ -450,7 +528,7 @@ public class FlatTitlePane
int maximizedWidth = screenBounds.width; int maximizedWidth = screenBounds.width;
int maximizedHeight = screenBounds.height; int maximizedHeight = screenBounds.height;
if( !SystemInfo.IS_JAVA_15_OR_LATER ) { if( !isMaximizedBoundsFixed() ) {
// on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment // on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment
maximizedX = 0; maximizedX = 0;
maximizedY = 0; maximizedY = 0;
@@ -470,22 +548,36 @@ public class FlatTitlePane
// (see https://bugs.openjdk.java.net/browse/JDK-8231564 and // (see https://bugs.openjdk.java.net/browse/JDK-8231564 and
// https://bugs.openjdk.java.net/browse/JDK-8176359) // https://bugs.openjdk.java.net/browse/JDK-8176359)
// and except for Java 8 on secondary screens where primary screen is scaled // and except for Java 8 on secondary screens where primary screen is scaled
Rectangle maximizedBounds = new Rectangle( Rectangle newMaximizedBounds = new Rectangle(
maximizedX + screenInsets.left, maximizedX + screenInsets.left,
maximizedY + screenInsets.top, maximizedY + screenInsets.top,
maximizedWidth - screenInsets.left - screenInsets.right, maximizedWidth - screenInsets.left - screenInsets.right,
maximizedHeight - screenInsets.top - screenInsets.bottom ); maximizedHeight - screenInsets.top - screenInsets.bottom );
// change maximized bounds if( !Objects.equals( oldMaximizedBounds, newMaximizedBounds ) ) {
frame.setMaximizedBounds( maximizedBounds ); // change maximized bounds
frame.setMaximizedBounds( newMaximizedBounds );
// remember maximized bounds in client property to be able to detect // remember maximized bounds in client property to be able to detect
// whether maximized bounds are modified from the application // whether maximized bounds are modified from the application
rootPane.putClientProperty( "_flatlaf.maximizedBounds", maximizedBounds ); rootPane.putClientProperty( "_flatlaf.maximizedBounds", newMaximizedBounds );
}
} }
}
// maximize window /**
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH ); * Frame.setMaximizedBounds() behaves different on some Java versions after issues
* https://bugs.openjdk.java.net/browse/JDK-8231564 and
* https://bugs.openjdk.java.net/browse/JDK-8176359
* (see also https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8176359)
* were fixed in Java 15 and backported to 11.0.8 and 13.0.4.
*/
private boolean isMaximizedBoundsFixed() {
return SystemInfo.isJava_15_orLater ||
(SystemInfo.javaVersion >= SystemInfo.toVersion( 11, 0, 8, 0 ) &&
SystemInfo.javaVersion < SystemInfo.toVersion( 12, 0, 0, 0 )) ||
(SystemInfo.javaVersion >= SystemInfo.toVersion( 13, 0, 4, 0 ) &&
SystemInfo.javaVersion < SystemInfo.toVersion( 14, 0, 0, 0 ));
} }
/** /**
@@ -540,6 +632,12 @@ public class FlatTitlePane
titleBarHeight--; titleBarHeight--;
JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight ); JBRCustomDecorations.setHitTestSpotsAndTitleBarHeight( window, hitTestSpots, titleBarHeight );
/*debug
debugHitTestSpots = hitTestSpots;
debugTitleBarHeight = titleBarHeight;
repaint();
debug*/
} }
protected void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) { protected void addJBRHitTestSpot( JComponent c, boolean subtractMenuBarMargins, List<Rectangle> hitTestSpots ) {
@@ -556,6 +654,11 @@ public class FlatTitlePane
hitTestSpots.add( r ); hitTestSpots.add( r );
} }
/*debug
private List<Rectangle> debugHitTestSpots;
private int debugTitleBarHeight;
debug*/
//---- class TitlePaneBorder ---------------------------------------------- //---- class TitlePaneBorder ----------------------------------------------
protected class FlatTitlePaneBorder protected class FlatTitlePaneBorder
@@ -565,12 +668,13 @@ public class FlatTitlePane
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
super.getBorderInsets( c, insets ); super.getBorderInsets( c, insets );
// if menu bar is embedded, add bottom insets of menu bar border
Border menuBarBorder = getMenuBarBorder(); Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) { if( menuBarBorder != null ) {
// if menu bar is embedded, add bottom insets of menu bar border
Insets menuBarInsets = menuBarBorder.getBorderInsets( c ); Insets menuBarInsets = menuBarBorder.getBorderInsets( c );
insets.bottom += menuBarInsets.bottom; insets.bottom += menuBarInsets.bottom;
} } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( hasJBRCustomDecoration() ) if( hasJBRCustomDecoration() )
insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() ); insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() );
@@ -580,10 +684,16 @@ public class FlatTitlePane
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// if menu bar is embedded, paint menu bar border // paint bottom border
Border menuBarBorder = getMenuBarBorder(); Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) if( menuBarBorder != null ) {
// if menu bar is embedded, paint menu bar border
menuBarBorder.paintBorder( c, g, x, y, width, height ); menuBarBorder.paintBorder( c, g, x, y, width, height );
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) {
// paint border between title pane and content if border color is specified
float lineHeight = UIScale.scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( hasJBRCustomDecoration() ) if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
@@ -591,7 +701,7 @@ public class FlatTitlePane
protected Border getMenuBarBorder() { protected Border getMenuBarBorder() {
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && isMenuBarEmbedded()) ? menuBar.getBorder() : null; return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded()) ? menuBar.getBorder() : null;
} }
} }
@@ -657,8 +767,7 @@ public class FlatTitlePane
//---- interface MouseListener ---- //---- interface MouseListener ----
private int lastXOnScreen; private Point dragOffset;
private int lastYOnScreen;
@Override @Override
public void mouseClicked( MouseEvent e ) { public void mouseClicked( MouseEvent e ) {
@@ -682,8 +791,10 @@ public class FlatTitlePane
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
lastXOnScreen = e.getXOnScreen(); if( window == null )
lastYOnScreen = e.getYOnScreen(); return; // should newer occur
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
} }
@Override public void mouseReleased( MouseEvent e ) {} @Override public void mouseReleased( MouseEvent e ) {}
@@ -694,46 +805,45 @@ public class FlatTitlePane
@Override @Override
public void mouseDragged( MouseEvent e ) { public void mouseDragged( MouseEvent e ) {
if( window == null )
return; // should newer occur
if( hasJBRCustomDecoration() ) if( hasJBRCustomDecoration() )
return; // do nothing if running in JBR return; // do nothing if running in JBR
int xOnScreen = e.getXOnScreen();
int yOnScreen = e.getYOnScreen();
if( lastXOnScreen == xOnScreen && lastYOnScreen == yOnScreen )
return;
// restore window if it is maximized // restore window if it is maximized
if( window instanceof Frame ) { if( window instanceof Frame ) {
Frame frame = (Frame) window; Frame frame = (Frame) window;
int state = frame.getExtendedState(); int state = frame.getExtendedState();
if( (state & Frame.MAXIMIZED_BOTH) != 0 ) { if( (state & Frame.MAXIMIZED_BOTH) != 0 ) {
int maximizedX = window.getX(); int maximizedWidth = window.getWidth();
int maximizedY = window.getY();
// restore window size, which also moves window to pre-maximized location // restore window size, which also moves window to pre-maximized location
frame.setExtendedState( state & ~Frame.MAXIMIZED_BOTH ); frame.setExtendedState( state & ~Frame.MAXIMIZED_BOTH );
// fix drag offset to ensure that window remains under mouse position
// for the case that dragging starts in the right area of the maximized window
int restoredWidth = window.getWidth(); int restoredWidth = window.getWidth();
int newX = maximizedX; int center = restoredWidth / 2;
JComponent rightComp = getComponentOrientation().isLeftToRight() ? buttonPanel : leftPanel; if( dragOffset.x > center ) {
if( xOnScreen >= maximizedX + restoredWidth - rightComp.getWidth() - 10 ) // this is same/similar to what Windows 10 does
newX = xOnScreen + rightComp.getWidth() + 10 - restoredWidth; if( dragOffset.x > maximizedWidth - center )
dragOffset.x = restoredWidth - (maximizedWidth - dragOffset.x);
// move window near mouse else
window.setLocation( newX, maximizedY ); dragOffset.x = center;
return; }
} }
} }
// compute new window location // compute new window location
int newX = window.getX() + (xOnScreen - lastXOnScreen); int newX = e.getXOnScreen() - dragOffset.x;
int newY = window.getY() + (yOnScreen - lastYOnScreen); int newY = e.getYOnScreen() - dragOffset.y;
if( newX == window.getX() && newY == window.getY() )
return;
// move window // move window
window.setLocation( newX, newY ); window.setLocation( newX, newY );
lastXOnScreen = xOnScreen;
lastYOnScreen = yOnScreen;
} }
@Override public void mouseMoved( MouseEvent e ) {} @Override public void mouseMoved( MouseEvent e ) {}
@@ -745,8 +855,13 @@ public class FlatTitlePane
updateJBRHitTestSpotsAndTitleBarHeightLater(); updateJBRHitTestSpotsAndTitleBarHeightLater();
} }
@Override
public void componentShown( ComponentEvent e ) {
// necessary for the case that the frame is maximized before it is shown
frameStateChanged();
}
@Override public void componentMoved( ComponentEvent e ) {} @Override public void componentMoved( ComponentEvent e ) {}
@Override public void componentShown( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {} @Override public void componentHidden( ComponentEvent e ) {}
} }
} }

View File

@@ -22,7 +22,6 @@ import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -50,18 +49,17 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ToggleButton.startBackground Color optional; if set, a gradient paint is used and ToggleButton.background is ignored * @uiDefault ToggleButton.startBackground Color optional; if set, a gradient paint is used and ToggleButton.background is ignored
* @uiDefault ToggleButton.endBackground Color optional; if set, a gradient paint is used * @uiDefault ToggleButton.endBackground Color optional; if set, a gradient paint is used
* @uiDefault ToggleButton.pressedBackground Color * @uiDefault ToggleButton.pressedBackground Color
* @uiDefault ToggleButton.disabledBackground Color optional
* @uiDefault ToggleButton.disabledText Color
* @uiDefault ToggleButton.toolbar.hoverBackground Color
* @uiDefault ToggleButton.toolbar.pressedBackground Color
*
* <!-- FlatToggleButtonUI -->
*
* @uiDefault ToggleButton.selectedBackground Color * @uiDefault ToggleButton.selectedBackground Color
* @uiDefault ToggleButton.selectedForeground Color * @uiDefault ToggleButton.selectedForeground Color
* @uiDefault ToggleButton.disabledBackground Color optional
* @uiDefault ToggleButton.disabledText Color
* @uiDefault ToggleButton.disabledSelectedBackground Color * @uiDefault ToggleButton.disabledSelectedBackground Color
* @uiDefault ToggleButton.toolbar.hoverBackground Color
* @uiDefault ToggleButton.toolbar.pressedBackground Color
* @uiDefault ToggleButton.toolbar.selectedBackground Color * @uiDefault ToggleButton.toolbar.selectedBackground Color
* *
* <!-- FlatToggleButtonUI -->
*
* @uiDefault ToggleButton.tab.underlineHeight int * @uiDefault ToggleButton.tab.underlineHeight int
* @uiDefault ToggleButton.tab.underlineColor Color * @uiDefault ToggleButton.tab.underlineColor Color
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color * @uiDefault ToggleButton.tab.disabledUnderlineColor Color
@@ -75,12 +73,6 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatToggleButtonUI public class FlatToggleButtonUI
extends FlatButtonUI extends FlatButtonUI
{ {
protected Color selectedBackground;
protected Color selectedForeground;
protected Color disabledSelectedBackground;
protected Color toolbarSelectedBackground;
protected int tabUnderlineHeight; protected int tabUnderlineHeight;
protected Color tabUnderlineColor; protected Color tabUnderlineColor;
protected Color tabDisabledUnderlineColor; protected Color tabDisabledUnderlineColor;
@@ -90,12 +82,8 @@ public class FlatToggleButtonUI
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatToggleButtonUI.class, FlatToggleButtonUI::new );
instance = new FlatToggleButtonUI();
return instance;
} }
@Override @Override
@@ -108,12 +96,6 @@ public class FlatToggleButtonUI
super.installDefaults( b ); super.installDefaults( b );
if( !defaults_initialized ) { if( !defaults_initialized ) {
selectedBackground = UIManager.getColor( "ToggleButton.selectedBackground" );
selectedForeground = UIManager.getColor( "ToggleButton.selectedForeground" );
disabledSelectedBackground = UIManager.getColor( "ToggleButton.disabledSelectedBackground" );
toolbarSelectedBackground = UIManager.getColor( "ToggleButton.toolbar.selectedBackground" );
tabUnderlineHeight = UIManager.getInt( "ToggleButton.tab.underlineHeight" ); tabUnderlineHeight = UIManager.getInt( "ToggleButton.tab.underlineHeight" );
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" ); tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" ); tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
@@ -164,10 +146,17 @@ public class FlatToggleButtonUI
int height = c.getHeight(); int height = c.getHeight();
int width = c.getWidth(); int width = c.getWidth();
boolean selected = ((AbstractButton)c).isSelected(); boolean selected = ((AbstractButton)c).isSelected();
Color enabledColor = selected ? clientPropertyColor( c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground ) : null;
// use component background if explicitly set
if( enabledColor == null ) {
Color bg = c.getBackground();
if( isCustomBackground( bg ) )
enabledColor = bg;
}
// paint background // paint background
Color background = buttonStateColor( c, Color background = buttonStateColor( c, enabledColor,
selected ? clientPropertyColor( c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground ) : null,
null, tabFocusBackground, tabHoverBackground, null ); null, tabFocusBackground, tabHoverBackground, null );
if( background != null ) { if( background != null ) {
g.setColor( background ); g.setColor( background );
@@ -185,30 +174,4 @@ public class FlatToggleButtonUI
} else } else
super.paintBackground( g, c ); super.paintBackground( g, c );
} }
@Override
protected Color getBackground( JComponent c ) {
ButtonModel model = ((AbstractButton)c).getModel();
if( model.isSelected() ) {
// in toolbar use same colors for disabled and enabled because
// we assume that toolbar icon is shown disabled
boolean toolBarButton = isToolBarButton( c );
return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
null, null,
toolBarButton ? toolbarPressedBackground : pressedBackground );
}
return super.getBackground( c );
}
@Override
protected Color getForeground( JComponent c ) {
if( c.isEnabled() && ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
return selectedForeground;
return super.getForeground( c );
}
} }

View File

@@ -16,15 +16,16 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.*;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Border for {@link javax.swing.JToolBar}. * Border for {@link javax.swing.JToolBar}.
@@ -39,7 +40,7 @@ public class FlatToolBarBorder
{ {
private static final int DOT_COUNT = 4; private static final int DOT_COUNT = 4;
private static final int DOT_SIZE = 2; private static final int DOT_SIZE = 2;
private static final int GRIP_WIDTH = DOT_SIZE * 3; private static final int GRIP_SIZE = DOT_SIZE * 3;
protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" ); protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
@@ -64,35 +65,27 @@ public class FlatToolBarBorder
} }
protected void paintGrip( Component c, Graphics g, int x, int y, int width, int height ) { protected void paintGrip( Component c, Graphics g, int x, int y, int width, int height ) {
int dotSize = scale( DOT_SIZE ); Rectangle r = calculateGripBounds( c, x, y, width, height );
int gapSize = dotSize; FlatUIUtils.paintGrip( g, r.x, r.y, r.width, r.height,
int gripSize = (dotSize * DOT_COUNT) + ((gapSize * (DOT_COUNT - 1))); ((JToolBar)c).getOrientation() == SwingConstants.VERTICAL,
DOT_COUNT, DOT_SIZE, DOT_SIZE, false );
}
// include toolbar margin in grip position calculation protected Rectangle calculateGripBounds( Component c, int x, int y, int width, int height ) {
Insets insets = getBorderInsets( c ); // include toolbar margin in grip bounds calculation
Insets insets = super.getBorderInsets( c, new Insets( 0, 0, 0, 0 ) );
Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( x, y, width, height ), insets );
// calculate grip position // calculate grip bounds
boolean horizontal = ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL; int gripSize = UIScale.scale( GRIP_SIZE );
if( horizontal ) { if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) {
if( c.getComponentOrientation().isLeftToRight() ) if( !c.getComponentOrientation().isLeftToRight() )
x += insets.left - (dotSize * 2); r.x = r.x + r.width - gripSize;
else r.width = gripSize;
x += width - insets.right + dotSize; } else
y += Math.round( (height - gripSize) / 2f ); r.height = gripSize;
} else {
// vertical
x += Math.round( (width - gripSize) / 2f );
y += insets.top - (dotSize * 2);
}
// paint dots return r;
for( int i = 0; i < DOT_COUNT; i++ ) {
g.fillOval( x, y, dotSize, dotSize );
if( horizontal )
y += dotSize + gapSize;
else
x += dotSize + gapSize;
}
} }
@Override @Override
@@ -101,7 +94,7 @@ public class FlatToolBarBorder
// add grip inset if floatable // add grip inset if floatable
if( c instanceof JToolBar && ((JToolBar)c).isFloatable() ) { if( c instanceof JToolBar && ((JToolBar)c).isFloatable() ) {
int gripInset = scale( GRIP_WIDTH ); int gripInset = UIScale.scale( GRIP_SIZE );
if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) { if( ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL ) {
if( c.getComponentOrientation().isLeftToRight() ) if( c.getComponentOrientation().isLeftToRight() )
insets.left += gripInset; insets.left += gripInset;

View File

@@ -50,12 +50,8 @@ public class FlatToolBarSeparatorUI
private boolean defaults_initialized = false; private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatToolBarSeparatorUI.class, FlatToolBarSeparatorUI::new );
instance = new FlatToolBarSeparatorUI();
return instance;
} }
@Override @Override
@@ -110,13 +106,15 @@ public class FlatToolBarSeparatorUI
float lineWidth = scale( 1f ); float lineWidth = scale( 1f );
float offset = scale( 2f ); float offset = scale( 2f );
FlatUIUtils.setRenderingHints( (Graphics2D) g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( separatorColor ); g.setColor( separatorColor );
if( isVertical( c ) ) if( isVertical( c ) )
((Graphics2D)g).fill( new Rectangle2D.Float( Math.round( (width - lineWidth) / 2f ), offset, lineWidth, height - (offset * 2) ) ); ((Graphics2D)g).fill( new Rectangle2D.Float( Math.round( (width - lineWidth) / 2f ), offset, lineWidth, height - (offset * 2) ) );
else else
((Graphics2D)g).fill( new Rectangle2D.Float( offset, Math.round( (height - lineWidth) / 2f ), width - (offset * 2), lineWidth ) ); ((Graphics2D)g).fill( new Rectangle2D.Float( offset, Math.round( (height - lineWidth) / 2f ), width - (offset * 2), lineWidth ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
private boolean isVertical( JComponent c ) { private boolean isVertical( JComponent c ) {

View File

@@ -52,12 +52,8 @@ public class FlatToolTipUI
{ {
private static PropertyChangeListener sharedPropertyChangedListener; private static PropertyChangeListener sharedPropertyChangedListener;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatToolTipUI.class, FlatToolTipUI::new );
instance = new FlatToolTipUI();
return instance;
} }
@Override @Override
@@ -94,6 +90,11 @@ public class FlatToolTipUI
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
// do not show tool tip if text is empty
String text = ((JToolTip)c).getTipText();
if( text == null || text.isEmpty() )
return new Dimension();
if( isMultiLine( c ) ) { if( isMultiLine( c ) ) {
FontMetrics fm = c.getFontMetrics( c.getFont() ); FontMetrics fm = c.getFontMetrics( c.getFont() );
Insets insets = c.getInsets(); Insets insets = c.getInsets();
@@ -115,7 +116,6 @@ public class FlatToolTipUI
FontMetrics fm = c.getFontMetrics( c.getFont() ); FontMetrics fm = c.getFontMetrics( c.getFont() );
Insets insets = c.getInsets(); Insets insets = c.getInsets();
FlatUIUtils.setRenderingHints( (Graphics2D) g );
g.setColor( c.getForeground() ); g.setColor( c.getForeground() );
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' ); List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );

View File

@@ -23,25 +23,31 @@ import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.Insets; import java.awt.Insets;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.Shape; import java.awt.Shape;
import java.awt.Window;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter; import java.awt.geom.Ellipse2D;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
import java.util.function.Consumer; import java.util.IdentityHashMap;
import java.util.WeakHashMap;
import java.util.function.Supplier;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.CompoundBorder; import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.DerivedColor;
@@ -58,6 +64,8 @@ public class FlatUIUtils
{ {
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" ); public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
private static WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
public static Rectangle addInsets( Rectangle r, Insets insets ) { public static Rectangle addInsets( Rectangle r, Insets insets ) {
return new Rectangle( return new Rectangle(
r.x - insets.left, r.x - insets.left,
@@ -120,6 +128,10 @@ public class FlatUIUtils
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue; return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
} }
public static boolean isChevron( String arrowType ) {
return !"triangle".equals( arrowType );
}
public static Color nonUIResource( Color c ) { public static Color nonUIResource( Color c ) {
return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c; return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c;
} }
@@ -136,7 +148,26 @@ public class FlatUIUtils
return FlatClientProperties.clientPropertyInt( c, FlatClientProperties.MINIMUM_HEIGHT, minimumHeight ); return FlatClientProperties.clientPropertyInt( c, FlatClientProperties.MINIMUM_HEIGHT, minimumHeight );
} }
public static boolean isTableCellEditor( Component c ) { public static boolean isCellEditor( Component c ) {
// check whether used in cell editor (check 3 levels up)
Component c2 = c;
for( int i = 0; i <= 2 && c2 != null; i++ ) {
Container parent = c2.getParent();
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c2 )
return true;
c2 = parent;
}
// check whether used as cell editor
// Table.editor is set in JTable.GenericEditor constructor
// Tree.cellEditor is set in sun.swing.FilePane.editFileName()
String name = c.getName();
if( "Table.editor".equals( name ) || "Tree.cellEditor".equals( name ) )
return true;
// for using combo box as cell editor in table
// JComboBox.isTableCellEditor is set in javax.swing.DefaultCellEditor(JComboBox) constructor
return c instanceof JComponent && Boolean.TRUE.equals( ((JComponent)c).getClientProperty( "JComboBox.isTableCellEditor" ) ); return c instanceof JComponent && Boolean.TRUE.equals( ((JComponent)c).getClientProperty( "JComboBox.isTableCellEditor" ) );
} }
@@ -150,9 +181,21 @@ public class FlatUIUtils
keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c ); keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c );
} }
public static boolean isRoundRect( Component c ) { /**
return c instanceof JComponent && FlatClientProperties.clientPropertyBoolean( * Returns whether the given component is in a window that is in full-screen mode.
(JComponent) c, FlatClientProperties.COMPONENT_ROUND_RECT, false ); */
public static boolean isFullScreen( Component c ) {
GraphicsConfiguration gc = c.getGraphicsConfiguration();
GraphicsDevice gd = (gc != null) ? gc.getDevice() : null;
Window fullScreenWindow = (gd != null) ? gd.getFullScreenWindow() : null;
return (fullScreenWindow != null && fullScreenWindow == SwingUtilities.windowForComponent( c ));
}
public static Boolean isRoundRect( Component c ) {
return (c instanceof JComponent)
? FlatClientProperties.clientPropertyBooleanStrict(
(JComponent) c, FlatClientProperties.COMPONENT_ROUND_RECT, null )
: null;
} }
/** /**
@@ -194,10 +237,57 @@ public class FlatUIUtils
/** /**
* Sets rendering hints used for painting. * Sets rendering hints used for painting.
*/ */
public static void setRenderingHints( Graphics2D g ) { public static Object[] setRenderingHints( Graphics g ) {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); Graphics2D g2 = (Graphics2D) g;
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, Object[] oldRenderingHints = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE ); MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
return oldRenderingHints;
}
/**
* Resets rendering hints previously set with {@link #setRenderingHints}.
*/
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
}
/**
* Temporary resets rendering hints set with {@link #setRenderingHints}
* and runs the given runnable.
* <p>
* This is intended for painting text while rendering hints are set.
* <p>
* If text antialiasing is disabled (in OS system settings or via
* {@code -Dawt.useSystemAAFontSettings=off}), but general antialiasing is enabled,
* then text is still painted using some kind of "grayscale" antialiasing,
* which may make the text look bold (depends on font and font size).
* To avoid this, temporary disable general antialiasing.
* This does not affect text rendering if text antialiasing is enabled (usually the default).
*/
public static void runWithoutRenderingHints( Graphics g, Object[] oldRenderingHints, Runnable runnable ) {
if( oldRenderingHints == null ) {
runnable.run();
return;
}
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints2 = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
resetRenderingHints( g2, oldRenderingHints );
runnable.run();
resetRenderingHints( g2, oldRenderingHints2 );
} }
public static Color deriveColor( Color color, Color baseColor ) { public static Color deriveColor( Color color, Color baseColor ) {
@@ -210,7 +300,7 @@ public class FlatUIUtils
* Paints an outer border, which is usually a focus border. * Paints an outer border, which is usually a focus border.
* <p> * <p>
* The outside bounds of the painted border are {@code x,y,width,height}. * The outside bounds of the painted border are {@code x,y,width,height}.
* The line width of the painted border is {@code focusWidth + lineWidth}. * The line thickness of the painted border is {@code focusWidth + lineWidth}.
* The given arc diameter refers to the inner rectangle ({@code x,y,width,height} minus {@code focusWidth}). * The given arc diameter refers to the inner rectangle ({@code x,y,width,height} minus {@code focusWidth}).
* *
* @see #paintComponentBorder * @see #paintComponentBorder
@@ -219,6 +309,9 @@ public class FlatUIUtils
public static void paintComponentOuterBorder( Graphics2D g, int x, int y, int width, int height, public static void paintComponentOuterBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc ) float focusWidth, float lineWidth, float arc )
{ {
if( focusWidth + lineWidth == 0 )
return; // nothing to paint
double systemScaleFactor = UIScale.getSystemScaleFactor( g ); double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) { if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175% // paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
@@ -255,6 +348,7 @@ public class FlatUIUtils
* <p> * <p>
* The outside bounds of the painted border are * The outside bounds of the painted border are
* {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}. * {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}.
* The line thickness of the painted border is {@code lineWidth}.
* The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}). * The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}).
* *
* @see #paintComponentOuterBorder * @see #paintComponentOuterBorder
@@ -263,6 +357,9 @@ public class FlatUIUtils
public static void paintComponentBorder( Graphics2D g, int x, int y, int width, int height, public static void paintComponentBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc ) float focusWidth, float lineWidth, float arc )
{ {
if( lineWidth == 0 )
return; // nothing to paint
double systemScaleFactor = UIScale.getSystemScaleFactor( g ); double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) { if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175% // paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
@@ -344,6 +441,52 @@ public class FlatUIUtils
return new RoundRectangle2D.Float( x, y, w, h, arc, arc ); return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
} }
static void paintFilledRectangle( Graphics g, Color color, float x, float y, float w, float h ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( color );
g2.fill( new Rectangle2D.Float( x, y, w, h ) );
} finally {
g2.dispose();
}
}
public static void paintGrip( Graphics g, int x, int y, int width, int height,
boolean horizontal, int dotCount, int dotSize, int gap, boolean centerPrecise )
{
dotSize = UIScale.scale( dotSize );
gap = UIScale.scale( gap );
int gripSize = (dotSize * dotCount) + ((gap * (dotCount - 1)));
// calculate grip position
float gx;
float gy;
if( horizontal ) {
gx = x + Math.round( (width - gripSize) / 2f );
gy = y + ((height - dotSize) / 2f);
if( !centerPrecise )
gy = Math.round( gy );
} else {
// vertical
gx = x + ((width - dotSize) / 2f);
gy = y + Math.round( (height - gripSize) / 2f );
if( !centerPrecise )
gx = Math.round( gx );
}
// paint dots
for( int i = 0; i < dotCount; i++ ) {
((Graphics2D)g).fill( new Ellipse2D.Float( gx, gy, dotSize, dotSize ) );
if( horizontal )
gx += dotSize + gap;
else
gy += dotSize + gap;
}
}
/** /**
* Fill background with parent's background color because the visible component * Fill background with parent's background color because the visible component
* is smaller than its bounds (for the focus decoration). * is smaller than its bounds (for the focus decoration).
@@ -420,15 +563,31 @@ public class FlatUIUtils
float x2 = x + width; float x2 = x + width;
float y2 = y + height; float y2 = y + height;
// same constant as in java.awt.geom.EllipseIterator.CtrlVal used to paint circles
double c = 0.5522847498307933;
double ci = 1. - c;
double ciTopLeft = arcTopLeft * ci;
double ciTopRight = arcTopRight * ci;
double ciBottomLeft = arcBottomLeft * ci;
double ciBottomRight = arcBottomRight * ci;
Path2D rect = new Path2D.Float(); Path2D rect = new Path2D.Float();
rect.moveTo( x2 - arcTopRight, y ); rect.moveTo( x2 - arcTopRight, y );
rect.quadTo( x2, y, x2, y + arcTopRight ); rect.curveTo( x2 - ciTopRight, y,
rect.lineTo( x2, y2 - arcBottomRight ); x2, y + ciTopRight,
rect.quadTo( x2, y2, x2 - arcBottomRight, y2 ); x2, y + arcTopRight );
rect.lineTo( x + arcBottomLeft, y2 ); rect.lineTo( x2, y2 - arcBottomRight );
rect.quadTo( x, y2, x, y2 - arcBottomLeft ); rect.curveTo( x2, y2 - ciBottomRight,
rect.lineTo( x, y + arcTopLeft ); x2 - ciBottomRight, y2,
rect.quadTo( x, y, x + arcTopLeft, y ); x2 - arcBottomRight, y2 );
rect.lineTo( x + arcBottomLeft, y2 );
rect.curveTo( x + ciBottomLeft, y2,
x, y2 - ciBottomLeft,
x, y2 - arcBottomLeft );
rect.lineTo( x, y + arcTopLeft );
rect.curveTo( x, y + ciTopLeft,
x + ciTopLeft, y,
x + arcTopLeft, y );
rect.closePath(); rect.closePath();
return rect; return rect;
@@ -505,35 +664,17 @@ public class FlatUIUtils
return explicitlySet; return explicitlySet;
} }
//---- class HoverListener ------------------------------------------------ /**
* Creates a shared component UI for the given key and the current Laf.
public static class HoverListener * Each Laf instance has its own shared component UI instance.
extends MouseAdapter * <p>
{ * This is for GUI builders that support Laf switching and
private final Component repaintComponent; * may use multiple Laf instances at the same time.
private final Consumer<Boolean> hoverChanged; */
public static ComponentUI createSharedUI( Object key, Supplier<ComponentUI> newInstanceSupplier ) {
public HoverListener( Component repaintComponent, Consumer<Boolean> hoverChanged ) { return sharedUIinstances
this.repaintComponent = repaintComponent; .computeIfAbsent( UIManager.getLookAndFeel(), k -> new IdentityHashMap<>() )
this.hoverChanged = hoverChanged; .computeIfAbsent( key, k -> newInstanceSupplier.get() );
}
@Override
public void mouseEntered( MouseEvent e ) {
hoverChanged.accept( true );
repaint();
}
@Override
public void mouseExited( MouseEvent e ) {
hoverChanged.accept( false );
repaint();
}
private void repaint() {
if( repaintComponent != null && repaintComponent.isEnabled() )
repaintComponent.repaint();
}
} }
//---- class RepaintFocusListener ----------------------------------------- //---- class RepaintFocusListener -----------------------------------------

View File

@@ -38,12 +38,8 @@ import javax.swing.plaf.basic.BasicViewportUI;
public class FlatViewportUI public class FlatViewportUI
extends BasicViewportUI extends BasicViewportUI
{ {
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
if( instance == null ) return FlatUIUtils.createSharedUI( FlatViewportUI.class, FlatViewportUI::new );
instance = new FlatViewportUI();
return instance;
} }
@Override @Override

View File

@@ -17,11 +17,13 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static java.awt.Cursor.*; import static java.awt.Cursor.*;
import static javax.swing.SwingConstants.*;
import java.awt.Container; import java.awt.Container;
import java.awt.Dialog; import java.awt.Dialog;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
@@ -34,54 +36,59 @@ import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener; import java.awt.event.WindowStateListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.function.Supplier;
import javax.swing.DesktopManager;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
* Resizes frames and dialogs. * Resizes frames, dialogs or internal frames.
* <p>
* Could also be used to implement resize support for any Swing component
* by creating a new subclass.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatWindowResizer public abstract class FlatWindowResizer
implements PropertyChangeListener, WindowStateListener, ComponentListener implements PropertyChangeListener, ComponentListener
{ {
protected final static Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1; protected final static Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1;
protected final JRootPane rootPane; protected final JComponent resizeComp;
protected final int borderDragThickness = FlatUIUtils.getUIInt( "RootPane.borderDragThickness", 5 ); protected final int borderDragThickness = FlatUIUtils.getUIInt( "RootPane.borderDragThickness", 5 );
protected final int cornerDragWidth = FlatUIUtils.getUIInt( "RootPane.cornerDragWidth", 16 ); protected final int cornerDragWidth = FlatUIUtils.getUIInt( "RootPane.cornerDragWidth", 16 );
protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorFrameMinimumSizeOnResize" ); protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorFrameMinimumSizeOnResize" );
protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorDialogMinimumSizeOnResize" ); protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorDialogMinimumSizeOnResize" );
protected final JComponent north; protected final DragBorderComponent topDragComp;
protected final JComponent south; protected final DragBorderComponent bottomDragComp;
protected final JComponent west; protected final DragBorderComponent leftDragComp;
protected final JComponent east; protected final DragBorderComponent rightDragComp;
protected Window window; protected FlatWindowResizer( JComponent resizeComp ) {
this.resizeComp = resizeComp;
public FlatWindowResizer( JRootPane rootPane ) { topDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR );
this.rootPane = rootPane; bottomDragComp = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR );
leftDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR );
rightDragComp = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR );
north = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR ); Container cont = (resizeComp instanceof JRootPane) ? ((JRootPane)resizeComp).getLayeredPane() : resizeComp;
south = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR ); Object cons = (cont instanceof JLayeredPane) ? WINDOW_RESIZER_LAYER : null;
west = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR ); cont.add( topDragComp, cons, 0 );
east = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR ); cont.add( bottomDragComp, cons, 1 );
cont.add( leftDragComp, cons, 2 );
cont.add( rightDragComp, cons, 3 );
JLayeredPane layeredPane = rootPane.getLayeredPane(); resizeComp.addComponentListener( this );
layeredPane.add( north, WINDOW_RESIZER_LAYER ); resizeComp.addPropertyChangeListener( "ancestor", this );
layeredPane.add( south, WINDOW_RESIZER_LAYER );
layeredPane.add( west, WINDOW_RESIZER_LAYER );
layeredPane.add( east, WINDOW_RESIZER_LAYER );
rootPane.addComponentListener( this ); if( resizeComp.isDisplayable() )
rootPane.addPropertyChangeListener( "ancestor", this );
if( rootPane.isDisplayable() )
addNotify(); addNotify();
} }
@@ -92,85 +99,96 @@ public class FlatWindowResizer
public void uninstall() { public void uninstall() {
removeNotify(); removeNotify();
rootPane.removeComponentListener( this ); resizeComp.removeComponentListener( this );
rootPane.removePropertyChangeListener( "ancestor", this ); resizeComp.removePropertyChangeListener( "ancestor", this );
JLayeredPane layeredPane = rootPane.getLayeredPane(); Container cont = topDragComp.getParent();
layeredPane.remove( north ); cont.remove( topDragComp );
layeredPane.remove( south ); cont.remove( bottomDragComp );
layeredPane.remove( west ); cont.remove( leftDragComp );
layeredPane.remove( east ); cont.remove( rightDragComp );
} }
public void doLayout() { public void doLayout() {
if( !north.isVisible() ) if( !topDragComp.isVisible() )
return; return;
int x = 0; int x = 0;
int y = 0; int y = 0;
int width = rootPane.getWidth(); int width = resizeComp.getWidth();
int height = rootPane.getHeight(); int height = resizeComp.getHeight();
if( width == 0 || height == 0 ) if( width == 0 || height == 0 )
return; return;
Insets resizeInsets = getResizeInsets();
int thickness = UIScale.scale( borderDragThickness ); int thickness = UIScale.scale( borderDragThickness );
int y2 = y + thickness; int topThickness = Math.max( resizeInsets.top, thickness );
int height2 = height - (thickness * 2); int bottomThickness = Math.max( resizeInsets.bottom, thickness );
int leftThickness = Math.max( resizeInsets.left, thickness );
int rightThickness = Math.max( resizeInsets.right, thickness );
int y2 = y + topThickness;
int height2 = height - topThickness - bottomThickness;
north.setBounds( x, y, width, thickness ); // set bounds of drag components
south.setBounds( x, y + height - thickness, width, thickness ); topDragComp.setBounds( x, y, width, topThickness );
west.setBounds( x, y2, thickness, height2 ); bottomDragComp.setBounds( x, y + height - bottomThickness, width, bottomThickness );
east.setBounds( x + width - thickness, y2, thickness, height2 ); leftDragComp.setBounds( x, y2, leftThickness, height2 );
rightDragComp.setBounds( x + width - rightThickness, y2, rightThickness, height2 );
// set corner drag widths
int cornerDelta = UIScale.scale( cornerDragWidth - borderDragThickness );
topDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
bottomDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
leftDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
rightDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
}
protected Insets getResizeInsets() {
return new Insets( 0, 0, 0, 0 );
} }
protected void addNotify() { protected void addNotify() {
Container parent = rootPane.getParent();
window = (parent instanceof Window) ? (Window) parent : null;
if( window instanceof Frame ) {
window.addPropertyChangeListener( "resizable", this );
window.addWindowStateListener( this );
}
updateVisibility(); updateVisibility();
} }
protected void removeNotify() { protected void removeNotify() {
if( window instanceof Frame ) {
window.removePropertyChangeListener( "resizable", this );
window.removeWindowStateListener( this );
}
window = null;
updateVisibility(); updateVisibility();
} }
protected void updateVisibility() { protected void updateVisibility() {
boolean visible = isWindowResizable(); boolean visible = isWindowResizable();
if( visible == north.isVisible() ) if( visible == topDragComp.isVisible() )
return; return;
north.setVisible( visible ); topDragComp.setVisible( visible );
south.setVisible( visible ); bottomDragComp.setVisible( visible );
west.setVisible( visible ); leftDragComp.setVisible( visible );
// The east component is not hidden, instead its bounds are set to 0,0,1,1 and // The east component is not hidden, instead its bounds are set to 0,0,1,1 and
// it is disabled. This is necessary so that DragBorderComponent.paintComponent() is invoked. // it is disabled. This is necessary so that DragBorderComponent.paintComponent() is invoked.
east.setEnabled( visible ); rightDragComp.setEnabled( visible );
if( visible ) { if( visible ) {
east.setVisible( true ); // necessary because it is initially invisible rightDragComp.setVisible( true ); // necessary because it is initially invisible
doLayout(); doLayout();
} else } else
east.setBounds( 0, 0, 1, 1 ); rightDragComp.setBounds( 0, 0, 1, 1 );
} }
protected boolean isWindowResizable() { boolean isDialog() {
if( window instanceof Frame )
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
if( window instanceof Dialog )
return ((Dialog)window).isResizable();
return false; return false;
} }
protected abstract boolean isWindowResizable();
protected abstract Rectangle getWindowBounds();
protected abstract void setWindowBounds( Rectangle r );
protected abstract boolean honorMinimumSizeOnResize();
protected abstract Dimension getWindowMinimumSize();
protected void beginResizing( int direction ) {}
protected void endResizing() {}
//---- interface PropertyChangeListener ----
@Override @Override
public void propertyChange( PropertyChangeEvent e ) { public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) { switch( e.getPropertyName() ) {
@@ -187,10 +205,7 @@ public class FlatWindowResizer
} }
} }
@Override //---- interface ComponentListener ----
public void windowStateChanged( WindowEvent e ) {
updateVisibility();
}
@Override @Override
public void componentResized( ComponentEvent e ) { public void componentResized( ComponentEvent e ) {
@@ -201,6 +216,165 @@ public class FlatWindowResizer
@Override public void componentShown( ComponentEvent e ) {} @Override public void componentShown( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {} @Override public void componentHidden( ComponentEvent e ) {}
//---- class WindowResizer ------------------------------------------------
/**
* Resizes frames and dialogs.
*/
public static class WindowResizer
extends FlatWindowResizer
implements WindowStateListener
{
protected Window window;
public WindowResizer( JRootPane rootPane ) {
super( rootPane );
}
@Override
protected void addNotify() {
Container parent = resizeComp.getParent();
window = (parent instanceof Window) ? (Window) parent : null;
if( window instanceof Frame ) {
window.addPropertyChangeListener( "resizable", this );
window.addWindowStateListener( this );
}
super.addNotify();
}
@Override
protected void removeNotify() {
if( window instanceof Frame ) {
window.removePropertyChangeListener( "resizable", this );
window.removeWindowStateListener( this );
}
window = null;
super.removeNotify();
}
@Override
protected boolean isWindowResizable() {
if( FlatUIUtils.isFullScreen( resizeComp ) )
return false;
if( window instanceof Frame )
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
if( window instanceof Dialog )
return ((Dialog)window).isResizable();
return false;
}
@Override
protected Rectangle getWindowBounds() {
return window.getBounds();
}
@Override
protected void setWindowBounds( Rectangle r ) {
window.setBounds( r );
// immediately layout drag border components
doLayout();
if( Toolkit.getDefaultToolkit().isDynamicLayoutActive() ) {
window.validate();
resizeComp.repaint();
}
}
@Override
protected boolean honorMinimumSizeOnResize() {
return
(honorFrameMinimumSizeOnResize && window instanceof Frame) ||
(honorDialogMinimumSizeOnResize && window instanceof Dialog);
}
@Override
protected Dimension getWindowMinimumSize() {
return window.getMinimumSize();
}
@Override
boolean isDialog() {
return window instanceof Dialog;
}
@Override
public void windowStateChanged( WindowEvent e ) {
updateVisibility();
}
}
//---- class InternalFrameResizer -----------------------------------------
/**
* Resizes internal frames.
*/
public static class InternalFrameResizer
extends FlatWindowResizer
{
protected final Supplier<DesktopManager> desktopManager;
public InternalFrameResizer( JInternalFrame frame, Supplier<DesktopManager> desktopManager ) {
super( frame );
this.desktopManager = desktopManager;
frame.addPropertyChangeListener( "resizable", this );
}
@Override
public void uninstall() {
getFrame().removePropertyChangeListener( "resizable", this );
super.uninstall();
}
private JInternalFrame getFrame() {
return (JInternalFrame) resizeComp;
}
@Override
protected Insets getResizeInsets() {
return getFrame().getInsets();
}
@Override
protected boolean isWindowResizable() {
return getFrame().isResizable();
}
@Override
protected Rectangle getWindowBounds() {
return getFrame().getBounds();
}
@Override
protected void setWindowBounds( Rectangle r ) {
desktopManager.get().resizeFrame( getFrame(), r.x, r.y, r.width, r.height );
}
@Override
protected boolean honorMinimumSizeOnResize() {
return true;
}
@Override
protected Dimension getWindowMinimumSize() {
return getFrame().getMinimumSize();
}
@Override
protected void beginResizing( int direction ) {
desktopManager.get().beginResizingFrame( getFrame(), direction );
}
@Override
protected void endResizing() {
desktopManager.get().endResizingFrame( getFrame() );
}
}
//---- class DragBorderComponent ------------------------------------------ //---- class DragBorderComponent ------------------------------------------
protected class DragBorderComponent protected class DragBorderComponent
@@ -212,9 +386,15 @@ public class FlatWindowResizer
private final int trailingResizeDir; private final int trailingResizeDir;
private int resizeDir = -1; private int resizeDir = -1;
private int dragStartMouseX;
private int dragStartMouseY; private int leadingCornerDragWidth;
private Rectangle dragStartWindowBounds; private int trailingCornerDragWidth;
// offsets of mouse position to window edges
private int dragLeftOffset;
private int dragRightOffset;
private int dragTopOffset;
private int dragBottomOffset;
protected DragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir ) { protected DragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir ) {
this.leadingResizeDir = leadingResizeDir; this.leadingResizeDir = leadingResizeDir;
@@ -228,6 +408,11 @@ public class FlatWindowResizer
addMouseMotionListener( this ); addMouseMotionListener( this );
} }
void setCornerDragWidths( int leading, int trailing ) {
leadingCornerDragWidth = leading;
trailingCornerDragWidth = trailing;
}
protected void setResizeDir( int resizeDir ) { protected void setResizeDir( int resizeDir ) {
if( this.resizeDir == resizeDir ) if( this.resizeDir == resizeDir )
return; return;
@@ -246,13 +431,26 @@ public class FlatWindowResizer
protected void paintComponent( Graphics g ) { protected void paintComponent( Graphics g ) {
super.paintChildren( g ); super.paintChildren( g );
// this is necessary because Dialog.setResizable() does not fire events // for dialogs: necessary because Dialog.setResizable() does not fire events
if( window instanceof Dialog ) // for frames: necessary because GraphicsDevice.setFullScreenWindow() does not fire events
updateVisibility(); updateVisibility();
/*debug /*debug
int width = getWidth();
int height = getHeight();
g.setColor( java.awt.Color.blue );
boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR);
if( topOrBottom ) {
g.drawLine( leadingCornerDragWidth, 0, leadingCornerDragWidth, height );
g.drawLine( width - trailingCornerDragWidth, 0, width - trailingCornerDragWidth, height );
} else {
g.drawLine( 0, leadingCornerDragWidth, width, leadingCornerDragWidth );
g.drawLine( 0, height - trailingCornerDragWidth, width, height - trailingCornerDragWidth );
}
g.setColor( java.awt.Color.red ); g.setColor( java.awt.Color.red );
g.drawRect( 0, 0, getWidth() - 1, getHeight() - 1 ); g.drawRect( 0, 0, width - 1, height - 1 );
debug*/ debug*/
} }
@@ -262,114 +460,116 @@ debug*/
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
if( window == null ) if( !isWindowResizable() )
return; return;
dragStartMouseX = e.getXOnScreen(); int xOnScreen = e.getXOnScreen();
dragStartMouseY = e.getYOnScreen(); int yOnScreen = e.getYOnScreen();
dragStartWindowBounds = window.getBounds(); Rectangle windowBounds = getWindowBounds();
// compute offsets of mouse position to window edges
dragLeftOffset = xOnScreen - windowBounds.x;
dragTopOffset = yOnScreen - windowBounds.y;
dragRightOffset = windowBounds.x + windowBounds.width - xOnScreen;
dragBottomOffset = windowBounds.y + windowBounds.height - yOnScreen;
int direction = 0;
switch( resizeDir ) {
case N_RESIZE_CURSOR: direction = NORTH; break;
case S_RESIZE_CURSOR: direction = SOUTH; break;
case W_RESIZE_CURSOR: direction = WEST; break;
case E_RESIZE_CURSOR: direction = EAST; break;
case NW_RESIZE_CURSOR: direction = NORTH_WEST; break;
case NE_RESIZE_CURSOR: direction = NORTH_EAST; break;
case SW_RESIZE_CURSOR: direction = SOUTH_WEST; break;
case SE_RESIZE_CURSOR: direction = SOUTH_EAST; break;
}
beginResizing( direction );
} }
@Override @Override
public void mouseReleased( MouseEvent e ) { public void mouseReleased( MouseEvent e ) {
dragStartWindowBounds = null; if( !isWindowResizable() )
return;
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
endResizing();
} }
@Override @Override public void mouseEntered( MouseEvent e ) {}
public void mouseEntered( MouseEvent e ) { @Override public void mouseExited( MouseEvent e ) {}
}
@Override
public void mouseExited( MouseEvent e ) {
}
@Override @Override
public void mouseMoved( MouseEvent e ) { public void mouseMoved( MouseEvent e ) {
boolean topBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR); boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR);
int xy = topBottom ? e.getX() : e.getY(); int xy = topOrBottom ? e.getX() : e.getY();
int wh = topBottom ? getWidth() : getHeight(); int wh = topOrBottom ? getWidth() : getHeight();
int cornerWH = UIScale.scale( cornerDragWidth - (topBottom ? 0 : borderDragThickness) );
setResizeDir( xy <= cornerWH setResizeDir( xy <= leadingCornerDragWidth
? leadingResizeDir ? leadingResizeDir
: (xy >= wh - cornerWH : (xy >= wh - trailingCornerDragWidth
? trailingResizeDir ? trailingResizeDir
: centerResizeDir) ); : centerResizeDir) );
} }
@Override @Override
public void mouseDragged( MouseEvent e ) { public void mouseDragged( MouseEvent e ) {
if( dragStartWindowBounds == null )
return;
if( !isWindowResizable() ) if( !isWindowResizable() )
return; return;
int mouseDeltaX = e.getXOnScreen() - dragStartMouseX; int xOnScreen = e.getXOnScreen();
int mouseDeltaY = e.getYOnScreen() - dragStartMouseY; int yOnScreen = e.getYOnScreen();
int deltaX = 0; // Get current window bounds and compute new bounds based them.
int deltaY = 0; // This is necessary because window manager may alter window bounds while resizing.
int deltaWidth = 0; // E.g. when having two monitors with different scale factors and resizing
int deltaHeight = 0; // a window on first screen to the second screen, then the window manager may
// decide at some point that the window should be only on second screen
// north // and adjusts its bounds.
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) { Rectangle oldBounds = getWindowBounds();
deltaY = mouseDeltaY; Rectangle newBounds = new Rectangle( oldBounds );
deltaHeight = -mouseDeltaY;
}
// south
if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
deltaHeight = mouseDeltaY;
// west
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
deltaX = mouseDeltaX;
deltaWidth = -mouseDeltaX;
}
// east
if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
deltaWidth = mouseDeltaX;
// compute new window bounds // compute new window bounds
Rectangle newBounds = new Rectangle( dragStartWindowBounds );
newBounds.x += deltaX; // top
newBounds.y += deltaY; if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
newBounds.width += deltaWidth; newBounds.y = yOnScreen - dragTopOffset;
newBounds.height += deltaHeight; newBounds.height += (oldBounds.y - newBounds.y);
}
// bottom
if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
newBounds.height = (yOnScreen + dragBottomOffset) - newBounds.y;
// left
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
newBounds.x = xOnScreen - dragLeftOffset;
newBounds.width += (oldBounds.x - newBounds.x);
}
// right
if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR )
newBounds.width = (xOnScreen + dragRightOffset) - newBounds.x;
// apply minimum window size // apply minimum window size
boolean honorMinimumSizeOnResize = Dimension minimumSize = honorMinimumSizeOnResize() ? getWindowMinimumSize() : null;
(honorFrameMinimumSizeOnResize && window instanceof Frame) ||
(honorDialogMinimumSizeOnResize && window instanceof Dialog);
Dimension minimumSize = honorMinimumSizeOnResize ? window.getMinimumSize() : null;
if( minimumSize == null ) if( minimumSize == null )
minimumSize = UIScale.scale( new Dimension( 150, 50 ) ); minimumSize = UIScale.scale( new Dimension( 150, 50 ) );
if( newBounds.width < minimumSize.width ) { if( newBounds.width < minimumSize.width ) {
if( deltaX != 0 ) if( newBounds.x != oldBounds.x )
newBounds.x -= (minimumSize.width - newBounds.width); newBounds.x -= (minimumSize.width - newBounds.width);
newBounds.width = minimumSize.width; newBounds.width = minimumSize.width;
} }
if( newBounds.height < minimumSize.height ) { if( newBounds.height < minimumSize.height ) {
if( deltaY != 0 ) if( newBounds.y != oldBounds.y )
newBounds.y -= (minimumSize.height - newBounds.height); newBounds.y -= (minimumSize.height - newBounds.height);
newBounds.height = minimumSize.height; newBounds.height = minimumSize.height;
} }
// set window bounds // set window bounds
if( !newBounds.equals( dragStartWindowBounds ) ) { if( !newBounds.equals( oldBounds ) )
window.setBounds( newBounds ); setWindowBounds( newBounds );
// immediately layout drag border components
FlatWindowResizer.this.doLayout();
if( Toolkit.getDefaultToolkit().isDynamicLayoutActive() ) {
window.validate();
rootPane.repaint();
}
}
} }
} }
} }

View File

@@ -191,7 +191,7 @@ public class JBRCustomDecorations
initialized = true; initialized = true;
// requires JetBrains Runtime 11 and Windows 10 // requires JetBrains Runtime 11 and Windows 10
if( !SystemInfo.IS_JETBRAINS_JVM_11_OR_LATER || !SystemInfo.IS_WINDOWS_10_OR_LATER ) if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
return; return;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) ) if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, true ) )

View File

@@ -0,0 +1,249 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JComponent;
import com.formdev.flatlaf.util.Animator.Interpolator;
/**
* Icon that automatically animates painting on component value changes.
* <p>
* {@link #getValue(Component)} returns the value of the component.
* If the value changes, then {@link #paintIconAnimated(Component, Graphics, int, int, float)}
* is invoked multiple times with animated value (from old value to new value).
* <p>
* Example for an animated icon:
* <pre>
* private class AnimatedMinimalTestIcon
* implements AnimatedIcon
* {
* &#64;Override public int getIconWidth() { return 100; }
* &#64;Override public int getIconHeight() { return 20; }
*
* &#64;Override
* public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
* int w = getIconWidth();
* int h = getIconHeight();
*
* g.setColor( Color.red );
* g.drawRect( x, y, w - 1, h - 1 );
* g.fillRect( x, y, Math.round( w * animatedValue ), h );
* }
*
* &#64;Override
* public float getValue( Component c ) {
* return ((AbstractButton)c).isSelected() ? 1 : 0;
* }
* }
*
* // sample usage
* JCheckBox checkBox = new JCheckBox( "test" );
* checkBox.setIcon( new AnimatedMinimalTestIcon() );
* </pre>
*
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
* is a instance of {@link JComponent}.
* A client property is set on the component to store the animation state.
*
* @author Karl Tauber
*/
public interface AnimatedIcon
extends Icon
{
@Override
public default void paintIcon( Component c, Graphics g, int x, int y ) {
AnimationSupport.paintIcon( this, c, g, x, y );
}
/**
* Paints the icon for the given animated value.
*
* @param c the component that this icon belongs to
* @param g the graphics context
* @param x the x coordinate of the icon
* @param y the y coordinate of the icon
* @param animatedValue the animated value, which is either equal to what {@link #getValue(Component)}
* returned, or somewhere between the previous value and the latest value
* that {@link #getValue(Component)} returned
*/
void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue );
/**
* Gets the value of the component.
* <p>
* This can be any value and depends on the component.
* If the value changes, then this class animates from the old value to the new one.
* <p>
* For a toggle button this could be {@code 0} for off and {@code 1} for on.
*/
float getValue( Component c );
/**
* Returns whether animation is enabled for this icon (default is {@code true}).
*/
default boolean isAnimationEnabled() {
return true;
}
/**
* Returns the duration of the animation in milliseconds (default is 150).
*/
default int getAnimationDuration() {
return 150;
}
/**
* Returns the resolution of the animation in milliseconds (default is 10).
* Resolution is the amount of time between timing events.
*/
default int getAnimationResolution() {
return 10;
}
/**
* Returns the interpolator for the animation.
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
*/
default Interpolator getAnimationInterpolator() {
return CubicBezierEasing.STANDARD_EASING;
}
/**
* Returns the client property key used to store the animation support.
*/
default Object getClientPropertyKey() {
return getClass();
}
//---- class AnimationSupport ---------------------------------------------
/**
* Animation support class that stores the animation state and implements the animation.
*/
class AnimationSupport
{
private float startValue;
private float targetValue;
private float animatedValue;
private float fraction;
private Animator animator;
// last x,y coordinates of the icon needed to repaint while animating
private int x;
private int y;
public static void paintIcon( AnimatedIcon icon, Component c, Graphics g, int x, int y ) {
if( !isAnimationEnabled( icon, c ) ) {
// paint without animation if animation is disabled or
// component is not a JComponent and therefore does not support
// client properties, which are required to keep animation state
paintIconImpl( icon, c, g, x, y, null );
return;
}
JComponent jc = (JComponent) c;
Object key = icon.getClientPropertyKey();
AnimationSupport as = (AnimationSupport) jc.getClientProperty( key );
if( as == null ) {
// painted first time --> do not animate, but remember current component value
as = new AnimationSupport();
as.startValue = as.targetValue = as.animatedValue = icon.getValue( c );
as.x = x;
as.y = y;
jc.putClientProperty( key, as );
} else {
// get component value
float value = icon.getValue( c );
if( value != as.targetValue ) {
// value changed --> (re)start animation
if( as.animator == null ) {
// create animator
AnimationSupport as2 = as;
as.animator = new Animator( icon.getAnimationDuration(), fraction -> {
// check whether component was removed while animation is running
if( !c.isDisplayable() ) {
as2.animator.stop();
return;
}
// compute animated value
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
as2.fraction = fraction;
// repaint icon
c.repaint( as2.x, as2.y, icon.getIconWidth(), icon.getIconHeight() );
}, () -> {
as2.startValue = as2.animatedValue = as2.targetValue;
as2.animator = null;
} );
}
if( as.animator.isRunning() ) {
// if animation is still running, restart it from the current
// animated value to the new target value with reduced duration
as.animator.cancel();
int duration2 = (int) (icon.getAnimationDuration() * as.fraction);
if( duration2 > 0 )
as.animator.setDuration( duration2 );
as.startValue = as.animatedValue;
} else {
// new animation
as.animator.setDuration( icon.getAnimationDuration() );
as.animator.setResolution( icon.getAnimationResolution() );
as.animator.setInterpolator( icon.getAnimationInterpolator() );
as.animatedValue = as.startValue;
}
as.targetValue = value;
as.animator.start();
}
as.x = x;
as.y = y;
}
paintIconImpl( icon, c, g, x, y, as );
}
private static void paintIconImpl( AnimatedIcon icon, Component c, Graphics g, int x, int y, AnimationSupport as ) {
float value = (as != null) ? as.animatedValue : icon.getValue( c );
icon.paintIconAnimated( c, g, x, y, value );
}
private static boolean isAnimationEnabled( AnimatedIcon icon, Component c ) {
return Animator.useAnimation() && icon.isAnimationEnabled() && c instanceof JComponent;
}
public static void saveIconLocation( AnimatedIcon icon, Component c, int x, int y ) {
if( !isAnimationEnabled( icon, c ) )
return;
AnimationSupport as = (AnimationSupport) ((JComponent)c).getClientProperty( icon.getClientPropertyKey() );
if( as != null ) {
as.x = x;
as.y = y;
}
}
}
}

View File

@@ -0,0 +1,340 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.util.ArrayList;
import javax.swing.Timer;
import com.formdev.flatlaf.FlatSystemProperties;
/**
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
* and "Timing Framework" library.
*
* @author Karl Tauber
*/
public class Animator
{
private int duration;
private int resolution = 10;
private Interpolator interpolator;
private final ArrayList<TimingTarget> targets = new ArrayList<>();
private final Runnable endRunnable;
private boolean running;
private boolean hasBegun;
private boolean timeToStop;
private long startTime;
private Timer timer;
/**
* Checks whether animations are enabled (the default) or disabled via
* system property {@code flatlaf.animation} set to {@code false}.
* This allows disabling all animations at command line with {@code -Dflatlaf.animation=false}.
*/
public static boolean useAnimation() {
return FlatSystemProperties.getBoolean( FlatSystemProperties.ANIMATION, true );
}
/**
* Creates an animation that runs duration milliseconds.
* Use {@link #addTarget(TimingTarget)} to receive timing events
* and {@link #start()} to start the animation.
*
* @param duration the duration of the animation in milliseconds
*/
public Animator( int duration ) {
this( duration, null, null );
}
/**
* Creates an animation that runs duration milliseconds.
* Use {@link #start()} to start the animation.
*
* @param duration the duration of the animation in milliseconds
* @param target the target that receives timing events
*/
public Animator( int duration, TimingTarget target ) {
this( duration, target, null );
}
/**
* Creates an animation that runs duration milliseconds.
* Use {@link #start()} to start the animation.
*
* @param duration the duration of the animation in milliseconds
* @param target the target that receives timing events
* @param endRunnable a runnable invoked when the animation ends; or {@code null}
*/
public Animator( int duration, TimingTarget target, Runnable endRunnable ) {
setDuration( duration );
addTarget( target );
this.endRunnable = endRunnable;
}
/**
* Returns the duration of the animation in milliseconds.
*/
public int getDuration() {
return duration;
}
/**
* Sets the duration of the animation in milliseconds.
*
* @throws IllegalStateException if animation is running
* @throws IllegalArgumentException if duration is &lt;= zero
*/
public void setDuration( int duration ) {
throwExceptionIfRunning();
if( duration <= 0 )
throw new IllegalArgumentException();
this.duration = duration;
}
/**
* Returns the resolution of the animation in milliseconds (default is 10).
* Resolution is the amount of time between timing events.
*/
public int getResolution() {
return resolution;
}
/**
* Sets the resolution of the animation in milliseconds.
*
* @param resolution the resolution of the animation in milliseconds
* @throws IllegalStateException if animation is running
* @throws IllegalArgumentException if resolution is &lt;= zero
*/
public void setResolution( int resolution ) {
throwExceptionIfRunning();
if( resolution <= 0 )
throw new IllegalArgumentException();
this.resolution = resolution;
}
/**
* Returns the interpolator for the animation.
* Default is {@code null}, which means linear.
*/
public Interpolator getInterpolator() {
return interpolator;
}
/**
* Sets the interpolator for the animation.
*
* @throws IllegalStateException if animation is running
*/
public void setInterpolator( Interpolator interpolator ) {
throwExceptionIfRunning();
this.interpolator = interpolator;
}
/**
* Adds a target to the animation that receives timing events.
*
* @param target the target that receives timing events
*/
public void addTarget( TimingTarget target ) {
if( target == null )
return;
synchronized( targets ) {
if( !targets.contains( target ) )
targets.add( target );
}
}
/**
* Removes a target from the animation.
*
* @param target the target that should be removed
*/
public void removeTarget( TimingTarget target ) {
synchronized( targets ) {
targets.remove( target );
}
}
/**
* Starts the animation.
*
* @throws IllegalStateException if animation is running
*/
public void start() {
throwExceptionIfRunning();
running = true;
hasBegun = false;
timeToStop = false;
startTime = System.nanoTime() / 1000000;
if( timer == null ) {
timer = new Timer( resolution, e -> {
if( !hasBegun ) {
begin();
hasBegun = true;
}
timingEvent( getTimingFraction() );
} );
} else
timer.setDelay( resolution );
timer.setInitialDelay( 0 );
timer.start();
}
/**
* Stops the animation before it normally ends.
* Invokes {@link TimingTarget#end()} on timing targets.
*/
public void stop() {
stop( false );
}
/**
* Cancels the animation before it normally ends.
* Does not invoke {@link TimingTarget#end()} on timing targets.
*/
public void cancel() {
stop( true );
}
private void stop( boolean cancel ) {
if( !running )
return;
if( timer != null )
timer.stop();
if( !cancel )
end();
running = false;
timeToStop = false;
}
/**
* Restarts the animation.
* Invokes {@link #cancel()} and {@link #start()}.
*/
public void restart() {
cancel();
start();
}
/**
* Returns whether this animation is running.
*/
public boolean isRunning() {
return running;
}
private float getTimingFraction() {
long currentTime = System.nanoTime() / 1000000;
long elapsedTime = currentTime - startTime;
timeToStop = (elapsedTime >= duration);
float fraction = clampFraction( (float) elapsedTime / duration );
if( interpolator != null )
fraction = clampFraction( interpolator.interpolate( fraction ) );
return fraction;
}
private float clampFraction( float fraction ) {
if( fraction < 0 )
return 0;
if( fraction > 1 )
return 1;
return fraction;
}
private void timingEvent( float fraction ) {
synchronized( targets ) {
for( TimingTarget target : targets )
target.timingEvent( fraction );
}
if( timeToStop )
stop();
}
private void begin() {
synchronized( targets ) {
for( TimingTarget target : targets )
target.begin();
}
}
private void end() {
synchronized( targets ) {
for( TimingTarget target : targets )
target.end();
}
if( endRunnable != null )
endRunnable.run();
}
private void throwExceptionIfRunning() {
if( isRunning() )
throw new IllegalStateException();
}
//---- interface TimingTarget ---------------------------------------------
/**
* Animation callbacks.
*/
@FunctionalInterface
public interface TimingTarget {
/**
* Invoked multiple times while animation is running.
*
* @param fraction the percent (0 to 1) elapsed of the current animation cycle
*/
void timingEvent( float fraction );
/**
* Invoked when the animation begins.
*/
default void begin() {}
/**
* Invoked when the animation ends.
*/
default void end() {}
}
//---- interface Interpolator ---------------------------------------------
/**
* Interpolator used by animation to change timing fraction. E.g. for easing.
*/
@FunctionalInterface
public interface Interpolator {
/**
* Interpolate the given fraction and returns a new fraction.
* Both fractions are in range [0, 1].
*
* @param fraction the percent (0 to 1) elapsed of the current animation cycle
* @return new fraction in range [0, 1]
*/
float interpolate( float fraction );
}
}

View File

@@ -28,11 +28,12 @@ public class ColorFunctions
public static Color applyFunctions( Color color, ColorFunction... functions ) { public static Color applyFunctions( Color color, ColorFunction... functions ) {
float[] hsl = HSLColor.fromRGB( color ); float[] hsl = HSLColor.fromRGB( color );
float alpha = color.getAlpha() / 255f; float alpha = color.getAlpha() / 255f;
float[] hsla = { hsl[0], hsl[1], hsl[2], alpha * 100 };
for( ColorFunction function : functions ) for( ColorFunction function : functions )
function.apply( hsl ); function.apply( hsla );
return HSLColor.toRGB( hsl, alpha ); return HSLColor.toRGB( hsla[0], hsla[1], hsla[2], hsla[3] / 100 );
} }
public static float clamp( float value ) { public static float clamp( float value ) {
@@ -43,16 +44,48 @@ public class ColorFunctions
: value); : value);
} }
/**
* Returns a color that is a mixture of two colors.
*
* @param color1 first color
* @param color2 second color
* @param weight the weight (in range 0-1) to mix the two colors.
* Larger weight uses more of first color, smaller weight more of second color.
* @return mixture of colors
*/
public static Color mix( Color color1, Color color2, float weight ) {
if( weight >= 1 )
return color1;
if( weight <= 0 )
return color2;
int r1 = color1.getRed();
int g1 = color1.getGreen();
int b1 = color1.getBlue();
int a1 = color1.getAlpha();
int r2 = color2.getRed();
int g2 = color2.getGreen();
int b2 = color2.getBlue();
int a2 = color2.getAlpha();
return new Color(
Math.round( r2 + ((r1 - r2) * weight) ),
Math.round( g2 + ((g1 - g2) * weight) ),
Math.round( b2 + ((b1 - b2) * weight) ),
Math.round( a2 + ((a1 - a2) * weight) ) );
}
//---- interface ColorFunction -------------------------------------------- //---- interface ColorFunction --------------------------------------------
public interface ColorFunction { public interface ColorFunction {
void apply( float[] hsl ); void apply( float[] hsla );
} }
//---- class HSLIncreaseDecrease ------------------------------------------ //---- class HSLIncreaseDecrease ------------------------------------------
/** /**
* Increase or decrease hue, saturation or luminance of a color in the HSL color space * Increase or decrease hue, saturation, luminance or alpha of a color in the HSL color space
* by an absolute or relative amount. * by an absolute or relative amount.
*/ */
public static class HSLIncreaseDecrease public static class HSLIncreaseDecrease
@@ -75,18 +108,45 @@ public class ColorFunctions
} }
@Override @Override
public void apply( float[] hsl ) { public void apply( float[] hsla ) {
float amount2 = increase ? amount : -amount; float amount2 = increase ? amount : -amount;
amount2 = autoInverse && shouldInverse( hsl ) ? -amount2 : amount2;
hsl[hslIndex] = clamp( relative if( hslIndex == 0 ) {
? (hsl[hslIndex] * ((100 + amount2) / 100)) // hue is range 0-360
: (hsl[hslIndex] + amount2) ); hsla[0] = (hsla[0] + amount2) % 360;
return;
}
amount2 = autoInverse && shouldInverse( hsla ) ? -amount2 : amount2;
hsla[hslIndex] = clamp( relative
? (hsla[hslIndex] * ((100 + amount2) / 100))
: (hsla[hslIndex] + amount2) );
} }
protected boolean shouldInverse( float[] hsl ) { protected boolean shouldInverse( float[] hsla ) {
return increase return increase
? hsl[hslIndex] >= 50 ? hsla[hslIndex] > 65
: hsl[hslIndex] < 50; : hsla[hslIndex] < 35;
}
}
//---- class HSLIncreaseDecrease ------------------------------------------
/**
* Set the alpha of a color.
*/
public static class Fade
implements ColorFunction
{
public final float amount;
public Fade( float amount ) {
this.amount = amount;
}
@Override
public void apply( float[] hsla ) {
hsla[3] = clamp( amount );
} }
} }
} }

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
/**
* An interpolator for {@link Animator} that uses a cubic bezier curve.
*
* @author Karl Tauber
*/
public class CubicBezierEasing
implements Animator.Interpolator
{
/**
* Standard easing as specified in Material design (0.4, 0, 0.2, 1).
*
* @see <a href="https://material.io/design/motion/speed.html#easing">https://material.io/design/motion/speed.html#easing</a>
*/
public static final CubicBezierEasing STANDARD_EASING = new CubicBezierEasing( 0.4f, 0f, 0.2f, 1f );
// common cubic-bezier easing functions (same as in CSS)
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function
public static final CubicBezierEasing EASE = new CubicBezierEasing( 0.25f, 0.1f, 0.25f, 1f );
public static final CubicBezierEasing EASE_IN = new CubicBezierEasing( 0.42f, 0f, 1f, 1f );
public static final CubicBezierEasing EASE_IN_OUT = new CubicBezierEasing( 0.42f, 0f, 0.58f, 1f );
public static final CubicBezierEasing EASE_OUT = new CubicBezierEasing( 0f, 0f, 0.58f, 1f );
private final float x1;
private final float y1;
private final float x2;
private final float y2;
/**
* Creates a cubic bezier easing interpolator with the given control points.
* The start point of the cubic bezier curve is always 0,0 and the end point 1,1.
*
* @param x1 the x coordinate of the first control point in range [0, 1]
* @param y1 the y coordinate of the first control point in range [0, 1]
* @param x2 the x coordinate of the second control point in range [0, 1]
* @param y2 the y coordinate of the second control point in range [0, 1]
*/
public CubicBezierEasing( float x1, float y1, float x2, float y2 ) {
if( x1 < 0 || x1 > 1 || y1 < 0 || y1 > 1 ||
x2 < 0 || x2 > 1 || y2 < 0 || y2 > 1 )
throw new IllegalArgumentException( "control points must be in range [0, 1]");
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
@Override
public float interpolate( float fraction ) {
if( fraction <= 0 || fraction >= 1 )
return fraction;
// use binary search
float low = 0;
float high = 1;
while( true ) {
float mid = (low + high) / 2;
float estimate = cubicBezier( mid, x1, x2 );
if( Math.abs( fraction - estimate ) < 0.0005f )
return cubicBezier( mid, y1, y2 );
if( estimate < fraction )
low = mid;
else
high = mid;
}
}
/**
* Computes the x or y point on a cubic bezier curve for a given t value.
*
* https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
*
* The general cubic bezier formula is:
* x = b0*x0 + b1*x1 + b2*x2 + b3*x3
* y = b0*y0 + b1*y1 + b2*y2 + b3*y3
*
* where:
* b0 = (1-t)^3
* b1 = 3 * t * (1-t)^2
* b2 = 3 * t^2 * (1-t)
* b3 = t^3
*
* x0,y0 is always 0,0 and x3,y3 is 1,1, so we can simplify to:
* x = b1*x1 + b2*x2 + b3
* y = b1*x1 + b2*x2 + b3
*/
private static float cubicBezier( float t, float xy1, float xy2 ) {
float invT = (1 - t);
float b1 = 3 * t * (invT * invT);
float b2 = 3 * (t * t) * invT;
float b3 = t * t * t;
return (b1 * xy1) + (b2 * xy2) + b3;
}
}

View File

@@ -117,10 +117,10 @@ public class HiDPIUtils
* This methods computes a correction value for the Y position. * This methods computes a correction value for the Y position.
*/ */
public static float computeTextYCorrection( Graphics2D g ) { public static float computeTextYCorrection( Graphics2D g ) {
if( !useTextYCorrection() || !SystemInfo.IS_WINDOWS ) if( !useTextYCorrection() || !SystemInfo.isWindows )
return 0; return 0;
if( !SystemInfo.IS_JAVA_9_OR_LATER ) if( !SystemInfo.isJava_9_orLater )
return UIScale.getUserScaleFactor() > 1 ? -UIScale.scale( 0.625f ) : 0; return UIScale.getUserScaleFactor() > 1 ? -UIScale.scale( 0.625f ) : 0;
AffineTransform t = g.getTransform(); AffineTransform t = g.getTransform();

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.util; package com.formdev.flatlaf.util;
import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -27,7 +28,7 @@ import com.formdev.flatlaf.FlatLaf;
/** /**
* Provides Java version compatibility methods. * Provides Java version compatibility methods.
* * <p>
* WARNING: This is private API and may change. * WARNING: This is private API and may change.
* *
* @author Karl Tauber * @author Karl Tauber
@@ -35,10 +36,12 @@ import com.formdev.flatlaf.FlatLaf;
public class JavaCompatibility public class JavaCompatibility
{ {
private static Method drawStringUnderlineCharAtMethod; private static Method drawStringUnderlineCharAtMethod;
private static Method getClippedStringMethod;
/** /**
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c, * Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
* Graphics g, String text, int underlinedIndex, int x, int y ) * Graphics g, String text, int underlinedIndex, int x, int y )
* <br>
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c, * Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.drawStringUnderlineCharAt( JComponent c,
* Graphics2D g, String string, int underlinedIndex, float x, float y ) * Graphics2D g, String string, int underlinedIndex, float x, float y )
*/ */
@@ -48,10 +51,10 @@ public class JavaCompatibility
synchronized( JavaCompatibility.class ) { synchronized( JavaCompatibility.class ) {
if( drawStringUnderlineCharAtMethod == null ) { if( drawStringUnderlineCharAtMethod == null ) {
try { try {
Class<?> cls = Class.forName( SystemInfo.IS_JAVA_9_OR_LATER Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
? "javax.swing.plaf.basic.BasicGraphicsUtils" ? "javax.swing.plaf.basic.BasicGraphicsUtils"
: "sun.swing.SwingUtilities2" ); : "sun.swing.SwingUtilities2" );
drawStringUnderlineCharAtMethod = cls.getMethod( "drawStringUnderlineCharAt", SystemInfo.IS_JAVA_9_OR_LATER drawStringUnderlineCharAtMethod = cls.getMethod( "drawStringUnderlineCharAt", SystemInfo.isJava_9_orLater
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class } ? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } ); : new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
} catch( Exception ex ) { } catch( Exception ex ) {
@@ -62,7 +65,7 @@ public class JavaCompatibility
} }
try { try {
if( SystemInfo.IS_JAVA_9_OR_LATER ) if( SystemInfo.isJava_9_orLater )
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, (float) x, (float) y ); drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, (float) x, (float) y );
else else
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y ); drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
@@ -71,4 +74,37 @@ public class JavaCompatibility
throw new RuntimeException( ex ); throw new RuntimeException( ex );
} }
} }
/**
* Java 8: sun.swing.SwingUtilities2.clipStringIfNecessary( JComponent c,
* FontMetrics fm, String string, int availTextWidth )
* <br>
* Java 9: javax.swing.plaf.basic.BasicGraphicsUtils.getClippedString( JComponent c,
* FontMetrics fm, String string, int availTextWidth )
*/
public static String getClippedString( JComponent c, FontMetrics fm, String string, int availTextWidth ) {
synchronized( JavaCompatibility.class ) {
if( getClippedStringMethod == null ) {
try {
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
? "javax.swing.plaf.basic.BasicGraphicsUtils"
: "sun.swing.SwingUtilities2" );
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
? "getClippedString"
: "clipStringIfNecessary",
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
} catch( Exception ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
throw new RuntimeException( ex );
}
}
}
try {
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
throw new RuntimeException( ex );
}
}
} }

View File

@@ -16,11 +16,20 @@
package com.formdev.flatlaf.util; package com.formdev.flatlaf.util;
import java.awt.Dimension;
import java.awt.Image; import java.awt.Image;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
//
// NOTE:
// This implementation is for Java 8 only.
// There is also a variant for Java 9 and later.
//
// Make sure that the API is in sync.
//
/** /**
* Support for multi-resolution images available since Java 9. * Support for multi-resolution images available since Java 9.
* *
@@ -28,26 +37,86 @@ import java.util.function.Function;
*/ */
public class MultiResolutionImageSupport public class MultiResolutionImageSupport
{ {
/**
* Checks whether multi-resolution image support is available.
*
* @return {@code true} when running on Java 9 or later; {@code false} on Java 8
*/
public static boolean isAvailable() { public static boolean isAvailable() {
return false; return false;
} }
/**
* Checks whether the given image is a multi-resolution image that implements
* the interface {@code java.awt.image.MultiResolutionImage}.
*/
public static boolean isMultiResolutionImage( Image image ) { public static boolean isMultiResolutionImage( Image image ) {
return false; return false;
} }
/**
* Creates a multi-resolution image from the given resolution variants.
*
* @param baseImageIndex index of the base image in the resolution variants array
* @param resolutionVariants image resolution variants (sorted by size; smallest first)
* @return a multi-resolution image on Java 9 or later; the base image on Java 8
*/
public static Image create( int baseImageIndex, Image... resolutionVariants ) { public static Image create( int baseImageIndex, Image... resolutionVariants ) {
return resolutionVariants[baseImageIndex]; return resolutionVariants[baseImageIndex];
} }
/**
* Creates a multi-resolution image for the given dimensions.
* Initially the image does not contain any image data.
* The real images are created (and cached) on demand by invoking the given producer function.
* <p>
* The given dimensions array is only used for {@link #getResolutionVariants(Image)}.
* The producer function may be invoked with any dimension (that is not contained in
* dimensions array) and is expected to produce a image for the passed in dimension.
*
* @param baseImageIndex index of the base image in the dimensions array
* @param dimensions dimensions of resolution variants (sorted by size; smallest first)
* @param producer producer function that creates a real image for the requested size
* @return a multi-resolution image on Java 9 or later; the base image on Java 8
*/
public static Image create( int baseImageIndex, Dimension[] dimensions, Function<Dimension, Image> producer ) {
return producer.apply( dimensions[baseImageIndex] );
}
/**
* Creates a multi-resolution image that maps images from another multi-resolution image
* using the given mapper function.
* <p>
* Can be used to apply filter to multi-resolution images on demand.
* E.g. passed in image is for "enabled" state and mapper function creates images
* for "disabled" state.
*
* @param image a multi-resolution image that is mapped using the given mapper function
* @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying an filter)
* @return a multi-resolution image on Java 9 or later; a mapped image on Java 8
*/
public static Image map( Image image, Function<Image, Image> mapper ) { public static Image map( Image image, Function<Image, Image> mapper ) {
return mapper.apply( image ); return mapper.apply( image );
} }
/**
* Get the image variant that best matches the given width and height.
* <p>
* If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariant(destImageWidth, destImageHeight)}.
* Otherwise returns the given image.
*/
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) { public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return image; return image;
} }
/**
* Get a list of all resolution variants.
* <p>
* If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariants()}.
* Otherwise returns a list containing only the given image.
*/
public static List<Image> getResolutionVariants( Image image ) { public static List<Image> getResolutionVariants( Image image ) {
return Collections.singletonList( image ); return Collections.singletonList( image );
} }

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.border.EmptyBorder;
/**
* Empty border that scales insets.
*
* @author Karl Tauber
*/
public class ScaledEmptyBorder
extends EmptyBorder
{
public ScaledEmptyBorder( int top, int left, int bottom, int right ) {
super( top, left, bottom, right );
}
public ScaledEmptyBorder( Insets insets ) {
super( insets );
}
@Override
public Insets getBorderInsets() {
return new Insets( scale( top ), scale( left ), scale( bottom ), scale( right ) );
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets.left = scale( left );
insets.top = scale( top );
insets.right = scale( right );
insets.bottom = scale( bottom );
return insets;
}
}

View File

@@ -26,6 +26,13 @@ import java.util.List;
*/ */
public class StringUtils public class StringUtils
{ {
/**
* Returns {@code true} if given string is {@code null} or length is zero.
*/
public static boolean isEmpty( String string ) {
return string == null || string.isEmpty();
}
public static String removeLeading( String string, String leading ) { public static String removeLeading( String string, String leading ) {
return string.startsWith( leading ) return string.startsWith( leading )
? string.substring( leading.length() ) ? string.substring( leading.length() )

View File

@@ -27,55 +27,57 @@ import java.util.StringTokenizer;
public class SystemInfo public class SystemInfo
{ {
// platforms // platforms
public static final boolean IS_WINDOWS; public static final boolean isWindows;
public static final boolean IS_MAC; public static final boolean isMacOS;
public static final boolean IS_LINUX; public static final boolean isLinux;
// OS versions // OS versions
public static final boolean IS_WINDOWS_10_OR_LATER; public static final long osVersion;
public static final boolean IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER; public static final boolean isWindows_10_orLater;
public static final boolean IS_MAC_OS_10_14_MOJAVE; public static final boolean isMacOS_10_11_ElCapitan_orLater;
public static final boolean IS_MAC_OS_10_15_CATALINA_OR_LATER; public static final boolean isMacOS_10_14_Mojave_orLater;
public static final boolean isMacOS_10_15_Catalina_orLater;
// Java versions // Java versions
public static final boolean IS_JAVA_9_OR_LATER; public static final long javaVersion;
public static final boolean IS_JAVA_11_OR_LATER; public static final boolean isJava_9_orLater;
public static final boolean IS_JAVA_15_OR_LATER; public static final boolean isJava_11_orLater;
public static final boolean isJava_15_orLater;
// Java VMs // Java VMs
public static final boolean IS_JETBRAINS_JVM; public static final boolean isJetBrainsJVM;
public static final boolean IS_JETBRAINS_JVM_11_OR_LATER; public static final boolean isJetBrainsJVM_11_orLater;
// UI toolkits // UI toolkits
public static final boolean IS_KDE; public static final boolean isKDE;
static { static {
// platforms // platforms
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH ); String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
IS_WINDOWS = osName.startsWith( "windows" ); isWindows = osName.startsWith( "windows" );
IS_MAC = osName.startsWith( "mac" ); isMacOS = osName.startsWith( "mac" );
IS_LINUX = osName.startsWith( "linux" ); isLinux = osName.startsWith( "linux" );
// OS versions // OS versions
long osVersion = scanVersion( System.getProperty( "os.version" ) ); osVersion = scanVersion( System.getProperty( "os.version" ) );
IS_WINDOWS_10_OR_LATER = (IS_WINDOWS && osVersion >= toVersion( 10, 0, 0, 0 )); isWindows_10_orLater = (isWindows && osVersion >= toVersion( 10, 0, 0, 0 ));
IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER = (IS_MAC && osVersion >= toVersion( 10, 11, 0, 0 )); isMacOS_10_11_ElCapitan_orLater = (isMacOS && osVersion >= toVersion( 10, 11, 0, 0 ));
IS_MAC_OS_10_14_MOJAVE = (IS_MAC && osVersion >= toVersion( 10, 14, 0, 0 )); isMacOS_10_14_Mojave_orLater = (isMacOS && osVersion >= toVersion( 10, 14, 0, 0 ));
IS_MAC_OS_10_15_CATALINA_OR_LATER = (IS_MAC && osVersion >= toVersion( 10, 15, 0, 0 )); isMacOS_10_15_Catalina_orLater = (isMacOS && osVersion >= toVersion( 10, 15, 0, 0 ));
// Java versions // Java versions
long javaVersion = scanVersion( System.getProperty( "java.version" ) ); javaVersion = scanVersion( System.getProperty( "java.version" ) );
IS_JAVA_9_OR_LATER = (javaVersion >= toVersion( 9, 0, 0, 0 )); isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
IS_JAVA_11_OR_LATER = (javaVersion >= toVersion( 11, 0, 0, 0 )); isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 ));
IS_JAVA_15_OR_LATER = (javaVersion >= toVersion( 15, 0, 0, 0 )); isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 ));
// Java VMs // Java VMs
IS_JETBRAINS_JVM = System.getProperty( "java.vm.vendor", "Unknown" ) isJetBrainsJVM = System.getProperty( "java.vm.vendor", "Unknown" )
.toLowerCase( Locale.ENGLISH ).contains( "jetbrains" ); .toLowerCase( Locale.ENGLISH ).contains( "jetbrains" );
IS_JETBRAINS_JVM_11_OR_LATER = IS_JETBRAINS_JVM && IS_JAVA_11_OR_LATER; isJetBrainsJVM_11_orLater = isJetBrainsJVM && isJava_11_orLater;
// UI toolkits // UI toolkits
IS_KDE = (IS_LINUX && System.getenv( "KDE_FULL_SESSION" ) != null); isKDE = (isLinux && System.getenv( "KDE_FULL_SESSION" ) != null);
} }
public static long scanVersion( String version ) { public static long scanVersion( String version ) {

View File

@@ -22,6 +22,7 @@ import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration; import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Toolkit;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
@@ -90,10 +91,10 @@ public class UIScale
jreHiDPI = false; jreHiDPI = false;
if( SystemInfo.IS_JAVA_9_OR_LATER ) { if( SystemInfo.isJava_9_orLater ) {
// Java 9 and later supports per-monitor scaling // Java 9 and later supports per-monitor scaling
jreHiDPI = true; jreHiDPI = true;
} else if( SystemInfo.IS_JETBRAINS_JVM ) { } else if( SystemInfo.isJetBrainsJVM ) {
// IntelliJ IDEA ships its own JetBrains Java 8 JRE that may supports per-monitor scaling // IntelliJ IDEA ships its own JetBrains Java 8 JRE that may supports per-monitor scaling
// see com.intellij.ui.JreHiDpiUtil.isJreHiDPIEnabled() // see com.intellij.ui.JreHiDpiUtil.isJreHiDPIEnabled()
try { try {
@@ -112,7 +113,7 @@ public class UIScale
} }
public static double getSystemScaleFactor( Graphics2D g ) { public static double getSystemScaleFactor( Graphics2D g ) {
return isSystemScalingEnabled() ? g.getDeviceConfiguration().getDefaultTransform().getScaleX() : 1; return isSystemScalingEnabled() ? getSystemScaleFactor( g.getDeviceConfiguration() ) : 1;
} }
public static double getSystemScaleFactor( GraphicsConfiguration gc ) { public static double getSystemScaleFactor( GraphicsConfiguration gc ) {
@@ -162,6 +163,13 @@ public class UIScale
if( !isUserScalingEnabled() ) if( !isUserScalingEnabled() )
return; return;
// apply custom scale factor specified in system property "flatlaf.uiScale"
float customScaleFactor = getCustomScaleFactor();
if( customScaleFactor > 0 ) {
setUserScaleFactor( customScaleFactor );
return;
}
// use font size to calculate scale factor (instead of DPI) // use font size to calculate scale factor (instead of DPI)
// because even if we are on a HiDPI display it is not sure // because even if we are on a HiDPI display it is not sure
// that a larger font size is set by the current LaF // that a larger font size is set by the current LaF
@@ -170,33 +178,70 @@ public class UIScale
if( font == null ) if( font == null )
font = UIManager.getFont( "Label.font" ); font = UIManager.getFont( "Label.font" );
setUserScaleFactor( computeScaleFactor( font ) ); float newScaleFactor;
if( SystemInfo.isWindows ) {
// Special handling for Windows to be compatible with OS scaling,
// which distinguish between "screen scaling" and "text scaling".
// - Windows "screen scaling" scales everything (text, icon, gaps, etc)
// and may have different scaling factors for each screen.
// - Windows "text scaling" increases only the font size, but on all screens.
//
// Both can be changed by the user in the Windows 10 Settings:
// - Settings > Display > Scale and layout
// - Settings > Ease of Access > Display > Make text bigger (100% - 225%)
if( font instanceof UIResource ) {
if( isSystemScalingEnabled() ) {
// Do not apply own scaling if the JRE scales using Windows screen scale factor.
// If user increases font size in Windows 10 settings, desktop property
// "win.messagebox.font" is changed and FlatLaf uses the larger font.
newScaleFactor = 1;
} else {
// If the JRE does not scale (Java 8), the size of the UI font
// (usually from desktop property "win.messagebox.font")
// combines the Windows screen and text scale factors.
// But the font in desktop property "win.defaultGUI.font" is only
// scaled with the Windows screen scale factor. So use it to compute
// our scale factor that is equal to Windows screen scale factor.
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.defaultGUI.font" );
newScaleFactor = computeScaleFactor( (winFont != null) ? winFont : font );
}
} else {
// If font was explicitly set from outside (is not a UIResource)
// use it to compute scale factor. This allows applications to
// use custom fonts (e.g. that the user can change in UI) and
// get scaling if a larger font size is used.
// E.g. FlatLaf Demo supports increasing font size in "Font" menu and UI scales.
newScaleFactor = computeScaleFactor( font );
}
} else
newScaleFactor = computeScaleFactor( font );
setUserScaleFactor( newScaleFactor );
} }
private static float computeScaleFactor( Font font ) { private static float computeScaleFactor( Font font ) {
// default font size // default font size
float fontSizeDivider = 12f; float fontSizeDivider = 12f;
if( SystemInfo.IS_WINDOWS ) { if( SystemInfo.isWindows ) {
// Windows LaF uses Tahoma font rather than the actual Windows system font (Segoe UI), // Windows LaF uses Tahoma font rather than the actual Windows system font (Segoe UI),
// and its size is always ca. 10% smaller than the actual system font size. // and its size is always ca. 10% smaller than the actual system font size.
// Tahoma 11 is used at 100% // Tahoma 11 is used at 100%
if( "Tahoma".equals( font.getFamily() ) ) if( "Tahoma".equals( font.getFamily() ) )
fontSizeDivider = 11f; fontSizeDivider = 11f;
} else if( SystemInfo.IS_MAC ) { } else if( SystemInfo.isMacOS ) {
// default font size on macOS is 13 // default font size on macOS is 13
fontSizeDivider = 13f; fontSizeDivider = 13f;
} else if( SystemInfo.IS_LINUX ) { } else if( SystemInfo.isLinux ) {
// default font size for Unity and Gnome is 15 and for KDE it is 13 // default font size for Unity and Gnome is 15 and for KDE it is 13
fontSizeDivider = SystemInfo.IS_KDE ? 13f : 15f; fontSizeDivider = SystemInfo.isKDE ? 13f : 15f;
} }
return font.getSize() / fontSizeDivider; return font.getSize() / fontSizeDivider;
} }
private static boolean isUserScalingEnabled() { private static boolean isUserScalingEnabled() {
// same as in IntelliJ IDEA return FlatSystemProperties.getBoolean( FlatSystemProperties.UI_SCALE_ENABLED, true );
return FlatSystemProperties.getBoolean( "hidpi", true );
} }
/** /**
@@ -204,8 +249,10 @@ public class UIScale
* to the given font. * to the given font.
*/ */
public static FontUIResource applyCustomScaleFactor( FontUIResource font ) { public static FontUIResource applyCustomScaleFactor( FontUIResource font ) {
String uiScale = System.getProperty( FlatSystemProperties.UI_SCALE ); if( !isUserScalingEnabled() )
float scaleFactor = parseScaleFactor( uiScale ); return font;
float scaleFactor = getCustomScaleFactor();
if( scaleFactor <= 0 ) if( scaleFactor <= 0 )
return font; return font;
@@ -217,6 +264,13 @@ public class UIScale
return new FontUIResource( font.deriveFont( (float) newFontSize ) ); return new FontUIResource( font.deriveFont( (float) newFontSize ) );
} }
/**
* Get custom scale factor specified in system property "flatlaf.uiScale".
*/
private static float getCustomScaleFactor() {
return parseScaleFactor( System.getProperty( FlatSystemProperties.UI_SCALE ) );
}
/** /**
* Similar to sun.java2d.SunGraphicsEnvironment.getScaleFactor(String) * Similar to sun.java2d.SunGraphicsEnvironment.getScaleFactor(String)
*/ */

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.util; package com.formdev.flatlaf.util;
import java.awt.Dimension;
import java.awt.Image; import java.awt.Image;
import java.awt.image.AbstractMultiResolutionImage; import java.awt.image.AbstractMultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage; import java.awt.image.BaseMultiResolutionImage;
@@ -27,6 +28,14 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
//
// NOTE:
// This implementation is for Java 9 and later.
// There is also a variant for Java 8.
//
// Make sure that the API is in sync.
//
/** /**
* Support for multi-resolution images available since Java 9. * Support for multi-resolution images available since Java 9.
* *
@@ -46,6 +55,10 @@ public class MultiResolutionImageSupport
return new BaseMultiResolutionImage( baseImageIndex, resolutionVariants ); return new BaseMultiResolutionImage( baseImageIndex, resolutionVariants );
} }
public static Image create( int baseImageIndex, Dimension[] dimensions, Function<Dimension, Image> producer ) {
return new ProducerMultiResolutionImage( dimensions, producer );
}
public static Image map( Image image, Function<Image, Image> mapper ) { public static Image map( Image image, Function<Image, Image> mapper ) {
return image instanceof MultiResolutionImage return image instanceof MultiResolutionImage
? new MappedMultiResolutionImage( image, mapper ) ? new MappedMultiResolutionImage( image, mapper )
@@ -66,6 +79,9 @@ public class MultiResolutionImageSupport
//---- class MappedMultiResolutionImage ----------------------------------- //---- class MappedMultiResolutionImage -----------------------------------
/**
* A multi-resolution image implementation that maps images on demand for requested sizes.
*/
private static class MappedMultiResolutionImage private static class MappedMultiResolutionImage
extends AbstractMultiResolutionImage extends AbstractMultiResolutionImage
{ {
@@ -102,8 +118,52 @@ public class MultiResolutionImageSupport
private Image mapAndCacheImage( Image image ) { private Image mapAndCacheImage( Image image ) {
return cache.computeIfAbsent( image, img -> { return cache.computeIfAbsent( image, img -> {
// using ImageIcon here makes sure that the image is loaded
return new ImageIcon( mapper.apply( img ) ).getImage(); return new ImageIcon( mapper.apply( img ) ).getImage();
} ); } );
} }
} }
//---- class ProducerMultiResolutionImage ---------------------------------
/**
* A multi-resolution image implementation that produces images on demand for requested sizes.
*/
private static class ProducerMultiResolutionImage
extends AbstractMultiResolutionImage
{
private final Dimension[] dimensions;
private final Function<Dimension, Image> producer;
private final IdentityHashMap<Dimension, Image> cache = new IdentityHashMap<>();
ProducerMultiResolutionImage( Dimension[] dimensions, Function<Dimension, Image> producer ) {
this.dimensions = dimensions;
this.producer = producer;
}
@Override
public Image getResolutionVariant( double destImageWidth, double destImageHeight ) {
return produceAndCacheImage( new Dimension( (int) destImageWidth, (int) destImageHeight ) );
}
@Override
public List<Image> getResolutionVariants() {
List<Image> mappedVariants = new ArrayList<>();
for( Dimension size : dimensions )
mappedVariants.add( produceAndCacheImage( size ) );
return mappedVariants;
}
@Override
protected Image getBaseImage() {
return produceAndCacheImage( dimensions[0] );
}
private Image produceAndCacheImage( Dimension size ) {
return cache.computeIfAbsent( size, size2 -> {
// using ImageIcon here makes sure that the image is loaded
return new ImageIcon( producer.apply( size2 ) ).getImage();
} );
}
}
} }

View File

@@ -20,23 +20,23 @@
#---- Button ---- #---- Button ----
Button.default.boldText=true Button.default.boldText = true
#---- Component ---- #---- Component ----
Component.focusWidth=2 Component.focusWidth = 2
Component.innerFocusWidth=0 Component.innerFocusWidth = 0
Component.innerOutlineWidth=0 Component.innerOutlineWidth = 0
Component.arrowType=triangle Component.arrowType = triangle
#---- ProgressBar ---- #---- ProgressBar ----
ProgressBar.foreground=#a0a0a0 ProgressBar.foreground = #a0a0a0
ProgressBar.selectionForeground=@background ProgressBar.selectionForeground = @background
#---- RadioButton ---- #---- RadioButton ----
RadioButton.icon.centerDiameter=5 RadioButton.icon.centerDiameter = 5

View File

@@ -20,308 +20,293 @@
#---- variables ---- #---- variables ----
@background=#3c3f41 @background = #3c3f41
@foreground=#bbbbbb @foreground = #bbb
@selectionBackground=#4B6EAF @selectionBackground = #4B6EAF
@selectionForeground=@foreground @selectionForeground = @foreground
@selectionInactiveBackground=#0D293E @selectionInactiveBackground = #0D293E
@selectionInactiveForeground=@foreground @selectionInactiveForeground = @foreground
@disabledText=#777777 @disabledText = #888
@textComponentBackground=#45494A @textComponentBackground = #45494A
@menuBackground=darken(@background,5%) @menuBackground = darken(@background,5%)
@menuHoverBackground=lighten(@menuBackground,10%,derived) @menuHoverBackground = lighten(@menuBackground,10%,derived)
@menuCheckBackground=lighten(@menuBackground,10%,derived) @menuCheckBackground = darken(@selectionBackground,10%,derived noAutoInverse)
@menuCheckHoverBackground=lighten(@menuBackground,20%,derived) @menuAcceleratorForeground = darken(@foreground,15%)
@cellFocusColor=#000000 @menuAcceleratorSelectionForeground = @selectionForeground
@icon=#adadad @cellFocusColor = #000
@icon = #adadad
# for buttons within components (e.g. combobox or spinner)
@buttonArrowColor = #9A9DA1
@buttonDisabledArrowColor = darken(@buttonArrowColor,25%)
@buttonHoverArrowColor = lighten(@buttonArrowColor,10%,derived noAutoInverse)
@buttonPressedArrowColor = lighten(@buttonArrowColor,20%,derived noAutoInverse)
# Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors) # Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors)
@dropCellBackground=darken(List.selectionBackground,10%,lazy) @dropCellBackground = darken(List.selectionBackground,10%,lazy)
@dropCellForeground=lazy(List.selectionForeground) @dropCellForeground = lazy(List.selectionForeground)
@dropLineColor=lighten(List.selectionBackground,10%,lazy) @dropLineColor = lighten(List.selectionBackground,10%,lazy)
@dropLineShortColor=lighten(List.selectionBackground,30%,lazy) @dropLineShortColor = lighten(List.selectionBackground,30%,lazy)
#---- globals ----
*.background=@background
*.foreground=@foreground
*.textBackground=@background
*.textForeground=@foreground
*.caretForeground=@foreground
*.inactiveBackground=@background
*.inactiveForeground=@foreground
*.selectionBackground=@selectionBackground
*.selectionForeground=@selectionForeground
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=darken(@foreground,15%)
*.acceleratorSelectionForeground=@selectionForeground
#---- system colors ---- #---- system colors ----
activeCaption=#434E60 activeCaption = #434E60
inactiveCaption=#393C3D inactiveCaption = #393C3D
controlHighlight=darken($controlShadow,20%) controlHighlight = darken($controlShadow,20%)
controlLtHighlight=darken($controlShadow,25%) controlLtHighlight = darken($controlShadow,25%)
controlDkShadow=lighten($controlShadow,10%) controlDkShadow = lighten($controlShadow,10%)
#---- Button ---- #---- Button ----
Button.background=#4c5052 Button.background = #4c5052
Button.hoverBackground=lighten($Button.background,3%,derived) Button.hoverBackground = lighten($Button.background,3%,derived)
Button.pressedBackground=lighten($Button.background,6%,derived) Button.pressedBackground = lighten($Button.background,6%,derived)
Button.selectedBackground = lighten($Button.background,10%,derived)
Button.selectedForeground = @foreground
Button.disabledSelectedBackground = lighten($Button.background,3%,derived)
Button.borderColor=#5e6060 Button.borderColor = #5e6060
Button.disabledBorderColor=#5e6060 Button.disabledBorderColor = $Button.borderColor
Button.focusedBorderColor=#466d94 Button.focusedBorderColor = $Component.focusedBorderColor
Button.hoverBorderColor=$Button.focusedBorderColor Button.hoverBorderColor = $Button.focusedBorderColor
Button.default.background=#365880 Button.default.background = #365880
Button.default.foreground=#bbbbbb Button.default.foreground = #bbb
Button.default.hoverBackground=lighten($Button.default.background,3%,derived) Button.default.hoverBackground = lighten($Button.default.background,3%,derived)
Button.default.pressedBackground=lighten($Button.default.background,6%,derived) Button.default.pressedBackground = lighten($Button.default.background,6%,derived)
Button.default.borderColor=#4c708c Button.default.borderColor = #4c708c
Button.default.hoverBorderColor=#537699 Button.default.hoverBorderColor = #537699
Button.default.focusedBorderColor=#537699 Button.default.focusedBorderColor = #537699
Button.default.focusColor=#43688c Button.default.focusColor = #43688c
Button.default.boldText=true Button.default.boldText = true
Button.toolbar.hoverBackground=lighten($Button.background,1%,derived) Button.toolbar.hoverBackground = lighten($Button.background,1%,derived)
Button.toolbar.pressedBackground=lighten($Button.background,4%,derived) Button.toolbar.pressedBackground = lighten($Button.background,4%,derived)
Button.toolbar.selectedBackground = lighten($Button.background,7%,derived)
#---- CheckBox ---- #---- CheckBox ----
# enabled # enabled
CheckBox.icon.borderColor=#6B6B6B CheckBox.icon.borderColor = #6B6B6B
CheckBox.icon.background=#43494A CheckBox.icon.background = #43494A
CheckBox.icon.selectedBorderColor=$CheckBox.icon.borderColor CheckBox.icon.selectedBorderColor = $CheckBox.icon.borderColor
CheckBox.icon.selectedBackground=$CheckBox.icon.background CheckBox.icon.selectedBackground = $CheckBox.icon.background
CheckBox.icon.checkmarkColor=#A7A7A7 CheckBox.icon.checkmarkColor = #A7A7A7
# disabled # disabled
CheckBox.icon.disabledBorderColor=#545556 CheckBox.icon.disabledBorderColor = #545556
CheckBox.icon.disabledBackground=@background CheckBox.icon.disabledBackground = @background
CheckBox.icon.disabledCheckmarkColor=#606060 CheckBox.icon.disabledCheckmarkColor = #606060
# focused # focused
CheckBox.icon.focusedBorderColor=#466D94 CheckBox.icon.focusedBorderColor = #466D94
CheckBox.icon.selectedFocusedBorderColor=#466D94
# hover # hover
CheckBox.icon.hoverBorderColor=$CheckBox.icon.focusedBorderColor CheckBox.icon.hoverBorderColor = $CheckBox.icon.focusedBorderColor
CheckBox.icon.hoverBackground=lighten($CheckBox.icon.background,3%,derived) CheckBox.icon.hoverBackground = lighten($CheckBox.icon.background,3%,derived)
# pressed # pressed
CheckBox.icon.pressedBackground=lighten($CheckBox.icon.background,6%,derived) CheckBox.icon.pressedBackground = lighten($CheckBox.icon.background,6%,derived)
# used if CheckBox.icon.style=filled
# used if CheckBox.icon.style = filled
# enabled # enabled
CheckBox.icon[filled].selectedBorderColor=$CheckBox.icon.checkmarkColor CheckBox.icon[filled].selectedBorderColor = $CheckBox.icon.checkmarkColor
CheckBox.icon[filled].selectedBackground=$CheckBox.icon.checkmarkColor CheckBox.icon[filled].selectedBackground = $CheckBox.icon.checkmarkColor
CheckBox.icon[filled].checkmarkColor=$CheckBox.icon.background CheckBox.icon[filled].checkmarkColor = $CheckBox.icon.background
# hover # hover
CheckBox.icon[filled].selectedHoverBackground=darken($CheckBox.icon[filled].selectedBackground,3%) CheckBox.icon[filled].selectedHoverBackground = darken($CheckBox.icon[filled].selectedBackground,3%,derived)
# pressed # pressed
CheckBox.icon[filled].selectedPressedBackground=darken($CheckBox.icon[filled].selectedBackground,6%) CheckBox.icon[filled].selectedPressedBackground = darken($CheckBox.icon[filled].selectedBackground,6%,derived)
#---- ComboBox ---- #---- ComboBox ----
ComboBox.background=@textComponentBackground ComboBox.buttonEditableBackground = darken($ComboBox.background,2%)
ComboBox.buttonBackground=@textComponentBackground
ComboBox.buttonEditableBackground=#404445
ComboBox.buttonArrowColor=#9A9DA1
ComboBox.buttonDisabledArrowColor=#585858
ComboBox.buttonHoverArrowColor=#bbbbbb
#---- Component ---- #---- Component ----
Component.borderColor=#646464 Component.borderColor = #646464
Component.disabledBorderColor=#646464 Component.disabledBorderColor = #646464
Component.focusedBorderColor=#466d94 Component.focusedBorderColor = #466d94
Component.focusColor=#3d6185 Component.focusColor = #3d6185
Component.linkColor=#589df6 Component.linkColor = #589df6
Component.grayFilter=-20,-70,100 Component.grayFilter = -20,-70,100
Component.error.borderColor=desaturate($Component.error.focusedBorderColor,25%) Component.error.borderColor = desaturate($Component.error.focusedBorderColor,25%)
Component.error.focusedBorderColor=#8b3c3c Component.error.focusedBorderColor = #8b3c3c
Component.warning.borderColor=darken(desaturate($Component.warning.focusedBorderColor,20%),10%) Component.warning.borderColor = darken(desaturate($Component.warning.focusedBorderColor,20%),10%)
Component.warning.focusedBorderColor=#ac7920 Component.warning.focusedBorderColor = #ac7920
Component.custom.borderColor=desaturate(#f00,50%,relative derived noAutoInverse) Component.custom.borderColor = desaturate(#f00,50%,relative derived noAutoInverse)
#---- Desktop ---- #---- Desktop ----
Desktop.background=#3E434C Desktop.background = #3E434C
#---- DesktopIcon ---- #---- DesktopIcon ----
DesktopIcon.background=lighten($Desktop.background,10%) DesktopIcon.background = lighten($Desktop.background,10%)
#---- InternalFrame ---- #---- InternalFrame ----
InternalFrame.activeTitleBackground=darken(@background,10%) InternalFrame.activeTitleBackground = darken(@background,10%)
InternalFrame.activeTitleForeground=@foreground InternalFrame.activeTitleForeground = @foreground
InternalFrame.inactiveTitleBackground=darken(@background,5%) InternalFrame.inactiveTitleBackground = darken(@background,5%)
InternalFrame.inactiveTitleForeground=@disabledText InternalFrame.inactiveTitleForeground = @disabledText
InternalFrame.activeBorderColor=darken(@background,7%) InternalFrame.activeBorderColor = darken(@background,7%)
InternalFrame.inactiveBorderColor=darken(@background,3%) InternalFrame.inactiveBorderColor = darken(@background,3%)
InternalFrame.buttonHoverBackground=lighten($InternalFrame.activeTitleBackground,10%,derived) InternalFrame.buttonHoverBackground = lighten($InternalFrame.activeTitleBackground,10%,derived)
InternalFrame.buttonPressedBackground=lighten($InternalFrame.activeTitleBackground,20%,derived) InternalFrame.buttonPressedBackground = lighten($InternalFrame.activeTitleBackground,20%,derived)
InternalFrame.closeHoverBackground=lazy(Actions.Red) InternalFrame.closeHoverBackground = lazy(Actions.Red)
InternalFrame.closePressedBackground=darken(Actions.Red,10%,lazy) InternalFrame.closePressedBackground = darken(Actions.Red,10%,lazy)
InternalFrame.closeHoverForeground=#fff InternalFrame.closeHoverForeground = #fff
InternalFrame.closePressedForeground=#fff InternalFrame.closePressedForeground = #fff
InternalFrame.activeDropShadowOpacity=0.5 InternalFrame.activeDropShadowOpacity = 0.5
InternalFrame.inactiveDropShadowOpacity=0.75 InternalFrame.inactiveDropShadowOpacity = 0.75
#---- List ----
List.background=@textComponentBackground
#---- Menu ---- #---- Menu ----
Menu.icon.arrowColor=#A7A7A7 Menu.icon.arrowColor = #A7A7A7
Menu.icon.disabledArrowColor=#606060 Menu.icon.disabledArrowColor = #606060
#---- MenuBar ---- #---- MenuBar ----
MenuBar.borderColor=#515151 MenuBar.borderColor = #515151
MenuBar.hoverBackground=@menuHoverBackground
#---- MenuItemCheckBox ---- #---- MenuItemCheckBox ----
MenuItemCheckBox.icon.checkmarkColor=#A7A7A7 MenuItemCheckBox.icon.checkmarkColor = #A7A7A7
MenuItemCheckBox.icon.disabledCheckmarkColor=#606060 MenuItemCheckBox.icon.disabledCheckmarkColor = #606060
#---- PasswordField ---- #---- PasswordField ----
PasswordField.capsLockIconColor=#ffffff64 PasswordField.capsLockIconColor = #ffffff64
#---- Popup ---- #---- Popup ----
Popup.dropShadowColor=#000 Popup.dropShadowColor = #000
Popup.dropShadowOpacity=0.25 Popup.dropShadowOpacity = 0.25
#---- PopupMenu ---- #---- PopupMenu ----
PopupMenu.borderColor=#5e5e5e PopupMenu.borderColor = #5e5e5e
#---- ProgressBar ---- #---- ProgressBar ----
ProgressBar.background=#555555 ProgressBar.background = #555
ProgressBar.foreground=#4A88C7 ProgressBar.foreground = #4A88C7
ProgressBar.selectionForeground=@foreground ProgressBar.selectionForeground = @foreground
ProgressBar.selectionBackground=@foreground ProgressBar.selectionBackground = @foreground
#---- RadioButton ----
RadioButton.icon[filled].centerDiameter=5
#---- RootPane ---- #---- RootPane ----
RootPane.activeBorderColor=darken(@background,7%,derived) RootPane.activeBorderColor = lighten(@background,7%,derived)
RootPane.inactiveBorderColor=darken(@background,5%,derived) RootPane.inactiveBorderColor = lighten(@background,5%,derived)
#---- ScrollBar ---- #---- ScrollBar ----
ScrollBar.track=lighten(@background,1%,derived noAutoInverse) ScrollBar.track = lighten(@background,1%,derived noAutoInverse)
ScrollBar.thumb=lighten($ScrollBar.track,10%,derived noAutoInverse) ScrollBar.thumb = lighten($ScrollBar.track,10%,derived noAutoInverse)
ScrollBar.hoverTrackColor=lighten($ScrollBar.track,4%,derived noAutoInverse) ScrollBar.hoverTrackColor = lighten($ScrollBar.track,4%,derived noAutoInverse)
ScrollBar.hoverThumbColor=lighten($ScrollBar.thumb,10%,derived noAutoInverse) ScrollBar.hoverThumbColor = lighten($ScrollBar.thumb,10%,derived noAutoInverse)
ScrollBar.pressedThumbColor=lighten($ScrollBar.thumb,15%,derived noAutoInverse) ScrollBar.pressedThumbColor = lighten($ScrollBar.thumb,15%,derived noAutoInverse)
ScrollBar.hoverButtonBackground=lighten(@background,5%,derived noAutoInverse) ScrollBar.hoverButtonBackground = lighten(@background,5%,derived noAutoInverse)
ScrollBar.pressedButtonBackground=lighten(@background,10%,derived noAutoInverse) ScrollBar.pressedButtonBackground = lighten(@background,10%,derived noAutoInverse)
#---- Separator ---- #---- Separator ----
Separator.foreground=#515151 Separator.foreground = #515151
#---- Slider ---- #---- Slider ----
Slider.trackColor=#646464 Slider.trackValueColor = #4A88C7
Slider.thumbColor=#A6A6A6 Slider.trackColor = #646464
Slider.tickColor=#888888 Slider.thumbColor = $Slider.trackValueColor
Slider.hoverColor=darken($Slider.thumbColor,15%,derived) Slider.tickColor = #888
Slider.disabledForeground=#4c5052 Slider.focusedColor = fade($Component.focusColor,70%,derived)
Slider.hoverThumbColor = lighten($Slider.thumbColor,5%,derived)
Slider.pressedThumbColor = lighten($Slider.thumbColor,8%,derived)
Slider.disabledTrackColor = #4c5052
Slider.disabledThumbColor = $Slider.disabledTrackColor
#---- SplitPane ---- #---- SplitPane ----
SplitPaneDivider.draggingColor=#646464 SplitPaneDivider.draggingColor = #646464
SplitPaneDivider.oneTouchHoverArrowColor=#7A7D81
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.disabledForeground=@disabledText TabbedPane.underlineColor = #4A88C7
TabbedPane.underlineColor=#4A88C7 TabbedPane.disabledUnderlineColor = #7a7a7a
TabbedPane.disabledUnderlineColor=#7a7a7a TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.hoverColor=#2e3133 TabbedPane.focusColor = #3d4b5c
TabbedPane.focusColor=#3d4b5c TabbedPane.contentAreaColor = #646464
TabbedPane.contentAreaColor=#323232
TabbedPane.buttonHoverBackground = darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.buttonPressedBackground = darken($TabbedPane.background,8%,derived noAutoInverse)
TabbedPane.closeBackground = null
TabbedPane.closeForeground = @disabledText
TabbedPane.closeHoverBackground = lighten($TabbedPane.background,5%,derived)
TabbedPane.closeHoverForeground = @foreground
TabbedPane.closePressedBackground = lighten($TabbedPane.background,10%,derived)
TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----
Table.background=@textComponentBackground Table.gridColor = lighten($Table.background,5%)
Table.gridColor=lighten($Table.background,3%)
#---- TableHeader ---- #---- TableHeader ----
TableHeader.background=@textComponentBackground TableHeader.separatorColor = lighten($TableHeader.background,10%)
TableHeader.separatorColor=lighten($TableHeader.background,10%) TableHeader.bottomSeparatorColor = $TableHeader.separatorColor
TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
#---- TitlePane ---- #---- TitlePane ----
TitlePane.embeddedForeground=darken($TitlePane.foreground,15%) TitlePane.embeddedForeground = darken($TitlePane.foreground,15%)
TitlePane.buttonHoverBackground=lighten($TitlePane.background,10%,derived) TitlePane.buttonHoverBackground = lighten($TitlePane.background,10%,derived)
TitlePane.buttonPressedBackground=lighten($TitlePane.background,20%,derived) TitlePane.buttonPressedBackground = lighten($TitlePane.background,20%,derived)
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.selectedBackground=lighten($ToggleButton.background,10%,derived) ToggleButton.selectedBackground = lighten($ToggleButton.background,10%,derived)
ToggleButton.selectedForeground=@foreground ToggleButton.disabledSelectedBackground = lighten($ToggleButton.background,3%,derived)
ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,3%,derived)
ToggleButton.toolbar.selectedBackground=lighten($ToggleButton.background,7%,derived) ToggleButton.toolbar.selectedBackground = lighten($ToggleButton.background,7%,derived)
#---- ToolTip ---- #---- ToolTip ----
ToolTip.border=4,6,4,6 ToolTip.border = 4,6,4,6
ToolTip.background=#1e2123 ToolTip.background = #1e2123
#---- Tree ---- #---- Tree ----
Tree.background=@textComponentBackground Tree.hash = lighten($Tree.background,5%)
Tree.hash=#505355

View File

@@ -20,27 +20,27 @@
#---- Button ---- #---- Button ----
Button.focusedBackground=null Button.focusedBackground = null
Button.default.background=#4D8AC9 Button.default.background = #4D8AC9
Button.default.foreground=#FFFFFF Button.default.foreground = #fff
Button.default.focusedBackground=null Button.default.focusedBackground = null
Button.default.borderColor=#3D75B2 Button.default.borderColor = #3D75B2
Button.default.hoverBorderColor=#A9C9F5 Button.default.hoverBorderColor = #A9C9F5
Button.default.focusedBorderColor=#A9C9F5 Button.default.focusedBorderColor = #A9C9F5
Button.default.focusColor=#97c3f3 Button.default.focusColor = #97c3f3
Button.default.boldText=true Button.default.boldText = true
Button.default.borderWidth=1 Button.default.borderWidth = 1
#---- CheckBox ---- #---- CheckBox ----
CheckBox.icon.style=filled CheckBox.icon.style = filled
#---- Component ---- #---- Component ----
Component.focusWidth=2 Component.focusWidth = 2
Component.innerFocusWidth=0 Component.innerFocusWidth = 0
Component.innerOutlineWidth=0 Component.innerOutlineWidth = 0
Component.arrowType=triangle Component.arrowType = triangle

View File

@@ -20,320 +20,305 @@
#---- variables ---- #---- variables ----
@background=#f2f2f2 @background = #f2f2f2
@foreground=#000000 @foreground = #000
@selectionBackground=#2675BF @selectionBackground = #2675BF
@selectionForeground=#ffffff @selectionForeground = #fff
@selectionInactiveBackground=#d4d4d4 @selectionInactiveBackground = #d4d4d4
@selectionInactiveForeground=@foreground @selectionInactiveForeground = @foreground
@disabledText=#8C8C8C @disabledText = #8C8C8C
@textComponentBackground=#ffffff @textComponentBackground = #fff
@menuBackground=#fff @menuBackground = #fff
@menuHoverBackground=darken(@menuBackground,10%,derived) @menuHoverBackground = darken(@menuBackground,10%,derived)
@menuCheckBackground=darken(@menuBackground,10%,derived) @menuCheckBackground = lighten(@selectionBackground,40%,derived noAutoInverse)
@menuCheckHoverBackground=darken(@menuBackground,20%,derived) @menuAcceleratorForeground = lighten(@foreground,30%)
@cellFocusColor=#000000 @menuAcceleratorSelectionForeground = @selectionForeground
@icon=#afafaf @cellFocusColor = #000
@icon = #afafaf
# for buttons within components (e.g. combobox or spinner)
@buttonArrowColor = #666
@buttonDisabledArrowColor = lighten(@buttonArrowColor,25%)
@buttonHoverArrowColor = lighten(@buttonArrowColor,20%,derived noAutoInverse)
@buttonPressedArrowColor = lighten(@buttonArrowColor,30%,derived noAutoInverse)
# Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors) # Drop (use lazy colors for IntelliJ platform themes, which usually do not specify these colors)
@dropCellBackground=lighten(List.selectionBackground,10%,lazy) @dropCellBackground = lighten(List.selectionBackground,10%,lazy)
@dropCellForeground=lazy(List.selectionForeground) @dropCellForeground = lazy(List.selectionForeground)
@dropLineColor=lighten(List.selectionBackground,20%,lazy) @dropLineColor = lighten(List.selectionBackground,20%,lazy)
@dropLineShortColor=darken(List.selectionBackground,20%,lazy) @dropLineShortColor = darken(List.selectionBackground,20%,lazy)
#---- globals ----
*.background=@background
*.foreground=@foreground
*.textBackground=#cccccc
*.textForeground=@foreground
*.caretForeground=@foreground
*.inactiveBackground=@background
*.inactiveForeground=@disabledText
*.selectionBackground=@selectionBackground
*.selectionForeground=@selectionForeground
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=lighten(@foreground,30%)
*.acceleratorSelectionForeground=@selectionForeground
#---- system colors ---- #---- system colors ----
activeCaption=#99b4d1 activeCaption = #99b4d1
inactiveCaption=#bfcddb inactiveCaption = #bfcddb
controlHighlight=#e3e3e3 controlHighlight = lighten($controlShadow,12%)
controlLtHighlight=#fff controlLtHighlight = lighten($controlShadow,25%)
controlDkShadow=darken($controlShadow,15%) controlDkShadow = darken($controlShadow,15%)
#---- Button ---- #---- Button ----
Button.background=#ffffff Button.background = #fff
Button.focusedBackground=#e3f1fa Button.focusedBackground = #e3f1fa
Button.hoverBackground=darken($Button.background,3%,derived) Button.hoverBackground = darken($Button.background,3%,derived)
Button.pressedBackground=darken($Button.background,10%,derived) Button.pressedBackground = darken($Button.background,10%,derived)
Button.selectedBackground = darken($Button.background,20%,derived)
Button.selectedForeground = @foreground
Button.disabledSelectedBackground = darken($Button.background,13%,derived)
Button.borderColor=$Component.borderColor Button.borderColor = $Component.borderColor
Button.disabledBorderColor=$Component.disabledBorderColor Button.disabledBorderColor = $Component.disabledBorderColor
Button.focusedBorderColor=$Component.focusedBorderColor Button.focusedBorderColor = $Component.focusedBorderColor
Button.hoverBorderColor=$Button.focusedBorderColor Button.hoverBorderColor = $Button.focusedBorderColor
Button.default.background=$Button.background Button.default.background = $Button.background
Button.default.foreground=@foreground Button.default.foreground = @foreground
Button.default.focusedBackground=$Button.focusedBackground Button.default.focusedBackground = $Button.focusedBackground
Button.default.hoverBackground=$Button.hoverBackground Button.default.hoverBackground = darken($Button.default.background,3%,derived)
Button.default.pressedBackground=$Button.pressedBackground Button.default.pressedBackground = darken($Button.default.background,10%,derived)
Button.default.borderColor=#4F9EE3 Button.default.borderColor = #4F9EE3
Button.default.hoverBorderColor=$Button.hoverBorderColor Button.default.hoverBorderColor = $Button.hoverBorderColor
Button.default.focusedBorderColor=$Button.focusedBorderColor Button.default.focusedBorderColor = $Button.focusedBorderColor
Button.default.focusColor=$Component.focusColor Button.default.focusColor = $Component.focusColor
Button.default.borderWidth=2 Button.default.borderWidth = 2
Button.toolbar.hoverBackground=darken($Button.background,12%,derived) Button.toolbar.hoverBackground = darken($Button.background,12%,derived)
Button.toolbar.pressedBackground=darken($Button.background,15%,derived) Button.toolbar.pressedBackground = darken($Button.background,15%,derived)
Button.toolbar.selectedBackground = $Button.selectedBackground
#---- CheckBox ---- #---- CheckBox ----
# enabled # enabled
CheckBox.icon.borderColor=#b0b0b0 CheckBox.icon.borderColor = #b0b0b0
CheckBox.icon.background=#FFFFFF CheckBox.icon.background = #fff
CheckBox.icon.selectedBorderColor=$CheckBox.icon.borderColor CheckBox.icon.selectedBorderColor = $CheckBox.icon.borderColor
CheckBox.icon.selectedBackground=$CheckBox.icon.background CheckBox.icon.selectedBackground = $CheckBox.icon.background
CheckBox.icon.checkmarkColor=#4F9EE3 CheckBox.icon.checkmarkColor = #4F9EE3
# disabled # disabled
CheckBox.icon.disabledBorderColor=#BDBDBD CheckBox.icon.disabledBorderColor = #BDBDBD
CheckBox.icon.disabledBackground=@background CheckBox.icon.disabledBackground = @background
CheckBox.icon.disabledCheckmarkColor=#ABABAB CheckBox.icon.disabledCheckmarkColor = #ABABAB
# focused # focused
CheckBox.icon.focusedBorderColor=#7B9FC7 CheckBox.icon.focusedBorderColor = #7B9FC7
CheckBox.icon.focusedBackground=$Button.focusedBackground CheckBox.icon.focusedBackground = $Button.focusedBackground
# hover # hover
CheckBox.icon.hoverBorderColor=$CheckBox.icon.focusedBorderColor CheckBox.icon.hoverBorderColor = $CheckBox.icon.focusedBorderColor
CheckBox.icon.hoverBackground=$Button.hoverBackground CheckBox.icon.hoverBackground = $Button.hoverBackground
# pressed # pressed
CheckBox.icon.pressedBackground=$Button.pressedBackground CheckBox.icon.pressedBackground = $Button.pressedBackground
# used if CheckBox.icon.style=filled # used if CheckBox.icon.style = filled
# enabled # enabled
CheckBox.icon[filled].selectedBorderColor=#4B97D9 CheckBox.icon[filled].selectedBorderColor = #4B97D9
CheckBox.icon[filled].selectedBackground=#4F9EE3 CheckBox.icon[filled].selectedBackground = #4F9EE3
CheckBox.icon[filled].checkmarkColor=#FFFFFF CheckBox.icon[filled].checkmarkColor = #fff
# focused # focused
CheckBox.icon[filled].selectedFocusedBorderColor=#ACCFF7 CheckBox.icon[filled].selectedFocusedBorderColor = #ACCFF7
CheckBox.icon[filled].selectedFocusedBackground=$CheckBox.icon[filled].selectedBackground CheckBox.icon[filled].selectedFocusedBackground = $CheckBox.icon[filled].selectedBackground
CheckBox.icon[filled].selectedFocusedCheckmarkColor=$CheckBox.icon.focusedBackground CheckBox.icon[filled].selectedFocusedCheckmarkColor = $CheckBox.icon.focusedBackground
# hover # hover
CheckBox.icon[filled].selectedHoverBackground=#5E94CE CheckBox.icon[filled].selectedHoverBackground = darken($CheckBox.icon[filled].selectedBackground,5%,derived)
# pressed # pressed
CheckBox.icon[filled].selectedPressedBackground=#72A1D4 CheckBox.icon[filled].selectedPressedBackground = darken($CheckBox.icon[filled].selectedBackground,10%,derived)
#---- ComboBox ---- #---- ComboBox ----
ComboBox.background=@textComponentBackground ComboBox.buttonEditableBackground = darken($ComboBox.background,2%)
ComboBox.buttonBackground=@textComponentBackground
ComboBox.buttonEditableBackground=#fafafa
ComboBox.buttonArrowColor=#666666
ComboBox.buttonDisabledArrowColor=#ABABAB
ComboBox.buttonHoverArrowColor=#999999
#---- Component ---- #---- Component ----
Component.borderColor=#c4c4c4 Component.borderColor = #c4c4c4
Component.disabledBorderColor=#cfcfcf Component.disabledBorderColor = #cfcfcf
Component.focusedBorderColor=#87afda Component.focusedBorderColor = #87afda
Component.focusColor=#97c3f3 Component.focusColor = #97c3f3
Component.linkColor=#2470B3 Component.linkColor = #2470B3
Component.grayFilter=25,-25,100 Component.grayFilter = 25,-25,100
Component.error.borderColor=lighten(desaturate($Component.error.focusedBorderColor,20%),25%) Component.error.borderColor = lighten(desaturate($Component.error.focusedBorderColor,20%),25%)
Component.error.focusedBorderColor=#e53e4d Component.error.focusedBorderColor = #e53e4d
Component.warning.borderColor=lighten(saturate($Component.warning.focusedBorderColor,25%),20%) Component.warning.borderColor = lighten(saturate($Component.warning.focusedBorderColor,25%),20%)
Component.warning.focusedBorderColor=#e2a53a Component.warning.focusedBorderColor = #e2a53a
Component.custom.borderColor=lighten(desaturate(#f00,20%,derived noAutoInverse),25%,derived noAutoInverse) Component.custom.borderColor = lighten(desaturate(#f00,20%,derived noAutoInverse),25%,derived noAutoInverse)
#---- Desktop ---- #---- Desktop ----
Desktop.background=#E6EBF0 Desktop.background = #E6EBF0
#---- DesktopIcon ---- #---- DesktopIcon ----
DesktopIcon.background=darken($Desktop.background,10%) DesktopIcon.background = darken($Desktop.background,10%)
#---- HelpButton ---- #---- HelpButton ----
HelpButton.questionMarkColor=#4F9EE3 HelpButton.questionMarkColor = #4F9EE3
#---- InternalFrame ---- #---- InternalFrame ----
InternalFrame.activeTitleBackground=#fff InternalFrame.activeTitleBackground = #fff
InternalFrame.activeTitleForeground=@foreground InternalFrame.activeTitleForeground = @foreground
InternalFrame.inactiveTitleBackground=#fafafa InternalFrame.inactiveTitleBackground = #fafafa
InternalFrame.inactiveTitleForeground=@disabledText InternalFrame.inactiveTitleForeground = @disabledText
InternalFrame.activeBorderColor=darken($Component.borderColor,20%) InternalFrame.activeBorderColor = darken($Component.borderColor,20%)
InternalFrame.inactiveBorderColor=$Component.borderColor InternalFrame.inactiveBorderColor = $Component.borderColor
InternalFrame.buttonHoverBackground=darken($InternalFrame.activeTitleBackground,10%,derived) InternalFrame.buttonHoverBackground = darken($InternalFrame.activeTitleBackground,10%,derived)
InternalFrame.buttonPressedBackground=darken($InternalFrame.activeTitleBackground,20%,derived) InternalFrame.buttonPressedBackground = darken($InternalFrame.activeTitleBackground,20%,derived)
InternalFrame.closeHoverBackground=lazy(Actions.Red) InternalFrame.closeHoverBackground = lazy(Actions.Red)
InternalFrame.closePressedBackground=darken(Actions.Red,10%,lazy) InternalFrame.closePressedBackground = darken(Actions.Red,10%,lazy)
InternalFrame.closeHoverForeground=#fff InternalFrame.closeHoverForeground = #fff
InternalFrame.closePressedForeground=#fff InternalFrame.closePressedForeground = #fff
InternalFrame.activeDropShadowOpacity=0.25 InternalFrame.activeDropShadowOpacity = 0.25
InternalFrame.inactiveDropShadowOpacity=0.5 InternalFrame.inactiveDropShadowOpacity = 0.5
#---- List ----
List.background=@textComponentBackground
#---- Menu ---- #---- Menu ----
Menu.icon.arrowColor=#666666 Menu.icon.arrowColor = #666
Menu.icon.disabledArrowColor=#ABABAB Menu.icon.disabledArrowColor = #ABABAB
#---- MenuBar ---- #---- MenuBar ----
MenuBar.borderColor=#cdcdcd MenuBar.borderColor = #cdcdcd
MenuBar.hoverBackground=@menuHoverBackground
#---- MenuItemCheckBox ---- #---- MenuItemCheckBox ----
MenuItemCheckBox.icon.checkmarkColor=#4F9EE3 MenuItemCheckBox.icon.checkmarkColor = #4F9EE3
MenuItemCheckBox.icon.disabledCheckmarkColor=#ABABAB MenuItemCheckBox.icon.disabledCheckmarkColor = #ABABAB
#---- PasswordField ---- #---- PasswordField ----
PasswordField.capsLockIconColor=#00000064 PasswordField.capsLockIconColor = #00000064
#---- Popup ---- #---- Popup ----
Popup.dropShadowColor=#000 Popup.dropShadowColor = #000
Popup.dropShadowOpacity=0.15 Popup.dropShadowOpacity = 0.15
#---- PopupMenu ---- #---- PopupMenu ----
PopupMenu.borderColor=#adadad PopupMenu.borderColor = #adadad
#---- ProgressBar ---- #---- ProgressBar ----
ProgressBar.background=#D1D1D1 ProgressBar.background = #D1D1D1
ProgressBar.foreground=#1E82E6 ProgressBar.foreground = #1E82E6
ProgressBar.selectionForeground=@textComponentBackground ProgressBar.selectionForeground = @textComponentBackground
ProgressBar.selectionBackground=@foreground ProgressBar.selectionBackground = @foreground
#---- RadioButton ----
RadioButton.icon[filled].centerDiameter=5
#---- RootPane ---- #---- RootPane ----
RootPane.activeBorderColor=#707070 RootPane.activeBorderColor = darken(@background,50%,derived)
RootPane.inactiveBorderColor=lighten($RootPane.activeBorderColor,20%,derived) RootPane.inactiveBorderColor = darken(@background,30%,derived)
#---- ScrollBar ---- #---- ScrollBar ----
ScrollBar.track=lighten(@background,1%,derived noAutoInverse) ScrollBar.track = lighten(@background,1%,derived noAutoInverse)
ScrollBar.thumb=darken($ScrollBar.track,10%,derived noAutoInverse) ScrollBar.thumb = darken($ScrollBar.track,10%,derived noAutoInverse)
ScrollBar.hoverTrackColor=darken($ScrollBar.track,3%,derived noAutoInverse) ScrollBar.hoverTrackColor = darken($ScrollBar.track,3%,derived noAutoInverse)
ScrollBar.hoverThumbColor=darken($ScrollBar.thumb,10%,derived noAutoInverse) ScrollBar.hoverThumbColor = darken($ScrollBar.thumb,10%,derived noAutoInverse)
ScrollBar.pressedThumbColor=darken($ScrollBar.thumb,20%,derived noAutoInverse) ScrollBar.pressedThumbColor = darken($ScrollBar.thumb,20%,derived noAutoInverse)
ScrollBar.hoverButtonBackground=darken(@background,5%,derived noAutoInverse) ScrollBar.hoverButtonBackground = darken(@background,5%,derived noAutoInverse)
ScrollBar.pressedButtonBackground=darken(@background,10%,derived noAutoInverse) ScrollBar.pressedButtonBackground = darken(@background,10%,derived noAutoInverse)
#---- Separator ---- #---- Separator ----
Separator.foreground=#d1d1d1 Separator.foreground = #d1d1d1
#---- Slider ---- #---- Slider ----
Slider.trackColor=#c4c4c4 Slider.trackValueColor = #1E82E6
Slider.thumbColor=#6e6e6e Slider.trackColor = #c4c4c4
Slider.tickColor=#888888 Slider.thumbColor = $Slider.trackValueColor
Slider.hoverColor=lighten($Slider.thumbColor,15%,derived) Slider.tickColor = #888
Slider.disabledForeground=#c0c0c0 Slider.focusedColor = fade($Component.focusColor,50%,derived)
Slider.hoverThumbColor = darken($Slider.thumbColor,5%,derived)
Slider.pressedThumbColor = darken($Slider.thumbColor,8%,derived)
Slider.disabledTrackColor = #c0c0c0
Slider.disabledThumbColor = $Slider.disabledTrackColor
#---- SplitPane ---- #---- SplitPane ----
SplitPaneDivider.draggingColor=#c4c4c4 SplitPaneDivider.draggingColor = #c4c4c4
SplitPaneDivider.oneTouchHoverArrowColor=#333333
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.disabledForeground=@disabledText TabbedPane.underlineColor = #4083C9
TabbedPane.underlineColor=#4083C9 TabbedPane.disabledUnderlineColor = #ababab
TabbedPane.disabledUnderlineColor=#ababab TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived)
TabbedPane.hoverColor=#d9d9d9 TabbedPane.focusColor = #dae4ed
TabbedPane.focusColor=#dae4ed TabbedPane.contentAreaColor = #bfbfbf
TabbedPane.contentAreaColor=#bfbfbf
TabbedPane.buttonHoverBackground = darken($TabbedPane.background,7%,derived)
TabbedPane.buttonPressedBackground = darken($TabbedPane.background,10%,derived)
TabbedPane.closeBackground = null
TabbedPane.closeForeground = @disabledText
TabbedPane.closeHoverBackground = darken($TabbedPane.background,20%,derived)
TabbedPane.closeHoverForeground = @foreground
TabbedPane.closePressedBackground = darken($TabbedPane.background,25%,derived)
TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ---- #---- Table ----
Table.background=@textComponentBackground Table.gridColor = darken($Table.background,5%)
Table.gridColor=darken($Table.background,3%)
#---- TableHeader ---- #---- TableHeader ----
TableHeader.background=@textComponentBackground TableHeader.separatorColor = darken($TableHeader.background,10%)
TableHeader.separatorColor=darken($TableHeader.background,10%) TableHeader.bottomSeparatorColor = $TableHeader.separatorColor
TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
#---- TitlePane ---- #---- TitlePane ----
TitlePane.embeddedForeground=lighten($TitlePane.foreground,35%) TitlePane.embeddedForeground = lighten($TitlePane.foreground,35%)
TitlePane.buttonHoverBackground=darken($TitlePane.background,10%,derived) TitlePane.buttonHoverBackground = darken($TitlePane.background,10%,derived)
TitlePane.buttonPressedBackground=darken($TitlePane.background,20%,derived) TitlePane.buttonPressedBackground = darken($TitlePane.background,20%,derived)
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.selectedBackground=darken($ToggleButton.background,20%,derived) ToggleButton.selectedBackground = darken($ToggleButton.background,20%,derived)
ToggleButton.selectedForeground=@foreground ToggleButton.disabledSelectedBackground = darken($ToggleButton.background,13%,derived)
ToggleButton.disabledSelectedBackground=darken($ToggleButton.background,13%,derived)
ToggleButton.toolbar.selectedBackground=$ToggleButton.selectedBackground ToggleButton.toolbar.selectedBackground = $ToggleButton.selectedBackground
#---- ToolTip ---- #---- ToolTip ----
ToolTip.border=4,6,4,6,$InternalFrame.activeBorderColor ToolTip.border = 4,6,4,6,$InternalFrame.activeBorderColor
ToolTip.background=#fafafa ToolTip.background = #fafafa
#---- Tree ---- #---- Tree ----
Tree.background=@textComponentBackground Tree.hash = darken($Tree.background,10%)
Tree.hash=#E6E6E6

View File

@@ -16,49 +16,189 @@
#---- Button ---- #---- Button ----
Button.startBackground=$Button.background Button.startBackground = $Button.background
Button.endBackground=$Button.background Button.endBackground = $Button.background
Button.startBorderColor=$Button.borderColor Button.startBorderColor = $Button.borderColor
Button.endBorderColor=$Button.borderColor Button.endBorderColor = $Button.borderColor
Button.default.startBackground=$Button.default.background Button.default.startBackground = $Button.default.background
Button.default.endBackground=$Button.default.background Button.default.endBackground = $Button.default.background
Button.default.startBorderColor=$Button.default.borderColor Button.default.startBorderColor = $Button.default.borderColor
Button.default.endBorderColor=$Button.default.borderColor Button.default.endBorderColor = $Button.default.borderColor
Button.hoverBorderColor=null Button.hoverBorderColor = null
Button.default.hoverBorderColor=null Button.default.hoverBorderColor = null
#---- HelpButton ---- #---- HelpButton ----
HelpButton.hoverBorderColor=null HelpButton.hoverBorderColor = null
#---- MenuItemCheckBox ----
# colors from intellij/checkmark.svg and darcula/checkmark.svg
[light]MenuItemCheckBox.icon.checkmarkColor=#3E3E3C
[dark]MenuItemCheckBox.icon.checkmarkColor=#fff9
#---- Slider ----
Slider.focusedColor = fade($Component.focusColor,40%,derived)
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.startBackground=$ToggleButton.background ToggleButton.startBackground = $ToggleButton.background
ToggleButton.endBackground=$ToggleButton.background ToggleButton.endBackground = $ToggleButton.background
[dark]ToggleButton.selectedBackground=lighten($ToggleButton.background,15%,derived) [dark]ToggleButton.selectedBackground = lighten($ToggleButton.background,15%,derived)
[dark]ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,5%,derived) [dark]ToggleButton.disabledSelectedBackground = lighten($ToggleButton.background,5%,derived)
#---- theme specific ---- #---- theme specific ----
[Gruvbox_Dark_Hard]ToggleButton.selectedBackground=$ToggleButton.selectedBackground @ijMenuCheckBackgroundL10 = lighten(@selectionBackground,10%,derived noAutoInverse)
[Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground=$ToggleButton.toolbar.selectedBackground @ijMenuCheckBackgroundL20 = lighten(@selectionBackground,20%,derived noAutoInverse)
@ijMenuCheckBackgroundD10 = darken(@selectionBackground,10%,derived noAutoInverse)
[Gruvbox_Dark_Medium]ToggleButton.selectedBackground=$ToggleButton.selectedBackground [Arc_Theme]ProgressBar.selectionBackground = #000
[Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground=$ToggleButton.toolbar.selectedBackground [Arc_Theme]ProgressBar.selectionForeground = #fff
[Arc_Theme]List.selectionInactiveForeground = #fff
[Arc_Theme]Table.selectionInactiveForeground = #fff
[Arc_Theme]Tree.selectionInactiveForeground = #fff
[Gruvbox_Dark_Soft]ToggleButton.selectedBackground=$ToggleButton.selectedBackground [Arc_Theme_-_Orange]ProgressBar.selectionBackground = #000
[Gruvbox_Dark_Soft]ToggleButton.toolbar.selectedBackground=$ToggleButton.toolbar.selectedBackground [Arc_Theme_-_Orange]ProgressBar.selectionForeground = #fff
[Arc_Theme_-_Orange]List.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Table.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Tree.selectionInactiveForeground = #fff
[Hiberbee_Dark]ToggleButton.selectedBackground=$ToggleButton.selectedBackground [Arc_Theme_Dark]ProgressBar.selectionBackground = #ddd
[Hiberbee_Dark]ToggleButton.selectedBackground=$ToggleButton.selectedBackground [Arc_Theme_Dark]ProgressBar.selectionForeground = #ddd
[Hiberbee_Dark]ToggleButton.toolbar.selectedBackground=$ToggleButton.toolbar.selectedBackground
[High_contrast]ToggleButton.selectedBackground=#fff [Arc_Theme_Dark_-_Orange]ProgressBar.selectionBackground = #ddd
[High_contrast]ToggleButton.selectedForeground=#000 [Arc_Theme_Dark_-_Orange]ProgressBar.selectionForeground = #fff
[High_contrast]ToggleButton.disabledSelectedBackground=#444
[High_contrast]ToggleButton.toolbar.selectedBackground=#fff [Cobalt_2]CheckBox.icon.background = #002946
[Cobalt_2]CheckBox.icon.checkmarkColor = #002946
[Cobalt_2]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Cobalt_2]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Cyan_light]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[Cyan_light]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived)
[Dracula]ProgressBar.selectionBackground = #fff
[Dracula]ProgressBar.selectionForeground = #fff
[Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Hard]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Medium]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[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
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[High_contrast]ToggleButton.selectedBackground = #fff
[High_contrast]ToggleButton.selectedForeground = #000
[High_contrast]ToggleButton.disabledSelectedBackground = #444
[High_contrast]ToggleButton.toolbar.selectedBackground = #fff
[Monocai]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Monocai]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
@Monocai.acceleratorForeground = lazy(MenuItem.disabledForeground)
@Monocai.acceleratorSelectionForeground = lighten(MenuItem.disabledForeground,10%,lazy)
[Monocai]CheckBoxMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]CheckBoxMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monocai]Menu.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]Menu.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monocai]MenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]MenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[One_Dark]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[One_Dark]Slider.focusedColor = fade(#568af2,40%)
[Solarized_Dark]Slider.focusedColor = fade($Component.focusColor,80%,derived)
[vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]Slider.trackValueColor = #ececee
[vuesion-theme]Slider.trackColor = #303a45
[vuesion-theme]Slider.thumbColor = #ececee
[vuesion-theme]Slider.focusedColor = fade(#ececee,20%)
# Material Theme UI Lite
[light][author-Mallowigi]MenuItem.checkBackground = @ijMenuCheckBackgroundD10
[light][author-Mallowigi]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundD10
[dark][author-Mallowigi]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[dark][author-Mallowigi]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[Dracula_Contrast]ProgressBar.selectionBackground = #fff
[Dracula_Contrast]ProgressBar.selectionForeground = #fff
[GitHub]ProgressBar.selectionBackground = #222
[GitHub]ProgressBar.selectionForeground = #222
[GitHub_Contrast]ProgressBar.selectionBackground = #222
[GitHub_Contrast]ProgressBar.selectionForeground = #222
[Light_Owl]ProgressBar.selectionBackground = #111
[Light_Owl]ProgressBar.selectionForeground = #fff
[Light_Owl_Contrast]ProgressBar.selectionBackground = #111
[Light_Owl_Contrast]ProgressBar.selectionForeground = #fff
[Material_Lighter]ProgressBar.selectionBackground = #222
[Material_Lighter]ProgressBar.selectionForeground = #fff
[Material_Lighter_Contrast]ProgressBar.selectionBackground = #222
[Material_Lighter_Contrast]ProgressBar.selectionForeground = #fff
[Material_Oceanic]ProgressBar.selectionBackground = #ddd
[Material_Oceanic]ProgressBar.selectionForeground = #ddd
[Material_Oceanic_Contrast]ProgressBar.selectionBackground = #ddd
[Material_Oceanic_Contrast]ProgressBar.selectionForeground = #ddd
[Material_Palenight]ProgressBar.selectionBackground = #ddd
[Material_Palenight]ProgressBar.selectionForeground = #ddd
[Material_Palenight_Contrast]ProgressBar.selectionBackground = #ddd
[Material_Palenight_Contrast]ProgressBar.selectionForeground = #ddd
[Night_Owl]ProgressBar.selectionBackground = #ddd
[Night_Owl]ProgressBar.selectionForeground = #ddd
[Night_Owl_Contrast]ProgressBar.selectionBackground = #ddd
[Night_Owl_Contrast]ProgressBar.selectionForeground = #ddd
[Solarized_Dark]ProgressBar.selectionBackground = #ccc
[Solarized_Dark]ProgressBar.selectionForeground = #ccc
[Material_Solarized_Dark_Contrast]ProgressBar.selectionBackground = #ccc
[Material_Solarized_Dark_Contrast]ProgressBar.selectionForeground = #ccc
[Solarized_Light]ProgressBar.selectionBackground = #222
[Solarized_Light]ProgressBar.selectionForeground = #fff
[Material_Solarized_Light_Contrast]ProgressBar.selectionBackground = #222
[Material_Solarized_Light_Contrast]ProgressBar.selectionForeground = #fff

View File

@@ -15,34 +15,51 @@
#---- FileChooser ---- #---- FileChooser ----
#fields #fields
FileChooser.lookInLabel.textAndMnemonic=Look &In: FileChooser.lookInLabel.textAndMnemonic = Look &In:
FileChooser.saveInLabelText=Save In: FileChooser.saveInLabelText = Save In:
FileChooser.fileNameLabel.textAndMnemonic=File &Name: FileChooser.fileNameLabel.textAndMnemonic = File &Name:
FileChooser.folderNameLabel.textAndMnemonic=Folder &name: FileChooser.folderNameLabel.textAndMnemonic = Folder &name:
FileChooser.filesOfTypeLabel.textAndMnemonic=Files of &Type: FileChooser.filesOfTypeLabel.textAndMnemonic = Files of &Type:
# toolbar # toolbar
FileChooser.upFolderToolTipText=Up One Level FileChooser.upFolderToolTipText = Up One Level
FileChooser.upFolderAccessibleName=Up FileChooser.upFolderAccessibleName = Up
FileChooser.homeFolderToolTipText=Home FileChooser.homeFolderToolTipText = Home
FileChooser.homeFolderAccessibleName=Home FileChooser.homeFolderAccessibleName = Home
FileChooser.newFolderToolTipText=Create New Folder FileChooser.newFolderToolTipText = Create New Folder
FileChooser.newFolderAccessibleName=New Folder FileChooser.newFolderAccessibleName = New Folder
FileChooser.listViewButtonToolTipText=List FileChooser.listViewButtonToolTipText = List
FileChooser.listViewButtonAccessibleName=List FileChooser.listViewButtonAccessibleName = List
FileChooser.detailsViewButtonToolTipText=Details FileChooser.detailsViewButtonToolTipText = Details
FileChooser.detailsViewButtonAccessibleName=Details FileChooser.detailsViewButtonAccessibleName = Details
# details table header # details table header
FileChooser.fileNameHeaderText=Name FileChooser.fileNameHeaderText = Name
FileChooser.fileSizeHeaderText=Size FileChooser.fileSizeHeaderText = Size
FileChooser.fileTypeHeaderText=Type FileChooser.fileTypeHeaderText = Type
FileChooser.fileDateHeaderText=Modified FileChooser.fileDateHeaderText = Modified
FileChooser.fileAttrHeaderText=Attributes FileChooser.fileAttrHeaderText = Attributes
# popup menu # popup menu
FileChooser.viewMenuLabelText=View FileChooser.viewMenuLabelText = View
FileChooser.refreshActionLabelText=Refresh FileChooser.refreshActionLabelText = Refresh
FileChooser.newFolderActionLabelText=New Folder FileChooser.newFolderActionLabelText = New Folder
FileChooser.listViewActionLabelText=List FileChooser.listViewActionLabelText = List
FileChooser.detailsViewActionLabelText=Details FileChooser.detailsViewActionLabelText = Details
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Collapse Left Pane
SplitPaneDivider.collapseRightToolTipText = Collapse Right Pane
SplitPaneDivider.collapseTopToolTipText = Collapse Top Pane
SplitPaneDivider.collapseBottomToolTipText = Collapse Bottom Pane
SplitPaneDivider.expandLeftToolTipText = Expand Left Pane
SplitPaneDivider.expandRightToolTipText = Expand Right Pane
SplitPaneDivider.expandTopToolTipText = Expand Top Pane
SplitPaneDivider.expandBottomToolTipText = Expand Bottom Pane
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText = Show Hidden Tabs

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
# base theme (light, dark, intellij or darcula) # base theme (light, dark, intellij or darcula)
@baseTheme=light @baseTheme = light
# add you theme defaults here # add you theme defaults here
@background=#ccc @background = #ccc

View File

@@ -2,3 +2,12 @@ FlatLaf Demo
============ ============
This sub-project contains the FlatLaf Demo source code. This sub-project contains the FlatLaf Demo source code.
Download
--------
[![Download Demo](https://download.formdev.com/flatlaf/images/download-demo.svg)](https://download.formdev.com/flatlaf/flatlaf-demo-latest.jar)
Run demo with `java -jar flatlaf-demo-<version>.jar` (or double-click it).
Requires Java 8 or newer.

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.demo; package com.formdev.flatlaf.demo;
import java.awt.Component;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.DefaultEditorKit; import javax.swing.text.DefaultEditorKit;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
@@ -114,14 +115,14 @@ class BasicComponentsPanel
JScrollPane scrollPane12 = new JScrollPane(); JScrollPane scrollPane12 = new JScrollPane();
JTextPane textPane4 = new JTextPane(); JTextPane textPane4 = new JTextPane();
JTextPane textPane5 = new JTextPane(); JTextPane textPane5 = new JTextPane();
JLabel label3 = new JLabel(); JLabel errorHintsLabel = new JLabel();
JTextField textField5 = new JTextField(); JTextField errorHintsTextField = new JTextField();
JComboBox<String> comboBox7 = new JComboBox<>(); JComboBox<String> errorHintsComboBox = new JComboBox<>();
JSpinner spinner3 = new JSpinner(); JSpinner errorHintsSpinner = new JSpinner();
JLabel label4 = new JLabel(); JLabel warningHintsLabel = new JLabel();
JTextField textField7 = new JTextField(); JTextField warningHintsTextField = new JTextField();
JComboBox<String> comboBox8 = new JComboBox<>(); JComboBox<String> warningHintsComboBox = new JComboBox<>();
JSpinner spinner4 = new JSpinner(); JSpinner warningHintsSpinner = new JSpinner();
JPopupMenu popupMenu1 = new JPopupMenu(); JPopupMenu popupMenu1 = new JPopupMenu();
JMenuItem cutMenuItem = new JMenuItem(); JMenuItem cutMenuItem = new JMenuItem();
JMenuItem copyMenuItem = new JMenuItem(); JMenuItem copyMenuItem = new JMenuItem();
@@ -129,12 +130,12 @@ class BasicComponentsPanel
//======== this ======== //======== this ========
setLayout(new MigLayout( setLayout(new MigLayout(
"hidemode 3", "insets dialog,hidemode 3",
// columns // columns
"[]" + "[sizegroup 1]" +
"[]" + "[sizegroup 1]" +
"[]" + "[sizegroup 1]" +
"[]" + "[sizegroup 1]" +
"[]" + "[]" +
"[]", "[]",
// rows // rows
@@ -606,44 +607,44 @@ class BasicComponentsPanel
textPane5.setText("No scroll pane"); textPane5.setText("No scroll pane");
add(textPane5, "cell 5 11,growx"); add(textPane5, "cell 5 11,growx");
//---- label3 ---- //---- errorHintsLabel ----
label3.setText("Error hints:"); errorHintsLabel.setText("Error hints:");
add(label3, "cell 0 12"); add(errorHintsLabel, "cell 0 12");
//---- textField5 ---- //---- errorHintsTextField ----
textField5.putClientProperty("JComponent.outline", "error"); errorHintsTextField.putClientProperty("JComponent.outline", "error");
add(textField5, "cell 1 12,growx"); add(errorHintsTextField, "cell 1 12,growx");
//---- comboBox7 ---- //---- errorHintsComboBox ----
comboBox7.putClientProperty("JComponent.outline", "error"); errorHintsComboBox.putClientProperty("JComponent.outline", "error");
comboBox7.setModel(new DefaultComboBoxModel<>(new String[] { errorHintsComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
"Editable" "Editable"
})); }));
comboBox7.setEditable(true); errorHintsComboBox.setEditable(true);
add(comboBox7, "cell 2 12,growx"); add(errorHintsComboBox, "cell 2 12,growx");
//---- spinner3 ---- //---- errorHintsSpinner ----
spinner3.putClientProperty("JComponent.outline", "error"); errorHintsSpinner.putClientProperty("JComponent.outline", "error");
add(spinner3, "cell 3 12,growx"); add(errorHintsSpinner, "cell 3 12,growx");
//---- label4 ---- //---- warningHintsLabel ----
label4.setText("Warning hints:"); warningHintsLabel.setText("Warning hints:");
add(label4, "cell 0 13"); add(warningHintsLabel, "cell 0 13");
//---- textField7 ---- //---- warningHintsTextField ----
textField7.putClientProperty("JComponent.outline", "warning"); warningHintsTextField.putClientProperty("JComponent.outline", "warning");
add(textField7, "cell 1 13,growx"); add(warningHintsTextField, "cell 1 13,growx");
//---- comboBox8 ---- //---- warningHintsComboBox ----
comboBox8.putClientProperty("JComponent.outline", "warning"); warningHintsComboBox.putClientProperty("JComponent.outline", "warning");
comboBox8.setModel(new DefaultComboBoxModel<>(new String[] { warningHintsComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
"Not editable" "Not editable"
})); }));
add(comboBox8, "cell 2 13,growx"); add(warningHintsComboBox, "cell 2 13,growx");
//---- spinner4 ---- //---- warningHintsSpinner ----
spinner4.putClientProperty("JComponent.outline", "warning"); warningHintsSpinner.putClientProperty("JComponent.outline", "warning");
add(spinner4, "cell 3 13,growx"); add(warningHintsSpinner, "cell 3 13,growx");
//======== popupMenu1 ======== //======== popupMenu1 ========
{ {
@@ -668,6 +669,33 @@ class BasicComponentsPanel
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() ); cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() ); pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
if( FlatLafDemo.screenshotsMode ) {
Component[] components = {
button13, button14, button15, button16, comboBox5, comboBox6,
textField6, passwordField5,
formattedTextFieldLabel, formattedTextField1, formattedTextField2, formattedTextField3, formattedTextField4, formattedTextField5,
textAreaLabel, scrollPane1, scrollPane2, scrollPane3, scrollPane4, textArea5,
editorPaneLabel, scrollPane5, scrollPane6, scrollPane7, scrollPane8, editorPane5,
textPaneLabel, scrollPane9, scrollPane10, scrollPane11, scrollPane12, textPane5,
errorHintsLabel, errorHintsTextField, errorHintsComboBox, errorHintsSpinner,
warningHintsLabel, warningHintsTextField, warningHintsComboBox, warningHintsSpinner,
};
for( Component c : components )
c.setVisible( false );
// move password fields one row up
Component[] formattedTextFields = { formattedTextFieldLabel, formattedTextField1, formattedTextField2, formattedTextField3, formattedTextField4 };
Component[] passwordFields = { passwordFieldLabel, passwordField1, passwordField2, passwordField3, passwordField4 };
MigLayout layout = (MigLayout) getLayout();
for( int i = 0; i < passwordFields.length; i++ ) {
Object cons = layout.getComponentConstraints( formattedTextFields[i] );
layout.setComponentConstraints( passwordFields[i], cons );
}
}
} }
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables

View File

@@ -7,8 +7,8 @@ new FormModel {
"JavaCodeGenerator.defaultVariableLocal": true "JavaCodeGenerator.defaultVariableLocal": true
} }
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3" "$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[][][][][][]" "$columnConstraints": "[sizegroup 1][sizegroup 1][sizegroup 1][sizegroup 1][][]"
"$rowConstraints": "[][][][][][][][][][][][]para[][]" "$rowConstraints": "[][][][][][][][][][][][]para[][]"
} ) { } ) {
name: "this" name: "this"
@@ -592,19 +592,19 @@ new FormModel {
"value": "cell 5 11,growx" "value": "cell 5 11,growx"
} ) } )
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3" name: "errorHintsLabel"
"text": "Error hints:" "text": "Error hints:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12" "value": "cell 0 12"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField5" name: "errorHintsTextField"
"$client.JComponent.outline": "error" "$client.JComponent.outline": "error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12,growx" "value": "cell 1 12,growx"
} ) } )
add( new FormComponent( "javax.swing.JComboBox" ) { add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox7" name: "errorHintsComboBox"
"$client.JComponent.outline": "error" "$client.JComponent.outline": "error"
"model": new javax.swing.DefaultComboBoxModel { "model": new javax.swing.DefaultComboBoxModel {
selectedItem: "Editable" selectedItem: "Editable"
@@ -615,25 +615,25 @@ new FormModel {
"value": "cell 2 12,growx" "value": "cell 2 12,growx"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) { add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner3" name: "errorHintsSpinner"
"$client.JComponent.outline": "error" "$client.JComponent.outline": "error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 12,growx" "value": "cell 3 12,growx"
} ) } )
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4" name: "warningHintsLabel"
"text": "Warning hints:" "text": "Warning hints:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13" "value": "cell 0 13"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField7" name: "warningHintsTextField"
"$client.JComponent.outline": "warning" "$client.JComponent.outline": "warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13,growx" "value": "cell 1 13,growx"
} ) } )
add( new FormComponent( "javax.swing.JComboBox" ) { add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox8" name: "warningHintsComboBox"
"$client.JComponent.outline": "warning" "$client.JComponent.outline": "warning"
"model": new javax.swing.DefaultComboBoxModel { "model": new javax.swing.DefaultComboBoxModel {
selectedItem: "Not editable" selectedItem: "Not editable"
@@ -643,7 +643,7 @@ new FormModel {
"value": "cell 2 13,growx" "value": "cell 2 13,growx"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) { add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner4" name: "warningHintsSpinner"
"$client.JComponent.outline": "warning" "$client.JComponent.outline": "warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 13,growx" "value": "cell 3 13,growx"

View File

@@ -27,8 +27,12 @@ import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel; import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import com.formdev.flatlaf.*; import com.formdev.flatlaf.*;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
import net.miginfocom.layout.UnitValue;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
/** /**
@@ -43,6 +47,18 @@ class ControlBar
ControlBar() { ControlBar() {
initComponents(); initComponents();
// remove top insets
MigLayout layout = (MigLayout) getLayout();
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
UnitValue[] insets = lc.getInsets();
lc.setInsets( new UnitValue[] {
new UnitValue( 0, UnitValue.PIXEL, null ),
insets[1],
insets[2],
insets[3]
} );
layout.setLayoutConstraints( lc );
// initialize look and feels combo box // initialize look and feels combo box
DefaultComboBoxModel<LookAndFeelInfo> lafModel = new DefaultComboBoxModel<>(); DefaultComboBoxModel<LookAndFeelInfo> lafModel = new DefaultComboBoxModel<>();
lafModel.addElement( new LookAndFeelInfo( "Flat Light (F1)", FlatLightLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "Flat Light (F1)", FlatLightLaf.class.getName() ) );
@@ -58,9 +74,9 @@ class ControlBar
className.equals( "com.sun.java.swing.plaf.motif.MotifLookAndFeel" ) ) className.equals( "com.sun.java.swing.plaf.motif.MotifLookAndFeel" ) )
continue; continue;
if( (SystemInfo.IS_WINDOWS && className.equals( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" )) || if( (SystemInfo.isWindows && className.equals( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" )) ||
(SystemInfo.IS_MAC && className.equals( "com.apple.laf.AquaLookAndFeel") ) || (SystemInfo.isMacOS && className.equals( "com.apple.laf.AquaLookAndFeel") ) ||
(SystemInfo.IS_LINUX && className.equals( "com.sun.java.swing.plaf.gtk.GTKLookAndFeel") ) ) (SystemInfo.isLinux && className.equals( "com.sun.java.swing.plaf.gtk.GTKLookAndFeel") ) )
name += " (F9)"; name += " (F9)";
else if( className.equals( MetalLookAndFeel.class.getName() ) ) else if( className.equals( MetalLookAndFeel.class.getName() ) )
name += " (F12)"; name += " (F12)";
@@ -111,15 +127,25 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() );
if( SystemInfo.IS_WINDOWS ) if( SystemInfo.isWindows )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" ); registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
else if( SystemInfo.IS_MAC ) else if( SystemInfo.isMacOS )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" ); registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.IS_LINUX ) else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" ); registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
// register Alt+UP and Alt+DOWN to switch to previous/next theme
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> frame.themesPanel.selectPreviousTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> frame.themesPanel.selectNextTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
// register ESC key to close frame // register ESC key to close frame
((JComponent)frame.getContentPane()).registerKeyboardAction( ((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> { e -> {
@@ -193,6 +219,8 @@ class ControlBar
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
try { try {
FlatAnimatedLafChange.showSnapshot();
// change look and feel // change look and feel
UIManager.setLookAndFeel( lafClassName ); UIManager.setLookAndFeel( lafClassName );
@@ -202,6 +230,7 @@ class ControlBar
// update all components // update all components
FlatLaf.updateUI(); FlatLaf.updateUI();
FlatAnimatedLafChange.hideSnapshotWithAnimation();
// increase size of frame if necessary // increase size of frame if necessary
int width = frame.getWidth(); int width = frame.getWidth();

View File

@@ -16,6 +16,9 @@
package com.formdev.flatlaf.demo; package com.formdev.flatlaf.demo;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
@@ -64,6 +67,41 @@ class DataComponentsPanel
} }
} }
private void rowSelectionChanged() {
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
}
private void columnSelectionChanged() {
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
}
private void showHorizontalLinesChanged() {
table1.setShowHorizontalLines( showHorizontalLinesCheckBox.isSelected() );
}
private void showVerticalLinesChanged() {
table1.setShowVerticalLines( showVerticalLinesCheckBox.isSelected() );
}
private void intercellSpacingChanged() {
table1.setIntercellSpacing( intercellSpacingCheckBox.isSelected() ? new Dimension( 1, 1 ) : new Dimension() );
}
private void redGridColorChanged() {
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
}
@Override
public void updateUI() {
super.updateUI();
EventQueue.invokeLater( () -> {
showHorizontalLinesChanged();
showVerticalLinesChanged();
intercellSpacingChanged();
} );
}
@SuppressWarnings( { "unchecked", "rawtypes" } ) @SuppressWarnings( { "unchecked", "rawtypes" } )
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
@@ -80,6 +118,13 @@ class DataComponentsPanel
JLabel tableLabel = new JLabel(); JLabel tableLabel = new JLabel();
JScrollPane scrollPane5 = new JScrollPane(); JScrollPane scrollPane5 = new JScrollPane();
table1 = new JTable(); table1 = new JTable();
JPanel tableOptionsPanel = new JPanel();
showHorizontalLinesCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
intercellSpacingCheckBox = new JCheckBox();
redGridColorCheckBox = new JCheckBox();
rowSelectionCheckBox = new JCheckBox();
columnSelectionCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox(); dndCheckBox = new JCheckBox();
JPopupMenu popupMenu2 = new JPopupMenu(); JPopupMenu popupMenu2 = new JPopupMenu();
JMenuItem menuItem3 = new JMenuItem(); JMenuItem menuItem3 = new JMenuItem();
@@ -89,20 +134,20 @@ class DataComponentsPanel
//======== this ======== //======== this ========
setLayout(new MigLayout( setLayout(new MigLayout(
"hidemode 3", "insets dialog,hidemode 3",
// columns // columns
"[]" + "[]" +
"[200]" + "[200,fill]" +
"[200]", "[200,fill]" +
"[fill]",
// rows // rows
"[]" + "[150,grow,sizegroup 1,fill]" +
"[::200]" + "[150,grow,sizegroup 1,fill]" +
"[::150]" + "[150,grow,sizegroup 1,fill]"));
"[]"));
//---- listLabel ---- //---- listLabel ----
listLabel.setText("JList:"); listLabel.setText("JList:");
add(listLabel, "cell 0 0"); add(listLabel, "cell 0 0,aligny top,growy 0");
//======== scrollPane1 ======== //======== scrollPane1 ========
{ {
@@ -134,7 +179,7 @@ class DataComponentsPanel
list1.setComponentPopupMenu(popupMenu2); list1.setComponentPopupMenu(popupMenu2);
scrollPane1.setViewportView(list1); scrollPane1.setViewportView(list1);
} }
add(scrollPane1, "cell 1 0,growx"); add(scrollPane1, "cell 1 0");
//======== scrollPane2 ======== //======== scrollPane2 ========
{ {
@@ -166,11 +211,11 @@ class DataComponentsPanel
list2.setEnabled(false); list2.setEnabled(false);
scrollPane2.setViewportView(list2); scrollPane2.setViewportView(list2);
} }
add(scrollPane2, "cell 2 0,growx"); add(scrollPane2, "cell 2 0");
//---- treeLabel ---- //---- treeLabel ----
treeLabel.setText("JTree:"); treeLabel.setText("JTree:");
add(treeLabel, "cell 0 1"); add(treeLabel, "cell 0 1,aligny top,growy 0");
//======== scrollPane3 ======== //======== scrollPane3 ========
{ {
@@ -207,7 +252,7 @@ class DataComponentsPanel
tree1.setComponentPopupMenu(popupMenu2); tree1.setComponentPopupMenu(popupMenu2);
scrollPane3.setViewportView(tree1); scrollPane3.setViewportView(tree1);
} }
add(scrollPane3, "cell 1 1,growx"); add(scrollPane3, "cell 1 1");
//======== scrollPane4 ======== //======== scrollPane4 ========
{ {
@@ -216,11 +261,11 @@ class DataComponentsPanel
tree2.setEnabled(false); tree2.setEnabled(false);
scrollPane4.setViewportView(tree2); scrollPane4.setViewportView(tree2);
} }
add(scrollPane4, "cell 2 1,growx"); add(scrollPane4, "cell 2 1");
//---- tableLabel ---- //---- tableLabel ----
tableLabel.setText("JTable:"); tableLabel.setText("JTable:");
add(tableLabel, "cell 0 2"); add(tableLabel, "cell 0 2,aligny top,growy 0");
//======== scrollPane5 ======== //======== scrollPane5 ========
{ {
@@ -297,13 +342,61 @@ class DataComponentsPanel
table1.setComponentPopupMenu(popupMenu2); table1.setComponentPopupMenu(popupMenu2);
scrollPane5.setViewportView(table1); scrollPane5.setViewportView(table1);
} }
add(scrollPane5, "cell 1 2 2 1,growx,width 300"); add(scrollPane5, "cell 1 2 2 1,width 300");
//---- dndCheckBox ---- //======== tableOptionsPanel ========
dndCheckBox.setText("enable drag and drop"); {
dndCheckBox.setMnemonic('D'); tableOptionsPanel.setLayout(new MigLayout(
dndCheckBox.addActionListener(e -> dndChanged()); "insets 0,hidemode 3",
add(dndCheckBox, "cell 0 3 3 1"); // columns
"[]",
// rows
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0"));
//---- showHorizontalLinesCheckBox ----
showHorizontalLinesCheckBox.setText("show horizontal lines");
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 0");
//---- showVerticalLinesCheckBox ----
showVerticalLinesCheckBox.setText("show vertical lines");
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 1");
//---- intercellSpacingCheckBox ----
intercellSpacingCheckBox.setText("intercell spacing");
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 2");
//---- redGridColorCheckBox ----
redGridColorCheckBox.setText("red grid color");
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 3");
//---- rowSelectionCheckBox ----
rowSelectionCheckBox.setText("row selection");
rowSelectionCheckBox.setSelected(true);
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 4");
//---- columnSelectionCheckBox ----
columnSelectionCheckBox.setText("column selection");
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 5");
//---- dndCheckBox ----
dndCheckBox.setText("enable drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
tableOptionsPanel.add(dndCheckBox, "cell 0 6");
}
add(tableOptionsPanel, "cell 3 2");
//======== popupMenu2 ======== //======== popupMenu2 ========
{ {
@@ -336,6 +429,12 @@ class DataComponentsPanel
private JTree tree1; private JTree tree1;
private JTree tree2; private JTree tree2;
private JTable table1; private JTable table1;
private JCheckBox showHorizontalLinesCheckBox;
private JCheckBox showVerticalLinesCheckBox;
private JCheckBox intercellSpacingCheckBox;
private JCheckBox redGridColorCheckBox;
private JCheckBox rowSelectionCheckBox;
private JCheckBox columnSelectionCheckBox;
private JCheckBox dndCheckBox; private JCheckBox dndCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables

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