Compare commits

...

759 Commits
0.12 ... 0.42

Author SHA1 Message Date
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
Karl Tauber
8a6242d9ea release 0.38 2020-07-10 15:45:35 +02:00
Karl Tauber
82294b68eb CheckBox: fixed colors in light IntelliJ themes (issue #126) 2020-07-10 15:35:02 +02:00
Karl Tauber
c232de1996 Window decorations: fixed cursor of components (issue #125) 2020-07-10 11:39:17 +02:00
Karl Tauber
dc18c8178d Theme Editor: fixed typo 2020-07-10 10:54:09 +02:00
Karl Tauber
6662714277 Theme Editor: auto-completion improved:
- reference completion shows all keys defined in current and base files
- support auto-activate for value provider
- do not auto-complete single choices
2020-07-10 10:33:10 +02:00
Karl Tauber
c404a0d1a9 Theme Editor:
- auto-activate key completion on any letter
- special completion provider for references
2020-07-08 19:15:52 +02:00
Karl Tauber
990da2b412 Theme Editor:
- auto-activate completion popup when '$' is pressed
- use keys auto-complete in value if value contains '$'
- more fine grained detection what completion provider should be used
2020-07-08 18:07:37 +02:00
Karl Tauber
1b974379c8 UIDefaultsLoader: check for endless recursion in resolveValue() 2020-07-08 17:57:40 +02:00
Karl Tauber
835faf9773 Theme Editor: auto-completion depending on caret position (none for comments, keys and values); added color functions 2020-07-08 14:29:11 +02:00
Karl Tauber
80deecb73e Theme Editor: close input streams when reading base properties files 2020-07-08 10:59:40 +02:00
Karl Tauber
64328ab9cc UIDefaultsLoader: trim value in resolveValue() to ignore spaces at the end of references/variables 2020-07-08 10:47:36 +02:00
Karl Tauber
eafad942e7 Theme Editor: added basic auto-complete for keys 2020-07-08 10:43:24 +02:00
Karl Tauber
eb5a3168b9 Theme Editor: support loading/resolving base properties 2020-07-07 21:42:10 +02:00
Karl Tauber
ac8225d8fb Theme Editor: support saving file; added inspector 2020-07-07 16:17:31 +02:00
Karl Tauber
6f71e4ada0 Theme Editor: use deferred properties loading 2020-07-07 14:21:31 +02:00
Karl Tauber
7ed90cddf8 Theme Editor: support color preview for color functions
UIDefaultsLoader: made some private methods package private and return parsed valued type
2020-07-07 14:03:39 +02:00
Karl Tauber
283ba83cef Window decorations: use derived color for RootPane.inactiveBorderColor in FlatLightLaf.properties to be consistent with FlatDarkLaf.properties 2020-07-06 15:47:44 +02:00
Karl Tauber
468c66e842 Window decorations: hide window icon if InternalFrame.icon is null or its width or height is zero 2020-07-06 14:45:52 +02:00
Karl Tauber
f22862b0a4 InternalFrame: use default icon in internal frames (issue #122) 2020-07-06 14:41:17 +02:00
Karl Tauber
9e731cb67a Tree: fixed cell editor border 2020-07-06 12:01:53 +02:00
Karl Tauber
7f911b61a2 Window decorations: no longer honor minimum size of frames on resizing window, but still do for dialogs 2020-07-06 11:30:49 +02:00
Karl Tauber
cace4a9bfd Window decorations: center title if menu bar is embedded 2020-07-05 11:01:58 +02:00
Karl Tauber
0992e97a1a README.md: added Mapton, Pseudo Assembler IDE, Sound Analysis and RemoteLight to list of projects that use FlatLaf 2020-07-04 23:47:47 +02:00
Karl Tauber
eee101f279 Merge remote-tracking branch 'uwemock/patch-1' 2020-07-04 21:46:08 +02:00
Karl Tauber
4b9f204951 Tree: fixed selection colors when used as cell renderer in another component (e.g. in Rhino JavaScript debugger) (issue #120) 2020-07-04 17:51:13 +02:00
Karl Tauber
019804407b Window decorations: hide window border if window is maximized 2020-07-01 12:11:53 +02:00
Karl Tauber
65b54ced7a Window decorations: made most fields protected for extending/subclassing 2020-07-01 10:49:18 +02:00
Karl Tauber
a308114b2f Window decorations:
- use window border color from UI defaults
- support "active" and "inactive" window border colors
- better window border colors for dark themes
2020-07-01 10:37:08 +02:00
Karl Tauber
41da023bdd hide focus indicator when the containing window became inactive 2020-07-01 00:21:22 +02:00
Karl Tauber
19fcb6a82c refactored some anonymous classes into nested classes for easier extending/subclassing 2020-06-30 17:02:48 +02:00
Karl Tauber
14c837ad05 release 0.37 2020-06-29 17:03:06 +02:00
Karl Tauber
9da634e225 CHANGELOG.md: added custom window decorations 2020-06-29 17:00:28 +02:00
Karl Tauber
0d91116e62 Merge branch 'origin/custom-window-decorations' into master
# Conflicts:
#	flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java
2020-06-29 15:59:38 +02:00
Karl Tauber
a31a8a03c1 Window decorations: made most classes/methods public/protected for extending/subclassing 2020-06-29 15:45:26 +02:00
Karl Tauber
e8d5210606 Window decorations: use default icon if no icon set on window 2020-06-29 12:20:57 +02:00
Karl Tauber
7b11e29122 Button and ToggleButton: support making buttons square (issue #118) 2020-06-29 10:49:07 +02:00
Karl Tauber
df7f693cf4 Demo: new window icon 2020-06-28 23:59:28 +02:00
Karl Tauber
14ddc2f629 Demo: use window decorations by default and added "Options > Window decorations" to menu 2020-06-28 12:12:58 +02:00
Karl Tauber
6669d0e59d Window decorations: support enabling/disabling embedding menu bar via UI value at runtime 2020-06-28 11:34:30 +02:00
Karl Tauber
8d80176a79 IntelliJ Themes: fixed menu bar and menu item margins in all Material UI Lite themes 2020-06-28 00:28:02 +02:00
Karl Tauber
e1dc302592 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2020-06-27 23:09:00 +02:00
Karl Tauber
84dbe39185 FileChooser: increase maximum row count of directory combobox popup list to 20 (was 8) 2020-06-27 22:49:22 +02:00
Karl Tauber
4af2c31dab Eclipse code formatter: insert space in casts 2020-06-27 22:26:49 +02:00
Karl Tauber
332f05b6e1 Window decorations: allow enabling/disabling custom window decorations via system properties "flatlaf.useWindowDecorations", "flatlaf.useJetBrainsCustomDecorations" and "flatlaf.menuBarEmbedded" (all boolean) 2020-06-27 19:36:36 +02:00
Karl Tauber
8b4786ad18 added class FlatSystemProperties to define/document own system properties used in FlatLaf 2020-06-27 17:57:59 +02:00
Karl Tauber
7e8aaffb92 Window decorations:
- double-click on icon closes window
- after switching LaF is was not possible to move window when running in JetBrains Runtime
2020-06-26 10:49:49 +02:00
Karl Tauber
7720d42584 Window decorations: reworked/fixed initialization when running in JetBrains Runtime 2020-06-26 00:22:28 +02:00
Karl Tauber
293b76f04b Window decorations: FlatWindowDecorationsTest: added "undecorated" checkbox 2020-06-25 17:55:42 +02:00
Karl Tauber
a1b0c0bbd4 ComboBox: increase maximum row count of popup list to 20 (was 8) 2020-06-25 17:00:10 +02:00
Karl Tauber
46d3204bc3 MenuBar:
- use derived colors for menu bar hover
- use derived colors for menu item selected background
- top-level JMenu now uses foreground color from parent JMenuBar

This allows changing menu bar background to dark with:
  UIManager.put( "MenuBar.background", Color.DARK_GRAY );
  UIManager.put( "MenuBar.foreground", Color.WHITE );
or
  menuBar.setBackground( Color.DARK_GRAY );
  menuBar.setForeground( Color.WHITE );

(issue #117)
2020-06-25 11:36:36 +02:00
Karl Tauber
c25ff57b61 Button, CheckBox, RadioButton and ToggleButton: do not paint focus indicator if AbstractButton.isFocusPainted() returns false 2020-06-24 16:45:41 +02:00
Karl Tauber
71e61f8f27 made class FlatCaret public for subclassing (issue #113) 2020-06-24 13:02:25 +02:00
Karl Tauber
6914a6132c Button: prefer explicitly set background/foreground over focused background and "default" background/foreground (issue #116) 2020-06-24 12:43:49 +02:00
Karl Tauber
b72916187a Button: invoke FlatButtonUI.getForeground(c) also if component is disabled to be consistent with getBackground(c) 2020-06-23 12:45:25 +02:00
Karl Tauber
7c9bbe6aef Merge branch 'master' into branch 'custom-window-decorations' 2020-06-23 11:11:14 +02:00
Karl Tauber
27eeb0a636 Demo: use uppercase leading characters 2020-06-22 23:35:56 +02:00
Karl Tauber
cf436962f8 fixed/improved vertical position of HTML text when scaled on HiDPI screens on Windows 2020-06-22 23:31:01 +02:00
Karl Tauber
7fb7a1ac85 fixed/improved vertical position of text when scaled on HiDPI screens on Windows when running on Java 8 2020-06-22 21:05:11 +02:00
Karl Tauber
15a714faed fixed/improved vertical position of text when scaled on HiDPI screens on Windows when running on Java 9 or later 2020-06-22 13:45:56 +02:00
Karl Tauber
ea2412d3a7 Improved subclassing:
- reviewed all private methods and made them protected/public where it might be useful for subclasses
- ComboBox and Spinner: added protected getBackground() and getForeground() methods to allow subclasses to change colors
- TabbedPane: moved tab separator painting to own method

(issue #113)
2020-06-20 10:46:56 +02:00
Karl Tauber
40321856f2 Testing: updated 3rd party Lafs 2020-06-19 19:49:26 +02:00
Karl Tauber
262ae7865b ComboBox and Spinner: support changing arrow button style (issue #114) 2020-06-19 18:12:23 +02:00
Karl Tauber
84cc86bef7 CheckBox and RadioButton: support changing selected icon style from outline to filled
renamed CheckBox.icon.focusedColor to CheckBox.icon.focusColor
2020-06-19 15:36:49 +02:00
Karl Tauber
1ba27730d6 UIDefaultsDump: fixed order of removed values in diff dumps 2020-06-19 13:38:25 +02:00
Karl Tauber
6568cee2e8 UIDefaultsDump: dump IntelliJ and Darcula themes (as differences to Light/dark themes; Windows only) 2020-06-18 20:29:40 +02:00
Karl Tauber
5496a60f62 CheckBox: reordered icon colors (grouped by state) to make it easier to maintain
(nothing else changed)
2020-06-18 18:09:32 +02:00
Karl Tauber
5c7378cf94 Button and ToggleButton: paint disabled background by default (issue #112) 2020-06-18 12:02:02 +02:00
Karl Tauber
fe15f44e96 ScrollBar: support pressed track, thumb and button colors (issue #115) 2020-06-18 11:04:38 +02:00
Karl Tauber
273d762cd3 ScrollBar: avoid continuous repainting scrollbar when moving mouse pointer over track and ScrollBar.hoverThumbWithTrack is enabled (regression in fd208a3879) 2020-06-17 23:53:06 +02:00
Karl Tauber
211030b5b6 TableHeader: support top/bottom/left positioned sort arrow when using Glazed Lists (issue #113) 2020-06-16 18:52:59 +02:00
Karl Tauber
212c553904 Testing: added class FlatGlazedListsTest for testing Glazed Lists (https://github.com/glazedlists/glazedlists) table sorting (issue #113) 2020-06-16 16:48:00 +02:00
Karl Tauber
dffe4f4451 Button and ToggleButton: support disabled background color (issue #112) 2020-06-15 23:34:21 +02:00
Karl Tauber
fd99af5fe6 added Java code style formatter profile 'FlatLaf' for Eclipse projects (#71) 2020-06-10 00:25:19 +02:00
Karl Tauber
aee539bbef CHANGELOG.md: added missing change to scrollbars on macOS and Linux 2020-06-09 18:25:56 +02:00
Karl Tauber
e7cdc9cf8c release 0.36 2020-06-09 12:33:27 +02:00
Karl Tauber
2443547b3b FlatTestFrame: removed no longer needed nested JRootPane (was used for UI inspector) 2020-06-08 16:03:04 +02:00
Karl Tauber
8424300b5f Demo: faster repainting when enabling/disabling components 2020-06-08 15:58:52 +02:00
Karl Tauber
81822cf7f6 Demo: added UI inspector 2020-06-08 15:45:19 +02:00
Karl Tauber
907956994f Extras: FlatInspector:
- do not increase inspection level when activated with keyboard shortcut
- added some javadoc
- added to CHANGELOG.md and flatlaf-extras/README.md
2020-06-08 15:44:52 +02:00
Karl Tauber
9246cc0607 Extras: added FlatInspector (moved from flatlaf-testing) 2020-06-08 15:03:34 +02:00
Karl Tauber
9f81d147d1 Demo on macOS: enabled screen menu bar by default, except if explicitly disabled 2020-06-08 14:29:47 +02:00
Karl Tauber
b9bd26b2fb FlatSVGIcon: support mapping custom colors 2020-06-08 14:11:06 +02:00
Karl Tauber
1838174678 added "use" tab to javadoc 2020-06-08 12:53:48 +02:00
Karl Tauber
0880a3380c Window decorations: hide drag border components if frame is maximized 2020-06-07 23:22:57 +02:00
Karl Tauber
2aad301938 Spinner: fixed arrow positions 2020-06-07 18:27:55 +02:00
Karl Tauber
e18e8e3158 Popup: made Popup.show(), hide() and component listener more robust when used in unusual ways (issue #106) 2020-06-07 15:25:11 +02:00
Karl Tauber
ff55cc1a2a Window decorations: do not overwrite maximized bounds if controlled from the application 2020-06-07 11:57:05 +02:00
Karl Tauber
d081b9e182 Window decorations: do not restore maximized bounds in method maximize() because when restoring an iconified frame by clicking on the Windows 10 taskbar the maximize() method is not invoked and the frame size becomes full screen size and overlaps taskbar 2020-06-07 11:31:11 +02:00
Karl Tauber
5e5b9f0990 Window decorations: fixed maximized bounds on Java 15 (issues #47 and #82) 2020-06-07 11:03:22 +02:00
Karl Tauber
97577e835e Window decorations: fixed top border when running in JetBrains Runtime (issues #47 and #82) 2020-06-06 22:16:26 +02:00
Karl Tauber
732ca8be56 FlatLaf.isLafDark() added 2020-06-06 22:00:54 +02:00
Karl Tauber
1381a34752 FlatInspector: ignore FlatWindowResizer 2020-06-06 15:38:22 +02:00
Karl Tauber
dd96712c2a Menu: no longer add 1px to bottom insets of JMenu contained in JMenuBar to fix vertical alignment of JMenu text with FlatTitlePane title text on Java 9+ on HiDPI screens (due to rounding)
(this extra 1px was actually not necessary)
2020-06-06 15:31:11 +02:00
Karl Tauber
2ad0aba382 Window decorations: enable dark window appearance on macOS when running in JetBrains Runtime (issues #47 and #82) 2020-06-06 13:53:22 +02:00
Karl Tauber
8e77eb0519 Window decorations: support resizing window (issues #47 and #82) 2020-06-06 12:20:33 +02:00
Karl Tauber
049dae6584 Button: support non-square icon-only buttons (issue #110) 2020-06-03 15:55:14 +02:00
Karl Tauber
1fffc67d13 Window decorations: added border (issues #47 and #82) 2020-06-02 17:49:30 +02:00
Karl Tauber
8500781cd5 Merge branch 'master' into branch 'custom-window-decorations'
# Conflicts:
#	flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java
2020-06-02 16:13:35 +02:00
Karl Tauber
6a8bf2acc5 FlatInspector: fixed highlight figure bounds of windows; limit used inspection level to existing components 2020-06-02 16:07:11 +02:00
Karl Tauber
c45a769aa3 update JFrame/JDialog background color when switching Laf 2020-06-02 15:46:36 +02:00
Karl Tauber
16d2e27d05 Window decorations: require Windows 10 (issues #47 and #82) 2020-05-31 15:31:28 +02:00
Karl Tauber
10c948d33c Window decorations: nested class FlatRootPaneUI.FlatRootLayout is no longer static (issues #47 and #82) 2020-05-31 14:53:13 +02:00
Karl Tauber
7ccd32dfbd Window decorations: fixed menu bar border if embedded (issues #47 and #82) 2020-05-31 14:45:44 +02:00
Karl Tauber
99c99b9218 Window decorations: support embedding menu bar into title pane (enabled by default) (issues #47 and #82) 2020-05-31 14:10:58 +02:00
Karl Tauber
e0b0617ad2 macOS Catalina: Use Helvetica Neue font 2020-05-30 21:44:52 +02:00
Karl Tauber
5add723852 Window decorations: support right-to-left component orientation (issues #47 and #82) 2020-05-30 18:33:22 +02:00
Karl Tauber
14ec6f6471 FlatInspector: increase/decrease inspection level with Ctrl/Shift keys 2020-05-30 17:35:54 +02:00
Karl Tauber
c4a1341aa9 FlatInspector:
- support ending inspection with ESC key
- inspect component at current mouse location when enabling inspector
2020-05-30 16:53:20 +02:00
Karl Tauber
fc68dfd7bc FlatInspector: support inspecting whole window including menubar and custom window decoration 2020-05-30 15:19:07 +02:00
Karl Tauber
436fc545c0 Window decorations: support native Windows 10 custom window decorations with JetBrains Runtime 11 (issues #47 and #82) 2020-05-29 16:44:33 +02:00
Karl Tauber
023d781daf Window decorations: set maximized title pane height to 22px (issues #47 and #82) 2020-05-29 11:17:46 +02:00
Karl Tauber
576c0048d0 Window decorations: make title pane height smaller when frame is maximized (issues #47 and #82) 2020-05-29 00:26:10 +02:00
Karl Tauber
4f79cdad50 Window decorations: support moving window (issues #47 and #82) 2020-05-28 23:49:46 +02:00
Karl Tauber
954cae8738 Window decorations: limit size of moximized windows so that they do not overlap the Windows task bar (issues #47 and #82) 2020-05-28 23:04:43 +02:00
Karl Tauber
b203ad63ee IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2020-05-28 12:17:46 +02:00
Karl Tauber
a560be11ed InternalFrame: renamed FlatInternalFrameMinimizeIcon to FlatInternalFrameRestoreIcon; added some missing @uiDefault to internal frame icons 2020-05-28 11:50:34 +02:00
Karl Tauber
506a1e6b62 Window decorations: iconify/maximize/restore/close button icons in Windows 10 style (issues #47 and #82) 2020-05-28 11:35:30 +02:00
Karl Tauber
626601f6aa Window decorations: added window icon (issues #47 and #82) 2020-05-27 11:40:41 +02:00
Karl Tauber
9ad32125c0 Window decorations: initial implementation (incomplete) (issues #47 and #82)
TODO
- move window
- resize window
- window icon
- window border
2020-05-26 23:35:05 +02:00
Karl Tauber
ebd6375672 Spinner: optimized up/down chevron arrow positions 2020-05-25 13:05:59 +02:00
Karl Tauber
502731d3b0 Spinner: optimized up/down arrow positions 2020-05-24 19:05:28 +02:00
Karl Tauber
283535c429 Demo: use Command modifier to change font size on macOS 2020-05-24 15:26:54 +02:00
Karl Tauber
5cef1f6730 Testing: added font size spinner to control bar; also support Ctrl+0, Ctrl++ and Ctrl+- to change font size 2020-05-24 15:26:07 +02:00
Karl Tauber
7d14fbe739 Testing: do not fail startup when LaF initialization throws UnsupportedClassVersionError (may occur when switching from Java 9+ to Java 8) 2020-05-24 14:48:05 +02:00
Karl Tauber
e9e1e350eb Spinner:
- repaint if JSpinner component gained/lost focus
- paint focus border if JSpinner component is focused
- if spinner gained focus, transfer it to the editor text field
2020-05-24 14:44:36 +02:00
Karl Tauber
566e42cc40 revalidate layout when minimum width client property is changed 2020-05-23 22:57:39 +02:00
Karl Tauber
0abfb5922a ComboBox: minimum width is now 72 pixels (was ~50 for non-editable and ~130 for editable comboboxes) 2020-05-23 22:25:18 +02:00
Karl Tauber
4af8d2f1c5 ComboBox: support custom borders in combobox editors (issue #102) 2020-05-23 18:26:59 +02:00
Karl Tauber
d2d4f73834 ScrollBar: use derived colors for track and thumb (issue #103) 2020-05-23 16:40:09 +02:00
Karl Tauber
53fce4e81d ScrollBar: rotate track/thumb insets for horizontal orientation because they are given for vertical orientation (issue #103) 2020-05-23 14:16:12 +02:00
Karl Tauber
08c439b46e ScrollBar: use rounded thumb on macOS (issue #103) 2020-05-23 13:58:05 +02:00
Karl Tauber
934eb9fc1d ScrollBar: use rounded thumb on Linux (issue #103) 2020-05-23 13:51:25 +02:00
Karl Tauber
fd208a3879 ScrollBar: made styling more flexible by supporting insets and arc for track and thumb (issue #103) 2020-05-23 13:32:31 +02:00
Karl Tauber
10b131e111 Demo: show Java vendor in bottom control bar 2020-05-23 11:28:28 +02:00
Karl Tauber
c4c6faa943 Ubuntu Linux: fixed poorly rendered font (2nd attempt) (issue #105) 2020-05-23 11:06:24 +02:00
Karl Tauber
c7a8d1e1b7 Linux: changing system font did not update FlatLaf font 2020-05-22 18:22:46 +02:00
Karl Tauber
b36ac1b824 UI defaults: added GTKLookAndFeel dump made on Fedora 31 (Adweita theme) 2020-05-21 18:38:46 +02:00
Karl Tauber
bc6cb492f1 Ubuntu Linux: fixed poorly rendered font (issue #105) 2020-05-21 17:11:58 +02:00
Karl Tauber
ce503cedc3 Demo: improved "Font" menu:
- add current font family and size to menu
- filter out unavailable fonts
- select active font family and size
- disable font menu items if non-FlatLaf LaF is active

also show current font in bottom control bar

(issue #105)
2020-05-21 12:24:40 +02:00
Karl Tauber
c900c9cc82 reduce derived colors calculations 2020-05-20 14:49:56 +02:00
Karl Tauber
87b73f26f5 replaced FlatUIUtils.setColor() with deriveColor() for more flexibility 2020-05-20 14:24:22 +02:00
uwemock
221a18c119 Update README.md
Added my project that uses FlatLaf
2020-05-20 07:20:42 +02:00
Karl Tauber
be529655d6 UIDefaultsLoader: on color functions use "autoInverse" option by default if "derived" option is set 2020-05-20 00:40:05 +02:00
Karl Tauber
2a0403a988 support CompoundBorder as component border with FlatBorder on the outside 2020-05-19 23:24:00 +02:00
Karl Tauber
815e23b930 ScrollBar: make hoverTrack and hoverThumb fields protected to allow subclasses implement own painting (issue #103) 2020-05-19 19:24:48 +02:00
Karl Tauber
f1c08e7769 FlatTestFrame: added Substance Business skin for testing light UI 2020-05-19 18:42:22 +02:00
Karl Tauber
571f028ca3 FlatComponentsTest: moved components that change something into own "control" panel 2020-05-19 11:31:52 +02:00
Karl Tauber
16d51fe6b4 ComboBox and Spinner: move arrow slightly to the left if round borders are used on the component 2020-05-18 23:26:34 +02:00
Karl Tauber
ddf9ed06ab release 0.35 2020-05-18 21:22:31 +02:00
Karl Tauber
1907f80024 Demo: fixed compiler warnings and improved error/warning hints 2020-05-18 21:07:11 +02:00
Karl Tauber
8c0ccdd227 Drop shadows on Windows: support medium-weight popups (issue #94) 2020-05-18 13:13:57 +02:00
Karl Tauber
dc098025b6 FileChooser: make top-right buttons look like toolbar buttons 2020-05-18 10:51:23 +02:00
Karl Tauber
c11222b5c7 FlatHtmlTest: added more HTML samples 2020-05-17 22:50:40 +02:00
Karl Tauber
03bc6eb69b FlatTestFrame: '2.0' --> '2' 2020-05-17 18:46:21 +02:00
Karl Tauber
1aa339de02 make component outline border wider if focus width is zero 2020-05-17 17:59:26 +02:00
Karl Tauber
531bb2a968 UIDefaultsDump: dump only differences for macOS
(to avoid the need for updating multiple dumps when changing UI defaults)
2020-05-17 17:52:12 +02:00
Karl Tauber
800dbf3ba9 support different component border colors to indicate errors, warnings or custom state (set client property JComponent.outline to error, warning or any java.awt.Color) 2020-05-17 13:43:19 +02:00
Karl Tauber
ff545e6ecd UIDefaultsLoader: support using a derived color function within another derived color function and create a derived color that joins the color functions 2020-05-17 12:14:14 +02:00
Karl Tauber
961fe38c7e UIDefaultsDump: dump color functions 2020-05-16 22:25:23 +02:00
Karl Tauber
19426394e2 UIDefaultsLoader: added saturate() and desaturate() color functions 2020-05-16 18:59:05 +02:00
Karl Tauber
069a4e8f0b ToolTip: fixed left/right insets of multi-line tooltips so that they are the same as in single-line tooltips (BasicToolTipUI adds 3 to the left and right) 2020-05-16 14:33:55 +02:00
Karl Tauber
a76b02b828 fixed broken FlatTestLaf.properties 2020-05-16 14:19:41 +02:00
Karl Tauber
fbb9bf5f0c Extras: TriStateCheckBox: fixed painting third state in LaFs that do not support third state 2020-05-16 12:29:35 +02:00
Karl Tauber
f632c355e8 FileChooser: scale file icons (issue #100) 2020-05-16 11:03:40 +02:00
Karl Tauber
e75caf5833 FileChooser: use system icons (issue #100) 2020-05-15 17:20:52 +02:00
Karl Tauber
b0c8f2cefd TextComponents: reduced duplicate code; fixed parameter order 2020-05-15 15:00:32 +02:00
Karl Tauber
2136d9f13d PasswordField: do not apply minimum width if columns property > 0 2020-05-15 14:06:33 +02:00
Karl Tauber
83fdeb7e0c ComboBox, Spinner and TextField: support round border style (set client property JComponent.roundRect to true) 2020-05-15 13:38:45 +02:00
Karl Tauber
26c77b3118 Button, ComboBox, TextField and DatePicker UI delegates now get Component.focusWidth and Button.arc/Component.arc/TextComponent.arc from component border 2020-05-15 11:32:53 +02:00
Karl Tauber
578d445ecb FlatBorder: moved scaling from getter methods to paintBorder() and getBorderInsets() 2020-05-14 23:35:11 +02:00
Karl Tauber
3bbc9517af Popup: fixed background flashing effect when drop shadows are disabled (issue #94) 2020-05-14 14:48:12 +02:00
Karl Tauber
a4d7f278cf Drop shadows on Windows: fix location of light weight popup in case it has left or top drop shadow (issue #94) 2020-05-14 11:44:00 +02:00
Karl Tauber
bf0ffc6ac2 Drop shadows: support enabling/disabling drop shadows per component (issue #94) 2020-05-14 11:39:09 +02:00
Karl Tauber
ace07cd9cb Drop shadows on Windows: fixed sub-pixel text rendering issue for heavy weight popups (issue #94) 2020-05-14 11:11:11 +02:00
Karl T
a341179426 Merge pull request #101 from cristatus/patch-2
Fix menu background flashing effect
2020-05-14 10:46:15 +02:00
Amit Mendapara
298f0dfd63 Fix menu background flashing effect
When using dark theme on light platform theme, there was a
background flashing effect on popups.

See #94
2020-05-14 11:43:59 +05:30
Karl Tauber
b8f953cd26 Drop shadows on Windows: use light weight popups by default (issue #94)
this fixes the sub-pixel text rendering issue (on Windows) for popups that fit into the owner window
2020-05-13 18:41:26 +02:00
Karl Tauber
a9cfe69ba7 FileChooser: fixed missing labels in file chooser when running on Java 9 or later (issue #98) 2020-05-13 12:50:41 +02:00
Karl Tauber
b3e0b99e8d Button and ToggleButton: support round button style (set client property JButton.buttonType to roundRect) 2020-05-13 11:45:01 +02:00
Karl Tauber
5bd40baed2 Extras: TriStateCheckBox: paint magenta rectangle when used in LaFs that do not support third state 2020-05-12 23:26:52 +02:00
Karl Tauber
d3a70b8bb2 CheckBox and RadioButton: Opaque flag is no longer ignored when checkbox is used as table cell renderer (issue #77)
this fix replaces/improves fix made in commit 3ba8133890
2020-05-12 22:35:05 +02:00
Karl Tauber
71e698603d ComboBox: fixed painting background outside of border if Component.arc is set to a large value 2020-05-12 22:29:59 +02:00
Karl Tauber
659ead903c TextField: avoid garbage in corners if TextComponent.arc is set to a large value 2020-05-12 18:58:17 +02:00
Karl Tauber
070c435f40 paint nicely rounded buttons, comboboxes, spinners and text fields when setting Button.arc, Component.arc or TextComponent.arc to a large value (e.g. 1000) 2020-05-12 17:48:35 +02:00
Karl Tauber
b668a526e3 changed "Flat" to "FlatLaf" in look and feel names and descriptions 2020-05-12 16:47:46 +02:00
Karl Tauber
01287d0669 Popup: allow disabling native drop shadows for popups on macOS 2020-05-12 16:42:55 +02:00
Karl T
ff481d759f Merge pull request #99 from cristatus/patch-1
Fix popup shadow issue on Linux

https://github.com/JFormDesigner/FlatLaf/issues/94#issuecomment-626344149
2020-05-10 19:25:17 +02:00
Amit Mendapara
71248f1708 Fix popup shadow issue on Linux
Linux adds drop shadow to heavy weight popups. So there is no
need to draw shadow manually.
2020-05-10 22:46:57 +05:30
Karl Tauber
0a0f834f23 Drop shadows:
- reworked drop shadows implementation to support 4-sided shadows
- use 4-sided shadow for internal frames
- made shadows configurable in UI defaults
- made shadows dark in dark themes

(issue #94)
2020-05-10 15:38:50 +02:00
Karl Tauber
06cad7ecd8 Popup: make sure that popup background is filled (issue #94) 2020-05-09 23:50:48 +02:00
Karl Tauber
ceba3e2f95 CHANGELOG.md: added Java 9 module descriptor to extras and swingx JARs 2020-05-09 15:59:29 +02:00
Karl Tauber
61c2fd8794 build.gradle.kts: use MigLayout 5.3-SNAPSHOT for better scaling
Demo: exclude module-info.class from fat JAR
2020-05-09 15:42:18 +02:00
Karl Tauber
db933fee4f build.gradle.kts: flatlaf-extras and flatlaf-swingx are now Java modules
flatlaf-jide-oss is not a Java module because jide-oss.jar does not run on the module path (tries to access private Windows LaF classes)
2020-05-09 15:32:25 +02:00
Karl Tauber
2656c2dc40 build.gradle.kts: moved publishing related configuration to precompiled script plugin 2020-05-09 13:54:16 +02:00
Karl Tauber
01cfe33865 build.gradle.kts: moved module-info and java9 related configuration to precompiled script plugins 2020-05-09 11:16:40 +02:00
Karl Tauber
d79a31cc79 build.gradle.kts: use withSourcesJar() and withJavadocJar()
this adds resources to sources.jar
2020-05-09 02:09:03 +02:00
Karl Tauber
9efab8b892 travis: added openjdk14 and removed openjdk13 2020-05-09 00:30:38 +02:00
Karl Tauber
aae845247a update to Gradle 6.4
./gradlew wrapper --gradle-version=6.4
2020-05-09 00:16:07 +02:00
Karl Tauber
3f45a9a75f Merge remote-tracking branch 'origin/drop-shadows' into master 2020-05-08 19:02:13 +02:00
Karl Tauber
c9016155ae Demo: added "Options > Always show mnemonics" to menu 2020-05-08 18:58:02 +02:00
Karl Tauber
1019e8f4af Extras: added FlatSVGIcon and download section 2020-05-08 18:50:02 +02:00
Karl Tauber
8e423b4552 release 0.34 2020-05-08 14:53:16 +02:00
Karl Tauber
0e288c955c Extras: added publishing tasks to build.gradle.kts 2020-05-08 14:44:41 +02:00
Karl Tauber
465dc8a66c Popup: added drop shadows to all popups (menu, combobox and tooltip) on all platforms (issue #94) 2020-05-08 11:02:20 +02:00
Karl Tauber
7e5c599cc0 added user scale factor to UI defaults to allow layout managers (e.g. MigLayout) to use it
(see https://github.com/mikaelgrev/miglayout/pull/76)
2020-05-07 23:28:57 +02:00
Karl Tauber
a961001a4b reorder entries in JAR file to fix issues #13 and #93 2020-05-07 14:45:22 +02:00
Karl Tauber
0a181f6407 InternalFrame: added drop shadows (issue #94)
also made borders of internal frames in dark themes darker
2020-05-07 00:07:02 +02:00
Karl Tauber
27a347db34 PopupMenu on macOS: enabled drop shadows for popup menus and combobox popups (issue #94) 2020-05-05 19:20:48 +02:00
Karl Tauber
b228dbb2df Demo on macOS: enabled screen menu bar by default 2020-05-05 19:10:57 +02:00
Karl Tauber
09cffc4340 UIDefaultsDump: avoid locale specific decimal separators in dumps 2020-05-05 18:53:31 +02:00
Karl Tauber
e79880d305 ToolTip: made border darker (to make it better and no longer paint disabled tips (issue #94) 2020-05-05 18:44:54 +02:00
Karl Tauber
34266761d1 UIDefaultsDump: dump FlatLineBorder parameters because they may be specified in properties files 2020-05-05 16:34:30 +02:00
Karl Tauber
77f17eaa3e FlatPropertiesLaf class added that allows creating FlatLaf theme from properties (issue #97) 2020-05-05 15:13:21 +02:00
Karl Tauber
ac70342cb3 Menus: made check background margin smaller (issue #96) 2020-05-05 13:56:41 +02:00
Karl Tauber
d2f16dcaf3 Menus:
- added 1px to menu item top and bottom margin
- changed gap between menu item icon and text from 4 to 6
- improved colors of checked menu items that have a icon

(issue #96)
2020-05-05 12:31:33 +02:00
Karl Tauber
abcce2bf68 Table: fixed inconsistent table selection / move shortcuts (issue #95) 2020-05-04 13:30:42 +02:00
Karl Tauber
514487074b Menus: after Alt+Tab to other window and back, activating menu with Alt key did not always work (issue #43) 2020-05-04 12:08:47 +02:00
Karl Tauber
f014e2473f Menus: on Windows, releasing Alt key now activates the menu bar (issue #43) 2020-05-04 10:57:10 +02:00
Karl Tauber
80981f7027 Demo: added "Extras" tab 2020-05-03 19:34:21 +02:00
Karl Tauber
8e6e971b51 IntelliJ Themes Demo: theme save and github buttons were not enabled when starting demo with active IntelliJ theme 2020-05-03 18:23:16 +02:00
Karl Tauber
4bd3b889dc FlatSVGIcon: support color filtering 2020-05-03 18:21:00 +02:00
Karl Tauber
464787dc1e FlatSVGIcon: use grayFilter and graphics proxy to paint disabled icons without bitmaps 2020-05-02 23:48:46 +02:00
Karl Tauber
a2541a9659 Menus: added gap between accelerator and arrow in menu items (issue #91) 2020-05-02 19:16:33 +02:00
Karl Tauber
099dd87241 UIDefaultsLoader: removed support for deprecated variable prefix '@@' 2020-05-02 16:20:17 +02:00
Karl Tauber
38eb914420 Mnemonics: scale underline; added mnemonic test app
FlatTestFrame: Metal Laf is now at F12 so that F10 is unused because F10 is a standard key to move focus to menu bar
2020-05-02 14:38:54 +02:00
Karl Tauber
162215b1cf UIDefaultsLoader:
- support percentage in rgb() and rgba() functions
- support rgba(color,alpha) to add alpha to any color
2020-05-02 11:52:53 +02:00
Karl Tauber
c6883f7a92 ToolTip: use BasicHTML.propertyKey to check whether tooltip contains HTML 2020-05-02 00:41:11 +02:00
Karl Tauber
584286b794 Demo: wrap OptionPanePanel in JPanel to avoid that JFormDesigner tries to convert it to a container when opening DemoFrame.jfd 2020-05-02 00:33:47 +02:00
Karl Tauber
a48713b7ca no longer always show mnemonics when a menu bar is active or a popup menu is visible (issue #43) 2020-05-01 00:22:04 +02:00
Karl Tauber
8f10c2d8bf Menus: removed now unused *.evenHeight from list of UI defaults 2020-04-30 22:33:42 +02:00
Karl Tauber
5c0de9aa1c macOS: Fixed NPE if using JMenuBar in JInternalFrame and macOS screen menu bar is enabled (issue #90) 2020-04-30 13:38:23 +02:00
Karl Tauber
5553fd6538 CHANGELOG.md: added changes made in 'menu-layout' branch 2020-04-30 00:54:36 +02:00
Karl Tauber
e3ed47b37c show mnemonics always when a menu bar is active or a popup menu is visible 2020-04-29 23:56:15 +02:00
Karl Tauber
976353d770 Menus: on Windows, pressing F10 now activates the menu bar without showing a menu popup 2020-04-29 23:29:34 +02:00
Karl Tauber
6fc216dff5 Menus: fixed text color of selected menu items that use HTML (issue #87) 2020-04-29 19:22:09 +02:00
Karl Tauber
3f3961d255 fixed broken FlatTestLaf.properties 2020-04-29 19:14:18 +02:00
Karl Tauber
875637bc6d Menus: support switching "underline" menu selection type at any time without updating UI (issue #49) 2020-04-29 14:46:33 +02:00
Karl Tauber
395333cb3d Merge branch 'origin/menu-layout' into master 2020-04-29 13:39:51 +02:00
Karl Tauber
870d039541 hide mnemonics if window is deactivated (e.g. Alt+Tab to another window) (issue #43) 2020-04-29 12:06:00 +02:00
Karl Tauber
e8c8bece3f Menus: support "underline" menu selection type (suggested in issue #49) 2020-04-29 00:26:25 +02:00
Karl Tauber
bd2f5dd6fe Menus: 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) (issue #3) 2020-04-28 18:00:01 +02:00
Karl Tauber
73f78d47ae refactored mnemonic code into own class 2020-04-28 12:02:10 +02:00
Karl Tauber
8f60755f02 release 0.33 2020-04-27 18:39:02 +02:00
Karl Tauber
44c455419b IntelliJ Themes: added Java 9 module descriptor to flatlaf-intellij-themes-<version>.jar 2020-04-27 18:25:49 +02:00
Karl Tauber
129bc9b3ae IntelliJ Themes Pack: use absolute resource location for loading themes (PR #88, issue #89) 2020-04-27 16:28:38 +02:00
Karl T
08ba7dd065 Merge pull request #88 from matt-pan/patch-1
IntelliJ Themes: Fix relative resource location for Material UI Lite Themes
2020-04-27 16:14:50 +02:00
Karl Tauber
dd2cf50a39 Menus: use simpler method to compute center offset (same as in SwingUtilities.layoutCompoundLabel()), which gives same results and avoids floats 2020-04-27 13:14:11 +02:00
Karl Tauber
06eeced5b2 Menus: made accelerator text in dark themes brighter; updated UI defaults dumps (issue #3) 2020-04-27 12:07:06 +02:00
Karl Tauber
be23e5709d Menus: support alignment and text position properties (issue #3) 2020-04-27 11:52:11 +02:00
Karl Tauber
2735185eb9 Menus: fixed icon in top-level JMenu (issue #3) 2020-04-26 14:20:09 +02:00
Karl Tauber
41dd0acfa3 Menus: use disabled and pressed icons (issue #3) 2020-04-26 11:27:59 +02:00
Karl Tauber
115a2df2b0 Menus: support HTML in new layout (issue #3) 2020-04-26 10:35:23 +02:00
Karl Tauber
fcbb3aeed1 Menus: new menu item layout and renderer
- stable left margin (always space for one icon)
- right aligned accelerators
- larger gap between text and accelerator

current limitations:
- no HTML text support
- text not vertically aligned with other menu items if icons have different sizes
- vertical/horizontal alignment/textPosition properties are ignored

(issues #3 and #54)
2020-04-26 10:13:52 +02:00
matt-pan
e9cb85127a Fix relative resource location
Using flatlaf intellijthemes as a dependency does not work for all themes from the subfolder 'material-theme-ui-lite' because of the usage of ".." to  to load the resource.
2020-04-25 17:46:57 +02:00
Karl Tauber
c9c703fe98 support multi-resolution images in disabled icons on Java 9+ (e.g. @2x icons on macOS) (issue #70) 2020-04-24 17:07:30 +02:00
Karl Tauber
0141dfbea2 CHANGELOG.md: added IntelliJ Theme fixes 2020-04-24 00:55:49 +02:00
Karl Tauber
fb7dafbc39 Merge branch 'disabled-icons' into master 2020-04-24 00:54:05 +02:00
Karl Tauber
0660f9a511 improved creation of disabled grayscale icons (issue #70) 2020-04-24 00:46:16 +02:00
Karl Tauber
a39ae5a8c5 FlatDisabledIconsTest: support palette icons 2020-04-24 00:16:44 +02:00
Karl Tauber
03e22e3dbf Demo: exclude module-info.class from JAR 2020-04-23 23:54:54 +02:00
Karl Tauber
d5e9fd0e5c IntelliJ Themes:
- fixed ComboBox size and Spinner border in all Material UI Lite themes
- limit tree row height in all Material UI Lite themes and some other themes
2020-04-23 23:46:14 +02:00
Karl Tauber
141138ebea IntelliJ Themes Pack: added readme 2020-04-23 18:16:57 +02:00
Karl Tauber
9026efeb26 release 0.32 2020-04-23 16:04:31 +02:00
Karl Tauber
2ab023beb0 UIDefaultsDump: used FlatAllIJThemes instead of IJThemesManager to get list of IJ themes 2020-04-23 14:02:25 +02:00
Karl Tauber
8e471fd720 IntelliJ Themes: generated Java classes for all themes (used IJThemesClassGenerator) 2020-04-23 13:59:59 +02:00
Karl Tauber
13cbbd8bc1 IntelliJ Themes: moved themes into own sub-project and build a JAR that contains all themes 2020-04-23 11:06:12 +02:00
Karl Tauber
b08ccc9767 UIDefaultsLoader: no longer support/use derived colors without base colors 2020-04-22 11:49:44 +02:00
Karl Tauber
801a7023a4 IntelliJ Themes: fixed toggle button selected backgrounds (issue #86) 2020-04-22 10:30:14 +02:00
Karl Tauber
dd06b554da ToggleButton: compute selected background color based on current component background (issue #32) 2020-04-22 09:48:58 +02:00
Karl Tauber
23f0504b30 IntelliJ Themes: fixed toggle button unselected background in most themes and foreground in Darcula and One Dark themes (issue #86) 2020-04-21 14:39:11 +02:00
Karl Tauber
262d172cde IntelliJ Themes: removed code that is obsolete since supporting gradient button background/border colors in commit de82dac8
make sure that gradient colors are predefined for improved compatibility
this fixes button background in Arc themes and toggle button background in Dark Flat and Light Flat themes
2020-04-21 10:02:11 +02:00
Karl Tauber
be81cb7876 IntelliJ Themes Demo: removed IntelliJ Light Preview theme because Flat light already uses colors from this theme since commit 78d5e03a 2020-04-21 09:41:53 +02:00
Karl Tauber
aaf9bd33cb UIDefaultsDump: support dumping IntelliJ themes (disabled)
can be used to check changes to UI defaults when modifying the IntelliJ theme converter
2020-04-21 09:37:24 +02:00
Karl Tauber
7381e2141f IntelliJ Themes Demo: updated Dracula, Gruvbox and Hiberbee themes (used IJThemesUpdater) 2020-04-21 00:10:18 +02:00
Karl Tauber
3923d941c1 IntelliJ Themes Demo: updated Material UI Lite themes (used IJThemesUpdater)
List.selectionInactiveBackground and Tree.selectionInactiveBackground now have better visible colors; other changes seem to be not used in FlatLaf
2020-04-20 23:28:39 +02:00
Karl Tauber
d134c33499 release 0.31 2020-04-20 11:44:00 +02:00
Karl Tauber
a2b615d4a7 focus indication border (or background) no longer hidden when temporary loosing focus (e.g. showing a popup menu) 2020-04-20 11:27:29 +02:00
Karl Tauber
37ecd9bd4f FlatDisabledIconsTest: renamed @2x_dark.png icons to _dark@2x.png so that they are automatically loaded on macOS Retina displays 2020-04-19 10:23:40 +02:00
Karl Tauber
2e1acb7871 List, Table and Tree: item selection color of focused components no longer change from blue to gray when temporary loosing focus (e.g. showing a popup menu) 2020-04-17 19:14:48 +02:00
Karl Tauber
2250185487 Testing: FlatDisabledIconsTest: use intellij dark icons in dark themes 2020-04-14 12:41:14 +02:00
Karl Tauber
97a1bf90a4 README.md: added Total Validator and GUIslice Builder to list of projects that use FlatLaf 2020-04-14 10:16:37 +02:00
Karl Tauber
73cb63c9f9 Testing: added FlatDisabledIconsTest to compare different methods to create disabled icons 2020-04-13 15:48:07 +02:00
Karl Tauber
f61f6b6006 Merge pull request #72 from basix86:disabledIcon into branch disabled-icons
Improve disabled button rendering #70
2020-04-11 13:57:05 +02:00
Karl Tauber
7d3ffbc45a README.md: added MegaMek and MekHQ to list of projects that use FlatLaf 2020-04-09 17:26:08 +02:00
Karl Tauber
93ac6fa88a README.md: added XMLmind XML Editor, MeteoInfo and lsfusion platform to list of projects that use FlatLaf 2020-04-09 14:43:14 +02:00
Karl Tauber
09d19a13b7 release 0.30 2020-04-09 13:51:11 +02:00
Karl Tauber
9eaee8d2c4 Windows: fixed rendering of Unicode characters (issue #81) 2020-04-09 13:39:37 +02:00
Karl Tauber
4da0c342f8 Theme Editor: paint real colors and HSL values in overlay on right side of editor (instead of behind editor text; previous commit) 2020-04-06 15:42:58 +02:00
Karl Tauber
70fed22737 Theme Editor: use real colors as background of color strings 2020-04-05 23:18:39 +02:00
Karl Tauber
acb62e347a Theme Editor: mark invalid color values with a curly underline 2020-04-05 18:04:28 +02:00
Karl Tauber
78d06d82d6 Theme Editor: fixed mark occurrences for property references (starting with '$') and property references in function parameters 2020-04-05 17:16:09 +02:00
Karl Tauber
4777bdd250 Theme Editor: do not mark token at caret if it does not occur elsewhere 2020-04-05 15:23:40 +02:00
Karl Tauber
266e9d92d5 Theme Editor: enabled mark occurrences 2020-04-05 14:37:25 +02:00
Karl Tauber
54c14d0dc8 Theme Editor: added editor theme 2020-04-05 11:56:28 +02:00
Karl Tauber
d59d353c2e Theme Editor: added token maker (based on .properties token maker) 2020-04-04 23:32:48 +02:00
Karl Tauber
204da2175b Theme Editor: initial commit 2020-04-04 14:13:20 +02:00
basix86
a8f659f2ac Pull Request #72: Improve disabled button rendering #70 2020-04-02 21:25:41 +02:00
basix86
a878ebc368 Merge branch 'master' into disabledIcon 2020-04-02 21:25:41 +02:00
Karl Tauber
152f235ca1 release 0.29 2020-04-01 23:26:47 +02:00
Karl Tauber
d094709dc8 ComboBox: no longer ignore JComboBox.prototypeDisplayValue when computing popup width (issue #80) 2020-03-31 18:53:55 +02:00
Karl Tauber
97d5792341 ComboBox: made class FlatComboPopup protected to allow subclassing (issue #80) 2020-03-31 17:29:12 +02:00
Karl Tauber
9429ba7d48 support specifying custom scale factor in system property flatlaf.uiScale also for Java 9 and later 2020-03-31 12:17:05 +02:00
Karl Tauber
af89dd13c1 support changing default font used for all components with automatic scaling UI if using larger font 2020-03-31 12:15:51 +02:00
Karl Tauber
60c6c5b37a FlatLineBorder: support specifying painted line thickness 2020-03-29 23:30:04 +02:00
Karl Tauber
5ed40cab1d Demo: support using own FlatLaf themes (.properties files) that are located in working directory of Demo application 2020-03-29 18:02:35 +02:00
Karl Tauber
1bebfe9cf2 IntelliJ Themes Demo: updated Arc, Arc Orange and Hiberbee themes (used IJThemesUpdater) 2020-03-28 09:41:51 +01:00
Karl Tauber
e2618c37a2 Testing: added "size variant" combobox to control bar if Aqua or Nimbus LaF are active 2020-03-28 09:41:03 +01:00
Karl Tauber
f2ab848c46 FlatOptionPaneTest: scroll pane added 2020-03-27 23:49:25 +01:00
Karl Tauber
93b82c0e97 FlatDefaultsAddon: added afterDefaultsLoading() method to allow modification of UI defaults by Addons 2020-03-27 23:21:55 +01:00
Karl Tauber
4ac5ad06f2 IntelliJ Themes: simplified applying theme properties to UI defaults 2020-03-27 18:54:30 +01:00
Karl Tauber
a3788038bb Tree: fixed repainting wide selection on focus gained/lost 2020-03-27 10:51:20 +01:00
Karl Tauber
12af2de99e no longer use system property sun.java2d.uiScale (Java 8 only) 2020-03-27 10:44:43 +01:00
Karl Tauber
225b722b1b Linux: fixed wrong font size if GDK_SCALE environment variable is set or if running on JetBrains Runtime (issue #69) 2020-03-26 17:20:09 +01:00
Karl Tauber
1d9c8ca65e Linux: fixed scaling if GDK_SCALE environment variable is set or if running on JetBrains Runtime (issue #69) 2020-03-26 13:06:12 +01:00
Karl Tauber
e51ffe2a1c release 0.28 2020-03-16 22:47:44 +01:00
Karl Tauber
c706a79f74 UIScale: fixed NPE in getSystemScaleFactor() (occurred in progress bar on startup in NB) 2020-03-16 22:46:33 +01:00
Karl Tauber
2608061d48 reviewed (and tested) all key bindings on macOS 2020-03-16 15:20:17 +01:00
Karl Tauber
df1634de3d FlatTestFrame: add JGoodies Windows LaF only when running on Windows 2020-03-15 10:21:28 +01:00
Karl Tauber
4aeabea3fe UI defaults: updated FlatLightLaf_InputMap_1.8.0_202-mac.txt on Mac 2020-03-15 10:16:28 +01:00
Karl Tauber
eb30f9d5bf copy all font attributes in FlatUIUtils.nonUIResource() and when scaling fonts (issue #75) 2020-03-12 11:22:43 +01:00
Karl Tauber
de718f847c README.md: added KeyStore Explorer and OWASP Zed Attack Proxy (ZAP) to list of projects that use FlatLaf 2020-03-12 11:15:10 +01:00
mmatessi
8ee6588d46 fix review #70 2020-03-05 13:08:49 +01:00
mmatessi
7c25f087fb NPE getDisabledIcon Fix 2020-03-05 13:07:15 +01:00
mmatessi
d0b0f098d9 disabledIcon 2020-03-05 13:07:15 +01:00
Karl Tauber
8835e20bfc .editorconfig: added Java code style for IntelliJ IDEA (#71) 2020-02-28 15:00:04 +01:00
Karl Tauber
92258f3ba3 ScrollBar: improved colors
Table: use color functions
2020-02-27 12:49:43 +01:00
Karl Tauber
1bda7595dd UI defaults: support dumping IntelliJ and Darcula themes (disabled) 2020-02-27 11:52:42 +01:00
Karl Tauber
60557fc8c8 reviewed (and tested) all key bindings on Windows 2020-02-26 23:47:38 +01:00
Karl Tauber
1a4a7831f6 UI defaults: support dumping 3rd party LaFs 2020-02-26 23:34:49 +01:00
Karl Tauber
d1415a8c53 TabbedPane: support Ctrl+TAB/Ctrl+Shift+TAB to switch to next/previous tab if a child of tabbedpane has focus 2020-02-26 00:11:10 +01:00
Karl Tauber
0d4f33ac6e ScrollBar: fixed left/right keys in right-to-left component orientation 2020-02-25 16:59:30 +01:00
Karl Tauber
87100bef7b re-worked Aqua LaF initialization of macOS 2020-02-24 22:51:36 +01:00
Karl Tauber
958dfa8ae9 UI defaults: get rid of Aqua InputMaps on macOS 2020-02-24 20:44:14 +01:00
Karl Tauber
d3752573e7 FlatInputMaps: use static import for DefaultEditorKit and changed order of keys
(nothing else changed)
2020-02-24 13:16:24 +01:00
Karl Tauber
cfd07cbcc8 PasswordField: warn about enabled Caps Lock 2020-02-23 13:15:12 +01:00
Karl Tauber
33e6ce1673 UI defaults: get rid of unused Aqua UI defaults on macOS
UI defaults on macOS are now (nearly) equal to other platforms.
There are only minor platform specific differences.
InputMaps are still used from Aqua LaF.
2020-02-23 10:57:37 +01:00
Karl Tauber
00ccda83f9 UIScale: default font size on macOS is 13 2020-02-22 14:39:43 +01:00
Karl Tauber
8d66cce6eb README.md: fixed Twitter link 2020-02-22 14:35:41 +01:00
Karl Tauber
ba35fb7525 Panel: added UI delegate 2020-02-21 22:28:58 +01:00
Karl Tauber
4fd2b24b10 README.md: fixed Twitter link 2020-02-21 19:10:27 +01:00
Karl Tauber
5f7a33b085 README.md: Buzz section added 2020-02-21 18:56:32 +01:00
Karl Tauber
65bc5b1f31 README.md: added mendelson AS2, AS4 and OFTP2 to list of projects that use FlatLaf 2020-02-21 18:42:44 +01:00
Karl Tauber
dd155e9f89 CheckBox and RadioButton: fixed NPE when button has children (similar to PR #68) 2020-02-21 16:52:38 +01:00
Karl T
5c4ef3b0f5 Merge pull request #68 from basix86/FlatButtonUi
FlatButtonUI getPreferredSize nullPointer fix
2020-02-21 16:33:56 +01:00
mmatessi
2129a48cc8 FlatButtonUI.getPreferredSize fix nullPointer when button.getComponentCount()>0 2020-02-21 10:34:57 +01:00
mmatessi
c0f2784599 FlatButtonUI.getPreferredSize fix nullPointer when button.getComponentCount()>0 2020-02-21 10:32:25 +01:00
Karl Tauber
fee00b2acb UI defaults: replaced "base" Metal LaF with BasicLookAndFeel (on Windows and Linux)
(UI defaults dumps did not change, so UI defaults are equal to Metal defaults)
2020-02-19 16:47:02 +01:00
Karl Tauber
ae8093313e UI defaults: add text antialiasing hint
(preparation for replacing "base" Metal LaF with BasicLookAndFeel)
2020-02-19 15:35:29 +01:00
Karl Tauber
5a4e321f78 UI defaults: added/modified InputMaps (on Windows and Linux) so that they are equal to Metal LaF InputMaps when using Basic LaF as base
(preparation for replacing "base" Metal LaF with BasicLookAndFeel)
2020-02-19 09:15:20 +01:00
Karl Tauber
9d1ed241b9 UI defaults: removed optional "pressed " from InputMap dumps (to make it easier to read) 2020-02-18 22:41:04 +01:00
Karl Tauber
b63cd241d2 refactored InputMap code into own class 2020-02-18 18:09:49 +01:00
Karl Tauber
1e4f2d85a2 UI defaults: removed FlatDarkLaf InputMap dumps because they are equal to the Light versions 2020-02-18 18:08:14 +01:00
Karl Tauber
5cb7be4a64 UI defaults: added Java 8 - 13 dumps of NimbusLookAndFeel
(only checked in dumps that are not equal to version predecessor)
2020-02-18 17:45:27 +01:00
Karl Tauber
0cd9068c0e UI defaults: split InputMap dumps into own files
(only checked in dumps that are not equal to version predecessor)
2020-02-18 17:40:21 +01:00
Karl Tauber
9d6afe3bde UI defaults: removed dumps that are equal to version predecessor 2020-02-18 17:04:24 +01:00
Karl Tauber
52702b5267 ColorChooser: use scaled dimension instead of temporary modifying UI defaults 2020-02-18 14:32:15 +01:00
Karl Tauber
cd144ff067 UI defaults: added UI values that are defined in MetalLookAndFeel but not in BasicLookAndFeel
also added UI values that have different values in Metal and Basic LaF

(preparation for replacing "base" Metal LaF with BasicLookAndFeel)
2020-02-18 13:49:19 +01:00
Karl Tauber
dd9784b3f2 UI defaults: added UI values that are defined in MetalLookAndFeel but not in BasicLookAndFeel; also added UI values that have different values in Metal and Basic LaF
(preparation for replacing "base" Metal LaF with BasicLookAndFeel)
2020-02-18 13:36:07 +01:00
Karl Tauber
d781b3d4a7 FlatLaf.getDefaults() simplified 2020-02-18 13:24:34 +01:00
Karl Tauber
790f490674 replaced MetalRootPaneUI on Windows and Linux (issue #47)
(preparation for replacing "base" Metal LaF with BasicLookAndFeel)
2020-02-18 12:52:53 +01:00
Karl Tauber
004a5cb765 UI defaults: added some missing colors an set unused colors to useful values 2020-02-18 12:11:58 +01:00
Karl Tauber
408b2d8376 Merge branch 'uidefaults-review' into master
# Conflicts:
#	flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatDarkLaf_1.8.0_202.txt
#	flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_1.8.0_202.txt
2020-02-18 00:43:17 +01:00
Karl Tauber
3aa53ff3be CHANGELOG.md: Gradianto themes 2020-02-17 23:36:22 +01:00
Karl Tauber
73b642799d UI defaults dumps updated 2020-02-17 23:35:38 +01:00
Karl Tauber
b522500379 TextField, FormattedTextField and PasswordField: support round border (issue #65) 2020-02-17 22:39:16 +01:00
Karl Tauber
f736ed401f - tuned/fixed component border painting
- HiDPIUtils.paintAtScale1x() now also works if the Graphics is scaled (as in FlatPaintingTest)
- FlatPaintingTest added
2020-02-17 22:19:36 +01:00
Karl Tauber
c02f824d74 FlatInspector: added layout manager and insets UI resource 2020-02-17 11:28:56 +01:00
Karl Tauber
6f9a61de56 IntelliJ Themes Demo: updated Arc, Arc Orange and Nord themes (used IJThemesUpdater) 2020-02-17 11:22:07 +01:00
Karl Tauber
4cebeda37a IntelliJ Themes: added popular Gradianto IntelliJ themes to demo 2020-02-17 11:16:27 +01:00
Karl Tauber
4275005a64 FlatTestFrame: avoid initializing LaF classes in isClassAvailable() because this invokes static class initialization, which logs a reflection warning on Java 13 for WebLaf 2020-02-17 10:13:48 +01:00
Karl Tauber
5f40ab130e build.gradle.kts:
- added version info to manifest
- added META-INF/LICENSE
- Java source/target compatibility now defined in single location
- defined source file encoding for java compiler
2020-02-16 16:58:01 +01:00
Karl Tauber
7489526eb7 build.gradle.kts:
- plugin versions now defined in single location (settings.gradle.kts)
- going back to Gradle 6.1.1
- using "extra" properties for bintray user and key
- allow easy enabling/disabling bintray upload dryMode and publishing
2020-02-16 12:18:08 +01:00
Karl Tauber
e439d91763 build.gradle.kts: fix bintray upload (broken since adding snapshot publishing) 2020-02-15 00:03:57 +01:00
Karl Tauber
793969e39b downgrade from Gradle 6.1.1 to 6.1 to check whether this fixes the broken bintray upload (worked in 0.26 with 6.1) 2020-02-14 16:30:46 +01:00
Karl Tauber
9f7ffe8d77 travis: changed condition for release stage again (because it did not start) 2020-02-14 16:04:53 +01:00
Karl Tauber
eecb867227 travis: changed condition for release stage (because it did not start) 2020-02-14 16:00:04 +01:00
Karl Tauber
bff9f135e6 release 0.27 2020-02-14 15:41:18 +01:00
Karl Tauber
03627281d7 ToolBar: added empty space around toolbar (issue #56) 2020-02-14 13:59:14 +01:00
Karl Tauber
c83b4093f0 ToolBar: added empty space around buttons in toolbar (issue #56) 2020-02-14 12:53:30 +01:00
Karl Tauber
7f9f22df3e merged PR #61 into master (with minor modifications)
Bug #60 Illegal reflective access operation on mac
2020-02-13 17:13:07 +01:00
Karl Tauber
fd48582a9f ToolBar: no longer use special rollover border for buttons in toolbar (issue #36) 2020-02-13 15:53:54 +01:00
Karl Tauber
e5761128f9 ToggleButton: make toggle button square if it has an icon but no text or text is "..." or a single character 2020-02-13 14:51:36 +01:00
Karl Tauber
87dd5a9ebb PasswordField: get echoChar from .properties files and no longer hard code it on macOS 2020-02-13 11:17:29 +01:00
Karl Tauber
11950f8b4d UI defaults: removed unused UI defaults from "base" Metal LaF 2020-02-13 10:52:19 +01:00
smile atom
f2ddfadc9d fix: #60 should compile on JDK 8 2020-02-12 19:01:35 -08:00
smile atom
b1a7983f18 fix: #60 Illegal reflective access operation on mac 2020-02-12 18:46:39 -08:00
Karl Tauber
b319cb278b UI defaults: updated dumps on macOS 2020-02-12 18:47:22 +01:00
Karl Tauber
78e3d781fc UI defaults: changed dump format of characters and updated dumps on Windows 2020-02-12 18:43:35 +01:00
Karl Tauber
34834917b0 UI defaults:
- added macOS Java 8 - 13 dumps of AquaLookAndFeel
- added macOS Java 8 dumps of FlatLightLaf and FlatDarkLaf

used macOS Mojave 10.14.
2020-02-12 14:23:56 +01:00
Karl Tauber
9446c287e9 UI defaults:
- class UIDefaultsDump implemented to dump UI defaults to text files
- added Java 8 - 13 dumps of BasicLookAndFeel, MetalLookAndFeel, WindowsLookAndFeel
- added Java 8 dumps of FlatLightLaf and FlatDarkLaf
2020-02-12 10:42:07 +01:00
Bill Culp
31b0cf396e fix #60 fix typo 2020-02-11 15:10:58 -08:00
Bill Culp
00bb13c230 fix #60 just use getDeclaredConstructor() 2020-02-11 15:05:22 -08:00
Bill Culp
3bf09ee731 fix #60 Illegal reflective access operation on mac 2020-02-11 15:00:28 -08:00
Bill Culp
5b07941c4c Merge branch 'master' of https://github.com/JFormDesigner/FlatLaf into bug-60 2020-02-11 14:57:51 -08:00
Karl Tauber
23c30ec46d FlatComponentsTest: add checkbox to change contentAreaFilled of all buttons (for issue #58) 2020-02-11 18:44:35 +01:00
Karl Tauber
22c06300f1 merged PR #63 (for issue #58) into master (with modifications)
bug: AbstractButton's ContentAreaFilled=false not honored when parent is a CellRendererPane
2020-02-11 18:34:44 +01:00
Karl Tauber
37cca1b106 merged PR #62 into master (with minor modifications)
Feat 59 Option to allow tabbed pane separator to take full height
2020-02-11 16:48:27 +01:00
Karl Tauber
f0a49c806e DesktopPane support implemented (issues #39 and #11) 2020-02-11 15:38:32 +01:00
Bill Culp
a1d5f65588 bug: AbstractButton's ContentAreaFilled=false not honored when parent is a CellRendererPane
docs: AbstractButton:setContentAreaFilled

Sets the contentAreaFilled property. If true the button will paint the content area. If you wish to have a transparent button, such as an icon only button, for example, then you should set this to false. Do not call setOpaque(false). The default value for the the contentAreaFilled property is true.
This function may cause the component's opaque property to change.

The exact behavior of calling this function varies on a component-by-component and L&F-by-L&F basis.

Parameters:
b - if true, the content should be filled; if false the content area is not filled
2020-02-10 23:37:27 -08:00
Bill Culp
b6789e14a4 Option to allow tabbed pane separator to take full height 2020-02-10 22:32:11 -08:00
Bill Culp
c72ee30a25 fix: Illegal reflective access operation on mac 2020-02-10 18:17:20 -08:00
Karl Tauber
686d667c4f Table: optimized position of column sort arrow (issue #34) 2020-02-08 10:38:48 +01:00
Karl Tauber
26d603db5d UIDefaultsLoader: support scaling float, insets and dimension 2020-02-07 17:25:14 +01:00
Karl Tauber
409840aef9 README.md: added snapshots 2020-02-05 18:23:27 +01:00
Karl Tauber
1f3c264afe travis: moved JDKs back to top-level and execute "test" stage first (replaces "build" stage) 2020-02-05 16:12:56 +01:00
Karl Tauber
cd69d9a1a7 travis: moved JDKs to build job 2020-02-05 15:54:29 +01:00
Karl Tauber
a000c8fd99 travis: use stages and added snapshot upload 2020-02-05 15:29:00 +01:00
Karl Tauber
84d05603ef build.gradle.kts: separate versions for release and development (snapshot) 2020-02-05 15:21:01 +01:00
Karl Tauber
9d046ecd1d build.gradle.kts: added snapshot publishing to oss.jfrog.org 2020-02-05 12:34:07 +01:00
Karl Tauber
030e1809f3 Table: support positioning the column sort arrow in header right, left, top or bottom (issue #34) 2020-02-03 21:27:08 +01:00
Karl Tauber
5853bd4a96 InternalFrame: made buttons larger and square (issue #39) 2020-02-02 17:12:34 +01:00
Karl Tauber
10695ff51b InternalFrame: fixed exception on macOS when minimizing internal frame (#39) 2020-01-27 15:52:04 +01:00
Karl Tauber
f421659fea update to Gradle 6.1.1
./gradlew wrapper --gradle-version=6.1.1
2020-01-27 15:33:30 +01:00
Karl Tauber
df4f51eff3 InternalFrame: basic implementation (issues #39 and #11) 2020-01-27 15:23:03 +01:00
Karl Tauber
7e61d6a850 README.md: added some projects that use FlatLaf 2020-01-26 17:31:48 +01:00
Karl Tauber
0910bd23c4 ProgressBar: fixed visual artifacts in indeterminate mode, on HiDPI screens at 125%, 150% and 175% scaling, when the progress moves around 2020-01-23 10:33:01 +01:00
Karl Tauber
5a29753912 release 0.26 2020-01-22 15:07:56 +01:00
Karl Tauber
a467356437 build.gradle.kts: disable javadoc warnings for missing @param or @return 2020-01-22 14:54:49 +01:00
Karl Tauber
094967f52a ProgressBar: made progress bar paint smooth in indeterminate mode 2020-01-22 14:21:38 +01:00
Karl Tauber
757b0812ba Menu: highlight items in menu bar on mouse hover (issue #49) 2020-01-20 23:35:50 +01:00
Karl Tauber
8f4f5d8c92 FlatClientProperties: fixed javadoc error 2020-01-20 20:17:58 +01:00
Karl Tauber
4e266483ba Menus: menu items now have larger left and right margins 2020-01-20 20:13:37 +01:00
Karl Tauber
7433dc9cf3 Menus: changed menu bar and popup menu background colors (made brighter in light themes and darker in dark themes)
made `JMenu`, `JMenuItem`, `JCheckBoxMenuItem` and `JRadioButtonMenuItem` non-opaque
2020-01-20 20:09:32 +01:00
Karl Tauber
409a773e36 IntelliJ Themes Demo: updated Arc, Arc Orange, and Material UI Lite themes (used IJThemesUpdater) 2020-01-20 14:58:39 +01:00
Karl Tauber
48bdd5c3df TextField, FormattedTextField and PasswordField: select all text when a text field gains focus for the first time and selection was not set explicitly 2020-01-19 18:05:12 +01:00
smileatom
5796057a75 Merge pull request #1 from JFormDesigner/master
merge latest changes
2020-01-18 14:43:29 -08:00
Karl Tauber
c8248e91ca release 0.25.1 2020-01-18 10:35:18 +01:00
Karl Tauber
7317ce44e7 update to Gradle 6.1
./gradlew wrapper --gradle-version=6.1
2020-01-18 10:25:55 +01:00
Karl Tauber
10e2a5b1eb release 0.25 2020-01-17 13:18:29 +01:00
Karl Tauber
e675d1b7e2 show mnemonics if the active window does not have a focused component; ignore invisible components (issue #43) 2020-01-17 11:03:48 +01:00
Karl Tauber
499c4dadd5 FlatDarkLaf.properties: use slightly brighter color for popup menu border 2020-01-17 10:53:46 +01:00
Karl Tauber
f550f84acd Menu: fixed vertical alignment of sub-menus (issue #42) 2020-01-17 01:01:30 +01:00
Karl Tauber
8021f1a7fc ComboBox on macOS: fixed keyboard navigation and show/hide popup 2020-01-16 23:43:05 +01:00
Karl Tauber
d50fe606ee Tree on macOS: fixed Left and Right keys to collapse or expand nodes 2020-01-16 21:33:23 +01:00
Karl Tauber
281f014aa0 FlatTestFrame: support testing 3rd party lafs 2020-01-15 19:10:42 +01:00
Karl Tauber
2f6da3e84a FlatInspector: improved inspecting parent levels: Ctrl adds 1 level, Shift adds 2 levels and Alt adds 4 levels; no longer limit inspecting to content pane 2020-01-15 18:13:43 +01:00
Karl Tauber
f9accc2a7a ProgressBar: support square painting and larger height even if no string is painted 2020-01-15 17:13:39 +01:00
Karl Tauber
fe15078bbd TabbedPane: support per component tab height 2020-01-15 12:52:39 +01:00
Karl Tauber
27d4b5eba7 ToggleButton: Support per component styling for tab-style toggle buttons with client properties JToggleButton.tab.underlineHeight (integer), JToggleButton.tab.underlineColor (Color) and JToggleButton.tab.selectedBackground (Color) (issue #45) 2020-01-15 11:05:16 +01:00
Karl Tauber
e378576632 ToggleButton renamed toggle button type "underline" to "tab" (value of client property JButton.buttonType is now tab) 2020-01-14 23:59:56 +01:00
Karl Tauber
74909da110 Button and ToggleButton:
- support per component minimum height (issue #44)
- do not apply minimum width if button border was changed (is no longer an instance of `FlatButtonBorder`)
- ToggleButton: no longer use focus width for underline style toggle buttons to compute component size, which reduces/fixes component size in "Flat IntelliJ" and "Flat Darcula" themes
- revalidate/repaint client properties minimum width/height or buttonType change
2020-01-14 18:42:06 +01:00
Karl Tauber
655bf112ac ScrollPane: fixed UI artifact at bottom right corner of scroll pane if both scroll bars are visible, which was caused by Component.innerFocusWidth > 0 (issue #35) 2020-01-14 15:03:07 +01:00
Karl Tauber
5c3638a5a4 Menu: hide mnemonics by default and show them only when Alt key is pressed (issue #43) 2020-01-14 12:09:31 +01:00
Karl Tauber
2459a3654b TabbedPane: hide cropped line in scroll-tab-layout (issue #40) 2020-01-14 10:51:07 +01:00
Karl Tauber
e9a3456cf5 Tree: Tree.textBackground now has a valid color and is no longer null; instead set Tree.rendererFillBackground to false to always get correct cell backgrounds (in IntelliJ themes or if tree.setBackground(...) was used)
undone commit 645be4bfa3
2020-01-14 10:44:00 +01:00
Karl Tauber
2bcdf774ff release 0.24 2020-01-10 10:02:54 +01:00
Karl Tauber
ef01f23384 improved Swing system colors controlHighlight, controlLtHighlight, controlShadow and controlDkShadow 2020-01-10 09:47:13 +01:00
Karl Tauber
ab7bbb6593 ProgressBar: now uses blueish color for the progress part in "Flat Dark" theme 2020-01-10 00:28:26 +01:00
Karl Tauber
f2dad88875 ToggleButton: support underline toggle button colors in IntelliJ themes 2020-01-10 00:13:39 +01:00
Karl Tauber
c474565ff5 UI inspector: support nested classes 2020-01-09 23:54:45 +01:00
Karl Tauber
fd9dbbd7e6 support smooth scrolling with touchpads and high precision mouse wheels (issue #27) 2020-01-09 23:43:58 +01:00
Karl Tauber
43ab095e0f Table: replaced Table.showGrid with Table.showHorizontalLines and Table.showVerticalLines (issue #38) 2020-01-09 20:55:55 +01:00
Karl Tauber
41e2888bf1 ScrollPane with Table: The border of buttons that are added to one of the four scroll pane corners are now removed if the center component is a table. Also, these corner buttons are made not focusable. 2020-01-08 23:25:57 +01:00
Karl Tauber
e7d5e22960 IntelliJ Themes Demo: fixed last invalid colors in Material UI Lite themes (issue #26) 2020-01-08 15:27:31 +01:00
Karl Tauber
3f3884193d Button and TextComponent: support per component minimum width 2020-01-08 14:47:40 +01:00
Karl Tauber
dfccabc2b9 ToggleButton: support underline toggle button style 2020-01-08 14:18:17 +01:00
Karl Tauber
af7c181596 Button and ToggleButton: support square button style 2020-01-08 13:59:39 +01:00
Karl Tauber
8e84112837 Label and ToolTip: fixed font sizes for HTML headings 2020-01-08 10:18:30 +01:00
Karl Tauber
822cd16daa IntelliJ Themes Demo: updated Dracula, Hiberbee and Material UI Lite themes (used IJThemesUpdater) (issue #26) 2020-01-07 23:40:24 +01:00
Karl Tauber
33ea84004d UIDefaultsLoader: changed .properties file loading order: now all core .properties files are loaded before loading addon .properties files, which makes it easier to overwrite core values in addons; also, addon loading order can be specified 2020-01-07 12:55:37 +01:00
Karl Tauber
8dbbe20840 TableHeader: paint column borders also if renderer has changed, but delegates to the system default renderer
(e.g. done in NetBeans class ETableHeader)
2020-01-06 16:36:23 +01:00
Karl Tauber
d990ccc4ab release 0.23.1 2020-01-02 22:39:34 +01:00
Karl Tauber
9f16249898 IntelliJ Themes: fixed checkbox colors in Material UI Lite dark themes 2020-01-02 21:09:11 +01:00
Karl Tauber
62fc3139cf ComboBox: fixed NPE in Oracle SQL Developer settings 2020-01-02 18:45:32 +01:00
Karl Tauber
452452dcc9 Tree: fixed wide selection if scrolled horizontally 2019-12-31 09:49:56 +01:00
Karl Tauber
b6fb06bc65 CHANGELOG.md: added some missing changes form 0.23 2019-12-30 18:10:33 +01:00
Karl Tauber
aac6bd1b7c release 0.23 2019-12-30 17:49:16 +01:00
Karl Tauber
d260001cbd IntelliJ Themes Demo: fixed progress bar in Hiberbee theme 2019-12-30 16:55:35 +01:00
Karl Tauber
9c470d77cb List and Tree: Hide cell focus indicator (black rectangle) by default. Can be enabled with List.showCellFocusIndicator=true / Tree.showCellFocusIndicator=true, but then the cell focus indicator is shown only if more than one item is selected.
Table: Hide cell focus indicator (black rectangle) by default if none of the selected cells is editable. Can be show always with `Table.showCellFocusIndicator=true`.
2019-12-30 16:31:51 +01:00
Karl Tauber
269075657d FlatDefaultsAddon: added default implementation to getDefaults() because most subclasses use the same implementation 2019-12-30 11:02:40 +01:00
Karl Tauber
d0029beb22 use 0.5 pixel "inner" focus border for "Flat Light" and "Flat Dark" themes 2019-12-30 10:48:15 +01:00
Karl Tauber
4b4837e3a1 IntelliJ Themes Demo: updated Hiberbee theme (used IJThemesUpdater) (issue #26) 2019-12-30 10:04:48 +01:00
Karl Tauber
d0160b8b6d added required module java.logging to module-info.java 2019-12-23 15:39:58 +01:00
Karl Tauber
ea351935b2 IntelliJ Themes: use CONFIG log level for reporting invalid colors so that they do not show up in the console by default (issue #26) 2019-12-23 15:30:57 +01:00
Karl Tauber
20ccc2951e IntelliJ Themes Demo: updated "Material Theme UI Lite" theme to version 7.0, downloaded from IntelliJ plugin repo (https://plugins.jetbrains.com/plugin/12124-material-theme-ui-lite/versions) because theme github repo is not up-to-date 2019-12-23 13:55:33 +01:00
Karl Tauber
6f9bad1bdf Tree: fixed painting wide selection while drag-and-drop 2019-12-23 13:43:50 +01:00
Karl Tauber
39a0d514a8 List, Table and Tree: added colors for drag-and-drop
- added "enable drag and drop" checkbox to Demo on "Data Components" tab
- support copying UI default values lazy
2019-12-23 13:10:50 +01:00
Karl Tauber
ad82c591cc use logging instead of printing errors to System.err 2019-12-23 11:29:17 +01:00
Karl Tauber
32ceb168d5 replaced prefix @@ with $ in .properties files 2019-12-23 00:18:27 +01:00
Karl Tauber
16146f4c88 support basic color functions in .properties files 2019-12-22 17:40:13 +01:00
Karl Tauber
60febbf3f8 Table: hide grid and changed intercell spacing to zero 2019-12-22 11:38:32 +01:00
Karl Tauber
4960b30cfb Tree: support wide selection (enabled by default) 2019-12-22 00:07:08 +01:00
Karl Tauber
56c161fea0 List and Tree: paint cell focus indicator (black rectangle) only if more than one item is selected 2019-12-21 21:46:36 +01:00
Karl Tauber
27c439d728 documented used UI defaults in UI delegates 2019-12-21 17:30:36 +01:00
Karl Tauber
78d5e03a66 updated colors in "Flat Light" and "Flat IntelliJ" themes with colors from "IntelliJ Light Theme", which provides blue coloring that better match platform colors 2019-12-21 11:13:11 +01:00
Karl Tauber
f25e647b6a fixed separator color in IntelliJ platform themes 2019-12-21 10:55:17 +01:00
Karl Tauber
e44212fc65 fixed link color (in HTML text) in IntelliJ platform themes 2019-12-20 18:36:25 +01:00
Karl Tauber
a483403774 Button: reset shadow variables on LaF switch 2019-12-20 14:43:08 +01:00
Karl Tauber
69750dba19 IntelliJ Themes Demo: updated themes (used IJThemesUpdater) 2019-12-19 18:26:43 +01:00
Karl Tauber
af962b99be InternalFrame: icons implemented (issue #11) 2019-12-19 17:37:36 +01:00
Karl Tauber
2399e54a4b release 0.22 2019-12-18 12:28:08 +01:00
Karl Tauber
aea5e8eb16 Demo: bottom horizontal slider bound to the progress bars 2019-12-18 12:21:38 +01:00
Karl Tauber
a3a60c1c4b ProgressBar: reduced thickness from 6 to 4 (as in IntelliJ and Windows 10) 2019-12-18 11:44:34 +01:00
Karl Tauber
c141cb6c6c CheckBox and RadioButton: fixed cut off outer focus border if checkbox/radiobutton border was explicitly set to a EmptyBorder 2019-12-17 23:24:38 +01:00
Karl Tauber
62765ab6ca TextComponent: support placeholder text that is displayed if text field is empty (set client property "JTextField.placeholderText" to a string) 2019-12-17 18:08:45 +01:00
Karl Tauber
e4f7fed523 TextComponent: scale caret width on HiDPI screens when running on Java 8 2019-12-17 17:34:54 +01:00
Karl Tauber
bf8cc268cc on Mac show mnemonics only when Ctrl and Alt keys are pressed (issue #4) 2019-12-17 11:35:16 +01:00
Karl Tauber
8450e74832 TabbedPane: support separators between tabs (TabbedPane.showTabSeparators) 2019-12-16 20:40:29 +01:00
Karl Tauber
475b258e4a Button: enabled Button.defaultButtonFollowsFocus on Windows, which allows pressing focused button with <kbd>Enter</kbd> key (as in Windows LaF) 2019-12-16 18:11:48 +01:00
Karl Tauber
f20803ae57 ProgressBar: If progress text is visible:
- use smaller font
  - reduced height
  - changed style to rounded rectangle
  - fixed painting issues on low values

Support configure of arc with `ProgressBar.arc`
2019-12-16 17:39:46 +01:00
Karl Tauber
3fcb17931a fixed clipped borders at 125%, 150% and 175% scaling when outer focus width is zero (default in "Flat Light" and "Flat Dark" themes) 2019-12-15 11:36:24 +01:00
Karl Tauber
736c7b8377 CheckBox: changed CheckBox.arc from radius to diameter to be consistent with Button.arc and Component.arc 2019-12-14 23:36:22 +01:00
Karl Tauber
05743e2d8b FlatUIUtils: renamed and documented component painting methods 2019-12-14 23:17:11 +01:00
Karl Tauber
6cd2c7f26d InternalFrame: test application implemented (issue #11) 2019-12-14 11:57:07 +01:00
Karl Tauber
469e5bd179 ToggleButton: removed "ToggleButton.arc" because it was only used for the background, but not for the border, which is painted in FlatButtonBorder 2019-12-14 00:05:47 +01:00
Karl Tauber
dbeb3f04e7 UI inspector:
- fixed wrong detection of components under mouse location if window contains a menubar
- fixed positioning of tooltip in bottom and right window area to avoid that the tooltip overlaps the inspected component
2019-12-13 23:49:49 +01:00
Karl Tauber
e9b17ac24a UI inspector: support using it in any application 2019-12-13 23:24:10 +01:00
Karl Tauber
65fbcedaa4 TabbedPane: support background color for selected tabs 2019-12-13 23:14:41 +01:00
Karl Tauber
c4183ada11 ScrollPane and FlatSpinner: made getHandler() methods private 2019-12-11 21:58:39 +01:00
Karl Tauber
27f9614633 release 0.21 2019-12-08 12:38:45 +01:00
Karl Tauber
2211cc5596 fixed Swing system colors in dark themes 2019-12-08 10:21:07 +01:00
Karl Tauber
46f0393648 UIDefaultsLoader:
- support `{instance}com.myapp.MyClass` to instantiate any public class with public no-arg constructor
- support `{class}com.myapp.MyClass`
- support loading addon classes from different classloaders (e.g. in NetBeans)
2019-12-07 17:53:59 +01:00
Karl Tauber
b4c1a97687 IntelliJ Themes:
- accept colors starting with two `#` as valid colors because IntelliJ IDEA does it too
- fixed wrong error message when a color reference is missing

(issue #26)
2019-12-06 11:13:50 +01:00
Karl Tauber
adcef385b0 FlatClientProperties: added javadoc comments 2019-12-03 11:33:12 +01:00
Karl Tauber
48e38b2855 ScrollBar: show decrease/increase arrow buttons if client property "JScrollBar.showButtons" is set to true on JScrollPane or JScrollBar (issue #25) 2019-12-03 10:53:39 +01:00
Karl Tauber
2cc8327a08 ScrollPane: paint disabled border if view component (e.g. JTextPane) is disabled 2019-12-01 18:23:30 +01:00
Karl Tauber
404e80082c Button: fixed help button styling in IntelliJ platform themes 2019-12-01 17:53:10 +01:00
Karl Tauber
3fbc21347a Demo: restore last used theme on startup 2019-12-01 13:10:38 +01:00
Karl Tauber
d76f0e2241 moved code that fixes color of links in HTML text from FlatLaf.getDefaults() to FlatLaf.initialize() and invoke it only once (for the case that getDefaults() is invoked from 3rd party code) 2019-11-30 23:23:34 +01:00
Karl Tauber
e5fcc59805 Button: optionally support shadows for improved compatibility with IntelliJ platform themes (e.g. for Material Design Dark theme) 2019-11-30 19:14:37 +01:00
Karl Tauber
de82dac873 Button: optionally support gradient border and gradient background for improved compatibility with IntelliJ platform themes (e.g. Vuesion and Spacegray themes) 2019-11-30 17:58:40 +01:00
Karl Tauber
a14ef72177 FlatLaf.isNativeLookAndFeel() now returns false 2019-11-30 15:41:27 +01:00
Karl Tauber
6ee5234351 release 0.20
(skipping 0.19)
2019-11-29 16:36:21 +01:00
Karl Tauber
398a041ddf screenshots updated 2019-11-29 16:35:28 +01:00
Karl Tauber
02bb73c335 FileChooser: scale file chooser size (issue #5) 2019-11-29 10:29:28 +01:00
Karl Tauber
f37c4cdc4d IntelliJ Themes: added "Save" and "GitHub" buttons to themes list toolbar in demo (and testing apps) to allow users quickly save .theme.json files (including license) or visit theme source code repository 2019-11-29 10:24:04 +01:00
Karl Tauber
d5c9bcddc8 fixed "cannot find symbol" error in NetBeans editor, when source/binary format is set to JDK 9 (or later) in NetBeans project (issue #13)
flatlaf.jar is no longer a multi-release jar
2019-11-28 19:04:42 +01:00
Karl Tauber
f3f8c81518 SwingX: JXTitledPanel support (issue #22) 2019-11-28 18:29:47 +01:00
Karl Tauber
a99ffd4821 fixed color of links in HTML text 2019-11-28 17:06:45 +01:00
Karl Tauber
7b5a9d9949 Demo: added square buttons 2019-11-28 11:29:09 +01:00
Karl Tauber
feb8d0591e Button: make button square if button text is "..." or a single character 2019-11-28 10:55:10 +01:00
Karl Tauber
2197808631 ComboBox: fixed issues with NetBeans org.openide.awt.ColorComboBox component 2019-11-28 10:33:26 +01:00
Karl Tauber
36b3ccc34f IntelliJ Themes: fixes for ScrollBar 2019-11-27 23:51:27 +01:00
Karl Tauber
a5b69d712c IntelliJ Themes Demo: updated themes (used IJThemesUpdater) 2019-11-26 16:26:03 +01:00
Karl Tauber
8edcca9745 IntelliJ Themes Demo: IJThemesUpdater tool added to update themes from source code repositories 2019-11-26 13:31:44 +01:00
Karl Tauber
f3b1f4b608 IntelliJ Themes Demo: replaced themes.properties with themes.json 2019-11-26 11:39:29 +01:00
Karl Tauber
879a8aaa6d renamed FlatLaf.initColorPalette() to initIconColors() 2019-11-26 11:06:13 +01:00
Karl Tauber
900d005d89 IntelliJ Themes: fixes for ComboBox, Spinner, CheckBox and RadioButton 2019-11-26 10:41:11 +01:00
Karl Tauber
c8225171a3 IntelliJ Themes: avoid double setting LaFs 2019-11-26 00:41:47 +01:00
Karl Tauber
c513c052fc SwingX: fixed too wide border when using date picker as table cell editor (issue #24) 2019-11-26 00:05:38 +01:00
Karl Tauber
711c4dd2b5 hex color values in .properties files now must start with a # character 2019-11-25 19:10:51 +01:00
Karl Tauber
81f1ebd1db added readme files to sub-projects 2019-11-24 16:59:13 +01:00
Karl Tauber
c722934510 look and feel identifier returned by FlatLaf.getID() now always starts with "FlatLaf" 2019-11-24 14:08:38 +01:00
Karl Tauber
3ffe8e225d IntelliJ Themes: fixes for Slider:
- use Component.focusColor if Slider.focusedColor is not set (which is the case in all IntelliJ themes)
- compute hover color based on thumb color
- ignore Slider.trackWidth (used in Material Theme UI Lite)
2019-11-24 12:58:38 +01:00
Karl Tauber
6d86cf8f9c IntelliJ Themes: added more popular open-source IntelliJ themes to demo
(downloaded latest .theme.json from source repos;
see themes.properties for source repo URLs)
2019-11-21 13:17:06 +01:00
Karl Tauber
e6e19110b2 HSLColor: fixed javadoc errors 2019-11-20 17:21:49 +01:00
Karl Tauber
b5c13bd1b3 IntelliJ Themes: fixes for ProgressBar 2019-11-20 17:09:49 +01:00
Karl Tauber
b129fe437c JIDE: fixed JidePopup border 2019-11-20 17:01:23 +01:00
Karl Tauber
645be4bfa3 IntelliJ Themes: Tree: fixed renderer background 2019-11-18 19:16:48 +01:00
Karl Tauber
321c49ee32 OptionPane: fixed button order on Mac 2019-11-18 17:31:24 +01:00
Karl Tauber
84db2f9b65 OptionPane:
- fixed background, which was wrong when OptionPane.background is different to Panel.background (e.g. in Hiberbee or Solarized themes)
- use non-UIResource borders in sub-panels to avoid that they are replaced when switching LaF
2019-11-18 13:12:27 +01:00
Karl Tauber
6ba1a419bc FlatSVGIcon:
- use SVGDiagram directly instead of using SVGIcon
- check cliping bounds before painting
- paint red box on errors
2019-11-17 13:04:46 +01:00
Karl Tauber
3de329a332 moved ScaledSVGIcon.java from demo to flatlaf-extras and renamed to FlatSVGIcon.java 2019-11-16 23:23:02 +01:00
Karl Tauber
f175c36736 IntelliJ Themes: OptionPane: use colors for error/information/question/warning icons from default color palette, which enables modification of icon colors by theme 2019-11-16 21:55:28 +01:00
Karl Tauber
f1de65b471 UI inspector: support anonymous classes 2019-11-16 20:13:37 +01:00
Karl Tauber
5069013e6e FileChooser implemented (issue #5) 2019-11-16 17:21:28 +01:00
Karl Tauber
c0642ed620 IntelliJ Themes: apply action/object icons color palette from .theme.json to FlatLaf default color palette in UI defaults 2019-11-16 13:36:19 +01:00
Karl Tauber
a145673dd1 use chevron arrows for table header ascending/descending sort icons (#7) 2019-11-15 23:41:26 +01:00
Karl Tauber
223af48c09 added default color palette for action icons and object icons (based on IntelliJ Platform colors)
and use color palette for file chooser icons (issue #5)
2019-11-15 23:40:52 +01:00
Karl Tauber
d72cfc37d6 fixed jittery submenu rendering on Mac (issue #10) 2019-11-15 18:24:22 +01:00
Karl Tauber
3ba8133890 Table: fixed selection background of checkbox in table cell 2019-11-14 12:33:04 +01:00
Karl Tauber
a907cd7f46 IntelliJ Themes: fixes for text component disabled backgrounds, button backgrounds, combobox disabled backgrounds and spinner disabled backgrounds 2019-11-14 11:28:38 +01:00
Karl Tauber
3b740cb494 IntelliJ Themes: fixes for ComboBox, text components and Spinner 2019-11-13 19:17:34 +01:00
Karl Tauber
0f0f21a7b1 IntelliJ Themes Demo: refresh themes list (from current working directory) on window activation 2019-11-13 13:54:17 +01:00
Karl Tauber
537f6703c0 IntelliJ Themes Demo: search for .theme.json files in current working directory and show them in themes list 2019-11-13 13:53:01 +01:00
Karl Tauber
da0c562ac2 FlatTestFrame: fixed exception when changing scale factor (when running in Java 8) 2019-11-13 10:48:20 +01:00
Karl Tauber
0bef71907c IntelliJ Themes: added core themes to list of themes 2019-11-13 10:44:39 +01:00
Karl Tauber
0ebd43bc5f IntelliJ Themes:
- added themes list to demo (and testing) apps
- added some popular open-source IntelliJ themes to demo
  (downloaded latest .theme.json from source repos;
  see themes.properties for source repo URLs)
2019-11-12 19:23:20 +01:00
Karl Tauber
3092fced3c Demo: moved Laf combobox related code to new class LookAndFeelsComboBox 2019-11-12 16:30:41 +01:00
Karl Tauber
a02784fcba IntelliJ Themes: fixes for Button, CheckBox, RadioButton, ComboBox and Spinner 2019-11-12 12:40:20 +01:00
Karl Tauber
924abde89e StringUtils added 2019-11-12 10:19:56 +01:00
Karl Tauber
f011468819 IntelliJ Themes: use single Laf class for light and dark IntelliJ themes and added IntelliJTheme$ThemeLaf.properties to allow (re-)setting UI defaults before .theme.json is applied 2019-11-12 10:11:26 +01:00
Karl Tauber
11f459d5b0 IntelliJ Themes:
- support theming check boxes and radio buttons
- fixed button background and border
2019-11-11 22:55:40 +01:00
Karl Tauber
974f7d5d68 IntelliJ Themes: reworked applying values so that it works and behaves the same way as in IntelliJ Platform 2019-11-11 18:35:31 +01:00
Karl Tauber
e60db4ff90 IntelliJ Themes: basic support for loading and applying IntelliJ Platform themes in .theme.json format 2019-11-11 16:25:37 +01:00
Karl Tauber
ebe1cd3367 IntelliJ Themes: added internal json parser for parsing IntelliJ .theme.json files
this is a partly copy of https://github.com/ralfstx/minimal-json (license is MIT)
2019-11-11 13:46:36 +01:00
Karl Tauber
30db9d13a5 UIDefaultsLoader:
- detect colors and UIs
- fixed parsing of colors with transparency
- allow '#' prefix colors
2019-11-11 13:39:11 +01:00
Karl Tauber
368611359c documented used UI defaults in most UI delegates 2019-11-10 18:18:57 +01:00
Karl Tauber
746918c054 README.md: fixed link to "JIDE Common Layer" addon 2019-11-10 10:26:34 +01:00
Karl Tauber
6572198178 release 0.18 2019-11-10 10:06:59 +01:00
Karl Tauber
f69b3f56dd ToolTip: use anti-aliasing to render multi-line tooltips 2019-11-10 00:09:51 +01:00
Karl Tauber
c379f2f3b0 JIDE: added missing dependency to flatlaf-testing project 2019-11-09 18:51:52 +01:00
Karl Tauber
e7194e43b4 JIDE: README.md added and publishing added to build.gradle.kts 2019-11-09 18:44:31 +01:00
Karl Tauber
883b282cd8 JIDE: JideTabbedPane: hover tab event if mouse is over close button
unfortunately it is not possible to replace JIDEs arrow and close buttons with own implementations
2019-11-09 18:21:25 +01:00
Karl Tauber
7c2b2d7f26 JIDE: basic JideTabbedPane implementation 2019-11-09 17:23:55 +01:00
Karl Tauber
08f525de5f TabbedPane: content pane is no longer opaque and use antialiasing for painting separator and content border 2019-11-09 15:53:02 +01:00
Karl Tauber
433659a5df TabbedPane: no longer modify BasicTabbedPaneUI.contentBorderInsets in getContentBorderInsets() because this is useless and confusing 2019-11-09 13:57:39 +01:00
Karl Tauber
7f50a30b29 TabbedPane: reworked painting in scroll-tab-layout, so that the separator line now spans the whole width and is no longer interrupted by the scroll buttons 2019-11-09 10:58:31 +01:00
Karl Tauber
d5944779e8 TabbedPane: use FlatClientProperties for JTabbedPane.hasFullBorder client property 2019-11-08 23:01:33 +01:00
Karl Tauber
fdaea31475 JIDE: flatlaf-jide-oss subproject created 2019-11-08 15:51:28 +01:00
Karl Tauber
a66ebd29b4 update to Gradle 5.6.4
./gradlew wrapper --gradle-version=5.6.4
2019-11-07 18:24:33 +01:00
Karl Tauber
f3006467e9 TextField and TextArea: do not apply minimum width if columns property > 0 2019-10-30 15:14:23 +01:00
Karl Tauber
aa52af4c8f added FlatLaf.isDark() 2019-10-29 11:01:48 +01:00
Karl Tauber
2e0fde464d release 0.17 2019-10-27 12:29:12 +01:00
Karl Tauber
9bf0124950 FlatBorder: replaced Paint with Color 2019-10-27 12:27:31 +01:00
Karl Tauber
eaa6db1d19 Table: fixed missing upper right corner (e.g. in SwingX JXTable with column control visible) 2019-10-27 12:25:39 +01:00
Karl Tauber
2ec142f000 Button: hover and pressed background colors are now derived from actual button background color (issue #21) 2019-10-27 11:03:40 +01:00
Karl Tauber
ec572436a9 extracted properties file parsing to new class UIDefaultsLoader 2019-10-25 23:07:44 +02:00
Karl Tauber
6e5e548c9d Testing: fixed content panel insets and removed 5,5 gaps 2019-10-25 10:44:24 +02:00
Karl Tauber
61c3bbad60 ComboBox and Spinner:
- make child components explicitly non-opaque
- paint parent background only if necessary
2019-10-25 10:28:24 +02:00
Karl Tauber
bc10c4e871 Made JComboBox, JProgressBar, JSpinner and JXDatePicker non-opaque.
`JPasswordField`, `JScrollPane` and `JTextField` are non-opaque if they have
an outside focus border (e.g. IntelliJ and Darcula themes).
(issues #20 and #17)
2019-10-25 10:28:18 +02:00
Karl Tauber
8b8d84c2a3 TextField and PasswordField: reduced duplicate code 2019-10-24 20:47:31 +02:00
Karl Tauber
5743b5d59f CheckBox: removed accidentally checked in debug output 2019-10-24 18:07:22 +02:00
Karl Tauber
9450ba5e46 Extras: fixed link in README.md 2019-10-24 15:31:13 +02:00
Karl Tauber
cfcbf3e61c CheckBox:
- compute focus border arc based on Component.focusWidth
- allow specifying border arc in UI defaults (CheckBox.arc)
2019-10-24 14:28:50 +02:00
Karl Tauber
136481c110 Testing: added "opaque" checkbox to test apps 2019-10-24 12:44:16 +02:00
Karl Tauber
7f43b3003c TriStateCheckBox component added 2019-10-24 12:36:40 +02:00
Karl Tauber
1b0c2687c8 Testing: added "background" checkbox to test apps 2019-10-24 10:52:53 +02:00
Karl Tauber
aeb80f862b build.gradle.kts: depend task "assemble" on "sourcesJar" and "javadocJar" so that they are built on Travic CI to file problems early (previously those tasks were build only just before publishing) 2019-10-23 17:06:25 +02:00
Karl Tauber
1de367e19e moved testing applications from src/test to new project flatlaf-testing (part 2) 2019-10-23 16:44:39 +02:00
Karl Tauber
62895a602f moved testing applications from src/test to new project flatlaf-testing (part 1) 2019-10-23 16:44:19 +02:00
Karl Tauber
6438e890bb release 0.16 2019-10-23 10:46:46 +02:00
Karl Tauber
7d72b13ac9 made JButton, JCheckBox, JRadioButton, JToggleButton and JSlider non-opaque (#20) 2019-10-23 10:36:33 +02:00
Karl Tauber
a2e21cb07b fixed Java 9 module descriptor (broken since 0.14) 2019-10-23 09:55:55 +02:00
Karl Tauber
06766cb4db Demo: missing SwingUtilities.invokeLater() added 2019-10-23 09:03:08 +02:00
Karl Tauber
72e8ab70a3 Demo: tooltips added to toolbar buttons 2019-10-22 12:09:04 +02:00
Karl Tauber
0f38af5922 ComboBox: right-to-left fixes (#18) 2019-10-22 12:07:23 +02:00
Karl Tauber
4181759008 right-to-left fixes:
-Slider: colored track (if ticks and labels are hidden) was on the left side of the thumb
- ToolTip: multi-line text was not aligned to the right

(issue #18)
2019-10-21 22:12:51 +02:00
Karl Tauber
fff0e5e946 fixed FlatTestLaf (broken by commit 342b932f9e) 2019-10-21 20:11:35 +02:00
Karl Tauber
be88eeb343 release 0.15 2019-10-21 18:29:40 +02:00
Karl Tauber
342b932f9e ToolTip:
- Improved styling of dark tooltips (darker background, no border).
- increased top and bottom margins
- use brighter color in light theme
- Fixed colors in tooltips of disabled components. (issue #15)
2019-10-21 18:07:43 +02:00
Karl Tauber
964dc14a8a ComboBox: fixed NPE in combobox with custom renderer after switching to FlatLaf (#16; regression in 0.14) 2019-10-21 17:04:23 +02:00
Karl Tauber
b56f462626 SwingX: added screenshots 2019-10-21 13:42:22 +02:00
Karl Tauber
4477b4c44e release 0.14 2019-10-21 10:52:33 +02:00
Karl Tauber
714c6e2920 TextField and PasswordField: fixed minimum width if focusWidth > 2 and not having a FlatBorder 2019-10-20 22:17:15 +02:00
Karl Tauber
0853a1aa2e SwingX: fixed preferred width of JXDatePicker, which was too large (#8) 2019-10-20 22:16:13 +02:00
Karl Tauber
f9d2312b3a ComboBox: fixed StackOverflowError when switching LaF (#14) 2019-10-20 20:04:10 +02:00
Karl Tauber
f53f205f52 SwingX: fixed JXDatePicker.TodayPanel colors (#8) 2019-10-20 18:18:06 +02:00
Karl Tauber
41ecbccc76 EditorPane and TextPane: fixed font and text color when using HTML content (#9) 2019-10-20 18:17:47 +02:00
Karl Tauber
5a952c187c SwingX: JXMonthView support (#8) 2019-10-20 10:47:53 +02:00
Karl Tauber
0a86d00c1e FlatLaf: allow specifying value type in value for cases where auto-detecting value type from key or value does not work 2019-10-19 13:47:53 +02:00
Karl Tauber
b3e9d82537 SwingX: added SwingX LaF addon (#8) 2019-10-19 09:36:43 +02:00
Karl Tauber
0970dceee2 SwingX: JXDatePicker support (#8) 2019-10-19 09:35:01 +02:00
Karl Tauber
ffef71d6db OptionPane: fixed rendering of longer HTML text (#12) 2019-10-18 18:41:14 +02:00
Karl Tauber
0ede8cd5b9 SwingX: build.gradle.kts: added maven publishing and bintray upload 2019-10-18 14:36:04 +02:00
Karl Tauber
c1a9f48e6b SwingX: JXBusyLabel support (#8) 2019-10-18 13:26:30 +02:00
Karl Tauber
3f7215c602 update to Gradle 5.6.3
./gradlew wrapper --gradle-version=5.6.3
2019-10-18 10:34:39 +02:00
Karl Tauber
8b5e3e344a SwingX: JXHeader support (#8) 2019-10-18 10:33:05 +02:00
Karl Tauber
212ff012d6 SwingX: JXTaskPaneContainer and JXTaskPane support (#8) 2019-10-18 10:31:24 +02:00
Karl Tauber
7c77b857f6 SwingX: added test app 2019-10-17 12:09:22 +02:00
Karl Tauber
423c01074a SwingX: flatlaf-swingx subproject created; JXHyperlink support (#8) 2019-10-17 11:21:23 +02:00
Karl Tauber
2dbd584e28 use KeyEventPostProcessor instead of AWTEventListener for listening for Alt key pressed (similar to WindowLookAndFeel) (#4) 2019-10-16 19:42:55 +02:00
Karl Tauber
250f435ceb build.gradle.kts: moved jcenter to root script 2019-10-16 19:39:41 +02:00
Karl Tauber
fa4e409555 ToolBar: disable focusability of buttons in toolbar 2019-10-15 19:00:51 +02:00
Karl Tauber
dfc3b7c796 README.md: intro updated 2019-10-15 11:40:19 +02:00
Karl Tauber
41df9859ad ComboBox: use small border if used as table editor 2019-10-15 10:41:28 +02:00
Karl Tauber
a8b8cbdf8c FlatTestFrame: reduced duplicate code 2019-10-14 17:55:38 +02:00
Karl Tauber
fe2909c56a Demo: build.gradle.kts: added bintray upload 2019-10-14 10:59:45 +02:00
Karl Tauber
3a2fe06c34 README.md: added Maven Central coordinates 2019-10-13 22:54:41 +02:00
Karl Tauber
873e8604ce added developer information to Maven POM for Maven Central publishing 2019-10-13 21:37:49 +02:00
609 changed files with 124104 additions and 7155 deletions

View File

@@ -6,3 +6,231 @@ insert_final_newline = true
charset = latin1 charset = latin1
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4
[*.java]
indent_style = tab
ij_continuation_indent_size = 4
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = false
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_resources = false
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = normal
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 0
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_block_brace_style = next_line_if_wrapped
ij_java_block_comment_at_first_column = true
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = normal
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = next_line
ij_java_class_count_to_use_import_on_demand = 99
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = off
ij_java_extends_keyword_wrap = split_into_lines
ij_java_extends_list_wrap = normal
ij_java_field_annotation_wrap = split_into_lines
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = java.**,javax.**,*,$*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 1
ij_java_keep_blank_lines_between_package_declaration_and_header = 1
ij_java_keep_blank_lines_in_code = 1
ij_java_keep_blank_lines_in_declarations = 1
ij_java_keep_control_statement_in_one_line = false
ij_java_keep_first_column_comment = false
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = false
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_at_first_column = true
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = next_line_if_wrapped
ij_java_method_call_chain_wrap = normal
ij_java_method_parameters_new_line_after_left_paren = false
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = normal
ij_java_modifier_list_wrap = false
ij_java_names_count_to_use_import_on_demand = 3
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = normal
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = true
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = true
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = false
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = false
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = false
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = false
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = false
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = false
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = false
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = true
ij_java_spaces_within_array_initializer_braces = true
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = true
ij_java_spaces_within_for_parentheses = true
ij_java_spaces_within_if_parentheses = true
ij_java_spaces_within_method_call_parentheses = true
ij_java_spaces_within_method_parentheses = true
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_switch_parentheses = true
ij_java_spaces_within_synchronized_parentheses = true
ij_java_spaces_within_try_parentheses = true
ij_java_spaces_within_while_parentheses = true
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = true
ij_java_ternary_operation_wrap = on_every_item
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = normal
ij_java_throws_list_wrap = normal
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false

View File

@@ -5,7 +5,7 @@ jdk:
- openjdk8 - openjdk8
- openjdk9 - openjdk9
- openjdk11 - openjdk11
- openjdk13 - openjdk14
before_cache: before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
@@ -19,11 +19,21 @@ before_install:
- ./gradlew --version - ./gradlew --version
- java -version - java -version
deploy: stages:
provider: script - name: test
script: ./gradlew bintrayUpload - name: snapshot
skip_cleanup: true # to upload artifacts created during the build if: branch = master AND type IN (push) AND tag IS blank
on: - name: release
branch: master if: type IN (push) AND tag IS present
jdk: openjdk11
tags: true 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,571 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 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
- Hide focus indicator when window is inactive.
- Custom window decorations: Improved/fixed window border color in dark themes.
- Custom window decorations: Hide window border if window is maximized.
- Custom window decorations: Center title if menu bar is embedded.
- Custom window decorations: Cursor of components (e.g. TextField) was not
changed. (issue #125)
- CheckBox: Fixed colors in light IntelliJ themes. (issue #126; regression in
0.37)
- InternalFrame: Use default icon in internal frames. (issue #122)
## 0.37
- Custom window decorations (Windows 10 only; PR #108; issues #47 and #82)
support:
- dark window title panes
- embedding menu bar into window title pane
- native Windows 10 borders and behavior when running in
[JetBrains Runtime 11](https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime)
or later (the JRE that IntelliJ IDEA uses)
- CheckBox and RadioButton: Support changing selected icon style from outline to
filled (as in FlatLaf IntelliJ theme) with `UIManager.put(
"CheckBox.icon.style", "filled" );`.
- Button and ToggleButton: Support disabled background color (use UI values
`Button.disabledBackground` and `ToggleButton.disabledBackground`). (issue
#112)
- Button and ToggleButton: Support making buttons square (set client property
`JButton.squareSize` to `true`). (issue #118)
- ScrollBar: Support pressed track, thumb and button colors (use UI values
`ScrollBar.pressedTrackColor`, `ScrollBar.pressedThumbColor` and
`ScrollBar.pressedButtonBackground`). (issue #115)
- ComboBox: Support changing arrow button style (set UI value
`ComboBox.buttonStyle` to `auto` (default), `button` or `none`). (issue #114)
- Spinner: Support changing arrows button style (set UI value
`Spinner.buttonStyle` to `button` (default) or `none`).
- TableHeader: Support top/bottom/left positioned sort arrow when using
[Glazed Lists](https://github.com/glazedlists/glazedlists). (issue #113)
- Button, CheckBox, RadioButton and ToggleButton: Do not paint focus indicator
if `AbstractButton.isFocusPainted()` returns `false`.
- ComboBox: Increase maximum row count of popup list to 20 (was 8). Set UI value
`ComboBox.maximumRowCount` to any integer to use a different value.
- Fixed/improved vertical position of text when scaled on HiDPI screens on
Windows.
- IntelliJ Themes: Updated Gradianto Themes.
- IntelliJ Themes: Fixed menu bar and menu item margins in all Material UI Lite
themes.
## 0.36
- ScrollBar: Made styling more flexible by supporting insets and arc for track
and thumb. (issue #103)
- ScrollBar: Use round thumb on macOS and Linux to make it look similar to
native platform scroll bars. (issue #103)
- ComboBox: Minimum width is now 72 pixels (was ~50 for non-editable and ~130
for editable comboboxes).
- ComboBox: Support custom borders in combobox editors. (issue #102)
- Button: Support non-square icon-only buttons. (issue #110)
- Ubuntu Linux: Fixed poorly rendered font. (issue #105)
- macOS Catalina: Use Helvetica Neue font.
- `FlatInspector` added (see [FlatLaf Extras](flatlaf-extras)).
## 0.35
- Added drop shadows to popup menus, combobox popups, tooltips and internal
frames. (issue #94)
- Support different component border colors to indicate errors, warnings or
custom state (set client property `JComponent.outline` to `error`, `warning`
or any `java.awt.Color`).
- Button and ToggleButton: Support round button style (set client property
`JButton.buttonType` to `roundRect`).
- ComboBox, Spinner and TextField: Support round border style (set client
property `JComponent.roundRect` to `true`).
- Paint nicely rounded buttons, comboboxes, spinners and text fields when
setting `Button.arc`, `Component.arc` or `TextComponent.arc` to a large value
(e.g. 1000).
- Added Java 9 module descriptor to `flatlaf-extras-<version>.jar` and
`flatlaf-swingx-<version>.jar`.
- CheckBox and RadioButton: Flag `opaque` is no longer ignored when checkbox or
radio button is used as table cell renderer. (issue #77)
- FileChooser: Use system icons. (issue #100)
- FileChooser: Fixed missing labels in file chooser when running on Java 9 or
later. (issue #98)
- PasswordField: Do not apply minimum width if `columns` property is greater
than zero.
## 0.34
- Menus: New menu item renderer brings stable left margins, right aligned
accelerators and larger gap between text and accelerator. This makes menus
look more modern and more similar to native platform menus.
- New underline menu selection style that displays selected menu items similar
to tabs (to enable use `UIManager.put( "MenuItem.selectionType", "underline"
);`).
- Menus: Fixed text color of selected menu items that use HTML. (issue #87)
- Menus: On Windows, pressing <kbd>F10</kbd> now activates the menu bar without
showing a menu popup (as usual on Windows platform). On other platforms the
first menu popup is shown.
- Menus: On Windows, releasing <kbd>Alt</kbd> key now activates the menu bar (as
usual on Windows platform). (issue #43)
- Menus: Fixed inconsistent left padding in menu items. (issue #3)
- Menus: Fixed: Setting `iconTextGap` property on a menu item did increase left
and right margins. (issue #54)
- Hide mnemonics if window is deactivated (e.g. <kbd>Alt+Tab</kbd> to another
window). (issue #43)
- macOS: Enabled drop shadows for popup menus and combobox popups. (issue #94)
- macOS: Fixed NPE if using `JMenuBar` in `JInternalFrame` and macOS screen menu
bar is enabled (with `-Dapple.laf.useScreenMenuBar=true`). (issue #90)
## 0.33
- Improved creation of disabled grayscale icons used in disabled buttons, labels
and tabs. They now have more contrast and are lighter in light themes and
darker in dark themes. (issue #70)
- IntelliJ Themes: Fixed ComboBox size and Spinner border in all Material UI
Lite themes and limit tree row height in all Material UI Lite themes and some
other themes.
- IntelliJ Themes: Material UI Lite themes did not work when using
[IntelliJ Themes Pack](flatlaf-intellij-themes) addon. (PR #88, issue #89)
- IntelliJ Themes: Added Java 9 module descriptor to
`flatlaf-intellij-themes-<version>.jar`.
## 0.32
- New [IntelliJ Themes Pack](flatlaf-intellij-themes) addon bundles many popular
open-source 3rd party themes from JetBrains Plugins Repository into a JAR and
provides Java classes to use them.
- IntelliJ Themes: Fixed button and toggle button colors. (issue #86)
- Updated IntelliJ Themes in demo to the latest versions.
- ToggleButton: Compute selected background color based on current component
background. (issue #32)
## 0.31
- Focus indication border (or background) no longer hidden when temporary
loosing focus (e.g. showing a popup menu).
- List, Table and Tree: Item selection color of focused components no longer
change from blue to gray when temporary loosing focus (e.g. showing a popup
menu).
## 0.30
- Windows: Fixed rendering of Unicode characters. Previously not all Unicode
characters were rendered on Windows. (issue #81)
## 0.29
- Linux: Fixed scaling if `GDK_SCALE` environment variable is set or if running
on JetBrains Runtime. (issue #69)
- Tree: Fixed repainting wide selection on focus gained/lost.
- ComboBox: No longer ignore `JComboBox.prototypeDisplayValue` when computing
popup width. (issue #80)
- Support changing default font used for all components with automatic scaling
UI if using larger font. Use `UIManager.put( "defaultFont", myFont );`
- No longer use system property `sun.java2d.uiScale`. (Java 8 only)
- Support specifying custom scale factor in system property `flatlaf.uiScale`
also for Java 9 and later.
- Demo: Support using own FlatLaf themes (`.properties` files) that are located
in working directory of Demo application. Shown in the "Themes" list under
category "Current Directory".
## 0.28
- PasswordField: Warn about enabled Caps Lock.
- TabbedPane: Support <kbd>Ctrl+TAB</kbd> / <kbd>Ctrl+Shift+TAB</kbd> to switch
to next / previous tab.
- TextField, FormattedTextField and PasswordField: Support round borders (see UI
default value `TextComponent.arc`). (issue #65)
- IntelliJ Themes: Added Gradianto themes to demo.
- Button, CheckBox and RadioButton: Fixed NPE when button has children. (PR #68)
- ScrollBar: Improved colors.
- Reviewed (and tested) all key bindings on Windows and macOS. Linux key
bindings are equal to Windows key bindings. macOS key bindings are slightly
different for platform specific behavior.
- UI default values are no longer based on Metal/Aqua UI defaults.
## 0.27
- Support `JInternalFrame` and `JDesktopPane`. (issues #39 and #11)
- Table: Support positioning the column sort arrow in header right, left, top or
bottom. (issue #34)
- ProgressBar: Fixed visual artifacts in indeterminate mode, on HiDPI screens at
125%, 150% and 175% scaling, when the progress moves around.
- TabbedPane: New option to allow tab separators to take full height (to enable
use `UIManager.put( "TabbedPane.tabSeparatorsFullHeight", true );`). (issue
#59, PR #62)
- CheckBox and RadioButton: Do not fill background if `contentAreaFilled` is
`false`. (issue #58, PR #63)
- ToggleButton: Make toggle button square if it has an icon but no text or text
is "..." or a single character.
- ToolBar: No longer use special rollover border for buttons in toolbar. (issue
#36)
- ToolBar: Added empty space around buttons in toolbar and toolbar itself (see
UI default values `Button.toolbar.spacingInsets` and `ToolBar.borderMargins`).
(issue #56)
- Fixed "illegal reflective access operation" warning on macOS when using Java
12 or later. (issue #60, PR #61)
## 0.26
- Menus:
- Changed menu bar and popup menu background colors (made brighter in light
themes and darker in dark themes).
- Highlight items in menu bar on mouse hover. (issue #49)
- Popup menus now have empty space at the top and bottom.
- Menu items now have larger left and right margins.
- Made `JMenu`, `JMenuItem`, `JCheckBoxMenuItem` and `JRadioButtonMenuItem`
non-opaque.
- TextField, FormattedTextField and PasswordField: Select all text when a text
field gains focus for the first time and selection was not set explicitly.
This can be configured to newer or always select all text on focus gain (see
UI default value `TextComponent.selectAllOnFocusPolicy`).
- ProgressBar: Made progress bar paint smooth in indeterminate mode.
## 0.25.1
Re-release of 0.25 because of problems with Maven Central.
## 0.25
- Hide menu mnemonics by default and show them only when <kbd>Alt</kbd> key is
pressed. (issue #43)
- Menu: Fixed vertical alignment of sub-menus. (issue #42)
- TabbedPane: In scroll-tab-layout, the cropped line is now hidden. (issue #40)
- Tree: UI default value `Tree.textBackground` now has a valid color and is no
longer `null`.
- Tree on macOS: Fixed <kbd>Left</kbd> and <kbd>Right</kbd> keys to collapse or
expand nodes.
- ComboBox on macOS: Fixed keyboard navigation and show/hide popup.
- Button and ToggleButton: Support per component minimum height (set client
property `JComponent.minimumHeight` to an integer). (issue #44)
- Button and ToggleButton: Do not apply minimum width if button border was
changed (is no longer an instance of `FlatButtonBorder`).
- ToggleButton: Renamed toggle button type "underline" to "tab" (value of client
property `JButton.buttonType` is now `tab`).
- ToggleButton: Support per component styling for tab-style toggle buttons with
client properties `JToggleButton.tab.underlineHeight` (integer),
`JToggleButton.tab.underlineColor` (Color) and
`JToggleButton.tab.selectedBackground` (Color). (issue #45)
- ToggleButton: No longer use focus width for tab-style toggle buttons to
compute component size, which reduces/fixes component size in "Flat IntelliJ"
and "Flat Darcula" themes.
- TabbedPane: Support per component tab height (set client property
`JTabbedPane.tabHeight` to an integer).
- ProgressBar: Support square painting (set client property
`JProgressBar.square` to `true`) and larger height even if no string is
painted (set client property `JProgressBar.largeHeight` to `true`).
## 0.24
- Support smooth scrolling with touchpads and high precision mouse wheels.
(issue #27)
- Changed `.properties` file loading order: Now all core `.properties` files are
loaded before loading addon `.properties` files. This makes it easier to
overwrite core values in addons. Also, addon loading order can be specified.
- TableHeader: Paint column borders if renderer has changed, but delegates to
the system default renderer (e.g. done in NetBeans).
- Label and ToolTip: Fixed font sizes for HTML headings.
- Button and ToggleButton: Support square button style (set client property
`JButton.buttonType` to `square`).
- ToggleButton: Support underline toggle button style (set client property
`JButton.buttonType` to `underline`).
- Button and TextComponent: Support per component minimum width (set client
property `JComponent.minimumWidth` to an integer).
- ScrollPane with Table: The border of buttons that are added to one of the four
scroll pane corners are now removed if the center component is a table. Also,
these corner buttons are made not focusable.
- Table: Replaced `Table.showGrid` with `Table.showHorizontalLines` and
`Table.showVerticalLines`. (issue #38)
- ProgressBar: Now uses blueish color for the progress part in "Flat Dark"
theme. In the "Flat Darcula" theme, it remains light gray.
- Improved Swing system colors `controlHighlight`, `controlLtHighlight`,
`controlShadow` and `controlDkShadow`.
## 0.23.1
- Tree: Fixed wide selection if scrolled horizontally.
- ComboBox: Fixed NPE in Oracle SQL Developer settings.
- IntelliJ Themes: Fixed checkbox colors in Material UI Lite dark themes.
## 0.23
- Updated colors in "Flat Light" and "Flat IntelliJ" themes with colors from
"IntelliJ Light Theme", which provides blue coloring that better matches
platform colors.
- Tree: Support wide selection (enabled by default).
- Table: Hide grid and changed intercell spacing to zero.
- List, Table and Tree: Added colors for drag-and-drop. Added "enable drag and
drop" checkbox to Demo on "Data Components" tab.
- List and Tree: Hide cell focus indicator (black rectangle) by default. Can be
enabled with `List.showCellFocusIndicator=true` /
`Tree.showCellFocusIndicator=true`, but then the cell focus indicator is shown
only if more than one item is selected.
- Table: Hide cell focus indicator (black rectangle) by default if none of the
selected cells is editable. Can be show always with
`Table.showCellFocusIndicator=true`.
- Support basic color functions in `.properties` files: `rgb(red,green,blue)`,
`rgba(red,green,blue,alpha)`, `hsl(hue,saturation,lightness)`,
`hsla(hue,saturation,lightness,alpha)`, `lighten(color,amount[,options])` and
`darken(color,amount[,options])`.
- Replaced prefix `@@` with `$` in `.properties` files.
- Fixed link color (in HTML text) and separator color in IntelliJ platform
themes.
- Use logging instead of printing errors to `System.err`.
- Updated IntelliJ Themes in demo to the latest versions.
- IntelliJ Themes: Fixed link and separator colors.
## 0.22
- TextComponent: Support placeholder text that is displayed if text field is
empty (set client property "JTextField.placeholderText" to a string).
- TextComponent: Scale caret width on HiDPI screens when running on Java 8.
- ProgressBar: If progress text is visible:
- use smaller font
- reduced height
- changed style to rounded rectangle
- fixed painting issues on low values
- ProgressBar: Support configure of arc with `ProgressBar.arc`.
- ProgressBar: Reduced thickness from 6 to 4.
- TabbedPane: Support background color for selected tabs
(`TabbedPane.selectedBackground`) and separators between tabs
(`TabbedPane.showTabSeparators`).
- CheckBox: changed `CheckBox.arc` from radius to diameter to be consistent with
`Button.arc` and `Component.arc`
- Button: Enabled `Button.defaultButtonFollowsFocus` on Windows, which allows
pressing focused button with <kbd>Enter</kbd> key (as in Windows LaF).
- Fixed clipped borders at 125%, 150% and 175% scaling when outer focus width is
zero (default in "Flat Light" and "Flat Dark" themes).
- On Mac show mnemonics only when <kbd>Ctrl</kbd> and <kbd>Alt</kbd> keys are
pressed. (issue #4)
## 0.21
- ScrollBar: Show decrease/increase arrow buttons if client property
"JScrollBar.showButtons" is set to `true` on `JScrollPane` or `JScrollBar`.
(issue #25)
- `FlatLaf.isNativeLookAndFeel()` now returns `false`.
- Button: Optionally support gradient borders, gradient backgrounds and shadows
for improved compatibility with IntelliJ platform themes (e.g. for Vuesion,
Spacegray and Material Design Dark themes).
- Button: Fixed help button styling in IntelliJ platform themes.
- ScrollPane: Paint disabled border if view component (e.g. JTextPane) is
disabled.
- Fixed Swing system colors in dark themes.
## 0.20
- Support using IntelliJ platform themes (.theme.json files).
- Support `JFileChooser`. (issue #5)
- Look and feel identifier returned by `FlatLaf.getID()` now always starts with
"FlatLaf". Use `UIManager.getLookAndFeel().getID().startsWith( "FlatLaf" )` to
check whether the current look and feel is FlatLaf.
- Fixed selection background of checkbox in table cell.
- Fixed color of links in HTML text.
- Fixed jittery submenu rendering on Mac. (issue #10)
- Fixed "cannot find symbol" error in NetBeans editor, when source/binary format
is set to JDK 9 (or later) in NetBeans project. (issue #13)
- Button: Make button square if button text is "..." or a single character.
- ComboBox: Fixed issues with NetBeans `org.openide.awt.ColorComboBox`
component.
- Hex color values in `.properties` files now must start with a `#` character.
- SwingX: Support `JXTitledPanel`. (issue #22)
- SwingX: Fixed too wide border when using date picker as table cell editor.
(issue #24)
- JIDE Common Layer: Fixed `JidePopup` border.
## 0.18
- TextField and TextArea: Do not apply minimum width if `columns` property is
greater than zero.
- TabbedPane: In scroll-tab-layout, the separator line now spans the whole width
and is no longer interrupted by the scroll buttons.
- TabbedPane: Content pane is no longer opaque. Use antialiasing for painting
separator and content border.
- ToolTip: Use anti-aliasing to render multi-line tooltips.
- JIDE Common Layer: Support `JideTabbedPane`.
## 0.17
- CheckBox: Support painting a third state (set client property
"JButton.selectedState" to "indeterminate").
- `TriStateCheckBox` component added (see [FlatLaf Extras](flatlaf-extras)).
- Made `JComboBox`, `JProgressBar`, `JSpinner` and `JXDatePicker` non-opaque.
`JPasswordField`, `JScrollPane` and `JTextField` are non-opaque if they have
an outside focus border (e.g. IntelliJ and Darcula themes). (issues #20 and
#17)
- Button: Hover and pressed background colors are now derived from actual button
background color. (issue #21)
- Table: Fixed missing upper right corner (e.g. in SwingX JXTable with column
control visible).
## 0.16
- Made some fixes for right-to-left support in ComboBox, Slider and ToolTip.
(issue #18)
- Fixed Java 9 module descriptor (broken since 0.14).
- Made `JButton`, `JCheckBox`, `JRadioButton`, `JToggleButton` and `JSlider`
non-opaque. (issue #20)
## 0.15
- ToolTip: Improved styling of dark tooltips (darker background, no border).
- ToolTip: Fixed colors in tooltips of disabled components. (issue #15)
- ComboBox: Fixed NPE in combobox with custom renderer after switching to
FlatLaf. (issue #16; regression in 0.14)
## 0.14
- ComboBox: Use small border if used as table editor.
- ToolBar: Disable focusability of buttons in toolbar.
- OptionPane: Fixed rendering of longer HTML text. (issue #12)
- EditorPane and TextPane: Fixed font and text color when using HTML content.
(issue #9)
- ComboBox: Fixed `StackOverflowError` when switching LaF. (issue #14)
- SwingX: Support `JXBusyLabel`, `JXDatePicker`, `JXHeader`, `JXHyperlink`,
`JXMonthView`, `JXTaskPaneContainer` and `JXTaskPane`. (issue #8)
## 0.13
- Added developer information to Maven POM for Maven Central publishing.
## 0.12 ## 0.12
- Support Linux. (issue #2) - Support Linux. (issue #2)

View File

@@ -1,19 +1,28 @@
FlatLaf - Flat Look and Feel FlatLaf - Flat Look and Feel
============================ ============================
**FlatLaf** is a modern open-source cross-platform Look and Feel for Java **FlatLaf** is a modern **open-source** cross-platform Look and Feel for Java
desktop applications. Swing desktop applications.
It looks mostly "flat" (no shadows or gradients), clean, simple and elegant. It looks almost flat (no shadows or gradients), clean, simple and elegant.
FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes, FlatLaf comes with **Light**, **Dark**, **IntelliJ** and **Darcula** themes,
scales on **HiDPI** displays and runs on Java 8 or newer. scales on **HiDPI** displays and runs on Java 8 or newer.
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 mostly 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
------------------------
FlatLaf can use 3rd party themes created for IntelliJ Platform (see
[IntelliJ Themes Pack](flatlaf-intellij-themes)):
![IntelliJ Platform Themes](images/intellij_platform_themes.png)
Demo Demo
@@ -28,9 +37,83 @@ Requires Java 8 or newer.
Download Download
-------- --------
FlatLaf binaries are available on **JCenter** and **Maven Central**.
If you use Maven or Gradle, add a dependency with following coordinates to your
build script:
groupId: com.formdev
artifactId: flatlaf
version: (see button below)
Otherwise download `flatlaf-<version>.jar` here:
[![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion) [![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
Download from JCenter and Maven Central is coming soon.
### Snapshots
FlatLaf snapshot binaries are available in
[JFrog Artifactory](https://oss.jfrog.org/artifactory/oss-snapshot-local/com/formdev/).
To access the latest snapshot, change the FlatLaf version(s) in the dependencies
to `<version>-SNAPSHOT` (e.g. `0.27-SNAPSHOT`) and add the repository
`https://oss.jfrog.org/artifactory/oss-snapshot-local` to your build (see
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
and
[Gradle](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_custom_repository)
docs).
Addons
------
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
- [Extras](flatlaf-extras)
- [SwingX](flatlaf-swingx)
- [JIDE Common Layer](flatlaf-jide-oss)
Projects using FlatLaf
----------------------
- [NetBeans](https://netbeans.apache.org/) 11.3
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
- ![New](images/new.svg) [jAlbum](https://jalbum.net/) 21 (commercial)
- [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
----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
Documentation Documentation

View File

@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
version = "0.12" val releaseVersion = "0.42"
val developmentVersion = "0.43-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
allprojects {
version = rootProject.version
repositories {
jcenter()
}
}
// check required Java version // check required Java version
if( JavaVersion.current() < JavaVersion.VERSION_1_8 ) if( JavaVersion.current() < JavaVersion.VERSION_1_8 )
@@ -27,3 +38,39 @@ println( "FlatLaf Version: ${version}" )
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" ) println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
println( "Java ${System.getProperty( "java.version" )}" ) println( "Java ${System.getProperty( "java.version" )}" )
println() println()
extra["bintray.user"] = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
extra["bintray.key"] = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
// if true, do not upload to bintray
extra["bintray.dryRun"] = false
// if true, uploaded artifacts are visible to all
// if false, only visible to owner when logged into bintray
extra["bintray.publish"] = true
allprojects {
tasks {
withType<JavaCompile>().configureEach {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
options.encoding = "ISO-8859-1"
}
withType<Jar>().configureEach {
// manifest for all created JARs
manifest.attributes(mapOf(
"Implementation-Vendor" to "FormDev Software GmbH",
"Implementation-Copyright" to "Copyright (C) ${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
"Implementation-Version" to project.version))
// add META-INF/LICENSE to all created JARs
from("${rootDir}/LICENSE") {
into("META-INF")
}
}
}
}

34
buildSrc/build.gradle.kts Normal file
View File

@@ -0,0 +1,34 @@
/*
* 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.
*/
plugins {
`kotlin-dsl`
}
// required for kotlin-dsl or embedded-kotlin plugins
repositories {
jcenter()
}
dependencies {
// NOTE: keep plugin versions in sync with settings.gradle.kts
// "com.jfrog.bintray" plugin
implementation( "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" )
// "com.jfrog.artifactory" plugin
implementation( "org.jfrog.buildinfo:build-info-extractor-gradle:4.13.0" )
}

View File

@@ -0,0 +1,80 @@
/*
* 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.
*/
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* Reorders entries in a JAR file so that .properties files are placed before .class files,
* which is necessary to workaround an issue in NetBeans 11.3 (and older).
* See issues #13 and #93.
*
* @author Karl Tauber
*/
public class ReorderJarEntries
{
public static void reorderJarEntries( File jarFile )
throws IOException
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream( (int) jarFile.length() + 1000 );
try( ZipOutputStream zipOutStream = new ZipOutputStream( outStream ) ) {
// 1st pass: copy .properties files
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
// 2st pass: copy other files
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
}
// replace JAR
Files.write( jarFile.toPath(), outStream.toByteArray(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING );
}
private static void copyFiles( ZipOutputStream dest, File jarFile, Predicate<String> filter )
throws IOException
{
try( ZipInputStream zipInputStream = new ZipInputStream( new FileInputStream( jarFile ) ) ) {
ZipEntry entry;
while( (entry = zipInputStream.getNextEntry()) != null ) {
if( filter.test( entry.getName() ) ) {
dest.putNextEntry( entry );
copyFile( zipInputStream, dest );
}
}
}
}
private static void copyFile( InputStream src, OutputStream dest )
throws IOException
{
byte[] buf = new byte[8*1024];
int len;
while( (len = src.read( buf )) > 0 )
dest.write( buf, 0, len );
dest.flush();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.
*/
plugins {
java
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets {
create( "java9" ) {
java {
setSrcDirs( listOf( "src/main/java9" ) )
}
}
}
tasks {
named<JavaCompile>( "compileJava9Java" ) {
sourceCompatibility = "9"
targetCompatibility = "9"
}
jar {
manifest.attributes( "Multi-Release" to "true" )
into( "META-INF/versions/9" ) {
from( sourceSets["java9"].output )
}
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.
*/
open class ModuleInfoExtension {
var paths: ArrayList<String> = ArrayList()
fun dependsOn( vararg paths: String ) {
this.paths.addAll( paths )
}
}
val extension = project.extensions.create<ModuleInfoExtension>( "flatlafModuleInfo" )
plugins {
java
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets {
create( "module-info" ) {
java {
// include "src/main/java" here to get compile errors if classes are
// used from other modules that are not specified in module dependencies
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
}
}
}
tasks {
named<JavaCompile>( "compileModuleInfoJava" ) {
sourceCompatibility = "9"
targetCompatibility = "9"
dependsOn( extension.paths )
options.compilerArgs.add( "--module-path" )
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath )
}
jar {
manifest.attributes( "Multi-Release" to "true" )
into( "META-INF/versions/9" ) {
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}
}
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.
*/
open class PublishExtension {
var artifactId: String? = null
var name: String? = null
var description: String? = null
}
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
plugins {
`maven-publish`
id( "com.jfrog.bintray" )
id( "com.jfrog.artifactory" )
}
publishing {
publications {
create<MavenPublication>( "maven" ) {
afterEvaluate {
artifactId = extension.artifactId
}
groupId = "com.formdev"
from( components["java"] )
pom {
afterEvaluate {
this@pom.name.set( extension.name )
this@pom.description.set( extension.description )
}
url.set( "https://github.com/JFormDesigner/FlatLaf" )
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
}
}
developers {
developer {
name.set( "Karl Tauber" )
organization.set( "FormDev Software GmbH" )
organizationUrl.set( "https://www.formdev.com/" )
}
}
scm {
url.set( "https://github.com/JFormDesigner/FlatLaf" )
}
}
}
}
}
bintray {
user = rootProject.extra["bintray.user"] as String?
key = rootProject.extra["bintray.key"] as String?
setPublications( "maven" )
with( pkg ) {
repo = "flatlaf"
afterEvaluate {
this@with.name = extension.artifactId
}
setLicenses( "Apache-2.0" )
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
with( version ) {
name = project.version.toString()
}
publish = rootProject.extra["bintray.publish"] as Boolean
dryRun = rootProject.extra["bintray.dryRun"] as Boolean
}
}
artifactory {
setContextUrl( "https://oss.jfrog.org" )
publish( closureOf<org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig> {
repository( delegateClosureOf<groovy.lang.GroovyObject> {
setProperty( "repoKey", "oss-snapshot-local" )
setProperty( "username", rootProject.extra["bintray.user"] as String? )
setProperty( "password", rootProject.extra["bintray.key"] as String? )
} )
defaults( delegateClosureOf<groovy.lang.GroovyObject> {
invokeMethod( "publications", "maven" )
setProperty( "publishArtifacts", true )
setProperty( "publishPom", true )
} )
} )
resolve( delegateClosureOf<org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig> {
setProperty( "repoKey", "jcenter" )
} )
}

View File

@@ -0,0 +1,377 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
org.eclipse.jdt.core.formatter.align_with_spaces=false
org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=48
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_record_components=16
org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=33
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=33
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=33
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=33
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=32
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=32
org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=0
org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=next_line_on_wrap
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line_on_wrap
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line_on_wrap
org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=next_line_on_wrap
org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=next_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true
org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=1
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.lineSplit=120
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.text_block_indentation=0
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
org.eclipse.jdt.core.formatter.wrap_before_logical_operator=false
org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter

View File

@@ -0,0 +1,3 @@
eclipse.preferences.version=1
formatter_profile=_FlatLaf
formatter_settings_version=19

4
flatlaf-core/README.md Normal file
View File

@@ -0,0 +1,4 @@
FlatLaf Core
============
This sub-project contains the FlatLaf core source code.

View File

@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,133 +14,48 @@
* limitations under the License. * limitations under the License.
*/ */
version = rootProject.version
plugins { plugins {
`java-library` `java-library`
`maven-publish` `flatlaf-module-info`
id( "com.jfrog.bintray" ) version "1.8.4" `flatlaf-java9`
} `flatlaf-publish`
repositories {
jcenter()
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets {
create( "module-info" ) {
java {
// include "src/main/java" here to get compile errors if classes are
// used from other modules that are not specified in module dependencies
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
}
}
}
}
dependencies {
testImplementation( "com.miglayout:miglayout-swing:5.2" )
testImplementation( "com.jgoodies:jgoodies-forms:1.9.0" )
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_1_8 withSourcesJar()
targetCompatibility = JavaVersion.VERSION_1_8 withJavadocJar()
} }
tasks { tasks {
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
named<JavaCompile>( "compileModuleInfoJava" ) {
sourceCompatibility = "9"
targetCompatibility = "9"
}
}
jar { jar {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) { doLast {
manifest.attributes( ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
"Multi-Release" to "true"
)
into( "META-INF/versions/9" ) {
from( sourceSets["module-info"].output )
include( "module-info.class" )
}
} }
} }
javadoc { javadoc {
options { options {
this as StandardJavadocDocletOptions this as StandardJavadocDocletOptions
use( true )
tags = listOf( "uiDefault", "clientProperty" ) tags = listOf( "uiDefault", "clientProperty" )
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
} }
isFailOnError = false isFailOnError = false
} }
register( "sourcesJar", Jar::class ) { named<Jar>("sourcesJar" ) {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "sources" )
from( sourceSets.main.get().allJava )
} }
register( "javadocJar", Jar::class ) { named<Jar>("javadocJar" ) {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "javadoc" )
from( javadoc )
} }
} }
publishing { flatlafPublish {
publications { artifactId = "flatlaf"
create<MavenPublication>( "maven" ) { name = "FlatLaf"
artifactId = "flatlaf" description = "Flat Look and Feel"
groupId = "com.formdev"
from( components["java"] )
artifact( tasks["sourcesJar"] )
artifact( tasks["javadocJar"] )
pom {
name.set( "FlatLaf" )
description.set( "Flat Look and Feel" )
url.set( "https://github.com/JFormDesigner/FlatLaf" )
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "http://www.apache.org/licenses/LICENSE-2.0.txt" )
}
}
scm {
url.set( "https://github.com/JFormDesigner/FlatLaf" )
}
}
}
}
}
bintray {
user = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
key = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
setPublications( "maven" )
with( pkg ) {
repo = "flatlaf"
name = "flatlaf"
setLicenses( "Apache-2.0" )
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
with( version ) {
name = project.version.toString()
}
publish = true
}
} }

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import java.awt.Color;
import java.util.Objects; import java.util.Objects;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -24,13 +25,329 @@ import javax.swing.JComponent;
*/ */
public interface FlatClientProperties public interface FlatClientProperties
{ {
/**
* Specifies type of a button.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong> {@link #BUTTON_TYPE_SQUARE}, {@link #BUTTON_TYPE_ROUND_RECT},
* {@link #BUTTON_TYPE_TAB}, {@link #BUTTON_TYPE_HELP} and {@link BUTTON_TYPE_TOOLBAR_BUTTON}
*/
String BUTTON_TYPE = "JButton.buttonType"; String BUTTON_TYPE = "JButton.buttonType";
/**
* Paint the button with square edges.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
*
* @see #BUTTON_TYPE
*/
String BUTTON_TYPE_SQUARE = "square";
/**
* Paint the button with round edges.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
*
* @see #BUTTON_TYPE
*/
String BUTTON_TYPE_ROUND_RECT = "roundRect";
/**
* Paint the toggle button in tab style.
* <p>
* <strong>Components</strong> {@link javax.swing.JToggleButton}
*
* @see #BUTTON_TYPE
*/
String BUTTON_TYPE_TAB = "tab";
/**
* Paint a help button (circle with question mark).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton}
*
* @see #BUTTON_TYPE
*/
String BUTTON_TYPE_HELP = "help"; String BUTTON_TYPE_HELP = "help";
/**
* Paint the button in toolbar style.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
*
* @see #BUTTON_TYPE
*/
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
/**
* Specifies selected state of a checkbox.
* <p>
* <strong>Component</strong> {@link javax.swing.JCheckBox}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong> {@link #SELECTED_STATE_INDETERMINATE}
*/
String SELECTED_STATE = "JButton.selectedState";
/**
* Paint an indeterminate state on a checkbox.
*
* @see #SELECTED_STATE
*/
String SELECTED_STATE_INDETERMINATE = "indeterminate";
/**
* Specifies whether the button preferred size will be made square (quadratically).
* <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String SQUARE_SIZE = "JButton.squareSize";
/**
* Specifies minimum width of a component.
* <p>
* <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>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
*/
String MINIMUM_WIDTH = "JComponent.minimumWidth";
/**
* Specifies minimum height of a component.
* <p>
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br>
*/
String MINIMUM_HEIGHT = "JComponent.minimumHeight";
/**
* Specifies the outline color of the component border.
* <p>
* <strong>Components</strong> {@link javax.swing.JButton}, {@link javax.swing.JComboBox},
* {@link javax.swing.JFormattedTextField}, {@link javax.swing.JPasswordField},
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
* {@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>Allowed Values</strong> {@link #OUTLINE_ERROR}, {@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";
/**
* Paint the component border in another color (usually reddish) to indicate an error.
*
* @see #OUTLINE
*/
String OUTLINE_ERROR = "error";
/**
* Paint the component border in another color (usually yellowish) to indicate a warning.
*
* @see #OUTLINE
*/
String OUTLINE_WARNING = "warning";
/**
* 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
* 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_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
/**
* Specifies whether the progress bar has always the larger height even if no string is painted.
* <p>
* <strong>Component</strong> {@link javax.swing.JProgressBar}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight";
/**
* Specifies whether the progress bar is paint with square edges.
* <p>
* <strong>Component</strong> {@link javax.swing.JProgressBar}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
/**
* Specifies whether the menu bar is embedded into the title pane if custom
* window decorations are enabled. Default is {@code true}.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
* <p>
* <strong>Component</strong> {@link javax.swing.JScrollBar} or {@link javax.swing.JScrollPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
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";
/**
* Specifies whether separators are shown between tabs.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String TABBED_PANE_SHOW_TAB_SEPARATORS = "JTabbedPane.showTabSeparators";
/**
* Specifies whether a full border is painted around a tabbed pane.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*/
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
/**
* Specifies the height of a tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer}
*/
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
/**
* Specifies whether all text is selected when the text component gains focus.
* <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong> {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
*/
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
/**
* Never select all text when the text component gains focus.
*
* @see #SELECT_ALL_ON_FOCUS_POLICY
*/
String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never";
/**
* Select all text when the text component gains focus for the first time
* and selection was not modified (is at end of text).
* This is the default.
*
* @see #SELECT_ALL_ON_FOCUS_POLICY
*/
String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once";
/**
* Always select all text when the text component gains focus.
*
* @see #SELECT_ALL_ON_FOCUS_POLICY
*/
String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always";
/**
* Placeholder text that is only painted if the text field is empty.
* <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses) or {@link javax.swing.JComboBox}<br>
* <strong>Value type</strong> {@link java.lang.String}
*/
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
/**
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}
*/
String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight";
/**
* Color of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.awt.Color}
*/
String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor";
/**
* Background color if selected and toggle button type is {@link #BUTTON_TYPE_TAB}.
* <p>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.awt.Color}
*/
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
/** /**
* Checks whether a client property of a component has the given value. * Checks whether a client property of a component has the given value.
*/ */
static boolean clientPropertyEquals( JComponent c, String key, Object value ) { static boolean clientPropertyEquals( JComponent c, String key, Object value ) {
return Objects.equals( c.getClientProperty( key ), value ); return Objects.equals( c.getClientProperty( key ), value );
} }
/**
* Checks whether a client property of a component is a boolean and returns its value.
* If the client property is not set, or not a boolean, defaultValue is returned.
*/
static boolean clientPropertyBoolean( 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 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.
* If the client property is not set, or not an integer, defaultValue is returned.
*/
static int clientPropertyInt( JComponent c, String key, int defaultValue ) {
Object value = c.getClientProperty( key );
return (value instanceof Integer) ? (int) value : defaultValue;
}
/**
* Checks whether a client property of a component is a color and returns its value.
* If the client property is not set, or not a color, defaultValue is returned.
*/
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
Object value = c.getClientProperty( key );
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

@@ -19,6 +19,8 @@ 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.
* *
* The UI defaults are loaded from FlatDarculaLaf.properties, FlatDarkLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarculaLaf public class FlatDarculaLaf
@@ -30,11 +32,11 @@ public class FlatDarculaLaf
@Override @Override
public String getName() { public String getName() {
return "Flat Darcula"; return "FlatLaf Darcula";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return "Flat Darcula Look and Feel"; return "FlatLaf Darcula Look and Feel";
} }
} }

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a dark color scheme. * A Flat LaF that has a dark color scheme.
* *
* The UI defaults are loaded from FlatDarkLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarkLaf public class FlatDarkLaf
@@ -30,11 +32,16 @@ public class FlatDarkLaf
@Override @Override
public String getName() { public String getName() {
return "Flat Dark"; return "FlatLaf Dark";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return "Flat Dark Look and Feel"; return "FlatLaf Dark Look and Feel";
}
@Override
public boolean isDark() {
return true;
} }
} }

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.io.InputStream;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
/**
* Addon for FlatLaf UI defaults.
*
* Allows loading of additional .properties files from addon JARs.
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
* <p>
* If you extend this class in a addon JAR, you also have to add a text file named
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
* to the addon JAR. The file must contain a single line with the class name.
* <p>
* See 'flatlaf-swingx' addon for an example
*
* @author Karl Tauber
*/
public abstract class FlatDefaultsAddon
{
/**
* Finds an addon .properties file for the given LaF class and returns
* it as input stream. Or {@code null} if not found.
* <p>
* This default implementation finds addon .properties file for the given LaF class
* in the same package as the subclass.
* <p>
* Override this method to load addon .properties files from other locations.
*/
public InputStream getDefaults( Class<?> lafClass ) {
Class<?> addonClass = this.getClass();
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
+ '/' + lafClass.getSimpleName() + ".properties";
return addonClass.getResourceAsStream( propertiesName );
}
/**
* Allows modifying UI defaults after loading UI defaults.
* The default implementation does nothing.
*/
public void afterDefaultsLoading( LookAndFeel laf, UIDefaults defaults ) {
}
/**
* Returns the priority used to sort addon loading.
* The order is only important if you want overwrite UI defaults of other addons.
* Lower numbers mean higher priority.
* Returns 10000 by default.
*/
public int getPriority() {
return 10000;
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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;
/**
* Default color palette for action icons and object icons.
* <p>
* The idea is to use only this well defined set of colors in SVG icons and
* then they are replaced at runtime to dark variants or to other theme colors.
* Then a single SVG icon (light variant) can be used for dark themes too.
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
* <p>
* Use the {@code *_DARK} colors only in {@code *_dark.svg} files.
* <p>
* The colors are based on IntelliJ Platform
* <a href="https://jetbrains.design/intellij/principles/icons/#action-icons">Action icons</a>
* and
* <a href="https://jetbrains.design/intellij/principles/icons/#noun-icons">Noun icons</a>
* <p>
* These colors may be changed by IntelliJ Platform themes.
* <p>
* You may use these colors also in your application (outside of SVG icons), but do
* not use the RGB values defined in this enum.<br>
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
*
* @author Karl Tauber
*/
public enum FlatIconColors
{
// colors for action icons
// see https://jetbrains.design/intellij/principles/icons/#action-icons
ACTIONS_RED ( 0xDB5860, "Actions.Red", true, false ),
ACTIONS_RED_DARK ( 0xC75450, "Actions.Red", false, true ),
ACTIONS_YELLOW ( 0xEDA200, "Actions.Yellow", true, false ),
ACTIONS_YELLOW_DARK ( 0xF0A732, "Actions.Yellow", false, true ),
ACTIONS_GREEN ( 0x59A869, "Actions.Green", true, false ),
ACTIONS_GREEN_DARK ( 0x499C54, "Actions.Green", false, true ),
ACTIONS_BLUE ( 0x389FD6, "Actions.Blue", true, false ),
ACTIONS_BLUE_DARK ( 0x3592C4, "Actions.Blue", false, true ),
ACTIONS_GREY ( 0x6E6E6E, "Actions.Grey", true, false ),
ACTIONS_GREY_DARK ( 0xAFB1B3, "Actions.Grey", false, true ),
ACTIONS_GREYINLINE ( 0x7F8B91, "Actions.GreyInline", true, false ),
ACTIONS_GREYINLINE_DARK ( 0x7F8B91, "Actions.GreyInline", false, true ),
// colors for object icons
// see https://jetbrains.design/intellij/principles/icons/#noun-icons
OBJECTS_GREY ( 0x9AA7B0, "Objects.Grey" ),
OBJECTS_BLUE ( 0x40B6E0, "Objects.Blue" ),
OBJECTS_GREEN ( 0x62B543, "Objects.Green" ),
OBJECTS_YELLOW ( 0xF4AF3D, "Objects.Yellow" ),
OBJECTS_YELLOW_DARK ( 0xD9A343, "Objects.YellowDark" ),
OBJECTS_PURPLE ( 0xB99BF8, "Objects.Purple" ),
OBJECTS_PINK ( 0xF98B9E, "Objects.Pink" ),
OBJECTS_RED ( 0xF26522, "Objects.Red" ),
OBJECTS_RED_STATUS ( 0xE05555, "Objects.RedStatus" ),
OBJECTS_GREEN_ANDROID ( 0xA4C639, "Objects.GreenAndroid" ),
OBJECTS_BLACK_TEXT ( 0x231F20, "Objects.BlackText" );
public final int rgb;
public final String key;
public final boolean light;
public final boolean dark;
FlatIconColors( int rgb, String key ) {
this( rgb, key, true, true );
}
FlatIconColors( int rgb, String key, boolean light, boolean dark ) {
this.rgb = rgb;
this.key = key;
this.light = light;
this.dark = dark;
}
}

View File

@@ -0,0 +1,658 @@
/*
* 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;
import javax.swing.InputMap;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIManager;
import javax.swing.plaf.InputMapUIResource;
import com.formdev.flatlaf.util.SystemInfo;
import static javax.swing.text.DefaultEditorKit.*;
import java.util.function.BooleanSupplier;
/**
* @author Karl Tauber
*/
class FlatInputMaps
{
static void initInputMaps( UIDefaults defaults ) {
initBasicInputMaps( defaults );
initTextComponentInputMaps( defaults );
if( SystemInfo.isMacOS )
initMacInputMaps( defaults );
}
private static void initBasicInputMaps( UIDefaults defaults ) {
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
"SPACE", "pressed",
"released SPACE", "released"
} ) );
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
"SPACE", "spacePopup",
"UP", mac( "selectPrevious2", "selectPrevious" ),
"DOWN", mac( "selectNext2", "selectNext" ),
"KP_UP", mac( "selectPrevious2", "selectPrevious" ),
"KP_DOWN", mac( "selectNext2", "selectNext" ),
mac( "alt UP", null ), "togglePopup",
mac( "alt DOWN", null ), "togglePopup",
mac( "alt KP_UP", null ), "togglePopup",
mac( "alt KP_DOWN", null ), "togglePopup"
);
if( !SystemInfo.isMacOS ) {
modifyInputMap( defaults, "FileChooser.ancestorInputMap",
"F2", "editFileName",
"BACK_SPACE", "Go Up"
);
}
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
if( bindings != null && rtlBindings != null ) {
Object[] newBindings = new Object[bindings.length + rtlBindings.length];
System.arraycopy( bindings, 0, newBindings, 0, bindings.length );
System.arraycopy( rtlBindings, 0, newBindings, bindings.length, rtlBindings.length );
defaults.put( "PopupMenu.selectedWindowInputMapBindings.RightToLeft", newBindings );
}
modifyInputMap( defaults, "TabbedPane.ancestorInputMap",
"ctrl TAB", "navigateNext",
"shift ctrl TAB", "navigatePrevious"
);
// swap Home/End with Ctrl+Home/End to make it consistent with List and Tree
modifyInputMap( () -> {
return UIManager.getBoolean( "Table.consistentHomeEndKeyBehavior" );
},
defaults, "Table.ancestorInputMap",
"HOME", "selectFirstRow",
"END", "selectLastRow",
"shift HOME", "selectFirstRowExtendSelection",
"shift END", "selectLastRowExtendSelection",
mac( "ctrl HOME", null ), "selectFirstColumn",
mac( "ctrl END", null ), "selectLastColumn",
mac( "shift ctrl HOME", null ), "selectFirstColumnExtendSelection",
mac( "shift ctrl END", null ), "selectLastColumnExtendSelection"
);
if( !SystemInfo.isMacOS ) {
modifyInputMap( defaults, "Tree.focusInputMap",
"ADD", "expand",
"SUBTRACT", "collapse"
);
}
}
private static void initTextComponentInputMaps( UIDefaults defaults ) {
Object[] commonTextComponentBindings = {
// move caret one character (without selecting text)
"LEFT", backwardAction,
"RIGHT", forwardAction,
"KP_LEFT", backwardAction,
"KP_RIGHT", forwardAction,
// move caret one character and select text
"shift LEFT", selectionBackwardAction,
"shift RIGHT", selectionForwardAction,
"shift KP_LEFT", selectionBackwardAction,
"shift KP_RIGHT", selectionForwardAction,
// move caret to word (without selecting text)
mac( "ctrl LEFT", "alt LEFT" ), previousWordAction,
mac( "ctrl RIGHT", "alt RIGHT" ), nextWordAction,
mac( "ctrl KP_LEFT", "alt KP_LEFT" ), previousWordAction,
mac( "ctrl KP_RIGHT", "alt KP_RIGHT" ), nextWordAction,
// move caret to word and select text
mac( "ctrl shift LEFT", "shift alt LEFT" ), selectionPreviousWordAction,
mac( "ctrl shift RIGHT", "shift alt RIGHT" ), selectionNextWordAction,
mac( "ctrl shift KP_LEFT", "shift alt KP_LEFT" ), selectionPreviousWordAction,
mac( "ctrl shift KP_RIGHT", "shift alt KP_RIGHT" ), selectionNextWordAction,
// move caret to line begin/end (without selecting text)
mac( "HOME", "meta LEFT" ), beginLineAction,
mac( "END", "meta RIGHT" ), endLineAction,
// move caret to line begin/end and select text
mac( "shift HOME", "shift meta LEFT" ), selectionBeginLineAction,
mac( "shift END", "shift meta RIGHT" ), selectionEndLineAction,
// select all/none
mac( "ctrl A", "meta A" ), selectAllAction,
mac( "ctrl BACK_SLASH", "meta BACK_SLASH" ), "unselect", // DefaultEditorKit.unselectAction
// delete previous/next character
"BACK_SPACE", deletePrevCharAction,
"shift BACK_SPACE", deletePrevCharAction,
"ctrl H", deletePrevCharAction,
"DELETE", deleteNextCharAction,
// delete previous/next word
mac( "ctrl BACK_SPACE", "alt BACK_SPACE" ), deletePrevWordAction,
mac( "ctrl DELETE", "alt DELETE" ), deleteNextWordAction,
// clipboard
mac( "ctrl X", "meta X" ), cutAction,
mac( "ctrl C", "meta C" ), copyAction,
mac( "ctrl V", "meta V" ), pasteAction,
"CUT", cutAction,
"COPY", copyAction,
"PASTE", pasteAction,
mac( "shift DELETE", null ), cutAction,
mac( "control INSERT", null ), copyAction,
mac( "shift INSERT", null ), pasteAction,
// misc
"control shift O", "toggle-componentOrientation", // DefaultEditorKit.toggleComponentOrientation
};
Object[] macCommonTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret one character (without selecting text)
"ctrl B", backwardAction,
"ctrl F", forwardAction,
// move caret to document begin/end (without selecting text)
"HOME", beginAction,
"END", endAction,
"meta UP", beginAction,
"meta DOWN", endAction,
"meta KP_UP", beginAction,
"meta KP_DOWN", endAction,
"ctrl P", beginAction,
"ctrl N", endAction,
"ctrl V", endAction,
// move caret to line begin/end (without selecting text)
"meta KP_LEFT", beginLineAction,
"meta KP_RIGHT", endLineAction,
"ctrl A", beginLineAction,
"ctrl E", endLineAction,
// move caret to document begin/end and select text
"shift meta UP", selectionBeginAction,
"shift meta DOWN", selectionEndAction,
"shift meta KP_UP", selectionBeginAction,
"shift meta KP_DOWN", selectionEndAction,
"shift HOME", selectionBeginAction,
"shift END", selectionEndAction,
// move caret to line begin/end and select text
"shift meta KP_LEFT", selectionBeginLineAction,
"shift meta KP_RIGHT", selectionEndLineAction,
"shift UP", selectionBeginLineAction,
"shift DOWN", selectionEndLineAction,
"shift KP_UP", selectionBeginLineAction,
"shift KP_DOWN", selectionEndLineAction,
// delete previous/next word
"ctrl W", deletePrevWordAction,
"ctrl D", deleteNextCharAction,
} : null;
Object[] singleLineTextComponentBindings = {
"ENTER", JTextField.notifyAction,
};
Object[] macSingleLineTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret to line begin/end (without selecting text)
"UP", beginLineAction,
"DOWN", endLineAction,
"KP_UP", beginLineAction,
"KP_DOWN", endLineAction,
} : null;
Object[] formattedTextComponentBindings = {
// reset
"ESCAPE", "reset-field-edit",
// increment/decrement
"UP", "increment",
"DOWN", "decrement",
"KP_UP", "increment",
"KP_DOWN", "decrement",
};
Object[] passwordTextComponentBindings = {
// move caret to line begin/end (without selecting text)
mac( "ctrl LEFT", "alt LEFT" ), beginLineAction,
mac( "ctrl RIGHT", "alt RIGHT" ), endLineAction,
mac( "ctrl KP_LEFT", "alt KP_LEFT" ), beginLineAction,
mac( "ctrl KP_RIGHT", "alt KP_RIGHT" ), endLineAction,
// move caret to line begin/end and select text
mac( "ctrl shift LEFT", "shift alt LEFT" ), selectionBeginLineAction,
mac( "ctrl shift RIGHT", "shift alt RIGHT" ), selectionEndLineAction,
mac( "ctrl shift KP_LEFT", "shift alt KP_LEFT" ), selectionBeginLineAction,
mac( "ctrl shift KP_RIGHT", "shift alt KP_RIGHT" ), selectionEndLineAction,
// delete previous/next word
mac( "ctrl BACK_SPACE", "alt BACK_SPACE" ), null,
mac( "ctrl DELETE", "alt DELETE" ), null,
};
Object[] multiLineTextComponentBindings = {
// move caret one line (without selecting text)
"UP", upAction,
"DOWN", downAction,
"KP_UP", upAction,
"KP_DOWN", downAction,
// move caret one line and select text
"shift UP", selectionUpAction,
"shift DOWN", selectionDownAction,
"shift KP_UP", selectionUpAction,
"shift KP_DOWN", selectionDownAction,
// move caret one page (without selecting text)
"PAGE_UP", pageUpAction,
"PAGE_DOWN", pageDownAction,
// move caret one page and select text
"shift PAGE_UP", "selection-page-up", // DefaultEditorKit.selectionPageUpAction
"shift PAGE_DOWN", "selection-page-down", // DefaultEditorKit.selectionPageDownAction
mac( "ctrl shift PAGE_UP", "shift meta PAGE_UP" ), "selection-page-left", // DefaultEditorKit.selectionPageLeftAction
mac( "ctrl shift PAGE_DOWN", "shift meta PAGE_DOWN" ), "selection-page-right", // DefaultEditorKit.selectionPageRightAction
// move caret to document begin/end (without selecting text)
mac( "ctrl HOME", "meta UP" ), beginAction,
mac( "ctrl END", "meta DOWN" ), endAction,
// move caret to document begin/end and select text
mac( "ctrl shift HOME", "shift meta UP" ), selectionBeginAction,
mac( "ctrl shift END", "shift meta DOWN" ), selectionEndAction,
// misc
"ENTER", insertBreakAction,
"TAB", insertTabAction,
// links
mac( "ctrl T", "meta T" ), "next-link-action",
mac( "ctrl shift T", "shift meta T" ), "previous-link-action",
mac( "ctrl SPACE", "meta SPACE" ), "activate-link-action",
};
Object[] macMultiLineTextComponentBindings = SystemInfo.isMacOS ? new Object[] {
// move caret one line (without selecting text)
"ctrl N", downAction,
"ctrl P", upAction,
// move caret to beginning/end of paragraph and select text
"shift alt UP", selectionBeginParagraphAction,
"shift alt DOWN", selectionEndParagraphAction,
"shift alt KP_UP", selectionBeginParagraphAction,
"shift alt KP_DOWN", selectionEndParagraphAction,
// move caret one page (without selecting text)
"ctrl V", pageDownAction,
} : null;
defaults.put( "TextField.focusInputMap", new LazyInputMapEx(
commonTextComponentBindings,
macCommonTextComponentBindings,
singleLineTextComponentBindings,
macSingleLineTextComponentBindings
) );
defaults.put( "FormattedTextField.focusInputMap", new LazyInputMapEx(
commonTextComponentBindings,
macCommonTextComponentBindings,
singleLineTextComponentBindings,
macSingleLineTextComponentBindings,
formattedTextComponentBindings
) );
defaults.put( "PasswordField.focusInputMap", new LazyInputMapEx(
commonTextComponentBindings,
macCommonTextComponentBindings,
singleLineTextComponentBindings,
macSingleLineTextComponentBindings,
passwordTextComponentBindings
) );
Object multiLineInputMap = new LazyInputMapEx(
commonTextComponentBindings,
macCommonTextComponentBindings,
multiLineTextComponentBindings,
macMultiLineTextComponentBindings
);
defaults.put( "TextArea.focusInputMap", multiLineInputMap );
defaults.put( "TextPane.focusInputMap", multiLineInputMap );
defaults.put( "EditorPane.focusInputMap", multiLineInputMap );
}
private static void initMacInputMaps( UIDefaults defaults ) {
// list
modifyInputMap( defaults, "List.focusInputMap",
"meta A", "selectAll",
"meta C", "copy",
"meta V", "paste",
"meta X", "cut",
// let parent scroll pane do the macOS typical scrolling without changing selection
"HOME", null,
"END", null,
"PAGE_UP", null,
"PAGE_DOWN", null,
"ctrl A", null,
"ctrl BACK_SLASH", null,
"ctrl C", null,
"ctrl DOWN", null,
"ctrl END", null,
"ctrl HOME", null,
"ctrl INSERT", null,
"ctrl KP_DOWN", null,
"ctrl KP_LEFT", null,
"ctrl KP_RIGHT", null,
"ctrl KP_UP", null,
"ctrl LEFT", null,
"ctrl PAGE_DOWN", null,
"ctrl PAGE_UP", null,
"ctrl RIGHT", null,
"ctrl SLASH", null,
"ctrl SPACE", null,
"ctrl UP", null,
"ctrl V", null,
"ctrl X", null,
"SPACE", null,
"shift ctrl DOWN", null,
"shift ctrl END", null,
"shift ctrl HOME", null,
"shift ctrl KP_DOWN", null,
"shift ctrl KP_LEFT", null,
"shift ctrl KP_RIGHT", null,
"shift ctrl KP_UP", null,
"shift ctrl LEFT", null,
"shift ctrl PAGE_DOWN", null,
"shift ctrl PAGE_UP", null,
"shift ctrl RIGHT", null,
"shift ctrl SPACE", null,
"shift ctrl UP", null,
"shift DELETE", null,
"shift INSERT", null,
"shift SPACE", null
);
modifyInputMap( defaults, "List.focusInputMap.RightToLeft",
"ctrl KP_LEFT", null,
"ctrl KP_RIGHT", null,
"ctrl LEFT", null,
"ctrl RIGHT", null,
"shift ctrl KP_LEFT", null,
"shift ctrl KP_RIGHT", null,
"shift ctrl LEFT", null,
"shift ctrl RIGHT", null
);
// scrollpane
modifyInputMap( defaults, "ScrollPane.ancestorInputMap",
"END", "scrollEnd",
"HOME", "scrollHome",
"ctrl END", null,
"ctrl HOME", null,
"ctrl PAGE_DOWN", null,
"ctrl PAGE_UP", null
);
modifyInputMap( defaults, "ScrollPane.ancestorInputMap.RightToLeft",
"ctrl PAGE_DOWN", null,
"ctrl PAGE_UP", null
);
// tabbedpane
modifyInputMap( defaults, "TabbedPane.ancestorInputMap",
"ctrl UP", null,
"ctrl KP_UP", null
);
modifyInputMap( defaults, "TabbedPane.focusInputMap",
"ctrl DOWN", null,
"ctrl KP_DOWN", null
);
// table
modifyInputMap( defaults, "Table.ancestorInputMap",
"alt TAB", "focusHeader",
"shift alt TAB", "focusHeader",
"meta A", "selectAll",
"meta C", "copy",
"meta V", "paste",
"meta X", "cut",
// let parent scroll pane do the macOS typical scrolling without changing selection
"HOME", null,
"END", null,
"PAGE_UP", null,
"PAGE_DOWN", null,
"ctrl A", null,
"ctrl BACK_SLASH", null,
"ctrl C", null,
"ctrl DOWN", null,
"ctrl END", null,
"ctrl HOME", null,
"ctrl INSERT", null,
"ctrl KP_DOWN", null,
"ctrl KP_LEFT", null,
"ctrl KP_RIGHT", null,
"ctrl KP_UP", null,
"ctrl LEFT", null,
"ctrl PAGE_DOWN", null,
"ctrl PAGE_UP", null,
"ctrl RIGHT", null,
"ctrl SLASH", null,
"ctrl SPACE", null,
"ctrl UP", null,
"ctrl V", null,
"ctrl X", null,
"F2", null,
"F8", null,
"SPACE", null,
"shift ctrl DOWN", null,
"shift ctrl END", null,
"shift ctrl HOME", null,
"shift ctrl KP_DOWN", null,
"shift ctrl KP_LEFT", null,
"shift ctrl KP_RIGHT", null,
"shift ctrl KP_UP", null,
"shift ctrl LEFT", null,
"shift ctrl PAGE_DOWN", null,
"shift ctrl PAGE_UP", null,
"shift ctrl RIGHT", null,
"shift ctrl SPACE", null,
"shift ctrl UP", null,
"shift DELETE", null,
"shift INSERT", null,
"shift SPACE", null
);
modifyInputMap( defaults, "Table.ancestorInputMap.RightToLeft",
"ctrl KP_LEFT", null,
"ctrl KP_RIGHT", null,
"ctrl LEFT", null,
"ctrl RIGHT", null,
"shift ctrl KP_LEFT", null,
"shift ctrl KP_RIGHT", null,
"shift ctrl LEFT", null,
"shift ctrl RIGHT", null
);
// tree node expanding/collapsing
modifyInputMap( defaults, "Tree.focusInputMap",
"LEFT", "selectParent",
"RIGHT", "selectChild",
"KP_LEFT", "selectParent",
"KP_RIGHT", "selectChild",
"shift LEFT", "selectParent",
"shift RIGHT", "selectChild",
"shift KP_LEFT", "selectParent",
"shift KP_RIGHT", "selectChild",
"alt LEFT", "selectParent",
"alt RIGHT", "selectChild",
"alt KP_LEFT", "selectParent",
"alt KP_RIGHT", "selectChild",
"shift HOME", "selectFirstExtendSelection",
"shift END", "selectLastExtendSelection",
"meta A", "selectAll",
"meta C", "copy",
"meta V", "paste",
"meta X", "cut",
// let parent scroll pane do the macOS typical scrolling without changing selection
"HOME", null,
"END", null,
"PAGE_UP", null,
"PAGE_DOWN", null,
"ctrl LEFT", null,
"ctrl RIGHT", null,
"ctrl KP_LEFT", null,
"ctrl KP_RIGHT", null,
"ctrl A", null,
"ctrl BACK_SLASH", null,
"ctrl C", null,
"ctrl DOWN", null,
"ctrl END", null,
"ctrl HOME", null,
"ctrl INSERT", null,
"ctrl KP_DOWN", null,
"ctrl KP_UP", null,
"ctrl PAGE_DOWN", null,
"ctrl PAGE_UP", null,
"ctrl SLASH", null,
"ctrl SPACE", null,
"ctrl UP", null,
"ctrl V", null,
"ctrl X", null,
"F2", null,
"SPACE", null,
"shift ctrl DOWN", null,
"shift ctrl END", null,
"shift ctrl HOME", null,
"shift ctrl KP_DOWN", null,
"shift ctrl KP_UP", null,
"shift ctrl PAGE_DOWN", null,
"shift ctrl PAGE_UP", null,
"shift ctrl SPACE", null,
"shift ctrl UP", null,
"shift DELETE", null,
"shift INSERT", null,
"shift PAGE_DOWN", null,
"shift PAGE_UP", null,
"shift SPACE", null
);
defaults.put( "Tree.focusInputMap.RightToLeft", new UIDefaults.LazyInputMap( new Object[] {
"LEFT", "selectChild",
"RIGHT", "selectParent",
"KP_LEFT", "selectChild",
"KP_RIGHT", "selectParent",
"shift LEFT", "selectChild",
"shift RIGHT", "selectParent",
"shift KP_LEFT", "selectChild",
"shift KP_RIGHT", "selectParent",
"alt LEFT", "selectChild",
"alt RIGHT", "selectParent",
"alt KP_LEFT", "selectChild",
"alt KP_RIGHT", "selectParent"
} ) );
}
private static void modifyInputMap( UIDefaults defaults, String key, Object... bindings ) {
modifyInputMap( null, defaults, 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 ) {
return SystemInfo.isMacOS ? macValue : value;
}
//---- class LazyInputMapEx -----------------------------------------------
/**
* Lazily creates a input map.
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
*/
private static class LazyInputMapEx
implements LazyValue
{
private final Object[][] bindingsArray;
LazyInputMapEx( Object[]... bindingsArray ) {
this.bindingsArray = bindingsArray;
}
@Override
public Object createValue( UIDefaults table ) {
InputMap inputMap = new InputMapUIResource();
for( Object[] bindings : bindingsArray )
LookAndFeel.loadKeyBindings( inputMap, bindings );
return inputMap;
}
}
//---- class LazyModifyInputMap -------------------------------------------
/**
* Takes a (lazy) base input map and lazily applies modifications to it specified in bindings.
*/
private static class LazyModifyInputMap
implements LazyValue
{
private final BooleanSupplier condition;
private final Object baseInputMap;
private final Object[] bindings;
LazyModifyInputMap( BooleanSupplier condition, Object baseInputMap, Object[] bindings ) {
this.condition = condition;
this.baseInputMap = baseInputMap;
this.bindings = bindings;
}
@Override
public Object createValue( UIDefaults table ) {
// get base input map
InputMap inputMap = (baseInputMap instanceof LazyValue)
? (InputMap) ((LazyValue)baseInputMap).createValue( table )
: (InputMap) baseInputMap;
if( condition != null && !condition.getAsBoolean() )
return inputMap;
// modify input map (replace or remove)
for( int i = 0; i < bindings.length; i += 2 ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( (String) bindings[i] );
if( bindings[i + 1] != null )
inputMap.put( keyStroke, bindings[i + 1] );
else
inputMap.remove( keyStroke );
}
return inputMap;
}
}
}

View File

@@ -19,6 +19,8 @@ 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.
* *
* The UI defaults are loaded from FlatIntelliJLaf.properties, FlatLightLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatIntelliJLaf public class FlatIntelliJLaf
@@ -30,11 +32,11 @@ public class FlatIntelliJLaf
@Override @Override
public String getName() { public String getName() {
return "Flat IntelliJ"; return "FlatLaf IntelliJ";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return "Flat IntelliJ Look and Feel"; return "FlatLaf IntelliJ Look and Feel";
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a light color scheme. * A Flat LaF that has a light color scheme.
* *
* The UI defaults are loaded from FlatLightLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatLightLaf public class FlatLightLaf
@@ -30,11 +32,16 @@ public class FlatLightLaf
@Override @Override
public String getName() { public String getName() {
return "Flat Light"; return "FlatLaf Light";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return "Flat Light Look and Feel"; return "FlatLaf Light Look and Feel";
}
@Override
public boolean isDark() {
return false;
} }
} }

View File

@@ -0,0 +1,122 @@
/*
* 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;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Properties;
/**
* A Flat LaF that is able to load UI defaults from properties passed to the constructor.
* <p>
* Specify the base theme in the properties with {@code @baseTheme=<baseTheme>}.
* Allowed values for {@code <baseTheme>} are {@code light} (the default), {@code dark},
* {@code intellij} or {@code darcula}.
* <p>
* The properties are applied after loading the base theme and may overwrite base properties.
* All features of FlatLaf properties files are available.
*
* @author Karl Tauber
*/
public class FlatPropertiesLaf
extends FlatLaf
{
private final String name;
private final String baseTheme;
private final boolean dark;
private final Properties properties;
public FlatPropertiesLaf( String name, File propertiesFile )
throws IOException
{
this( name, new FileInputStream( propertiesFile ) );
}
public FlatPropertiesLaf( String name, InputStream in )
throws IOException
{
this( name, loadProperties( in ) );
}
private static Properties loadProperties( InputStream in )
throws IOException
{
Properties properties = new Properties();
try( InputStream in2 = in ) {
properties.load( in2 );
}
return properties;
}
public FlatPropertiesLaf( String name, Properties properties ) {
this.name = name;
this.properties = properties;
baseTheme = properties.getProperty( "@baseTheme", "light" );
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme );
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return name;
}
@Override
public boolean isDark() {
return dark;
}
@Override
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
switch( baseTheme.toLowerCase() ) {
default:
case "light":
lafClasses.add( FlatLightLaf.class );
break;
case "dark":
lafClasses.add( FlatDarkLaf.class );
break;
case "intellij":
lafClasses.add( FlatLightLaf.class );
lafClasses.add( FlatIntelliJLaf.class );
break;
case "darcula":
lafClasses.add( FlatDarkLaf.class );
lafClasses.add( FlatDarculaLaf.class );
break;
}
return lafClasses;
}
@Override
protected Properties getAdditionalDefaults() {
return properties;
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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;
import javax.swing.JDialog;
import javax.swing.JFrame;
/**
* Defines/documents own system properties used in FlatLaf.
*
* @author Karl Tauber
*/
public interface FlatSystemProperties
{
/**
* Specifies a custom scale factor used to scale the UI.
* <p>
* If Java runtime scales (Java 9 or later), this scale factor is applied on top
* of the Java system scale factor. Java 8 does not scale and this scale factor
* replaces the user scale factor that FlatLaf computes based on the font.
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
* which has the same syntax as this one.
* <p>
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
*/
String UI_SCALE = "flatlaf.uiScale";
/**
* 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.
* By default, if not running in a JetBrains Runtime, the Liberation Sans font
* is used because there are rendering issues (in Java) with Ubuntu fonts.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code false}
*/
String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont";
/**
* Specifies whether custom look and feel window decorations should be used
* when creating {@code JFrame} or {@code JDialog}.
* <p>
* If this system property is set, FlatLaf invokes {@link JFrame#setDefaultLookAndFeelDecorated(boolean)}
* and {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} on LaF initialization.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> none
*/
String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations";
/**
* Specifies whether JetBrains Runtime custom window decorations should be used
* when creating {@code JFrame} or {@code JDialog}.
* Requires that the application runs in a
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime</a>
* (based on OpenJDK).
* <p>
* Setting this to {@code true} forces using JetBrains Runtime custom window decorations
* even if they are not enabled by the application.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
/**
* Specifies whether menubar is embedded into custom window decorations.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
/**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
/**
* Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.
* If the system property is not set, {@code defaultValue} is returned.
*/
static boolean getBoolean( String key, boolean defaultValue ) {
String value = System.getProperty( key );
return (value != null) ? Boolean.parseBoolean( value ) : defaultValue;
}
/**
* Checks whether a system property is set and returns {@code Boolean.TRUE} if its value
* is {@code "true"} (case-insensitive) or returns {@code Boolean.FALSE} if its value
* is {@code "false"} (case-insensitive). Otherwise {@code defaultValue} is returned.
*/
static Boolean getBooleanStrict( String key, Boolean defaultValue ) {
String value = System.getProperty( key );
if( "true".equalsIgnoreCase( value ) )
return Boolean.TRUE;
if( "false".equalsIgnoreCase( value ) )
return Boolean.FALSE;
return defaultValue;
}
}

View File

@@ -0,0 +1,613 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.UIDefaults;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.json.Json;
import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.StringUtils;
/**
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
*
* .theme.json files are used by Theme plugins for IntelliJ IDEA and other
* JetBrains IDEs that are based on IntelliJ platform.
*
* Here you can find IntelliJ Theme plugins:
* https://plugins.jetbrains.com/search?tags=Theme
*
* The IntelliJ .theme.json file are documented here:
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html
*
* @author Karl Tauber
*/
public class IntelliJTheme
{
public final String name;
public final boolean dark;
public final String author;
private final Map<String, String> colors;
private final Map<String, Object> ui;
private final Map<String, Object> icons;
private Map<String, ColorUIResource> namedColors = Collections.emptyMap();
/**
* Loads a IntelliJ .theme.json file from the given input stream,
* creates a Laf instance for it and installs it.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
public static boolean install( InputStream in ) {
try {
return FlatLaf.install( createLaf( in ) );
} catch( Exception ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load IntelliJ theme", ex );
return false;
}
}
/**
* Loads a IntelliJ .theme.json file from the given input stream and
* creates a Laf instance for it.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
public static FlatLaf createLaf( InputStream in )
throws IOException
{
return createLaf( new IntelliJTheme( in ) );
}
/**
* Creates a Laf instance for the given IntelliJ theme.
*/
public static FlatLaf createLaf( IntelliJTheme theme ) {
return new ThemeLaf( theme );
}
/**
* Loads a IntelliJ .theme.json file from the given input stream.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
@SuppressWarnings( "unchecked" )
public IntelliJTheme( InputStream in )
throws IOException
{
Map<String, Object> json;
try( Reader reader = new InputStreamReader( in, StandardCharsets.UTF_8 ) ) {
json = (Map<String, Object>) Json.parse( reader );
} catch( ParseException ex ) {
throw new IOException( ex.getMessage(), ex );
}
name = (String) json.get( "name" );
dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
author = (String) json.get( "author" );
colors = (Map<String, String>) json.get( "colors" );
ui = (Map<String, Object>) json.get( "ui" );
icons = (Map<String, Object>) json.get( "icons" );
}
private void applyProperties( UIDefaults defaults ) {
if( ui == null )
return;
defaults.put( "Component.isIntelliJTheme", true );
// enable button shadows
defaults.put( "Button.paintShadow", true );
defaults.put( "Button.shadowWidth", dark ? 2 : 1 );
Map<Object, Object> themeSpecificDefaults = removeThemeSpecificDefaults( defaults );
loadNamedColors( defaults );
// convert Json "ui" structure to UI defaults
ArrayList<Object> defaultsKeysCache = new ArrayList<>();
Set<String> uiKeys = new HashSet<>();
for( Map.Entry<String, Object> e : ui.entrySet() )
apply( e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
applyColorPalette( 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
Object panelBackground = defaults.get( "Panel.background" );
defaults.put( "Button.disabledBackground", panelBackground );
defaults.put( "ToggleButton.disabledBackground", panelBackground );
// 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 helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
if( helpButtonBackground == null )
helpButtonBackground = defaults.get( "Button.background" );
if( helpButtonBorderColor == null )
helpButtonBorderColor = defaults.get( "Button.borderColor" );
defaults.put( "HelpButton.background", helpButtonBackground );
defaults.put( "HelpButton.borderColor", helpButtonBorderColor );
defaults.put( "HelpButton.disabledBackground", panelBackground );
defaults.put( "HelpButton.disabledBorderColor", defaults.get( "Button.disabledBorderColor" ) );
defaults.put( "HelpButton.focusedBorderColor", defaults.get( "Button.focusedBorderColor" ) );
defaults.put( "HelpButton.focusedBackground", defaults.get( "Button.focusedBackground" ) );
// IDEA uses TextField.background for editable ComboBox and Spinner
defaults.put( "ComboBox.editableBackground", defaults.get( "TextField.background" ) );
defaults.put( "Spinner.background", defaults.get( "TextField.background" ) );
// Spinner arrow button always has same colors as ComboBox arrow button
defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonEditableBackground" ) );
defaults.put( "Spinner.buttonArrowColor", defaults.get( "ComboBox.buttonArrowColor" ) );
defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) );
// some themes specify colors for TextField.background, but forget to specify it for other components
// (probably because those components are not used in IntelliJ)
if( uiKeys.contains( "TextField.background" ) ) {
Object textFieldBackground = defaults.get( "TextField.background" );
if( !uiKeys.contains( "FormattedTextField.background" ) )
defaults.put( "FormattedTextField.background", textFieldBackground );
if( !uiKeys.contains( "PasswordField.background" ) )
defaults.put( "PasswordField.background", textFieldBackground );
if( !uiKeys.contains( "EditorPane.background" ) )
defaults.put( "EditorPane.background", textFieldBackground );
if( !uiKeys.contains( "TextArea.background" ) )
defaults.put( "TextArea.background", textFieldBackground );
if( !uiKeys.contains( "TextPane.background" ) )
defaults.put( "TextPane.background", textFieldBackground );
if( !uiKeys.contains( "Spinner.background" ) )
defaults.put( "Spinner.background", textFieldBackground );
}
// fix ToggleButton
if( !uiKeys.contains( "ToggleButton.startBackground" ) && !uiKeys.contains( "*.startBackground" ) )
defaults.put( "ToggleButton.startBackground", defaults.get( "Button.startBackground" ) );
if( !uiKeys.contains( "ToggleButton.endBackground" ) && !uiKeys.contains( "*.endBackground" ) )
defaults.put( "ToggleButton.endBackground", defaults.get( "Button.endBackground" ) );
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
// limit tree row height
int rowHeight = defaults.getInt( "Tree.rowHeight" );
if( rowHeight > 22 )
defaults.put( "Tree.rowHeight", 22 );
// apply theme specific UI defaults at the end to allow overwriting
defaults.putAll( themeSpecificDefaults );
}
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
// search for theme specific UI defaults keys
ArrayList<String> themeSpecificKeys = new ArrayList<>();
for( Object key : defaults.keySet() ) {
if( key instanceof String && ((String)key).startsWith( "[" ) )
themeSpecificKeys.add( (String) key );
}
// remove theme specific UI defaults and remember only those for current theme
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
for( String key : themeSpecificKeys ) {
Object value = defaults.remove( key );
if( key.startsWith( currentThemePrefix ) )
themeSpecificDefaults.put( key.substring( currentThemePrefix.length() ), value );
}
return themeSpecificDefaults;
}
/**
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#defining-named-colors
*/
private void loadNamedColors( UIDefaults defaults ) {
if( colors == null )
return;
namedColors = new HashMap<>();
for( Map.Entry<String, String> e : colors.entrySet() ) {
String value = e.getValue();
ColorUIResource color = UIDefaultsLoader.parseColor( value );
if( color != null ) {
String key = e.getKey();
namedColors.put( key, color );
defaults.put( "ColorPalette." + key, color );
}
}
}
/**
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#custom-ui-control-colors
*/
@SuppressWarnings( "unchecked" )
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
if( value instanceof Map ) {
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
} else {
if( "".equals( value ) )
return; // ignore empty value
uiKeys.add( key );
// 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" )) )
return; // ignore
// map keys
key = uiKeyMapping.getOrDefault( key, key );
if( key.isEmpty() )
return; // ignore key
String valueStr = value.toString();
// map named colors
Object uiValue = namedColors.get( valueStr );
// parse value
if( uiValue == null ) {
// fix errors (missing '#' for colors)
if( !valueStr.startsWith( "#" ) && (key.endsWith( "ground" ) || key.endsWith( "Color" )) )
valueStr = fixColorIfValid( "#" + valueStr, valueStr );
else if( valueStr.startsWith( "##" ) )
valueStr = fixColorIfValid( valueStr.substring( 1 ), valueStr );
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) ) {
List<String> parts = StringUtils.split( valueStr, ',' );
if( parts.size() == 5 && !parts.get( 4 ).startsWith( "#" ) ) {
parts.set( 4, "#" + parts.get( 4 ) );
valueStr = String.join( ",", parts );
}
}
// parse value
try {
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
} catch( RuntimeException ex ) {
UIDefaultsLoader.logParseError( Level.CONFIG, key, valueStr, ex );
return; // ignore invalid value
}
}
if( key.startsWith( "*." ) ) {
// wildcard
String tail = key.substring( 1 );
// because we can not iterate over the UI defaults keys while
// modifying UI defaults in the same loop, we have to copy the keys
if( defaultsKeysCache.size() != defaults.size() ) {
defaultsKeysCache.clear();
Enumeration<Object> e = defaults.keys();
while( e.hasMoreElements() )
defaultsKeysCache.add( e.nextElement() );
}
// replace all values in UI defaults that match the wildcard key
for( Object k : defaultsKeysCache ) {
if( k instanceof String ) {
// support replacing of mapped keys
// (e.g. set ComboBox.buttonEditableBackground to *.background
// because it is mapped from ComboBox.ArrowButton.background)
String km = uiKeyInverseMapping.getOrDefault( k, (String) k );
if( km.endsWith( tail ) && !((String)k).startsWith( "CheckBox.icon." ) )
defaults.put( k, uiValue );
}
}
} else
defaults.put( key, uiValue );
}
}
private String fixColorIfValid( String newColorStr, String colorStr ) {
try {
// check whether it is valid
UIDefaultsLoader.parseColorRGBA( newColorStr );
return newColorStr;
} catch( IllegalArgumentException ex ) {
return colorStr;
}
}
private void applyColorPalette( UIDefaults defaults ) {
if( icons == null )
return;
Object palette = icons.get( "ColorPalette" );
if( !(palette instanceof Map) )
return;
@SuppressWarnings( "unchecked" )
Map<String, Object> colorPalette = (Map<String, Object>) palette;
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
String key = e.getKey();
Object value = e.getValue();
if( key.startsWith( "Checkbox." ) || !(value instanceof String) )
continue;
if( dark )
key = StringUtils.removeTrailing( key, ".Dark" );
ColorUIResource color = toColor( (String) value );
if( color != null )
defaults.put( key, color );
}
}
private ColorUIResource toColor( String value ) {
// map named colors
ColorUIResource color = namedColors.get( value );
// parse color
return (color != null) ? color : UIDefaultsLoader.parseColor( value );
}
/**
* Because IDEA uses SVGs for check boxes and radio buttons the colors for
* this two components are specified in "icons > ColorPalette".
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
*/
private void applyCheckBoxColors( UIDefaults defaults ) {
if( icons == null )
return;
Object palette = icons.get( "ColorPalette" );
if( !(palette instanceof Map) )
return;
boolean checkboxModified = false;
@SuppressWarnings( "unchecked" )
Map<String, Object> colorPalette = (Map<String, Object>) palette;
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
String key = e.getKey();
Object value = e.getValue();
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
continue;
if( key.equals( "Checkbox.Background.Default" ) ||
key.equals( "Checkbox.Foreground.Selected" ) )
{
// This two keys do not work correctly in IDEA because they
// map SVG color "#ffffff" to another color, but checkBox.svg and
// radio.svg (in package com.intellij.ide.ui.laf.icons.intellij)
// use "#fff". So use white to get same appearance as in IDEA.
value = "#ffffff";
}
String key2 = checkboxDuplicateColors.get( key );
if( dark )
key = StringUtils.removeTrailing( key, ".Dark" );
String newKey = checkboxKeyMapping.get( key );
if( newKey != null ) {
String checkBoxIconPrefix = "CheckBox.icon.";
if( !dark && newKey.startsWith( checkBoxIconPrefix ) )
newKey = "CheckBox.icon[filled].".concat( newKey.substring( checkBoxIconPrefix.length() ) );
ColorUIResource color = toColor( (String) value );
if( color != null ) {
defaults.put( newKey, color );
if( key2 != null ) {
// When IDEA replaces colors in SVGs it uses color values and not the keys
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
// have same color value:
// - Checkbox.Background.Default.Dark has same color as Checkbox.Background.Selected.Dark
// - Checkbox.Border.Default.Dark has same color as Checkbox.Border.Selected.Dark
// - Checkbox.Focus.Thin.Default.Dark has same color as Checkbox.Focus.Thin.Selected.Dark
//
// So if only e.g. Checkbox.Background.Default.Dark is specified in .theme.json,
// then this color is also used for Checkbox.Background.Selected.Dark.
//
// If Checkbox.Background.Default.Dark and Checkbox.Background.Selected.Dark
// are specified in .theme.json, then the later specified is used for both.
if( dark )
key2 = StringUtils.removeTrailing( key2, ".Dark" );
String newKey2 = checkboxKeyMapping.get( key2 );
if( newKey2 != null )
defaults.put( newKey2, color );
}
}
checkboxModified = true;
}
}
// remove hover and pressed colors
if( checkboxModified ) {
defaults.remove( "CheckBox.icon.focusWidth" );
defaults.remove( "CheckBox.icon.hoverBorderColor" );
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" );
defaults.remove( "CheckBox.icon[filled].focusWidth" );
defaults.remove( "CheckBox.icon[filled].hoverBorderColor" );
defaults.remove( "CheckBox.icon[filled].focusedBackground" );
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" );
}
}
private static Map<String, String> uiKeyMapping = new HashMap<>();
private static Map<String, String> uiKeyCopying = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
static {
// ComboBox
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
// Component
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
uiKeyMapping.put( "Component.errorFocusColor", "Component.error.focusedBorderColor" );
uiKeyMapping.put( "Component.inactiveWarningFocusColor", "Component.warning.borderColor" );
uiKeyMapping.put( "Component.warningFocusColor", "Component.warning.focusedBorderColor" );
// Link
uiKeyMapping.put( "Link.activeForeground", "Component.linkColor" );
// Menu
uiKeyMapping.put( "Menu.border", "Menu.margin" );
uiKeyMapping.put( "MenuItem.border", "MenuItem.margin" );
uiKeyCopying.put( "CheckBoxMenuItem.margin", "MenuItem.margin" );
uiKeyCopying.put( "RadioButtonMenuItem.margin", "MenuItem.margin" );
uiKeyMapping.put( "PopupMenu.border", "PopupMenu.borderInsets" );
// IDEA uses List.selectionBackground also for menu selection
uiKeyCopying.put( "Menu.selectionBackground", "List.selectionBackground" );
uiKeyCopying.put( "MenuItem.selectionBackground", "List.selectionBackground" );
uiKeyCopying.put( "CheckBoxMenuItem.selectionBackground", "List.selectionBackground" );
uiKeyCopying.put( "RadioButtonMenuItem.selectionBackground", "List.selectionBackground" );
// ProgressBar
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" );
uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" );
uiKeyCopying.put( "ProgressBar.selectionForeground", "ProgressBar.background" );
uiKeyCopying.put( "ProgressBar.selectionBackground", "ProgressBar.foreground" );
// ScrollBar
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
uiKeyMapping.put( "ScrollBar.thumbColor", "ScrollBar.thumb" );
// Separator
uiKeyMapping.put( "Separator.separatorColor", "Separator.foreground" );
// Slider
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
// TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
uiKeyMapping.put( "TitlePane.inactiveInfoForeground", "TitlePane.inactiveForeground" );
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
uiKeyCopying.put( "ToggleButton.tab.underlineColor", "TabbedPane.underlineColor" );
uiKeyCopying.put( "ToggleButton.tab.disabledUnderlineColor", "TabbedPane.disabledUnderlineColor" );
uiKeyCopying.put( "ToggleButton.tab.selectedBackground", "TabbedPane.selectedBackground" );
uiKeyCopying.put( "ToggleButton.tab.hoverBackground", "TabbedPane.hoverColor" );
uiKeyCopying.put( "ToggleButton.tab.focusBackground", "TabbedPane.focusColor" );
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
checkboxKeyMapping.put( "Checkbox.Border.Disabled", "CheckBox.icon.disabledBorderColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Default", "CheckBox.icon.focusedBorderColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Wide", "CheckBox.icon.focusColor" );
checkboxKeyMapping.put( "Checkbox.Foreground.Disabled", "CheckBox.icon.disabledCheckmarkColor" );
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
checkboxDuplicateColors.put( "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
checkboxDuplicateColors.put( "Checkbox.Focus.Thin.Default.Dark", "Checkbox.Focus.Thin.Selected.Dark" );
@SuppressWarnings( "unchecked" )
Map.Entry<String, String>[] entries = checkboxDuplicateColors.entrySet().toArray( new Map.Entry[checkboxDuplicateColors.size()] );
for( Map.Entry<String, String> e : entries )
checkboxDuplicateColors.put( e.getValue(), e.getKey() );
}
//---- class ThemeLaf -----------------------------------------------------
public static class ThemeLaf
extends FlatLaf
{
private final IntelliJTheme theme;
public ThemeLaf( IntelliJTheme theme ) {
this.theme = theme;
}
@Override
public String getName() {
return theme.name;
}
@Override
public String getDescription() {
return theme.name;
}
@Override
public boolean isDark() {
return theme.dark;
}
public IntelliJTheme getTheme() {
return theme;
}
@Override
void applyAdditionalDefaults( UIDefaults defaults ) {
theme.applyProperties( defaults );
}
@Override
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
lafClasses.add( theme.dark ? FlatDarculaLaf.class : FlatIntelliJLaf.class );
lafClasses.add( ThemeLaf.class );
return lafClasses;
}
}
}

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf; package com.formdev.flatlaf;
import java.awt.Font; import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.io.BufferedReader; import java.io.BufferedReader;
@@ -27,8 +28,10 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import javax.swing.text.StyleContext; import java.util.logging.Level;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* @author Karl Tauber * @author Karl Tauber
@@ -36,7 +39,7 @@ import com.formdev.flatlaf.util.SystemInfo;
class LinuxFontPolicy class LinuxFontPolicy
{ {
static Font getFont() { static Font getFont() {
return SystemInfo.IS_KDE ? getKDEFont() : getGnomeFont(); return SystemInfo.isKDE ? getKDEFont() : getGnomeFont();
} }
/** /**
@@ -71,6 +74,13 @@ class LinuxFontPolicy
family = family.isEmpty() ? word : (family + ' ' + word); family = family.isEmpty() ? word : (family + ' ' + word);
} }
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
// --> use Liberation Sans font
if( family.startsWith( "Ubuntu" ) &&
!SystemInfo.isJetBrainsJVM &&
!FlatSystemProperties.getBoolean( FlatSystemProperties.USE_UBUNTU_FONT, false ) )
family = "Liberation Sans";
// scale font size // scale font size
double dsize = size * getGnomeFontScale(); double dsize = size * getGnomeFontScale();
size = (int) (dsize + 0.5); size = (int) (dsize + 0.5);
@@ -86,9 +96,7 @@ class LinuxFontPolicy
} }
private static Font createFont( String family, int style, int size, double dsize ) { private static Font createFont( String family, int style, int size, double dsize ) {
// using StyleContext.getFont() here because it uses Font font = FlatLaf.createCompositeFont( family, style, size );
// sun.font.FontUtilities.getCompositeFontUIResource()
Font font = new StyleContext().getFont( family, style, size );
// set font size in floating points // set font size in floating points
font = font.deriveFont( style, (float) dsize ); font = font.deriveFont( style, (float) dsize );
@@ -97,6 +105,10 @@ class LinuxFontPolicy
} }
private static double getGnomeFontScale() { private static double getGnomeFontScale() {
// do not scale font here if JRE scales
if( isSystemScaling() )
return 96. / 72.;
// see class com.sun.java.swing.plaf.gtk.PangoFonts background information // see class com.sun.java.swing.plaf.gtk.PangoFonts background information
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" ); Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
@@ -151,7 +163,7 @@ class LinuxFontPolicy
int size = 10; int size = 10;
if( generalFont != null ) { if( generalFont != null ) {
List<String> strs = FlatLaf.split( generalFont, ',' ); List<String> strs = StringUtils.split( generalFont, ',' );
try { try {
family = strs.get( 0 ); family = strs.get( 0 );
size = Integer.parseInt( strs.get( 1 ) ); size = Integer.parseInt( strs.get( 1 ) );
@@ -160,13 +172,13 @@ class LinuxFontPolicy
if( "1".equals( strs.get( 5 ) ) ) if( "1".equals( strs.get( 5 ) ) )
style |= Font.ITALIC; style |= Font.ITALIC;
} catch( RuntimeException ex ) { } catch( RuntimeException ex ) {
ex.printStackTrace(); FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
} }
} }
// font dpi // font dpi
int dpi = 96; int dpi = 96;
if( forceFontDPI != null ) { if( forceFontDPI != null && !isSystemScaling() ) {
try { try {
dpi = Integer.parseInt( forceFontDPI ); dpi = Integer.parseInt( forceFontDPI );
if( dpi <= 0 ) if( dpi <= 0 )
@@ -174,7 +186,7 @@ class LinuxFontPolicy
if( dpi < 50 ) if( dpi < 50 )
dpi = 50; dpi = 50;
} catch( NumberFormatException ex ) { } catch( NumberFormatException ex ) {
ex.printStackTrace(); FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
} }
} }
@@ -213,7 +225,7 @@ class LinuxFontPolicy
while( (line = reader.readLine()) != null ) while( (line = reader.readLine()) != null )
lines.add( line ); lines.add( line );
} catch( IOException ex ) { } catch( IOException ex ) {
ex.printStackTrace(); FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to read '" + filename + "'.", ex );
} }
return lines; return lines;
} }
@@ -245,4 +257,15 @@ class LinuxFontPolicy
} }
return null; return null;
} }
/**
* Returns true if the JRE scales, which is the case if:
* - environment variable GDK_SCALE is set and running on Java 9 or later
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
*/
private static boolean isSystemScaling() {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
return UIScale.getSystemScaleFactor( gc ) > 1;
}
} }

View File

@@ -0,0 +1,259 @@
/*
* 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;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.lang.ref.WeakReference;
import javax.swing.AbstractButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Show/hide mnemonics.
*
* @author Karl Tauber
*/
class MnemonicHandler
implements KeyEventPostProcessor, ChangeListener
{
private static boolean showMnemonics;
private static WeakReference<Window> lastShowMnemonicWindow;
private static WindowListener windowListener;
static boolean isShowMnemonics() {
return showMnemonics || !UIManager.getBoolean( "Component.hideMnemonics" );
}
void install() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor( this );
MenuSelectionManager.defaultManager().addChangeListener( this );
}
void uninstall() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor( this );
MenuSelectionManager.defaultManager().removeChangeListener( this );
}
@Override
public boolean postProcessKeyEvent( KeyEvent e ) {
int keyCode = e.getKeyCode();
if( SystemInfo.isMacOS ) {
// Ctrl+Alt keys must be pressed on Mac
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT )
showMnemonics( shouldShowMnemonics( e ) && e.isControlDown() && e.isAltDown(), e.getComponent() );
} else {
// Alt key must be pressed on Windows and Linux
if( SystemInfo.isWindows )
return processKeyEventOnWindows( e );
if( keyCode == KeyEvent.VK_ALT )
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
}
return false;
}
private boolean shouldShowMnemonics( KeyEvent e ) {
return e.getID() == KeyEvent.KEY_PRESSED ||
MenuSelectionManager.defaultManager().getSelectedPath().length > 0;
}
private static int altPressedEventCount;
private static boolean selectMenuOnAltReleased;
/**
* Special Alt key behavior on Windows.
*
* Press-and-release Alt key selects first menu (if available) and moves focus
* temporary to menu bar. If menu bar has focus (some menu is selected),
* pressing Alt key unselects menu and moves focus back to permanent focus owner.
*/
private boolean processKeyEventOnWindows( KeyEvent e ) {
if( e.getKeyCode() != KeyEvent.VK_ALT ) {
selectMenuOnAltReleased = false;
return false;
}
if( e.getID() == KeyEvent.KEY_PRESSED ) {
altPressedEventCount++;
if( altPressedEventCount == 1 && !e.isConsumed() ) {
MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
selectMenuOnAltReleased = (menuSelectionManager.getSelectedPath().length == 0);
// if menu is selected when Alt key is pressed then clear menu selection
if( !selectMenuOnAltReleased )
menuSelectionManager.clearSelectedPath();
}
// show mnemonics
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
// avoid that the system menu of the window gets focus
e.consume();
return true;
} else if( e.getID() == KeyEvent.KEY_RELEASED ) {
altPressedEventCount = 0;
boolean mnemonicsShown = false;
if( selectMenuOnAltReleased && !e.isConsumed() ) {
MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
if( menuSelectionManager.getSelectedPath().length == 0 ) {
// get menu bar and first menu
Component c = e.getComponent();
JRootPane rootPane = SwingUtilities.getRootPane( c );
Window window = (rootPane != null) ? SwingUtilities.getWindowAncestor( rootPane ) : null;
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
if( menuBar == null && window instanceof JFrame )
menuBar = ((JFrame)window).getJMenuBar();
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
// select first menu and show mnemonics
if( firstMenu != null ) {
menuSelectionManager.setSelectedPath( new MenuElement[] { menuBar, firstMenu } );
showMnemonics( true, c );
mnemonicsShown = true;
}
}
}
selectMenuOnAltReleased = false;
// hide mnemonics
if( !mnemonicsShown )
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
}
return false;
}
@Override
public void stateChanged( ChangeEvent e ) {
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
if( selectedPath.length == 0 && altPressedEventCount == 0 ) {
// hide mnemonics when menu selection was canceled
showMnemonics( false, null );
}
}
static void showMnemonics( boolean show, Component c ) {
if( show == showMnemonics )
return;
showMnemonics = show;
// check whether it is necessary to repaint
if( !UIManager.getBoolean( "Component.hideMnemonics" ) )
return;
if( show ) {
// get root pane
JRootPane rootPane = SwingUtilities.getRootPane( c );
if( rootPane == null )
return;
// get window
Window window = SwingUtilities.getWindowAncestor( rootPane );
if( window == null )
return;
// repaint components with mnemonics in focused window
repaintMnemonics( window );
// hide mnemonics if window is deactivated (e.g. Alt+Tab to another window)
windowListener = new WindowAdapter() {
@Override
public void windowDeactivated( WindowEvent e ) {
altPressedEventCount = 0;
selectMenuOnAltReleased = false;
// use invokeLater() to avoid that the listener is removed
// while the listener queue is iterated to fire this event
EventQueue.invokeLater( () -> {
showMnemonics( false, null );
} );
}
};
window.addWindowListener( windowListener );
lastShowMnemonicWindow = new WeakReference<>( window );
} else if( lastShowMnemonicWindow != null ) {
Window window = lastShowMnemonicWindow.get();
if( window != null ) {
repaintMnemonics( window );
if( windowListener != null ) {
window.removeWindowListener( windowListener );
windowListener = null;
}
}
lastShowMnemonicWindow = null;
}
}
private static void repaintMnemonics( Container container ) {
for( Component c : container.getComponents() ) {
if( !c.isVisible() )
continue;
if( hasMnemonic( c ) )
c.repaint();
if( c instanceof Container )
repaintMnemonics( (Container) c );
}
}
private static boolean hasMnemonic( Component c ) {
if( c instanceof JLabel && ((JLabel)c).getDisplayedMnemonicIndex() >= 0 )
return true;
if( c instanceof AbstractButton && ((AbstractButton)c).getDisplayedMnemonicIndex() >= 0 )
return true;
if( c instanceof JTabbedPane ) {
JTabbedPane tabPane = (JTabbedPane) c;
int tabCount = tabPane.getTabCount();
for( int i = 0; i < tabCount; i++ ) {
if( tabPane.getDisplayedMnemonicIndexAt( i ) >= 0 )
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,858 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.function.Function;
import java.util.logging.Level;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.InsetsUIResource;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.HSLColor;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* Load UI defaults from properties files associated to Flat LaF classes and add to UI defaults.
*
* Each class that extend the LaF class may have its own .properties file
* in the same package as the class. Properties from superclasses are loaded
* first to give subclasses a chance to override defaults.
* E.g. if running FlatDarkLaf, then the FlatLaf.properties is loaded first
* and FlatDarkLaf.properties loaded second.
*
* @author Karl Tauber
*/
class UIDefaultsLoader
{
private static final String TYPE_PREFIX = "{";
private static final String TYPE_PREFIX_END = "}";
private static final String VARIABLE_PREFIX = "@";
private static final String PROPERTY_PREFIX = "$";
private static final String OPTIONAL_PREFIX = "?";
private static final String GLOBAL_PREFIX = "*.";
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
Properties additionalDefaults, boolean dark, UIDefaults defaults )
{
// determine classes in class hierarchy in reverse order
ArrayList<Class<?>> lafClasses = new ArrayList<>();
for( Class<?> lafClass = lookAndFeelClass;
FlatLaf.class.isAssignableFrom( lafClass );
lafClass = lafClass.getSuperclass() )
{
lafClasses.add( 0, lafClass );
}
loadDefaultsFromProperties( lafClasses, addons, additionalDefaults, dark, defaults );
}
static void loadDefaultsFromProperties( List<Class<?>> lafClasses, List<FlatDefaultsAddon> addons,
Properties additionalDefaults, boolean dark, UIDefaults defaults )
{
try {
// load core properties files
Properties properties = new Properties();
for( Class<?> lafClass : lafClasses ) {
String propertiesName = '/' + lafClass.getName().replace( '.', '/' ) + ".properties";
try( InputStream in = lafClass.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
}
}
// load properties from addons
for( FlatDefaultsAddon addon : addons ) {
for( Class<?> lafClass : lafClasses ) {
try( InputStream in = addon.getDefaults( lafClass ) ) {
if( in != null )
properties.load( in );
}
}
}
// collect addon class loaders
List<ClassLoader> addonClassLoaders = new ArrayList<>();
for( FlatDefaultsAddon addon : addons ) {
ClassLoader addonClassLoader = addon.getClass().getClassLoader();
if( !addonClassLoaders.contains( 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
if( additionalDefaults != null )
properties.putAll( additionalDefaults );
// collect all platform specific keys (but do not modify properties)
ArrayList<String> platformSpecificKeys = new ArrayList<>();
for( Object okey : properties.keySet() ) {
String key = (String) okey;
if( key.startsWith( "[" ) &&
(key.startsWith( "[win]" ) ||
key.startsWith( "[mac]" ) ||
key.startsWith( "[linux]" ) ||
key.startsWith( "[light]" ) ||
key.startsWith( "[dark]" )) )
platformSpecificKeys.add( key );
}
// remove platform specific properties and re-add only properties
// for current platform, but with platform prefix removed
if( !platformSpecificKeys.isEmpty() ) {
// handle light/dark specific properties
String lightOrDarkPrefix = dark ? "[dark]" : "[light]";
for( String key : platformSpecificKeys ) {
if( key.startsWith( lightOrDarkPrefix ) )
properties.put( key.substring( lightOrDarkPrefix.length() ), properties.remove( key ) );
}
// handle platform specific properties
String platformPrefix =
SystemInfo.isWindows ? "[win]" :
SystemInfo.isMacOS ? "[mac]" :
SystemInfo.isLinux ? "[linux]" : "[unknown]";
for( String key : platformSpecificKeys ) {
Object value = properties.remove( key );
if( key.startsWith( platformPrefix ) )
properties.put( key.substring( platformPrefix.length() ), value );
}
}
// get (and remove) globals, which override all other defaults that end with same suffix
HashMap<String, String> globals = 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( GLOBAL_PREFIX ) ) {
globals.put( key.substring( GLOBAL_PREFIX.length() ), (String) e.getValue() );
it.remove();
}
}
// override UI defaults with globals
for( Object okey : defaults.keySet() ) {
if( okey instanceof String && ((String)okey).contains( "." ) ) {
String key = (String) okey;
String globalKey = key.substring( key.lastIndexOf( '.' ) + 1 );
String globalValue = globals.get( globalKey );
if( globalValue != null && !properties.containsKey( key ) )
properties.put( key, globalValue );
}
}
Function<String, String> propertiesGetter = key -> {
return properties.getProperty( key );
};
Function<String, String> resolver = value -> {
return resolveValue( value, propertiesGetter );
};
// parse and add properties to UI defaults
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( key.startsWith( VARIABLE_PREFIX ) )
continue;
String value = resolveValue( (String) e.getValue(), propertiesGetter );
try {
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( Level.SEVERE, key, value, ex );
}
}
} catch( IOException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load properties files.", ex );
}
}
static void logParseError( Level level, String key, String value, RuntimeException ex ) {
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
}
static String resolveValue( String value, Function<String, String> propertiesGetter ) {
value = value.trim();
String value0 = value;
if( value.startsWith( PROPERTY_PREFIX ) )
value = value.substring( PROPERTY_PREFIX.length() );
else if( !value.startsWith( VARIABLE_PREFIX ) )
return value;
boolean optional = false;
if( value.startsWith( OPTIONAL_PREFIX ) ) {
value = value.substring( OPTIONAL_PREFIX.length() );
optional = true;
}
String newValue = propertiesGetter.apply( value );
if( newValue == null ) {
if( optional )
return "null";
throw new IllegalArgumentException( "variable or property '" + value + "' not found" );
}
if( newValue.equals( value0 ) )
throw new IllegalArgumentException( "endless recursion in variable or property '" + value + "'" );
return resolveValue( newValue, propertiesGetter );
}
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
private static ValueType[] tempResultValueType = new ValueType[1];
static Object parseValue( String key, String value ) {
return parseValue( key, value, null, v -> v, Collections.emptyList() );
}
static Object parseValue( String key, String value, ValueType[] resultValueType,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
{
if( resultValueType == null )
resultValueType = tempResultValueType;
value = value.trim();
// null, false, true
switch( value ) {
case "null": resultValueType[0] = ValueType.NULL; return null;
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
}
// check for function "lazy"
// Syntax: lazy(uiKey)
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
resultValueType[0] = ValueType.LAZY;
String uiKey = value.substring( 5, value.length() - 1 ).trim();
return (LazyValue) t -> {
return lazyUIManagerGet( uiKey );
};
}
ValueType valueType = ValueType.UNKNOWN;
// check whether value type is specified in the value
if( value.startsWith( "#" ) )
valueType = ValueType.COLOR;
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
valueType = ValueType.STRING;
value = value.substring( 1, value.length() - 1 );
} else if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
String typeStr = value.substring( TYPE_PREFIX.length(), end );
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
// remove type from value
value = value.substring( end + TYPE_PREFIX_END.length() );
} catch( IllegalArgumentException ex ) {
// ignore
}
}
}
// determine value type from key
if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( "ground" ) || key.endsWith( "Color" ) )
valueType = ValueType.COLOR;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER;
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
valueType = ValueType.ICON;
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
valueType = ValueType.INSETS;
else if( key.endsWith( "Size" ) )
valueType = ValueType.DIMENSION;
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
valueType = ValueType.INTEGER;
else if( key.endsWith( "Char" ) )
valueType = ValueType.CHARACTER;
else if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
else if( key.endsWith( "grayFilter" ) )
valueType = ValueType.GRAYFILTER;
}
resultValueType[0] = valueType;
// parse value
switch( valueType ) {
case STRING: return value;
case CHARACTER: return parseCharacter( value );
case INTEGER: return parseInteger( value, true );
case FLOAT: return parseFloat( value, true );
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
case ICON: return parseInstance( value, addonClassLoaders );
case INSETS: return parseInsets( value );
case DIMENSION: return parseDimension( value );
case COLOR: return parseColorOrFunction( value, resolver, true );
case SCALEDINTEGER: return parseScaledInteger( value );
case SCALEDFLOAT: return parseScaledFloat( value );
case SCALEDINSETS: return parseScaledInsets( value );
case SCALEDDIMENSION:return parseScaledDimension( value );
case INSTANCE: return parseInstance( value, addonClassLoaders );
case CLASS: return parseClass( value, addonClassLoaders );
case GRAYFILTER: return parseGrayFilter( value );
case UNKNOWN:
default:
// colors
Object color = parseColorOrFunction( value, resolver, false );
if( color != null ) {
resultValueType[0] = ValueType.COLOR;
return color;
}
// integer
Integer integer = parseInteger( value, false );
if( integer != null ) {
resultValueType[0] = ValueType.INTEGER;
return integer;
}
// float
Float f = parseFloat( value, false );
if( f != null ) {
resultValueType[0] = ValueType.FLOAT;
return f;
}
// string
resultValueType[0] = ValueType.STRING;
return value;
}
}
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor[,lineThickness]]
List<String> parts = split( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
: null;
float lineThickness = (parts.size() >= 6) ? parseFloat( parts.get( 5 ), true ) : 1f;
return (LazyValue) t -> {
return (lineColor != null)
? new FlatLineBorder( insets, lineColor, lineThickness )
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value, addonClassLoaders );
}
private static Object parseInstance( String value, List<ClassLoader> addonClassLoaders ) {
return (LazyValue) t -> {
try {
return findClass( value, addonClassLoaders ).newInstance();
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
return null;
}
};
}
private static Object parseClass( String value, List<ClassLoader> addonClassLoaders ) {
return (LazyValue) t -> {
try {
return findClass( value, addonClassLoaders );
} catch( ClassNotFoundException ex ) {
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to find class '" + value + "'.", ex );
return null;
}
};
}
private static Class<?> findClass( String className, List<ClassLoader> addonClassLoaders )
throws ClassNotFoundException
{
try {
return Class.forName( className );
} catch( ClassNotFoundException ex ) {
// search in addons class loaders
for( ClassLoader addonClassLoader : addonClassLoaders ) {
try {
return addonClassLoader.loadClass( className );
} catch( ClassNotFoundException ex2 ) {
// ignore
}
}
throw ex;
}
}
private static Insets parseInsets( String value ) {
List<String> numbers = split( value, ',' );
try {
return new InsetsUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ),
Integer.parseInt( numbers.get( 2 ) ),
Integer.parseInt( numbers.get( 3 ) ) );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException( "invalid insets '" + value + "'" );
}
}
private static Dimension parseDimension( String value ) {
List<String> numbers = split( value, ',' );
try {
return new DimensionUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ) );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException( "invalid size '" + value + "'" );
}
}
private static Object parseColorOrFunction( String value, Function<String, String> resolver, boolean reportError ) {
if( value.endsWith( ")" ) )
return parseColorFunctions( value, resolver, reportError );
return parseColor( value, reportError );
}
static ColorUIResource parseColor( String value ) {
return parseColor( value, false );
}
private static ColorUIResource parseColor( String value, boolean reportError ) {
try {
int rgba = parseColorRGBA( value );
return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
: new ColorUIResource( new Color( rgba, true ) );
} catch( IllegalArgumentException ex ) {
if( reportError )
throw new IllegalArgumentException( "invalid color '" + value + "'" );
// not a color --> ignore
}
return null;
}
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as {@code rgba} integer suitable for {@link java.awt.Color},
* which includes alpha component in bits 24-31.
*
* @throws IllegalArgumentException
*/
static int parseColorRGBA( String value ) {
int len = value.length();
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
throw new IllegalArgumentException();
// parse hex
int n = 0;
for( int i = 1; i < len; i++ ) {
char ch = value.charAt( i );
int digit;
if( ch >= '0' && ch <= '9' )
digit = ch - '0';
else if( ch >= 'a' && ch <= 'f' )
digit = ch - 'a' + 10;
else if( ch >= 'A' && ch <= 'F' )
digit = ch - 'A' + 10;
else
throw new IllegalArgumentException();
n = (n << 4) | digit;
}
if( len <= 5 ) {
// double nibbles
int n1 = n & 0xf000;
int n2 = n & 0xf00;
int n3 = n & 0xf0;
int n4 = n & 0xf;
n = (n1 << 16) | (n1 << 12) | (n2 << 12) | (n2 << 8) | (n3 << 8) | (n3 << 4) | (n4 << 4) | n4;
}
return (len == 4 || len == 7)
? (0xff000000 | n) // set alpha to 255
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
}
private static Object parseColorFunctions( String value, Function<String, String> resolver, boolean reportError ) {
int paramsStart = value.indexOf( '(' );
if( paramsStart < 0 ) {
if( reportError )
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
return null;
}
String function = value.substring( 0, paramsStart ).trim();
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
if( params.isEmpty() )
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
switch( function ) {
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
case "hsl": return parseColorHslOrHsla( false, params );
case "hsla": return parseColorHslOrHsla( true, params );
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
}
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
}
/**
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha)
* - red: 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%
* - alpha: an integer 0-255 or a percentage 0-100%
*/
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
Function<String, String> resolver, boolean reportError )
{
if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color
String colorStr = params.get( 0 );
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
return new ColorUIResource( new Color( ((alpha & 0xff) << 24) | (color.getRGB() & 0xffffff), true ) );
}
int red = parseInteger( params.get( 0 ), 0, 255, true );
int green = parseInteger( params.get( 1 ), 0, 255, true );
int blue = parseInteger( params.get( 2 ), 0, 255, true );
int alpha = hasAlpha ? parseInteger( params.get( 3 ), 0, 255, true ) : 255;
return hasAlpha
? new ColorUIResource( new Color( red, green, blue, alpha ) )
: new ColorUIResource( red, green, blue );
}
/**
* Syntax: hsl(hue,saturation,lightness) or hsla(hue,saturation,lightness,alpha)
* - hue: an integer 0-360 representing degrees
* - saturation: a percentage 0-100%
* - lightness: a percentage 0-100%
* - alpha: a percentage 0-100%
*/
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
int hue = parseInteger( params.get( 0 ), 0, 360, false );
int saturation = parsePercentage( params.get( 1 ) );
int lightness = parsePercentage( params.get( 2 ) );
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
float[] hsl = new float[] { hue, saturation, lightness };
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
}
/**
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
* saturate(color,amount[,options]) or desaturate(color,amount[,options])
* - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100%
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
*/
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
List<String> params, Function<String, String> resolver, boolean reportError )
{
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
boolean relative = false;
boolean autoInverse = false;
boolean lazy = false;
boolean derived = false;
if( params.size() > 2 ) {
String options = params.get( 2 );
relative = options.contains( "relative" );
autoInverse = options.contains( "autoInverse" );
lazy = options.contains( "lazy" );
derived = options.contains( "derived" );
// use autoInverse by default for derived colors, except if noAutoInverse is set
if( derived && !options.contains( "noAutoInverse" ) )
autoInverse = true;
}
// create function
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease(
hslIndex, increase, amount, relative, autoInverse );
if( lazy ) {
return (LazyValue) t -> {
Object color = lazyUIManagerGet( colorStr );
return (color instanceof Color)
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
: null;
};
}
// parse base color
String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
if( baseColor == null )
return null;
// apply this function to base color
Color newColor = ColorFunctions.applyFunctions( baseColor, function );
if( derived ) {
ColorFunction[] functions;
if( baseColor instanceof DerivedColor && resolvedColorStr == colorStr ) {
// if the base color is also derived, join the color functions
// but only if base color function is specified directly in this function
ColorFunction[] baseFunctions = ((DerivedColor)baseColor).getFunctions();
functions = new ColorFunction[baseFunctions.length + 1];
System.arraycopy( baseFunctions, 0, functions, 0, baseFunctions.length );
functions[baseFunctions.length] = function;
} else
functions = new ColorFunction[] { function };
return new DerivedColor( newColor, functions );
}
return new ColorUIResource( newColor );
}
private static int parsePercentage( String value ) {
if( !value.endsWith( "%" ) )
throw new NumberFormatException( "invalid percentage '" + value + "'" );
int val;
try {
val = Integer.parseInt( value.substring( 0, value.length() - 1 ) );
} catch( NumberFormatException ex ) {
throw new NumberFormatException( "invalid percentage '" + value + "'" );
}
if( val < 0 || val > 100 )
throw new IllegalArgumentException( "percentage out of range (0-100%) '" + value + "'" );
return val;
}
private static Character parseCharacter( String value ) {
if( value.length() != 1 )
throw new IllegalArgumentException( "invalid character '" + value + "'" );
return value.charAt( 0 );
}
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage ) {
if( allowPercentage && value.endsWith( "%" ) ) {
int percent = parsePercentage( value );
return (max * percent) / 100;
}
Integer integer = parseInteger( value, true );
if( integer.intValue() < min || integer.intValue() > max )
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
return integer;
}
private static Integer parseInteger( String value, boolean reportError ) {
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
if( reportError )
throw new NumberFormatException( "invalid integer '" + value + "'" );
}
return null;
}
private static Float parseFloat( String value, boolean reportError ) {
try {
return Float.parseFloat( value );
} catch( NumberFormatException ex ) {
if( reportError )
throw new NumberFormatException( "invalid float '" + value + "'" );
}
return null;
}
private static ActiveValue parseScaledInteger( String value ) {
int val = parseInteger( value, true );
return (ActiveValue) t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledFloat( String value ) {
float val = parseFloat( value, true );
return (ActiveValue) t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledInsets( String value ) {
Insets insets = parseInsets( value );
return (ActiveValue) t -> {
return UIScale.scale( insets );
};
}
private static ActiveValue parseScaledDimension( String value ) {
Dimension dimension = parseDimension( value );
return (ActiveValue) t -> {
return UIScale.scale( dimension );
};
}
private static Object parseGrayFilter( String value ) {
List<String> numbers = split( value, ',' );
try {
int brightness = Integer.parseInt( numbers.get( 0 ) );
int contrast = Integer.parseInt( numbers.get( 1 ) );
int alpha = Integer.parseInt( numbers.get( 2 ) );
return (LazyValue) t -> {
return new GrayFilter( brightness, contrast, alpha );
};
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException( "invalid gray filter '" + value + "'" );
}
}
/**
* Split string and trim parts.
*/
private static List<String> split( String str, char delim ) {
List<String> result = StringUtils.split( str, delim );
// trim strings
int size = result.size();
for( int i = 0; i < size; i++ )
result.set( i, result.get( i ).trim() );
return result;
}
/**
* Splits function parameters and allows using functions as parameters.
* In other words: Delimiters surrounded by '(' and ')' are ignored.
*/
private static List<String> splitFunctionParams( String str, char delim ) {
ArrayList<String> strs = new ArrayList<>();
int nestLevel = 0;
int start = 0;
int strlen = str.length();
for( int i = 0; i < strlen; i++ ) {
char ch = str.charAt( i );
if( ch == '(' )
nestLevel++;
else if( ch == ')' )
nestLevel--;
else if( nestLevel == 0 && ch == delim ) {
strs.add( str.substring( start, i ).trim() );
start = i + 1;
}
}
strs.add( str.substring( start ).trim() );
return strs;
}
/**
* For use in LazyValue to get value for given key from UIManager and report error
* if not found. If key is prefixed by '?', then no error is reported.
*/
private static Object lazyUIManagerGet( String uiKey ) {
boolean optional = false;
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
optional = true;
}
Object value = UIManager.get( uiKey );
if( value == null && !optional )
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: '" + uiKey + "' not found in UI defaults." );
return value;
}
}

View File

@@ -16,15 +16,18 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; 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 Table.sortIconColor Color * @uiDefault Table.sortIconColor Color
* *
* @author Karl Tauber * @author Karl Tauber
@@ -32,6 +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 Color sortIconColor = UIManager.getColor( "Table.sortIconColor" ); protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
public FlatAscendingSortIcon() { public FlatAscendingSortIcon() {
@@ -41,6 +45,14 @@ public class FlatAscendingSortIcon
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
g.setColor( sortIconColor ); g.setColor( sortIconColor );
g.fill( FlatUIUtils.createPath( 0.5,5, 9.5,5, 5,0 ) ); if( chevron ) {
// chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
g.setStroke( new BasicStroke( 1f ) );
g.draw( path );
} else {
// triangle arrow
g.fill( FlatUIUtils.createPath( 0.5,5, 5,0, 9.5,5 ) );
}
} }
} }

View File

@@ -0,0 +1,59 @@
/*
* 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.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "caps lock" icon for {@link javax.swing.JPasswordField}.
*
* @uiDefault PasswordField.capsLockIconColor Color
*
* @author Karl Tauber
*/
public class FlatCapsLockIcon
extends FlatAbstractIcon
{
public FlatCapsLockIcon() {
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
/*
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
<rect width="6" height="2" x="5" y="12" fill="#FFF"/>
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
</g>
</svg>
*/
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
path.append( new Rectangle2D.Float( 5, 12, 6, 2 ), false );
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
g.fill( path );
}
}

View File

@@ -16,14 +16,18 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.BasicStroke; import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* Icon for {@link javax.swing.JCheckBox}. * Icon for {@link javax.swing.JCheckBox}.
@@ -32,49 +36,91 @@ import com.formdev.flatlaf.ui.FlatButtonUI;
* is painted outside of the icon bounds. Make sure that the checkbox * is painted outside of the icon bounds. Make sure that the checkbox
* has margins, which are equal or greater than focusWidth. * has margins, which are equal or greater than focusWidth.
* *
* @uiDefault CheckBox.icon.style String optional; "outline"/null (default) or "filled"
* @uiDefault Component.focusWidth int * @uiDefault Component.focusWidth int
* @uiDefault Component.focusColor Color * @uiDefault Component.focusColor Color
* @uiDefault CheckBox.icon.focusWidth int optional; defaults to Component.focusWidth
* @uiDefault CheckBox.icon.focusColor Color optional; defaults to Component.focusColor
* @uiDefault CheckBox.icon.borderColor Color * @uiDefault CheckBox.icon.borderColor Color
* @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.selectedBorderColor Color
* @uiDefault CheckBox.icon.focusedBorderColor Color
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional
* @uiDefault CheckBox.icon.background Color * @uiDefault CheckBox.icon.background Color
* @uiDefault CheckBox.icon.disabledBackground Color * @uiDefault CheckBox.icon.selectedBorderColor Color
* @uiDefault CheckBox.icon.focusedBackground Color optional
* @uiDefault CheckBox.icon.hoverBackground Color optional
* @uiDefault CheckBox.icon.pressedBackground Color optional
* @uiDefault CheckBox.icon.selectedBackground Color * @uiDefault CheckBox.icon.selectedBackground Color
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional
* @uiDefault CheckBox.icon.checkmarkColor Color * @uiDefault CheckBox.icon.checkmarkColor Color
* @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.disabledBackground Color
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color * @uiDefault CheckBox.icon.disabledCheckmarkColor Color
* @uiDefault CheckBox.icon.focusedBorderColor Color
* @uiDefault CheckBox.icon.focusedBackground Color optional
* @uiDefault CheckBox.icon.selectedFocusedBorderColor Color optional
* @uiDefault CheckBox.icon.selectedFocusedBackground Color optional
* @uiDefault CheckBox.icon.hoverBorderColor Color optional
* @uiDefault CheckBox.icon.hoverBackground Color optional
* @uiDefault CheckBox.icon.selectedHoverBackground Color optional
* @uiDefault CheckBox.icon.pressedBackground Color optional
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional
* @uiDefault CheckBox.arc int
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatCheckBoxIcon public class FlatCheckBoxIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" ); protected final String style = UIManager.getString( "CheckBox.icon.style" );
protected final Color focusColor = UIManager.getColor( "Component.focusColor" ); public final int focusWidth = getUIInt( "CheckBox.icon.focusWidth",
UIManager.getInt( "Component.focusWidth" ), style );
protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusColor",
UIManager.getColor( "Component.focusColor" ) );
protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
protected final Color borderColor = UIManager.getColor( "CheckBox.icon.borderColor" ); // enabled
protected final Color disabledBorderColor = UIManager.getColor( "CheckBox.icon.disabledBorderColor" ); protected final Color borderColor = getUIColor( "CheckBox.icon.borderColor", style );
protected final Color selectedBorderColor = UIManager.getColor( "CheckBox.icon.selectedBorderColor" ); protected final Color background = getUIColor( "CheckBox.icon.background", style );
protected final Color focusedBorderColor = UIManager.getColor( "CheckBox.icon.focusedBorderColor" ); protected final Color selectedBorderColor = getUIColor( "CheckBox.icon.selectedBorderColor", style );
protected final Color hoverBorderColor = UIManager.getColor( "CheckBox.icon.hoverBorderColor" ); protected final Color selectedBackground = getUIColor( "CheckBox.icon.selectedBackground", style );
protected final Color selectedFocusedBorderColor = UIManager.getColor( "CheckBox.icon.selectedFocusedBorderColor" ); protected final Color checkmarkColor = getUIColor( "CheckBox.icon.checkmarkColor", style );
protected final Color background = UIManager.getColor( "CheckBox.icon.background" );
protected final Color disabledBackground = UIManager.getColor( "CheckBox.icon.disabledBackground" ); // disabled
protected final Color focusedBackground = UIManager.getColor( "CheckBox.icon.focusedBackground" ); protected final Color disabledBorderColor = getUIColor( "CheckBox.icon.disabledBorderColor", style );
protected final Color hoverBackground = UIManager.getColor( "CheckBox.icon.hoverBackground" ); protected final Color disabledBackground = getUIColor( "CheckBox.icon.disabledBackground", style );
protected final Color pressedBackground = UIManager.getColor( "CheckBox.icon.pressedBackground" ); protected final Color disabledCheckmarkColor = getUIColor( "CheckBox.icon.disabledCheckmarkColor", style );
protected final Color selectedBackground = UIManager.getColor( "CheckBox.icon.selectedBackground" );
protected final Color selectedHoverBackground = UIManager.getColor( "CheckBox.icon.selectedHoverBackground" ); // focused
protected final Color selectedPressedBackground = UIManager.getColor( "CheckBox.icon.selectedPressedBackground" ); protected final Color focusedBorderColor = getUIColor( "CheckBox.icon.focusedBorderColor", style );
protected final Color checkmarkColor = UIManager.getColor( "CheckBox.icon.checkmarkColor" ); protected final Color focusedBackground = getUIColor( "CheckBox.icon.focusedBackground", style );
protected final Color disabledCheckmarkColor = UIManager.getColor( "CheckBox.icon.disabledCheckmarkColor" ); protected final Color selectedFocusedBorderColor = getUIColor( "CheckBox.icon.selectedFocusedBorderColor", style );
protected final Color selectedFocusedBackground = getUIColor( "CheckBox.icon.selectedFocusedBackground", style );
protected final Color selectedFocusedCheckmarkColor = getUIColor( "CheckBox.icon.selectedFocusedCheckmarkColor", style );
// hover
protected final Color hoverBorderColor = getUIColor( "CheckBox.icon.hoverBorderColor", style );
protected final Color hoverBackground = getUIColor( "CheckBox.icon.hoverBackground", style );
protected final Color selectedHoverBackground = getUIColor( "CheckBox.icon.selectedHoverBackground", style );
// pressed
protected final Color pressedBackground = getUIColor( "CheckBox.icon.pressedBackground", style );
protected final Color selectedPressedBackground = getUIColor( "CheckBox.icon.selectedPressedBackground", style );
protected static Color getUIColor( String key, String style ) {
if( style != null ) {
Color color = UIManager.getColor( styleKey( key, style ) );
if( color != null )
return color;
}
return UIManager.getColor( key );
}
protected static int getUIInt( String key, int defaultValue, String style ) {
if( style != null ) {
Object value = UIManager.get( styleKey( key, style ) );
if( value instanceof Integer )
return (Integer) value;
}
return FlatUIUtils.getUIInt( key, defaultValue );
}
private static String styleKey( String key, String style ) {
return key.replace( ".icon.", ".icon[" + style + "]." );
}
static final int ICON_SIZE = 15; static final int ICON_SIZE = 15;
@@ -84,10 +130,12 @@ public class FlatCheckBoxIcon
@Override @Override
protected void paintIcon( Component c, Graphics2D g2 ) { protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) ? ((AbstractButton)c).isSelected() : false; boolean indeterminate = c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
boolean isFocused = FlatUIUtils.isPermanentFocusOwner( c );
// paint focused border // paint focused border
if( c.hasFocus() && focusWidth > 0 ) { if( isFocused && focusWidth > 0 && FlatButtonUI.isFocusPainted( c ) ) {
g2.setColor( focusColor ); g2.setColor( focusColor );
paintFocusBorder( g2 ); paintFocusBorder( g2 );
} }
@@ -102,33 +150,44 @@ public class FlatCheckBoxIcon
paintBorder( g2 ); paintBorder( g2 );
// paint background // paint background
g2.setColor( FlatButtonUI.buttonStateColor( c, g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
selected ? selectedBackground : background, selected ? selectedBackground : background,
disabledBackground, disabledBackground,
focusedBackground, (selected && selectedFocusedBackground != null) ? selectedFocusedBackground : focusedBackground,
selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground, (selected && selectedHoverBackground != null) ? selectedHoverBackground : hoverBackground,
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ) ); (selected && selectedPressedBackground != null) ? selectedPressedBackground : pressedBackground ),
background ) );
paintBackground( g2 ); paintBackground( g2 );
// paint checkmark // paint checkmark
if( selected ) { if( selected || indeterminate ) {
g2.setColor( c.isEnabled() ? checkmarkColor : disabledCheckmarkColor ); g2.setColor( c.isEnabled()
paintCheckmark( g2 ); ? ((selected && isFocused && selectedFocusedCheckmarkColor != null)
? selectedFocusedCheckmarkColor
: checkmarkColor)
: disabledCheckmarkColor );
if( indeterminate )
paintIndeterminate( g2 );
else
paintCheckmark( g2 );
} }
} }
protected void paintFocusBorder( Graphics2D g2 ) { protected void paintFocusBorder( Graphics2D g2 ) {
// 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);
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, 8, 8 ); int arcwh = arc + (focusWidth * 2);
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
} }
protected void paintBorder( Graphics2D g2 ) { protected void paintBorder( Graphics2D g2 ) {
g2.fillRoundRect( 1, 0, 14, 14, 4, 4 ); int arcwh = arc;
g2.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
} }
protected void paintBackground( Graphics2D g2 ) { protected void paintBackground( Graphics2D g2 ) {
g2.fillRoundRect( 2, 1, 12, 12, 3, 3 ); int arcwh = arc - 1;
g2.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
} }
protected void paintCheckmark( Graphics2D g2 ) { protected void paintCheckmark( Graphics2D g2 ) {
@@ -140,4 +199,8 @@ public class FlatCheckBoxIcon
g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ); g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g2.draw( path ); g2.draw( path );
} }
protected void paintIndeterminate( Graphics2D g2 ) {
g2.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
}
} }

View File

@@ -30,7 +30,8 @@ import javax.swing.UIManager;
* *
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color * @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color * @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
* @uiDefault Menu.selectionForeground Color * @uiDefault MenuItem.selectionForeground Color
* @uiDefault MenuItem.selectionType String
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -39,7 +40,7 @@ public class FlatCheckBoxMenuItemIcon
{ {
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" ); protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" ); protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" ); protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
public FlatCheckBoxMenuItemIcon() { public FlatCheckBoxMenuItemIcon() {
super( 15, 15, null ); super( 15, 15, null );
@@ -66,10 +67,15 @@ public class FlatCheckBoxMenuItemIcon
g2.draw( path ); g2.draw( path );
} }
private Color getCheckmarkColor( Component c ) { protected Color getCheckmarkColor( Component c ) {
if( c instanceof JMenuItem && ((JMenuItem)c).isArmed() ) if( c instanceof JMenuItem && ((JMenuItem)c).isArmed() && !isUnderlineSelection() )
return selectionForeground; return selectionForeground;
return c.isEnabled() ? checkmarkColor : disabledCheckmarkColor; return c.isEnabled() ? checkmarkColor : disabledCheckmarkColor;
} }
protected boolean isUnderlineSelection() {
// not storing value of "MenuItem.selectionType" in class to allow changing at runtime
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
}
} }

View File

@@ -16,15 +16,18 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils; 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 Table.sortIconColor Color * @uiDefault Table.sortIconColor Color
* *
* @author Karl Tauber * @author Karl Tauber
@@ -32,6 +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 Color sortIconColor = UIManager.getColor( "Table.sortIconColor" ); protected final Color sortIconColor = UIManager.getColor( "Table.sortIconColor" );
public FlatDescendingSortIcon() { public FlatDescendingSortIcon() {
@@ -41,6 +45,14 @@ public class FlatDescendingSortIcon
@Override @Override
protected void paintIcon( Component c, Graphics2D g ) { protected void paintIcon( Component c, Graphics2D g ) {
g.setColor( sortIconColor ); g.setColor( sortIconColor );
g.fill( FlatUIUtils.createPath( 0.5,0, 9.5,0, 5,5 ) ); if( chevron ) {
// chevron arrow
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
g.setStroke( new BasicStroke( 1f ) );
g.draw( path );
} else {
// triangle arrow
g.fill( FlatUIUtils.createPath( 0.5,0, 5,5, 9.5,0 ) );
}
} }
} }

View File

@@ -23,7 +23,7 @@ import javax.swing.UIManager;
/** /**
* "details view" icon for {@link javax.swing.JFileChooser}. * "details view" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileChooser.icon.detailsViewColor Color * @uiDefault Actions.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -31,7 +31,7 @@ public class FlatFileChooserDetailsViewIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileChooserDetailsViewIcon() { public FlatFileChooserDetailsViewIcon() {
super( 16, 16, UIManager.getColor( "FileChooser.icon.detailsViewColor" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@Override @Override

View File

@@ -24,7 +24,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "home folder" icon for {@link javax.swing.JFileChooser}. * "home folder" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileChooser.icon.homeFolderColor Color * @uiDefault Actions.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +32,7 @@ public class FlatFileChooserHomeFolderIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileChooserHomeFolderIcon() { public FlatFileChooserHomeFolderIcon() {
super( 16, 16, UIManager.getColor( "FileChooser.icon.homeFolderColor" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@Override @Override

View File

@@ -23,7 +23,7 @@ import javax.swing.UIManager;
/** /**
* "list view" icon for {@link javax.swing.JFileChooser}. * "list view" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileChooser.icon.listViewColor Color * @uiDefault Actions.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -31,7 +31,7 @@ public class FlatFileChooserListViewIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileChooserListViewIcon() { public FlatFileChooserListViewIcon() {
super( 16, 16, UIManager.getColor( "FileChooser.icon.listViewColor" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@Override @Override

View File

@@ -24,7 +24,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "new folder" icon for {@link javax.swing.JFileChooser}. * "new folder" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileChooser.icon.newFolderColor Color * @uiDefault Actions.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +32,7 @@ public class FlatFileChooserNewFolderIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileChooserNewFolderIcon() { public FlatFileChooserNewFolderIcon() {
super( 16, 16, UIManager.getColor( "FileChooser.icon.newFolderColor" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@Override @Override

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.icons; package com.formdev.flatlaf.icons;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import javax.swing.UIManager; import javax.swing.UIManager;
@@ -24,15 +25,18 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "up folder" icon for {@link javax.swing.JFileChooser}. * "up folder" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileChooser.icon.upFolderColor Color * @uiDefault Actions.Grey Color
* @uiDefault Actions.Blue Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatFileChooserUpFolderIcon public class FlatFileChooserUpFolderIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
private final Color blueColor = UIManager.getColor( "Actions.Blue" );
public FlatFileChooserUpFolderIcon() { public FlatFileChooserUpFolderIcon() {
super( 16, 16, UIManager.getColor( "FileChooser.icon.upFolderColor" ) ); super( 16, 16, UIManager.getColor( "Actions.Grey" ) );
} }
@Override @Override
@@ -47,6 +51,8 @@ public class FlatFileChooserUpFolderIcon
*/ */
g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 9,5, 9,9, 13,9, 13,5, 14,5, 14,13, 2,13 ) ); g.fill( FlatUIUtils.createPath( 2,3, 5.5,3, 7,5, 9,5, 9,9, 13,9, 13,5, 14,5, 14,13, 2,13 ) );
g.setColor( blueColor );
g.fill( FlatUIUtils.createPath( 12,4, 12,8, 10,8, 10,4, 8,4, 11,1, 14,4, 12,4 ) ); g.fill( FlatUIUtils.createPath( 12,4, 12,8, 10,8, 10,4, 8,4, 11,1, 14,4, 12,4 ) );
} }
} }

View File

@@ -25,7 +25,7 @@ import javax.swing.UIManager;
/** /**
* "computer" icon for {@link javax.swing.JFileChooser}. * "computer" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileView.icon.computerColor Color * @uiDefault Objects.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -33,7 +33,7 @@ public class FlatFileViewComputerIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileViewComputerIcon() { public FlatFileViewComputerIcon() {
super( 16, 16, UIManager.getColor( "FileView.icon.computerColor" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@Override @Override

View File

@@ -24,7 +24,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "directory" icon for {@link javax.swing.JFileChooser}. * "directory" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileView.icon.directoryColor Color * @uiDefault Objects.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +32,7 @@ public class FlatFileViewDirectoryIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileViewDirectoryIcon() { public FlatFileViewDirectoryIcon() {
super( 16, 16, UIManager.getColor( "FileView.icon.directoryColor" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@Override @Override

View File

@@ -24,7 +24,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "file" icon for {@link javax.swing.JFileChooser}. * "file" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileView.icon.fileColor Color * @uiDefault Objects.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +32,7 @@ public class FlatFileViewFileIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileViewFileIcon() { public FlatFileViewFileIcon() {
super( 16, 16, UIManager.getColor( "FileView.icon.fileColor" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@Override @Override

View File

@@ -25,7 +25,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "floppy drive" icon for {@link javax.swing.JFileChooser}. * "floppy drive" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileView.icon.floppyDriveColor Color * @uiDefault Objects.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -33,7 +33,7 @@ public class FlatFileViewFloppyDriveIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileViewFloppyDriveIcon() { public FlatFileViewFloppyDriveIcon() {
super( 16, 16, UIManager.getColor( "FileView.icon.floppyDriveColor" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@Override @Override

View File

@@ -25,7 +25,7 @@ import javax.swing.UIManager;
/** /**
* "hard drive" icon for {@link javax.swing.JFileChooser}. * "hard drive" icon for {@link javax.swing.JFileChooser}.
* *
* @uiDefault FileView.icon.hardDriveColor Color * @uiDefault Objects.Grey Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -33,7 +33,7 @@ public class FlatFileViewHardDriveIcon
extends FlatAbstractIcon extends FlatAbstractIcon
{ {
public FlatFileViewHardDriveIcon() { public FlatFileViewHardDriveIcon() {
super( 16, 16, UIManager.getColor( "FileView.icon.hardDriveColor" ) ); super( 16, 16, UIManager.getColor( "Objects.Grey" ) );
} }
@Override @Override

View File

@@ -24,6 +24,7 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI; import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* Help button icon for {@link javax.swing.JButton}. * Help button icon for {@link javax.swing.JButton}.
@@ -81,10 +82,10 @@ public class FlatHelpButtonIcon
*/ */
boolean enabled = c.isEnabled(); boolean enabled = c.isEnabled();
boolean focused = c.hasFocus(); boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
// paint focused border // paint focused border
if( focused ) { if( focused && FlatButtonUI.isFocusPainted( c ) ) {
g2.setColor( focusColor ); g2.setColor( focusColor );
g2.fill( new Ellipse2D.Float( 0.5f, 0.5f, iconSize - 1, iconSize - 1 ) ); g2.fill( new Ellipse2D.Float( 0.5f, 0.5f, iconSize - 1, iconSize - 1 ) );
} }
@@ -99,12 +100,12 @@ public class FlatHelpButtonIcon
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) ); g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
// paint background // paint background
g2.setColor( FlatButtonUI.buttonStateColor( c, g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
background, background,
disabledBackground, disabledBackground,
focusedBackground, focusedBackground,
hoverBackground, hoverBackground,
pressedBackground ) ); pressedBackground ), background ) );
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) ); g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
// paint question mark // paint question mark

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* Base class for internal frame icons.
*
* @uiDefault InternalFrame.buttonSize Dimension
* @uiDefault InternalFrame.buttonHoverBackground Color
* @uiDefault InternalFrame.buttonPressedBackground Color
*
* @author Karl Tauber
*/
public abstract class FlatInternalFrameAbstractIcon
extends FlatAbstractIcon
{
private final Color hoverBackground;
private final Color pressedBackground;
public FlatInternalFrameAbstractIcon() {
this( UIManager.getDimension( "InternalFrame.buttonSize" ),
UIManager.getColor( "InternalFrame.buttonHoverBackground" ),
UIManager.getColor( "InternalFrame.buttonPressedBackground" ) );
}
public FlatInternalFrameAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
super( size.width, size.height, null );
this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
}
protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) {
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
g.fillRect( 0, 0, width, height );
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
/**
* "close" icon for {@link javax.swing.JInternalFrame}.
*
* @uiDefault InternalFrame.buttonSize Dimension
* @uiDefault InternalFrame.closeHoverBackground Color
* @uiDefault InternalFrame.closePressedBackground Color
* @uiDefault InternalFrame.closeHoverForeground Color
* @uiDefault InternalFrame.closePressedForeground Color
*
* @author Karl Tauber
*/
public class FlatInternalFrameCloseIcon
extends FlatInternalFrameAbstractIcon
{
private final Color hoverForeground = UIManager.getColor( "InternalFrame.closeHoverForeground" );
private final Color pressedForeground = UIManager.getColor( "InternalFrame.closePressedForeground" );
public FlatInternalFrameCloseIcon() {
super( UIManager.getDimension( "InternalFrame.buttonSize" ),
UIManager.getColor( "InternalFrame.closeHoverBackground" ),
UIManager.getColor( "InternalFrame.closePressedBackground" ) );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
float mx = width / 2;
float my = height / 2;
float r = 3.25f;
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( 1f ) );
g.draw( path );
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
/**
* "iconify" icon for {@link javax.swing.JInternalFrame}.
*
* @author Karl Tauber
*/
public class FlatInternalFrameIconifyIcon
extends FlatInternalFrameAbstractIcon
{
public FlatInternalFrameIconifyIcon() {
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( c.getForeground() );
g.fillRect( (width / 2) - 4, height / 2, 8, 1 );
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "maximize" icon for {@link javax.swing.JInternalFrame}.
*
* @author Karl Tauber
*/
public class FlatInternalFrameMaximizeIcon
extends FlatInternalFrameAbstractIcon
{
public FlatInternalFrameMaximizeIcon() {
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( c.getForeground() );
g.fill( FlatUIUtils.createRectangle( (width / 2) - 4, (height / 2) - 4, 8, 8, 1 ) );
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "restore" (or "minimize") icon for {@link javax.swing.JInternalFrame}.
*
* @author Karl Tauber
*/
public class FlatInternalFrameRestoreIcon
extends FlatInternalFrameAbstractIcon
{
public FlatInternalFrameRestoreIcon() {
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( c.getForeground() );
int x = (width / 2) - 4;
int y = (height / 2) - 4;
Path2D r1 = FlatUIUtils.createRectangle( x + 1, y - 1, 8, 8, 1 );
Path2D r2 = FlatUIUtils.createRectangle( x - 1, y + 1, 8, 8, 1 );
Area area = new Area( r1 );
area.subtract( new Area( new Rectangle2D.Float( x - 1, y + 1, 8, 8 ) ) );
g.fill( area );
g.fill( r2 );
}
}

View File

@@ -32,6 +32,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @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
* @uiDefault MenuItem.selectionType String
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -60,14 +61,19 @@ public class FlatMenuArrowIcon
g.draw( path ); g.draw( path );
} else { } else {
// triangle arrow // triangle arrow
g.fill( FlatUIUtils.createPath( 0,0.5, 0,9.5, 5,5 ) ); g.fill( FlatUIUtils.createPath( 0,0.5, 5,5, 0,9.5 ) );
} }
} }
private Color getArrowColor( Component c ) { protected Color getArrowColor( Component c ) {
if( c instanceof JMenu && ((JMenu)c).isSelected() ) if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
return selectionForeground; return selectionForeground;
return c.isEnabled() ? arrowColor : disabledArrowColor; return c.isEnabled() ? arrowColor : disabledArrowColor;
} }
protected boolean isUnderlineSelection() {
// not storing value of "MenuItem.selectionType" in class to allow changing at runtime
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
}
} }

View File

@@ -22,11 +22,12 @@ import java.awt.Graphics2D;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* Base class for icons for {@link javax.swing.JOptionPane}. * Base class for icons for {@link javax.swing.JOptionPane}.
* *
* @uiDefault OptionPane.icon.foreground Color * @uiDefault OptionPane.icon.foreground Color default is transparent
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -35,8 +36,8 @@ public abstract class FlatOptionPaneAbstractIcon
{ {
protected final Color foreground = UIManager.getColor( "OptionPane.icon.foreground" ); protected final Color foreground = UIManager.getColor( "OptionPane.icon.foreground" );
protected FlatOptionPaneAbstractIcon( String colorKey ) { protected FlatOptionPaneAbstractIcon( String colorKey, String defaultColorKey ) {
super( 32, 32, UIManager.getColor( colorKey ) ); super( 32, 32, FlatUIUtils.getUIColor( colorKey, defaultColorKey ) );
} }
@Override @Override

View File

@@ -24,7 +24,8 @@ import java.awt.geom.Rectangle2D;
/** /**
* "Error" icon for {@link javax.swing.JOptionPane}. * "Error" icon for {@link javax.swing.JOptionPane}.
* *
* @uiDefault OptionPane.icon.errorColor Color * @uiDefault OptionPane.icon.errorColor Color optional; defaults to Actions.Red
* @uiDefault Actions.Red Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +33,7 @@ public class FlatOptionPaneErrorIcon
extends FlatOptionPaneAbstractIcon extends FlatOptionPaneAbstractIcon
{ {
public FlatOptionPaneErrorIcon() { public FlatOptionPaneErrorIcon() {
super( "OptionPane.icon.errorColor" ); super( "OptionPane.icon.errorColor", "Actions.Red" );
} }
/* /*

View File

@@ -24,7 +24,8 @@ import java.awt.geom.Rectangle2D;
/** /**
* "Information" icon for {@link javax.swing.JOptionPane}. * "Information" icon for {@link javax.swing.JOptionPane}.
* *
* @uiDefault OptionPane.icon.informationColor Color * @uiDefault OptionPane.icon.informationColor Color optional; defaults to Actions.Blue
* @uiDefault Actions.Blue Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +33,7 @@ public class FlatOptionPaneInformationIcon
extends FlatOptionPaneAbstractIcon extends FlatOptionPaneAbstractIcon
{ {
public FlatOptionPaneInformationIcon() { public FlatOptionPaneInformationIcon() {
super( "OptionPane.icon.informationColor" ); super( "OptionPane.icon.informationColor", "Actions.Blue" );
} }
/* /*

View File

@@ -24,7 +24,8 @@ import java.awt.geom.Rectangle2D;
/** /**
* "Question" icon for {@link javax.swing.JOptionPane}. * "Question" icon for {@link javax.swing.JOptionPane}.
* *
* @uiDefault OptionPane.icon.questionColor Color * @uiDefault OptionPane.icon.questionColor Color optional; defaults to Actions.Blue
* @uiDefault Actions.Blue Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +33,7 @@ public class FlatOptionPaneQuestionIcon
extends FlatOptionPaneAbstractIcon extends FlatOptionPaneAbstractIcon
{ {
public FlatOptionPaneQuestionIcon() { public FlatOptionPaneQuestionIcon() {
super( "OptionPane.icon.questionColor" ); super( "OptionPane.icon.questionColor", "Actions.Blue" );
} }
/* /*

View File

@@ -24,7 +24,8 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* "Warning" icon for {@link javax.swing.JOptionPane}. * "Warning" icon for {@link javax.swing.JOptionPane}.
* *
* @uiDefault OptionPane.icon.warningColor Color * @uiDefault OptionPane.icon.warningColor Color optional; defaults to Actions.Yellow
* @uiDefault Actions.Yellow Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -32,7 +33,7 @@ public class FlatOptionPaneWarningIcon
extends FlatOptionPaneAbstractIcon extends FlatOptionPaneAbstractIcon
{ {
public FlatOptionPaneWarningIcon() { public FlatOptionPaneWarningIcon() {
super( "OptionPane.icon.warningColor" ); super( "OptionPane.icon.warningColor", "Actions.Yellow" );
} }
/* /*

View File

@@ -18,7 +18,6 @@ package com.formdev.flatlaf.icons;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/** /**
* Icon for {@link javax.swing.JRadioButton}. * Icon for {@link javax.swing.JRadioButton}.
@@ -34,7 +33,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
public class FlatRadioButtonIcon public class FlatRadioButtonIcon
extends FlatCheckBoxIcon extends FlatCheckBoxIcon
{ {
protected final int centerDiameter = FlatUIUtils.getUIInt( "RadioButton.icon.centerDiameter", 8 ); protected final int centerDiameter = getUIInt( "RadioButton.icon.centerDiameter", 8, style );
@Override @Override
protected void paintFocusBorder( Graphics2D g2 ) { protected void paintFocusBorder( Graphics2D g2 ) {

View File

@@ -0,0 +1,76 @@
/*
* 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.Dimension;
import java.awt.Graphics2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
/**
* Base class for window icons.
*
* @uiDefault TitlePane.buttonSize Dimension
* @uiDefault TitlePane.buttonHoverBackground Color
* @uiDefault TitlePane.buttonPressedBackground Color
*
* @author Karl Tauber
*/
public abstract class FlatWindowAbstractIcon
extends FlatAbstractIcon
{
private final Color hoverBackground;
private final Color pressedBackground;
public FlatWindowAbstractIcon() {
this( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.buttonHoverBackground" ),
UIManager.getColor( "TitlePane.buttonPressedBackground" ) );
}
public FlatWindowAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
super( size.width, size.height, null );
this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
paintBackground( c, g );
g.setColor( getForeground( c ) );
HiDPIUtils.paintAtScale1x( g, 0, 0, width, height, this::paintIconAt1x );
}
protected abstract void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) {
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
g.fillRect( 0, 0, width, height );
}
}
protected Color getForeground( Component c ) {
return c.getForeground();
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
/**
* "close" icon for windows (frames and dialogs).
*
* @uiDefault TitlePane.closeHoverBackground Color
* @uiDefault TitlePane.closePressedBackground Color
* @uiDefault TitlePane.closeHoverForeground Color
* @uiDefault TitlePane.closePressedForeground Color
*
* @author Karl Tauber
*/
public class FlatWindowCloseIcon
extends FlatWindowAbstractIcon
{
private final Color hoverForeground = UIManager.getColor( "TitlePane.closeHoverForeground" );
private final Color pressedForeground = UIManager.getColor( "TitlePane.closePressedForeground" );
public FlatWindowCloseIcon() {
super( UIManager.getDimension( "TitlePane.buttonSize" ),
UIManager.getColor( "TitlePane.closeHoverBackground" ),
UIManager.getColor( "TitlePane.closePressedBackground" ) );
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int ix2 = ix + iwh - 1;
int iy2 = iy + iwh - 1;
int thickness = (int) scaleFactor;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Line2D.Float( ix, iy, ix2, iy2 ), false );
path.append( new Line2D.Float( ix, iy2, ix2, iy ), false );
g.setStroke( new BasicStroke( thickness ) );
g.draw( path );
}
@Override
protected Color getForeground( Component c ) {
return FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground );
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.Graphics2D;
/**
* "iconify" icon for windows (frames and dialogs).
*
* @author Karl Tauber
*/
public class FlatWindowIconifyIcon
extends FlatWindowAbstractIcon
{
public FlatWindowIconifyIcon() {
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iw = (int) (10 * scaleFactor);
int ih = (int) scaleFactor;
int ix = x + ((width - iw) / 2);
int iy = y + ((height - ih) / 2);
g.fillRect( ix, iy, iw, ih );
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.Graphics2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "maximize" icon for windows (frames and dialogs).
*
* @author Karl Tauber
*/
public class FlatWindowMaximizeIcon
extends FlatWindowAbstractIcon
{
public FlatWindowMaximizeIcon() {
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int thickness = (int) scaleFactor;
g.fill( FlatUIUtils.createRectangle( ix, iy, iwh, iwh, thickness ) );
}
}

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.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "restore" icon for windows (frames and dialogs).
*
* @author Karl Tauber
*/
public class FlatWindowRestoreIcon
extends FlatWindowAbstractIcon
{
public FlatWindowRestoreIcon() {
}
@Override
protected void paintIconAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
int iwh = (int) (10 * scaleFactor);
int ix = x + ((width - iwh) / 2);
int iy = y + ((height - iwh) / 2);
int thickness = (int) scaleFactor;
int rwh = (int) (8 * scaleFactor);
int ro2 = iwh - rwh;
Path2D r1 = FlatUIUtils.createRectangle( ix + ro2, iy, rwh, rwh, thickness );
Path2D r2 = FlatUIUtils.createRectangle( ix, iy + ro2, rwh, rwh, thickness );
Area area = new Area( r1 );
area.subtract( new Area( new Rectangle2D.Float( ix, iy + ro2, rwh, rwh ) ) );
g.fill( area );
g.fill( r2 );
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.json;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author Karl Tauber
*/
public class Json
{
public static Object parse( Reader reader )
throws IOException, ParseException
{
DefaultHandler handler = new DefaultHandler();
new JsonParser( handler ).parse( reader );
return handler.getValue();
}
//---- class DefaultHandler -----------------------------------------------
static class DefaultHandler
extends JsonHandler<List<Object>, Map<String, Object>>
{
private Object value;
@Override
public List<Object> startArray() {
return new ArrayList<>();
}
@Override
public Map<String, Object> startObject() {
return new LinkedHashMap<>();
}
@Override
public void endNull() {
value = "null";
}
@Override
public void endBoolean( boolean bool ) {
value = bool ? "true" : "false";
}
@Override
public void endString( String string ) {
value = string;
}
@Override
public void endNumber( String string ) {
value = string;
}
@Override
public void endArray( List<Object> array ) {
value = array;
}
@Override
public void endObject( Map<String, Object> object ) {
value = object;
}
@Override
public void endArrayValue( List<Object> array ) {
array.add( value );
}
@Override
public void endObjectValue( Map<String, Object> object, String name ) {
object.put( name, value );
}
Object getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,266 @@
/*******************************************************************************
* Copyright (c) 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
// from https://github.com/ralfstx/minimal-json
package com.formdev.flatlaf.json;
/**
* A handler for parser events. Instances of this class can be given to a {@link JsonParser}. The
* parser will then call the methods of the given handler while reading the input.
* <p>
* The default implementations of these methods do nothing. Subclasses may override only those
* methods they are interested in. They can use <code>getLocation()</code> to access the current
* character position of the parser at any point. The <code>start*</code> methods will be called
* while the location points to the first character of the parsed element. The <code>end*</code>
* methods will be called while the location points to the character position that directly follows
* the last character of the parsed element. Example:
* </p>
*
* <pre>
* ["lorem ipsum"]
* ^ ^
* startString endString
* </pre>
* <p>
* Subclasses that build an object representation of the parsed JSON can return arbitrary handler
* objects for JSON arrays and JSON objects in {@link #startArray()} and {@link #startObject()}.
* These handler objects will then be provided in all subsequent parser events for this particular
* array or object. They can be used to keep track the elements of a JSON array or object.
* </p>
*
* @param <A>
* The type of handlers used for JSON arrays
* @param <O>
* The type of handlers used for JSON objects
* @see JsonParser
*/
abstract class JsonHandler<A, O> {
JsonParser parser;
/**
* Returns the current parser location.
*
* @return the current parser location
*/
protected Location getLocation() {
return parser.getLocation();
}
/**
* Indicates the beginning of a <code>null</code> literal in the JSON input. This method will be
* called when reading the first character of the literal.
*/
public void startNull() {
}
/**
* Indicates the end of a <code>null</code> literal in the JSON input. This method will be called
* after reading the last character of the literal.
*/
public void endNull() {
}
/**
* Indicates the beginning of a boolean literal (<code>true</code> or <code>false</code>) in the
* JSON input. This method will be called when reading the first character of the literal.
*/
public void startBoolean() {
}
/**
* Indicates the end of a boolean literal (<code>true</code> or <code>false</code>) in the JSON
* input. This method will be called after reading the last character of the literal.
*
* @param value
* the parsed boolean value
*/
public void endBoolean(boolean value) {
}
/**
* Indicates the beginning of a string in the JSON input. This method will be called when reading
* the opening double quote character (<code>'&quot;'</code>).
*/
public void startString() {
}
/**
* Indicates the end of a string in the JSON input. This method will be called after reading the
* closing double quote character (<code>'&quot;'</code>).
*
* @param string
* the parsed string
*/
public void endString(String string) {
}
/**
* Indicates the beginning of a number in the JSON input. This method will be called when reading
* the first character of the number.
*/
public void startNumber() {
}
/**
* Indicates the end of a number in the JSON input. This method will be called after reading the
* last character of the number.
*
* @param string
* the parsed number string
*/
public void endNumber(String string) {
}
/**
* Indicates the beginning of an array in the JSON input. This method will be called when reading
* the opening square bracket character (<code>'['</code>).
* <p>
* This method may return an object to handle subsequent parser events for this array. This array
* handler will then be provided in all calls to {@link #startArrayValue(Object)
* startArrayValue()}, {@link #endArrayValue(Object) endArrayValue()}, and
* {@link #endArray(Object) endArray()} for this array.
* </p>
*
* @return a handler for this array, or <code>null</code> if not needed
*/
public A startArray() {
return null;
}
/**
* Indicates the end of an array in the JSON input. This method will be called after reading the
* closing square bracket character (<code>']'</code>).
*
* @param array
* the array handler returned from {@link #startArray()}, or <code>null</code> if not
* provided
*/
public void endArray(A array) {
}
/**
* Indicates the beginning of an array element in the JSON input. This method will be called when
* reading the first character of the element, just before the call to the <code>start</code>
* method for the specific element type ({@link #startString()}, {@link #startNumber()}, etc.).
*
* @param array
* the array handler returned from {@link #startArray()}, or <code>null</code> if not
* provided
*/
public void startArrayValue(A array) {
}
/**
* Indicates the end of an array element in the JSON input. This method will be called after
* reading the last character of the element value, just after the <code>end</code> method for the
* specific element type (like {@link #endString(String) endString()}, {@link #endNumber(String)
* endNumber()}, etc.).
*
* @param array
* the array handler returned from {@link #startArray()}, or <code>null</code> if not
* provided
*/
public void endArrayValue(A array) {
}
/**
* Indicates the beginning of an object in the JSON input. This method will be called when reading
* the opening curly bracket character (<code>'{'</code>).
* <p>
* This method may return an object to handle subsequent parser events for this object. This
* object handler will be provided in all calls to {@link #startObjectName(Object)
* startObjectName()}, {@link #endObjectName(Object, String) endObjectName()},
* {@link #startObjectValue(Object, String) startObjectValue()},
* {@link #endObjectValue(Object, String) endObjectValue()}, and {@link #endObject(Object)
* endObject()} for this object.
* </p>
*
* @return a handler for this object, or <code>null</code> if not needed
*/
public O startObject() {
return null;
}
/**
* Indicates the end of an object in the JSON input. This method will be called after reading the
* closing curly bracket character (<code>'}'</code>).
*
* @param object
* the object handler returned from {@link #startObject()}, or null if not provided
*/
public void endObject(O object) {
}
/**
* Indicates the beginning of the name of an object member in the JSON input. This method will be
* called when reading the opening quote character ('&quot;') of the member name.
*
* @param object
* the object handler returned from {@link #startObject()}, or <code>null</code> if not
* provided
*/
public void startObjectName(O object) {
}
/**
* Indicates the end of an object member name in the JSON input. This method will be called after
* reading the closing quote character (<code>'"'</code>) of the member name.
*
* @param object
* the object handler returned from {@link #startObject()}, or null if not provided
* @param name
* the parsed member name
*/
public void endObjectName(O object, String name) {
}
/**
* Indicates the beginning of the name of an object member in the JSON input. This method will be
* called when reading the opening quote character ('&quot;') of the member name.
*
* @param object
* the object handler returned from {@link #startObject()}, or <code>null</code> if not
* provided
* @param name
* the member name
*/
public void startObjectValue(O object, String name) {
}
/**
* Indicates the end of an object member value in the JSON input. This method will be called after
* reading the last character of the member value, just after the <code>end</code> method for the
* specific member type (like {@link #endString(String) endString()}, {@link #endNumber(String)
* endNumber()}, etc.).
*
* @param object
* the object handler returned from {@link #startObject()}, or null if not provided
* @param name
* the parsed member name
*/
public void endObjectValue(O object, String name) {
}
}

View File

@@ -0,0 +1,514 @@
/*******************************************************************************
* Copyright (c) 2013, 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
// from https://github.com/ralfstx/minimal-json
package com.formdev.flatlaf.json;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
/**
* A streaming parser for JSON text. The parser reports all events to a given handler.
*/
class JsonParser {
private static final int MAX_NESTING_LEVEL = 1000;
private static final int MIN_BUFFER_SIZE = 10;
private static final int DEFAULT_BUFFER_SIZE = 1024;
private final JsonHandler<Object, Object> handler;
private Reader reader;
private char[] buffer;
private int bufferOffset;
private int index;
private int fill;
private int line;
private int lineOffset;
private int current;
private StringBuilder captureBuffer;
private int captureStart;
private int nestingLevel;
/*
* | bufferOffset
* v
* [a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t] < input
* [l|m|n|o|p|q|r|s|t|?|?] < buffer
* ^ ^
* | index fill
*/
/**
* Creates a new JsonParser with the given handler. The parser will report all parser events to
* this handler.
*
* @param handler
* the handler to process parser events
*/
@SuppressWarnings("unchecked")
public JsonParser(JsonHandler<?, ?> handler) {
if (handler == null) {
throw new NullPointerException("handler is null");
}
this.handler = (JsonHandler<Object, Object>)handler;
handler.parser = this;
}
/**
* Parses the given input string. The input must contain a valid JSON value, optionally padded
* with whitespace.
*
* @param string
* the input string, must be valid JSON
* @throws ParseException
* if the input is not valid JSON
*/
public void parse(String string) {
if (string == null) {
throw new NullPointerException("string is null");
}
int bufferSize = Math.max(MIN_BUFFER_SIZE, Math.min(DEFAULT_BUFFER_SIZE, string.length()));
try {
parse(new StringReader(string), bufferSize);
} catch (IOException exception) {
// StringReader does not throw IOException
throw new RuntimeException(exception);
}
}
/**
* Reads the entire input from the given reader and parses it as JSON. The input must contain a
* valid JSON value, optionally padded with whitespace.
* <p>
* Characters are read in chunks into a default-sized input buffer. Hence, wrapping a reader in an
* additional <code>BufferedReader</code> likely won't improve reading performance.
* </p>
*
* @param reader
* the reader to read the input from
* @throws IOException
* if an I/O error occurs in the reader
* @throws ParseException
* if the input is not valid JSON
*/
public void parse(Reader reader) throws IOException {
parse(reader, DEFAULT_BUFFER_SIZE);
}
/**
* Reads the entire input from the given reader and parses it as JSON. The input must contain a
* valid JSON value, optionally padded with whitespace.
* <p>
* Characters are read in chunks into an input buffer of the given size. Hence, wrapping a reader
* in an additional <code>BufferedReader</code> likely won't improve reading performance.
* </p>
*
* @param reader
* the reader to read the input from
* @param buffersize
* the size of the input buffer in chars
* @throws IOException
* if an I/O error occurs in the reader
* @throws ParseException
* if the input is not valid JSON
*/
public void parse(Reader reader, int buffersize) throws IOException {
if (reader == null) {
throw new NullPointerException("reader is null");
}
if (buffersize <= 0) {
throw new IllegalArgumentException("buffersize is zero or negative");
}
this.reader = reader;
buffer = new char[buffersize];
bufferOffset = 0;
index = 0;
fill = 0;
line = 1;
lineOffset = 0;
current = 0;
captureStart = -1;
read();
skipWhiteSpace();
readValue();
skipWhiteSpace();
if (!isEndOfText()) {
throw error("Unexpected character");
}
}
private void readValue() throws IOException {
switch (current) {
case 'n':
readNull();
break;
case 't':
readTrue();
break;
case 'f':
readFalse();
break;
case '"':
readString();
break;
case '[':
readArray();
break;
case '{':
readObject();
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
readNumber();
break;
default:
throw expected("value");
}
}
private void readArray() throws IOException {
Object array = handler.startArray();
read();
if (++nestingLevel > MAX_NESTING_LEVEL) {
throw error("Nesting too deep");
}
skipWhiteSpace();
if (readChar(']')) {
nestingLevel--;
handler.endArray(array);
return;
}
do {
skipWhiteSpace();
handler.startArrayValue(array);
readValue();
handler.endArrayValue(array);
skipWhiteSpace();
} while (readChar(','));
if (!readChar(']')) {
throw expected("',' or ']'");
}
nestingLevel--;
handler.endArray(array);
}
private void readObject() throws IOException {
Object object = handler.startObject();
read();
if (++nestingLevel > MAX_NESTING_LEVEL) {
throw error("Nesting too deep");
}
skipWhiteSpace();
if (readChar('}')) {
nestingLevel--;
handler.endObject(object);
return;
}
do {
skipWhiteSpace();
handler.startObjectName(object);
String name = readName();
handler.endObjectName(object, name);
skipWhiteSpace();
if (!readChar(':')) {
throw expected("':'");
}
skipWhiteSpace();
handler.startObjectValue(object, name);
readValue();
handler.endObjectValue(object, name);
skipWhiteSpace();
} while (readChar(','));
if (!readChar('}')) {
throw expected("',' or '}'");
}
nestingLevel--;
handler.endObject(object);
}
private String readName() throws IOException {
if (current != '"') {
throw expected("name");
}
return readStringInternal();
}
private void readNull() throws IOException {
handler.startNull();
read();
readRequiredChar('u');
readRequiredChar('l');
readRequiredChar('l');
handler.endNull();
}
private void readTrue() throws IOException {
handler.startBoolean();
read();
readRequiredChar('r');
readRequiredChar('u');
readRequiredChar('e');
handler.endBoolean(true);
}
private void readFalse() throws IOException {
handler.startBoolean();
read();
readRequiredChar('a');
readRequiredChar('l');
readRequiredChar('s');
readRequiredChar('e');
handler.endBoolean(false);
}
private void readRequiredChar(char ch) throws IOException {
if (!readChar(ch)) {
throw expected("'" + ch + "'");
}
}
private void readString() throws IOException {
handler.startString();
handler.endString(readStringInternal());
}
private String readStringInternal() throws IOException {
read();
startCapture();
while (current != '"') {
if (current == '\\') {
pauseCapture();
readEscape();
startCapture();
} else if (current < 0x20) {
throw expected("valid string character");
} else {
read();
}
}
String string = endCapture();
read();
return string;
}
private void readEscape() throws IOException {
read();
switch (current) {
case '"':
case '/':
case '\\':
captureBuffer.append((char)current);
break;
case 'b':
captureBuffer.append('\b');
break;
case 'f':
captureBuffer.append('\f');
break;
case 'n':
captureBuffer.append('\n');
break;
case 'r':
captureBuffer.append('\r');
break;
case 't':
captureBuffer.append('\t');
break;
case 'u':
char[] hexChars = new char[4];
for (int i = 0; i < 4; i++) {
read();
if (!isHexDigit()) {
throw expected("hexadecimal digit");
}
hexChars[i] = (char)current;
}
captureBuffer.append((char)Integer.parseInt(new String(hexChars), 16));
break;
default:
throw expected("valid escape sequence");
}
read();
}
private void readNumber() throws IOException {
handler.startNumber();
startCapture();
readChar('-');
int firstDigit = current;
if (!readDigit()) {
throw expected("digit");
}
if (firstDigit != '0') {
while (readDigit()) {
}
}
readFraction();
readExponent();
handler.endNumber(endCapture());
}
private boolean readFraction() throws IOException {
if (!readChar('.')) {
return false;
}
if (!readDigit()) {
throw expected("digit");
}
while (readDigit()) {
}
return true;
}
private boolean readExponent() throws IOException {
if (!readChar('e') && !readChar('E')) {
return false;
}
if (!readChar('+')) {
readChar('-');
}
if (!readDigit()) {
throw expected("digit");
}
while (readDigit()) {
}
return true;
}
private boolean readChar(char ch) throws IOException {
if (current != ch) {
return false;
}
read();
return true;
}
private boolean readDigit() throws IOException {
if (!isDigit()) {
return false;
}
read();
return true;
}
private void skipWhiteSpace() throws IOException {
while (isWhiteSpace()) {
read();
}
}
private void read() throws IOException {
if (index == fill) {
if (captureStart != -1) {
captureBuffer.append(buffer, captureStart, fill - captureStart);
captureStart = 0;
}
bufferOffset += fill;
fill = reader.read(buffer, 0, buffer.length);
index = 0;
if (fill == -1) {
current = -1;
index++;
return;
}
}
if (current == '\n') {
line++;
lineOffset = bufferOffset + index;
}
current = buffer[index++];
}
private void startCapture() {
if (captureBuffer == null) {
captureBuffer = new StringBuilder();
}
captureStart = index - 1;
}
private void pauseCapture() {
int end = current == -1 ? index : index - 1;
captureBuffer.append(buffer, captureStart, end - captureStart);
captureStart = -1;
}
private String endCapture() {
int start = captureStart;
int end = index - 1;
captureStart = -1;
if (captureBuffer.length() > 0) {
captureBuffer.append(buffer, start, end - start);
String captured = captureBuffer.toString();
captureBuffer.setLength(0);
return captured;
}
return new String(buffer, start, end - start);
}
Location getLocation() {
int offset = bufferOffset + index - 1;
int column = offset - lineOffset + 1;
return new Location(offset, line, column);
}
private ParseException expected(String expected) {
if (isEndOfText()) {
return error("Unexpected end of input");
}
return error("Expected " + expected);
}
private ParseException error(String message) {
return new ParseException(message, getLocation());
}
private boolean isWhiteSpace() {
return current == ' ' || current == '\t' || current == '\n' || current == '\r';
}
private boolean isDigit() {
return current >= '0' && current <= '9';
}
private boolean isHexDigit() {
return current >= '0' && current <= '9'
|| current >= 'a' && current <= 'f'
|| current >= 'A' && current <= 'F';
}
private boolean isEndOfText() {
return current == -1;
}
}

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
// from https://github.com/ralfstx/minimal-json
package com.formdev.flatlaf.json;
/**
* An immutable object that represents a location in the parsed text.
*/
public class Location {
/**
* The absolute character index, starting at 0.
*/
public final int offset;
/**
* The line number, starting at 1.
*/
public final int line;
/**
* The column number, starting at 1.
*/
public final int column;
Location(int offset, int line, int column) {
this.offset = offset;
this.column = column;
this.line = line;
}
@Override
public String toString() {
return line + ":" + column;
}
@Override
public int hashCode() {
return offset;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Location other = (Location)obj;
return offset == other.offset && column == other.column && line == other.line;
}
}

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (c) 2013, 2016 EclipseSource.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
// from https://github.com/ralfstx/minimal-json
package com.formdev.flatlaf.json;
/**
* An unchecked exception to indicate that an input does not qualify as valid JSON.
*/
public class ParseException extends RuntimeException {
private final Location location;
ParseException(String message, Location location) {
super(message + " at " + location);
this.location = location;
}
/**
* Returns the location at which the error occurred.
*
* @return the error location
*/
public Location getLocation() {
return location;
}
/**
* Returns the absolute character index at which the error occurred. The offset of the first
* character of a document is 0.
*
* @return the character offset at which the error occurred, will be &gt;= 0
* @deprecated Use {@link #getLocation()} instead
*/
@Deprecated
public int getOffset() {
return location.offset;
}
/**
* Returns the line number in which the error occurred. The number of the first line is 1.
*
* @return the line in which the error occurred, will be &gt;= 1
* @deprecated Use {@link #getLocation()} instead
*/
@Deprecated
public int getLine() {
return location.line;
}
/**
* Returns the column number at which the error occurred, i.e. the number of the character in its
* line. The number of the first character of a line is 1.
*
* @return the column in which the error occurred, will be &gt;= 1
* @deprecated Use {@link #getLocation()} instead
*/
@Deprecated
public int getColumn() {
return location.column;
}
}

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.BasicStroke; import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
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;
@@ -26,6 +27,7 @@ import java.awt.Shape;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import javax.swing.JComponent;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicArrowButton;
@@ -38,19 +40,30 @@ public class FlatArrowButton
extends BasicArrowButton extends BasicArrowButton
implements UIResource implements UIResource
{ {
public static final int DEFAULT_ARROW_WIDTH = 8;
private final boolean chevron; private final boolean chevron;
private final Color foreground; private final Color foreground;
private final Color disabledForeground; private final Color disabledForeground;
private final Color hoverForeground; private final Color hoverForeground;
private final Color hoverBackground; private final Color hoverBackground;
private final Color pressedBackground;
private int arrowWidth = DEFAULT_ARROW_WIDTH;
private int xOffset = 0; private int xOffset = 0;
private int yOffset = 0; private int yOffset = 0;
private boolean hover; private boolean hover;
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 )
{
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 );
@@ -59,11 +72,12 @@ public class FlatArrowButton
this.disabledForeground = disabledForeground; this.disabledForeground = disabledForeground;
this.hoverForeground = hoverForeground; this.hoverForeground = hoverForeground;
this.hoverBackground = hoverBackground; this.hoverBackground = hoverBackground;
this.pressedBackground = pressedBackground;
setOpaque( false ); setOpaque( false );
setBorder( null ); setBorder( null );
if( hoverForeground != null || hoverBackground != null ) { if( hoverForeground != null || hoverBackground != null || pressedBackground != null ) {
addMouseListener( new MouseAdapter() { addMouseListener( new MouseAdapter() {
@Override @Override
public void mouseEntered( MouseEvent e ) { public void mouseEntered( MouseEvent e ) {
@@ -76,14 +90,38 @@ public class FlatArrowButton
hover = false; hover = false;
repaint(); repaint();
} }
@Override
public void mousePressed( MouseEvent e ) {
pressed = true;
repaint();
}
@Override
public void mouseReleased( MouseEvent e ) {
pressed = false;
repaint();
}
} ); } );
} }
} }
public int getArrowWidth() {
return arrowWidth;
}
public void setArrowWidth( int arrowWidth ) {
this.arrowWidth = arrowWidth;
}
protected boolean isHover() { protected boolean isHover() {
return hover; return hover;
} }
protected boolean isPressed() {
return pressed;
}
public int getXOffset() { public int getXOffset() {
return xOffset; return xOffset;
} }
@@ -100,6 +138,10 @@ public class FlatArrowButton
this.yOffset = yOffset; this.yOffset = yOffset;
} }
protected Color deriveBackground( Color background ) {
return background;
}
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
return scale( super.getPreferredSize() ); return scale( super.getPreferredSize() );
@@ -119,33 +161,54 @@ public class FlatArrowButton
int height = getHeight(); int height = getHeight();
boolean enabled = isEnabled(); boolean enabled = isEnabled();
// paint hover background // paint hover or pressed background
if( enabled && isHover() && hoverBackground != null ) { if( enabled ) {
g.setColor( hoverBackground ); Color background = (pressedBackground != null && isPressed())
g.fillRect( 0, 0, width, height ); ? deriveBackground( pressedBackground )
: ((hoverBackground != null && isHover())
? deriveBackground( hoverBackground )
: null);
if( background != null ) {
g.setColor( background );
g.fillRect( 0, 0, width, height );
}
} }
int direction = getDirection(); int direction = getDirection();
boolean vert = (direction == NORTH || direction == SOUTH); boolean vert = (direction == NORTH || direction == SOUTH);
int w = scale( chevron ? 8 : 9 ); // compute width/height
int h = scale( chevron ? 4 : 5 ); int w = scale( arrowWidth + (chevron ? 0 : 1) );
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
// rotate width/height
int rw = vert ? w : h; int rw = vert ? w : h;
int rh = vert ? h : w; int rh = vert ? h : w;
// chevron lines end 1px outside of width/height
if( chevron ) {
// add 1px to width/height for position calculation only
rw++;
rh++;
}
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) ); int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) ); int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
// optimization for small chevron arrows (e.g. OneTouchButtons in SplitPane) // move arrow for round borders
if( x + rw >= width && x > 0 ) Container parent = getParent();
x--; if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
if( y + rh >= height && y > 0 ) x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
y--;
// paint arrow // paint arrow
g.setColor( enabled g.setColor( enabled
? (isHover() && hoverForeground != null ? hoverForeground : foreground) ? (isHover() && hoverForeground != null ? hoverForeground : foreground)
: disabledForeground ); : disabledForeground );
g.translate( x, y ); g.translate( x, y );
/*debug
debugPaint( g2, vert, rw, rh );
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 ) ) ); g2.setStroke( new BasicStroke( scale( 1f ) ) );
@@ -157,7 +220,7 @@ public class FlatArrowButton
g.translate( -x, -y ); g.translate( -x, -y );
} }
public static Shape createArrowShape( int direction, boolean chevron, int w, int h ) { public static Shape createArrowShape( int direction, boolean chevron, float w, float h ) {
switch( direction ) { switch( direction ) {
case NORTH: return FlatUIUtils.createPath( !chevron, 0,h, (w / 2f),0, w,h ); case NORTH: return FlatUIUtils.createPath( !chevron, 0,h, (w / 2f),0, w,h );
case SOUTH: return FlatUIUtils.createPath( !chevron, 0,0, (w / 2f),h, w,0 ); case SOUTH: return FlatUIUtils.createPath( !chevron, 0,0, (w / 2f),h, w,0 );
@@ -166,4 +229,22 @@ public class FlatArrowButton
default: return new Path2D.Float(); default: return new Path2D.Float();
} }
} }
/*debug
private void debugPaint( Graphics g, boolean vert, int w, int h ) {
Color oldColor = g.getColor();
g.setColor( Color.red );
g.drawRect( 0, 0, w - 1, h - 1 );
int xy1 = -2;
int xy2 = h + 1;
for( int i = 0; i < 20; i++ ) {
g.drawRect( vert ? 0 : xy1, vert ? xy1 : 0, 0, 0 );
g.drawRect( vert ? 0 : xy2, vert ? xy2 : 0, 0, 0 );
xy1 -= 2;
xy2 += 2;
}
g.setColor( oldColor );
}
debug*/
} }

View File

@@ -36,23 +36,31 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders; import javax.swing.plaf.basic.BasicBorders;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
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 * @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.focusedBorderColor Color
* @uiDefault Component.warning.borderColor Color
* @uiDefault Component.warning.focusedBorderColor Color
* @uiDefault Component.custom.borderColor Color
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -60,52 +68,106 @@ public class FlatBorder
extends BasicBorders.MarginBorder extends BasicBorders.MarginBorder
{ {
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" ); protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
protected final int innerFocusWidth = UIManager.getInt( "Component.innerFocusWidth" ); protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
protected final Color focusColor = UIManager.getColor( "Component.focusColor" ); protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
protected final Color borderColor = UIManager.getColor( "Component.borderColor" ); protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" ); protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" ); protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
@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(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
float focusWidth = getFocusWidth(); float focusWidth = scale( (float) getFocusWidth( c ) );
float borderWidth = getBorderWidth( c ); float borderWidth = scale( (float) getBorderWidth( c ) );
float arc = getArc(); float arc = scale( (float) getArc( c ) );
Color outlineColor = getOutlineColor( c );
if( isFocused( c ) ) { // paint outer border
g2.setColor( getFocusColor( c ) ); if( outlineColor != null || isFocused( c ) ) {
FlatUIUtils.paintOutlineBorder( g2, x, y, width, height, focusWidth, float innerWidth = !isCellEditor( c ) && !(c instanceof JScrollPane)
getLineWidth() + scale( (float) innerFocusWidth ), arc ); ? (outlineColor != null ? innerOutlineWidth : innerFocusWidth)
: 0;
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height,
focusWidth, borderWidth + scale( innerWidth ), arc );
} }
g2.setPaint( getBorderColor( c ) ); // paint border
FlatUIUtils.drawRoundRectangle( g2, x, y, width, height, focusWidth, borderWidth, arc ); g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
} finally { } finally {
g2.dispose(); g2.dispose();
} }
} }
/**
* Returns the outline color of the component border specified in client property
* {@link FlatClientProperties#OUTLINE}.
*/
protected Color getOutlineColor( Component c ) {
if( !(c instanceof JComponent) )
return null;
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
if( outline instanceof String ) {
switch( (String) outline ) {
case FlatClientProperties.OUTLINE_ERROR:
return isFocused( c ) ? errorFocusedBorderColor : errorBorderColor;
case FlatClientProperties.OUTLINE_WARNING:
return isFocused( c ) ? warningFocusedBorderColor : warningBorderColor;
}
} else if( outline instanceof Color ) {
Color color = (Color) outline;
// use color functions to compute color for unfocused state
if( !isFocused( c ) && customBorderColor instanceof DerivedColor )
color = ((DerivedColor)customBorderColor).derive( color );
return color;
} else if( outline instanceof Color[] && ((Color[])outline).length >= 2 )
return ((Color[])outline)[isFocused( c ) ? 0 : 1];
return null;
}
protected Color getFocusColor( Component c ) { protected Color getFocusColor( Component c ) {
return focusColor; return focusColor;
} }
protected Paint getBorderColor( Component c ) { protected Paint getBorderColor( Component c ) {
boolean enabled = c.isEnabled() && (!(c instanceof JTextComponent) || ((JTextComponent)c).isEditable()); return isEnabled( c )
return enabled
? (isFocused( c ) ? focusedBorderColor : borderColor) ? (isFocused( c ) ? focusedBorderColor : borderColor)
: disabledBorderColor; : disabledBorderColor;
} }
protected boolean isEnabled( Component c ) {
if( c instanceof JScrollPane ) {
// check whether view component is disabled
JViewport viewport = ((JScrollPane)c).getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
if( view != null && !isEnabled( view ) )
return false;
}
return c.isEnabled() && (!(c instanceof JTextComponent) || ((JTextComponent)c).isEditable());
}
protected boolean isFocused( Component c ) { protected boolean isFocused( Component c ) {
if( c instanceof JScrollPane ) { if( c instanceof JScrollPane ) {
JViewport viewport = ((JScrollPane)c).getViewport(); JViewport viewport = ((JScrollPane)c).getViewport();
Component view = (viewport != null) ? viewport.getView() : null; Component view = (viewport != null) ? viewport.getView() : null;
if( view != null ) { if( view != null ) {
if( view.hasFocus() ) if( FlatUIUtils.isPermanentFocusOwner( view ) )
return true; return true;
if( (view instanceof JTable && ((JTable)view).isEditing()) || if( (view instanceof JTable && ((JTable)view).isEditing()) ||
@@ -119,44 +181,81 @@ public class FlatBorder
return false; return false;
} else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) { } else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) {
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent(); Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
return (editorComponent != null) ? editorComponent.hasFocus() : false; return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
} else if( c instanceof JSpinner ) { } else if( c instanceof JSpinner ) {
if( FlatUIUtils.isPermanentFocusOwner( c ) )
return true;
JComponent editor = ((JSpinner)c).getEditor(); JComponent editor = ((JSpinner)c).getEditor();
if( editor instanceof JSpinner.DefaultEditor ) { if( editor instanceof JSpinner.DefaultEditor ) {
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField(); JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
if( textField != null ) if( textField != null )
return textField.hasFocus(); return FlatUIUtils.isPermanentFocusOwner( textField );
} }
return false; return false;
} else } else
return c.hasFocus(); return FlatUIUtils.isPermanentFocusOwner( c );
}
protected boolean isCellEditor( Component c ) {
return FlatUIUtils.isCellEditor( c );
} }
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
float ow = getFocusWidth() + getLineWidth(); float focusWidth = scale( (float) getFocusWidth( c ) );
float ow = focusWidth + scale( (float) getLineWidth( c ) );
insets = super.getBorderInsets( c, insets ); insets = super.getBorderInsets( c, insets );
insets.top = Math.round( scale( (float) insets.top ) + ow ); insets.top = Math.round( scale( (float) insets.top ) + ow );
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;
} }
protected float getFocusWidth() { /**
return scale( (float) focusWidth ); * Returns the (unscaled) thickness of the outer focus border.
*/
protected int getFocusWidth( Component c ) {
if( isCellEditor( c ) )
return 0;
return focusWidth;
} }
protected float getLineWidth() { /**
return scale( 1f ); * Returns the (unscaled) line thickness used to compute the border insets.
* This may be different to {@link #getBorderWidth}.
*/
protected int getLineWidth( Component c ) {
return 1;
} }
protected float getBorderWidth( Component c ) { /**
return getLineWidth(); * Returns the (unscaled) line thickness used to paint the border.
* This may be different to {@link #getLineWidth}.
*/
protected int getBorderWidth( Component c ) {
return getLineWidth( c );
} }
protected float getArc() { /**
* Returns the (unscaled) arc diameter of the border.
*/
protected int getArc( Component c ) {
return 0; return 0;
} }
} }

View File

@@ -16,28 +16,36 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
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.GradientPaint;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Paint; import java.awt.Paint;
import javax.swing.JButton; import javax.swing.AbstractButton;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Border for {@link javax.swing.JButton}. * Border for {@link javax.swing.JButton}.
* *
* @uiDefault Button.borderColor Color * @uiDefault Button.borderColor Color
* @uiDefault Button.startBorderColor Color optional; if set, a gradient paint is used and Button.borderColor is ignored
* @uiDefault Button.endBorderColor Color optional; if set, a gradient paint is used
* @uiDefault Button.disabledBorderColor Color * @uiDefault Button.disabledBorderColor Color
* @uiDefault Button.focusedBorderColor Color * @uiDefault Button.focusedBorderColor Color
* @uiDefault Button.hoverBorderColor Color optional * @uiDefault Button.hoverBorderColor Color optional
* @uiDefault Button.default.borderColor Color * @uiDefault Button.default.borderColor Color
* @uiDefault Button.default.startBorderColor Color optional; if set, a gradient paint is used and Button.default.borderColor is ignored
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
* @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.spacingInsets Insets
* @uiDefault Button.arc int * @uiDefault Button.arc int
* *
* @author Karl Tauber * @author Karl Tauber
@@ -45,21 +53,29 @@ import javax.swing.plaf.UIResource;
public class FlatButtonBorder public class FlatButtonBorder
extends FlatBorder extends FlatBorder
{ {
protected final Color borderColor = UIManager.getColor( "Button.borderColor" ); protected final Color borderColor = FlatUIUtils.getUIColor( "Button.startBorderColor", "Button.borderColor" );
protected final Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" ); protected final Color disabledBorderColor = UIManager.getColor( "Button.disabledBorderColor" );
protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" ); protected final Color focusedBorderColor = UIManager.getColor( "Button.focusedBorderColor" );
protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" ); protected final Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
protected final Color defaultBorderColor = UIManager.getColor( "Button.default.borderColor" ); protected final Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
protected final Color defaultEndBorderColor = UIManager.getColor( "Button.default.endBorderColor" );
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 toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
protected final int arc = UIManager.getInt( "Button.arc" ); protected final int arc = UIManager.getInt( "Button.arc" );
@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( FlatButtonUI.isContentAreaFilled( c ) && !FlatButtonUI.isHelpButton( c ) ) if( FlatButtonUI.isContentAreaFilled( c ) &&
super.paintBorder( c, g, x, y, width, height ); !FlatButtonUI.isToolBarButton( c ) &&
!FlatButtonUI.isHelpButton( c ) &&
!FlatToggleButtonUI.isTabButton( c ) )
super.paintBorder( c, g, x, y, width, height );
} }
@Override @Override
@@ -67,35 +83,71 @@ public class FlatButtonBorder
return FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c ); return FlatButtonUI.isDefaultButton( c ) ? defaultFocusColor : super.getFocusColor( c );
} }
@Override
protected boolean isFocused( Component c ) {
return FlatButtonUI.isFocusPainted( c ) && super.isFocused( c );
}
@Override @Override
protected Paint getBorderColor( Component c ) { protected Paint getBorderColor( Component c ) {
boolean def = FlatButtonUI.isDefaultButton( c ); boolean def = FlatButtonUI.isDefaultButton( c );
return FlatButtonUI.buttonStateColor( c, Paint color = FlatButtonUI.buttonStateColor( c,
def ? defaultBorderColor : borderColor, def ? defaultBorderColor : borderColor,
disabledBorderColor, disabledBorderColor,
def ? defaultFocusedBorderColor : focusedBorderColor, def ? defaultFocusedBorderColor : focusedBorderColor,
def ? defaultHoverBorderColor : hoverBorderColor, def ? defaultHoverBorderColor : hoverBorderColor,
null ); null );
// change to gradient paint if start/end colors are specified
Color startBg = def ? defaultBorderColor : borderColor;
Color endBg = def ? defaultEndBorderColor : endBorderColor;
if( color == startBg && endBg != null && !startBg.equals( endBg ) )
color = new GradientPaint( 0, 0, startBg, 0, c.getHeight(), endBg );
return color;
} }
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
insets = super.getBorderInsets( c, insets ); if( FlatButtonUI.isToolBarButton( c ) ) {
// In toolbars, use button margin only if explicitly set.
// Otherwise use toolbar margin specified in UI defaults.
Insets margin = (c instanceof AbstractButton)
? ((AbstractButton)c).getMargin()
: null;
// use smaller left and right insets for icon-only buttons (so that they are square) FlatUIUtils.setInsets( insets, UIScale.scale( FlatUIUtils.addInsets( toolbarSpacingInsets,
if( FlatButtonUI.isIconOnlyButton( c ) && ((JButton)c).getMargin() instanceof UIResource ) (margin != null && !(margin instanceof UIResource)) ? margin : toolbarMargin ) ) );
insets.left = insets.right = Math.min( insets.top, insets.bottom ); } else {
insets = super.getBorderInsets( c, insets );
// use smaller left and right insets for icon-only or single-character buttons (so that they are square)
if( FlatButtonUI.isIconOnlyOrSingleCharacterButton( c ) && ((AbstractButton)c).getMargin() instanceof UIResource )
insets.left = insets.right = Math.min( insets.top, insets.bottom );
}
return insets; return insets;
} }
@Override @Override
protected float getBorderWidth( Component c ) { protected int getFocusWidth( Component c ) {
return FlatButtonUI.isDefaultButton( c ) ? scale( (float) defaultBorderWidth ) : super.getBorderWidth( c ); return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
} }
@Override @Override
protected float getArc() { protected int getBorderWidth( Component c ) {
return scale( (float) arc ); return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : borderWidth;
}
@Override
protected int getArc( Component c ) {
if( isCellEditor( c ) )
return 0;
switch( FlatButtonUI.getButtonType( c ) ) {
case FlatButtonUI.TYPE_SQUARE: return 0;
case FlatButtonUI.TYPE_ROUND_RECT: return Short.MAX_VALUE;
default: return arc;
}
} }
} }

View File

@@ -23,69 +23,110 @@ import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.ButtonModel; import javax.swing.ButtonModel;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.JToolBar; import javax.swing.JToolBar;
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.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicButtonUI;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JButton}. * Provides the Flat LaF UI delegate for {@link javax.swing.JButton}.
* *
* TODO document used UI defaults of superclass * <!-- BasicButtonUI -->
*
* @uiDefault Button.font Font
* @uiDefault Button.background Color
* @uiDefault Button.foreground Color
* @uiDefault Button.border Border
* @uiDefault Button.margin Insets
* @uiDefault Button.rollover boolean
*
* <!-- FlatButtonUI -->
* *
* @uiDefault Component.focusWidth int
* @uiDefault Button.arc int
* @uiDefault Button.minimumWidth int * @uiDefault Button.minimumWidth int
* @uiDefault Button.iconTextGap int * @uiDefault Button.iconTextGap int
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.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.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.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.default.foreground Color * @uiDefault Button.default.foreground Color
* @uiDefault Button.default.focusedBackground Color optional * @uiDefault Button.default.focusedBackground Color optional
* @uiDefault Button.default.hoverBackground Color optional * @uiDefault Button.default.hoverBackground Color optional
* @uiDefault Button.default.pressedBackground Color optional * @uiDefault Button.default.pressedBackground Color optional
* @uiDefault Button.default.boldText boolean * @uiDefault Button.default.boldText boolean
* @uiDefault Button.paintShadow boolean default is false
* @uiDefault Button.shadowWidth int default is 2
* @uiDefault Button.shadowColor Color optional
* @uiDefault Button.default.shadowColor Color optional
* @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
*/ */
public class FlatButtonUI public class FlatButtonUI
extends BasicButtonUI extends BasicButtonUI
{ {
protected int focusWidth;
protected int arc;
protected int minimumWidth; protected int minimumWidth;
protected int iconTextGap; protected int iconTextGap;
protected Color background;
protected Color foreground;
protected Color startBackground;
protected Color endBackground;
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 disabledText; protected Color disabledText;
protected Color disabledSelectedBackground;
protected Color defaultBackground; protected Color defaultBackground;
protected Color defaultEndBackground;
protected Color defaultForeground; protected Color defaultForeground;
protected Color defaultFocusedBackground; protected Color defaultFocusedBackground;
protected Color defaultHoverBackground; protected Color defaultHoverBackground;
protected Color defaultPressedBackground; protected Color defaultPressedBackground;
protected boolean defaultBoldText; protected boolean defaultBoldText;
protected int shadowWidth;
protected Color shadowColor;
protected Color defaultShadowColor;
protected Insets toolbarSpacingInsets;
protected Color toolbarHoverBackground; protected Color toolbarHoverBackground;
protected Color toolbarPressedBackground; protected Color toolbarPressedBackground;
protected Color toolbarSelectedBackground;
private Icon helpButtonIcon; private Icon helpButtonIcon;
@@ -106,34 +147,61 @@ public class FlatButtonUI
if( !defaults_initialized ) { if( !defaults_initialized ) {
String prefix = getPropertyPrefix(); String prefix = getPropertyPrefix();
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( prefix + "arc" );
minimumWidth = UIManager.getInt( prefix + "minimumWidth" ); minimumWidth = UIManager.getInt( prefix + "minimumWidth" );
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 ); iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
background = UIManager.getColor( prefix + "background" );
foreground = UIManager.getColor( prefix + "foreground" );
startBackground = UIManager.getColor( prefix + "startBackground" );
endBackground = UIManager.getColor( prefix + "endBackground" );
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" );
disabledText = UIManager.getColor( prefix + "disabledText" ); disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
defaultBackground = UIManager.getColor( "Button.default.background" ); if( UIManager.getBoolean( "Button.paintShadow" ) ) {
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
shadowColor = UIManager.getColor( "Button.shadowColor" );
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
} else {
shadowWidth = 0;
shadowColor = null;
defaultShadowColor = null;
}
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" ); defaultForeground = UIManager.getColor( "Button.default.foreground" );
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" ); defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" ); defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" ); defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" ); defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
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" );
defaults_initialized = true; defaults_initialized = true;
} }
if( startBackground != null ) {
Color bg = b.getBackground();
if( bg == null || bg instanceof UIResource )
b.setBackground( startBackground );
}
LookAndFeel.installProperty( b, "opaque", false );
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) ); LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
MigLayoutVisualPadding.install( b, focusWidth ); MigLayoutVisualPadding.install( b );
} }
@Override @Override
@@ -144,61 +212,146 @@ public class FlatButtonUI
defaults_initialized = false; defaults_initialized = false;
} }
@Override
protected BasicButtonListener createButtonListener( AbstractButton b ) {
return new FlatButtonListener( b );
}
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case SQUARE_SIZE:
case MINIMUM_WIDTH:
case MINIMUM_HEIGHT:
b.revalidate();
break;
case BUTTON_TYPE:
b.revalidate();
b.repaint();
break;
}
}
static boolean isContentAreaFilled( Component c ) { static boolean isContentAreaFilled( Component c ) {
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled(); return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
} }
public static boolean isFocusPainted( Component c ) {
return !(c instanceof AbstractButton) || ((AbstractButton)c).isFocusPainted();
}
static boolean isDefaultButton( Component c ) { static boolean isDefaultButton( Component c ) {
return c instanceof JButton && ((JButton)c).isDefaultButton(); return c instanceof JButton && ((JButton)c).isDefaultButton();
} }
static boolean isIconOnlyButton( Component c ) { /**
String text; * Returns true if the button has an icon but no text,
return c instanceof JButton && * or it it does not have an icon and the text is either "..." or one character.
((JButton)c).getIcon() != null && */
((text = ((JButton)c).getText()) == null || text.isEmpty()); static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
return false;
Icon icon = ((AbstractButton)c).getIcon();
String text = ((AbstractButton)c).getText();
return (icon != null && (text == null || text.isEmpty())) ||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
}
// same indices as in parameters to clientPropertyChoice()
static final int TYPE_OTHER = -1;
static final int TYPE_SQUARE = 0;
static final int TYPE_ROUND_RECT = 1;
static int getButtonType( Component c ) {
return (c instanceof AbstractButton)
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT )
: TYPE_OTHER;
} }
static boolean isHelpButton( Component c ) { static boolean isHelpButton( Component c ) {
return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP ); return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP );
} }
static boolean isToolBarButton( JComponent c ) { static boolean isToolBarButton( Component c ) {
return c.getParent() instanceof JToolBar; return c.getParent() instanceof JToolBar ||
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
} }
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
if( isHelpButton( c ) ) { // fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() )
FlatUIUtils.paintParentBackground( g, c ); FlatUIUtils.paintParentBackground( g, c );
if( isHelpButton( c ) ) {
helpButtonIcon.paintIcon( c, g, 0, 0 ); helpButtonIcon.paintIcon( c, g, 0, 0 );
return; return;
} }
if( c.isOpaque() && isContentAreaFilled( c ) ) { if( isContentAreaFilled( c ) )
FlatUIUtils.paintParentBackground( g, c ); paintBackground( g, c );
Color background = getBackground( c );
if( background != null ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
Border border = c.getBorder();
float focusWidth = (border instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (border instanceof FlatButtonBorder || isToolBarButton( c )) ? scale( (float) this.arc ) : 0;
g2.setColor( background );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
} finally {
g2.dispose();
}
}
}
paint( g, c ); paint( g, c );
} }
protected void paintBackground( Graphics g, JComponent c ) {
Color background = getBackground( c );
if( background == null )
return;
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
boolean isToolBarButton = isToolBarButton( c );
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
float arc = FlatUIUtils.getBorderArc( c );
boolean def = isDefaultButton( c );
int x = 0;
int y = 0;
int width = c.getWidth();
int height = c.getHeight();
if( isToolBarButton ) {
Insets spacing = UIScale.scale( toolbarSpacingInsets );
x += spacing.left;
y += spacing.top;
width -= spacing.left + spacing.right;
height -= spacing.top + spacing.bottom;
}
// paint shadow
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
if( !isToolBarButton && shadowColor != null && shadowWidth > 0 && focusWidth > 0 &&
!(isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c )) && c.isEnabled() )
{
g2.setColor( shadowColor );
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
width - focusWidth * 2, height - focusWidth * 2, arc, arc ) );
}
// paint background
Color startBg = def ? defaultBackground : startBackground;
Color endBg = def ? defaultEndBackground : endBackground;
if( background == startBg && endBg != null && !startBg.equals( endBg ) )
g2.setPaint( new GradientPaint( 0, 0, startBg, 0, height, endBg ) );
else
g2.setColor( FlatUIUtils.deriveColor( background, getBackgroundBase( c, def ) ) );
FlatUIUtils.paintComponentBackground( g2, x, y, width, height, focusWidth, arc );
} finally {
g2.dispose();
}
}
@Override
public void paint( Graphics g, JComponent c ) {
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
}
@Override @Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) { protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
if( isHelpButton( b ) ) if( isHelpButton( b ) )
@@ -215,10 +368,10 @@ public class FlatButtonUI
} }
} }
paintText( g, b, textRect, text, b.isEnabled() ? getForeground( b ) : disabledText ); paintText( g, b, textRect, text, getForeground( b ) );
} }
static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) { public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
FontMetrics fm = b.getFontMetrics( b.getFont() ); FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1; int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -228,8 +381,19 @@ 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 null; return disabledBackground;
// toolbar button // toolbar button
if( isToolBarButton( c ) ) { if( isToolBarButton( c ) ) {
@@ -245,13 +409,26 @@ public class FlatButtonUI
boolean def = isDefaultButton( c ); boolean def = isDefaultButton( c );
return buttonStateColor( c, return buttonStateColor( c,
def ? defaultBackground : c.getBackground(), getBackgroundBase( c, def ),
null, null,
def ? defaultFocusedBackground : focusedBackground, isCustomBackground( c.getBackground() ) ? null : (def ? defaultFocusedBackground : focusedBackground),
def ? defaultHoverBackground : hoverBackground, def ? defaultHoverBackground : hoverBackground,
def ? defaultPressedBackground : pressedBackground ); def ? defaultPressedBackground : pressedBackground );
} }
protected Color getBackgroundBase( JComponent c, boolean def ) {
// use component background if explicitly set
Color bg = c.getBackground();
if( isCustomBackground( bg ) )
return bg;
return def ? defaultBackground : bg;
}
protected boolean isCustomBackground( Color bg ) {
return bg != background && (startBackground == null || bg != startBackground);
}
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor, public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
Color focusedColor, Color hoverColor, Color pressedColor ) Color focusedColor, Color hoverColor, Color pressedColor )
{ {
@@ -266,15 +443,30 @@ public class FlatButtonUI
if( hoverColor != null && b != null && b.getModel().isRollover() ) if( hoverColor != null && b != null && b.getModel().isRollover() )
return hoverColor; return hoverColor;
if( focusedColor != null && c.hasFocus() ) if( focusedColor != null && isFocusPainted( c ) && FlatUIUtils.isPermanentFocusOwner( c ) )
return focusedColor; return focusedColor;
return enabledColor; return enabledColor;
} }
protected Color getForeground( JComponent c ) { protected Color getForeground( JComponent c ) {
if( !c.isEnabled() )
return disabledText;
if( ((AbstractButton)c).isSelected() && !isToolBarButton( c ) )
return selectedForeground;
// use component foreground if explicitly set
Color fg = c.getForeground();
if( isCustomForeground( fg ) )
return fg;
boolean def = isDefaultButton( c ); boolean def = isDefaultButton( c );
return def ? defaultForeground : c.getForeground(); return def ? defaultForeground : fg;
}
protected boolean isCustomForeground( Color fg ) {
return fg != foreground;
} }
@Override @Override
@@ -283,11 +475,43 @@ public class FlatButtonUI
return new Dimension( helpButtonIcon.getIconWidth(), helpButtonIcon.getIconHeight() ); return new Dimension( helpButtonIcon.getIconWidth(), helpButtonIcon.getIconHeight() );
Dimension prefSize = super.getPreferredSize( c ); Dimension prefSize = super.getPreferredSize( c );
if( prefSize == null )
return null;
// apply minimum width, if not in toolbar and not a icon-only button // make square or apply minimum width/height
if( !isToolBarButton( c ) && !isIconOnlyButton( c ) ) boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
prefSize.width = Math.max( prefSize.width, scale( minimumWidth + (focusWidth * 2) ) ); if( clientPropertyBoolean( c, SQUARE_SIZE, false ) ) {
// make button square (increase width or height so that they are equal)
prefSize.width = prefSize.height = Math.max( prefSize.width, prefSize.height );
} else if( isIconOnlyOrSingleCharacter && ((AbstractButton)c).getIcon() == null ) {
// make single-character-no-icon button square (increase width)
prefSize.width = Math.max( prefSize.width, prefSize.height );
} else if( !isIconOnlyOrSingleCharacter && !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
// apply minimum width/height
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + Math.round( focusWidth * 2 ) );
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + Math.round( focusWidth * 2 ) );
}
return prefSize; return prefSize;
} }
//---- class FlatButtonListener -------------------------------------------
protected class FlatButtonListener
extends BasicButtonListener
{
private final AbstractButton b;
protected FlatButtonListener( AbstractButton b ) {
super( b );
this.b = b;
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatButtonUI.this.propertyChange( b, e );
}
}
} }

View File

@@ -0,0 +1,128 @@
/*
* 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.ui;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.EventQueue;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import javax.swing.JFormattedTextField;
import javax.swing.plaf.UIResource;
import javax.swing.text.DefaultCaret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
/**
* Caret that can select all text on focus gained.
*
* @author Karl Tauber
*/
public class FlatCaret
extends DefaultCaret
implements UIResource
{
private final String selectAllOnFocusPolicy;
private boolean wasFocused;
private boolean wasTemporaryLost;
private boolean isMousePressed;
public FlatCaret( String selectAllOnFocusPolicy ) {
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
}
@Override
public void install( JTextComponent c ) {
super.install( c );
// the dot and mark are lost when switching LaF
// --> move dot to end of text so that all text may be selected when it gains focus
Document doc = c.getDocument();
if( doc != null && getDot() == 0 && getMark() == 0 ) {
int length = doc.getLength();
if( length > 0 )
setDot( length );
}
}
@Override
public void focusGained( FocusEvent e ) {
if( !wasTemporaryLost && !isMousePressed )
selectAllOnFocusGained();
wasTemporaryLost = false;
wasFocused = true;
super.focusGained( e );
}
@Override
public void focusLost( FocusEvent e ) {
wasTemporaryLost = e.isTemporary();
super.focusLost( e );
}
@Override
public void mousePressed( MouseEvent e ) {
isMousePressed = true;
super.mousePressed( e );
}
@Override
public void mouseReleased( MouseEvent e ) {
isMousePressed = false;
super.mouseReleased( e );
}
protected void selectAllOnFocusGained() {
JTextComponent c = getComponent();
Document doc = c.getDocument();
if( doc == null || !c.isEnabled() || !c.isEditable() )
return;
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
if( selectAllOnFocusPolicy == null )
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
return;
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
// policy is "once" (or null or unknown)
// was already focused?
if( wasFocused )
return;
// check whether selection was modified before gaining focus
int dot = getDot();
int mark = getMark();
if( dot != mark || dot != doc.getLength() )
return;
}
// select all
if( c instanceof JFormattedTextField ) {
EventQueue.invokeLater( () -> {
setDot( 0 );
moveDot( doc.getLength() );
} );
} else {
setDot( 0 );
moveDot( doc.getLength() );
}
}
}

View File

@@ -16,20 +16,47 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import java.awt.Dimension;
import java.beans.PropertyChangeListener; import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}. * Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
* *
* <!-- BasicCheckBoxMenuItemUI -->
*
* @uiDefault CheckBoxMenuItem.font Font
* @uiDefault CheckBoxMenuItem.background Color
* @uiDefault CheckBoxMenuItem.foreground Color
* @uiDefault CheckBoxMenuItem.disabledForeground Color
* @uiDefault CheckBoxMenuItem.selectionBackground Color
* @uiDefault CheckBoxMenuItem.selectionForeground Color
* @uiDefault CheckBoxMenuItem.acceleratorForeground Color
* @uiDefault CheckBoxMenuItem.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault CheckBoxMenuItem.border Border
* @uiDefault CheckBoxMenuItem.borderPainted boolean
* @uiDefault CheckBoxMenuItem.margin Insets
* @uiDefault CheckBoxMenuItem.arrowIcon Icon
* @uiDefault CheckBoxMenuItem.checkIcon Icon
* @uiDefault CheckBoxMenuItem.opaque boolean
*
* <!-- FlatCheckBoxMenuItemUI -->
*
* @uiDefault MenuItem.iconTextGap int
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatCheckBoxMenuItemUI public class FlatCheckBoxMenuItemUI
extends BasicCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI
{ {
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatCheckBoxMenuItemUI(); return new FlatCheckBoxMenuItemUI();
} }
@@ -38,20 +65,30 @@ public class FlatCheckBoxMenuItemUI
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
// scale LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
defaultTextIconGap = scale( defaultTextIconGap );
renderer = createRenderer();
} }
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override @Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) { protected void uninstallDefaults() {
PropertyChangeListener superListener = super.createPropertyChangeListener( c ); super.uninstallDefaults();
return e -> {
superListener.propertyChange( e ); renderer = null;
if( e.getPropertyName() == "iconTextGap" ) }
defaultTextIconGap = scale( defaultTextIconGap );
}; protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
}
@Override
public void paint( Graphics g, JComponent c ) {
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
acceleratorForeground, acceleratorSelectionForeground );
} }
} }

View File

@@ -22,6 +22,21 @@ import javax.swing.plaf.ComponentUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBox}. * Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBox}.
* *
* <!-- BasicRadioButtonUI -->
*
* @uiDefault CheckBox.font Font
* @uiDefault CheckBox.background Color
* @uiDefault CheckBox.foreground Color
* @uiDefault CheckBox.border Border
* @uiDefault CheckBox.margin Insets
* @uiDefault CheckBox.rollover boolean
* @uiDefault CheckBox.icon Icon
*
* <!-- FlatRadioButtonUI -->
*
* @uiDefault CheckBox.iconTextGap int
* @uiDefault CheckBox.disabledText Color
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatCheckBoxUI public class FlatCheckBoxUI

View File

@@ -16,16 +16,18 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicColorChooserUI; import javax.swing.plaf.basic.BasicColorChooserUI;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JColorChooser}. * Provides the Flat LaF UI delegate for {@link javax.swing.JColorChooser}.
* *
* <!-- BasicColorChooserUI -->
*
* @uiDefault ColorChooser.font Font
* @uiDefault ColorChooser.background Color
* @uiDefault ColorChooser.foreground Color
* @uiDefault ColorChooser.showPreviewPanelText boolean * @uiDefault ColorChooser.showPreviewPanelText boolean
* @uiDefault ColorChooser.swatchesSwatchSize Dimension * @uiDefault ColorChooser.swatchesSwatchSize Dimension
* @uiDefault ColorChooser.swatchesRecentSwatchSize Dimension * @uiDefault ColorChooser.swatchesRecentSwatchSize Dimension
@@ -38,21 +40,4 @@ public class FlatColorChooserUI
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatColorChooserUI(); return new FlatColorChooserUI();
} }
@Override
public void installUI( JComponent c ) {
if( UIScale.getUserScaleFactor() != 1f ) {
// temporary scale swatch sizes
Dimension swatchSize = UIManager.getDimension( "ColorChooser.swatchesSwatchSize" );
Dimension swatchSize2 = UIManager.getDimension( "ColorChooser.swatchesRecentSwatchSize" );
UIManager.put( "ColorChooser.swatchesSwatchSize", UIScale.scale( swatchSize ) );
UIManager.put( "ColorChooser.swatchesRecentSwatchSize", UIScale.scale( swatchSize2 ) );
super.installUI( c );
UIManager.put( "ColorChooser.swatchesSwatchSize", null );
UIManager.put( "ColorChooser.swatchesRecentSwatchSize", null );
} else
super.installUI( c );
}
} }

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; 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.ComponentOrientation;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
@@ -27,19 +28,30 @@ 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.event.ActionEvent;
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.MouseListener; import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.InputMap;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComboBox; 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.KeyStroke;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.AbstractBorder; import javax.swing.border.AbstractBorder;
@@ -49,18 +61,33 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup; import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}. * Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
* *
* TODO document used UI defaults of superclass * <!-- BasicComboBoxUI -->
* *
* @uiDefault Component.focusWidth int * @uiDefault ComboBox.font Font
* @uiDefault Component.arc int * @uiDefault ComboBox.background Color
* @uiDefault ComboBox.foreground Color
* @uiDefault ComboBox.border Border
* @uiDefault ComboBox.padding Insets
* @uiDefault ComboBox.squareButton boolean default is true
*
* <!-- FlatComboBoxUI -->
*
* @uiDefault ComboBox.minimumWidth int
* @uiDefault ComboBox.editorColumns int
* @uiDefault ComboBox.maximumRowCount int
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
* @uiDefault Component.arrowType String triangle (default) or chevron * @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color * @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color * @uiDefault Component.disabledBorderColor Color
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
* @uiDefault ComboBox.disabledBackground Color * @uiDefault ComboBox.disabledBackground Color
* @uiDefault ComboBox.disabledForeground Color * @uiDefault ComboBox.disabledForeground Color
* @uiDefault ComboBox.buttonBackground Color * @uiDefault ComboBox.buttonBackground Color
@@ -74,12 +101,15 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatComboBoxUI public class FlatComboBoxUI
extends BasicComboBoxUI extends BasicComboBoxUI
{ {
protected int focusWidth; protected int minimumWidth;
protected int arc; protected int editorColumns;
protected String buttonStyle;
protected String arrowType; protected String arrowType;
protected boolean isIntelliJTheme;
protected Color borderColor; protected Color borderColor;
protected Color disabledBorderColor; protected Color disabledBorderColor;
protected Color editableBackground;
protected Color disabledBackground; protected Color disabledBackground;
protected Color disabledForeground; protected Color disabledForeground;
@@ -90,7 +120,9 @@ public class FlatComboBoxUI
protected Color buttonHoverArrowColor; protected Color buttonHoverArrowColor;
private MouseListener hoverListener; private MouseListener hoverListener;
private boolean hover; protected boolean hover;
private WeakReference<Component> lastRendererComponent;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatComboBoxUI(); return new FlatComboBoxUI();
@@ -122,12 +154,17 @@ public class FlatComboBoxUI
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
focusWidth = UIManager.getInt( "Component.focusWidth" ); LookAndFeel.installProperty( comboBox, "opaque", false );
arc = UIManager.getInt( "Component.arc" );
minimumWidth = UIManager.getInt( "ComboBox.minimumWidth" );
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
buttonStyle = UIManager.getString( "ComboBox.buttonStyle" );
arrowType = UIManager.getString( "Component.arrowType" ); arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
borderColor = UIManager.getColor( "Component.borderColor" ); borderColor = UIManager.getColor( "Component.borderColor" );
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" ); disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" ); disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" ); disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
@@ -137,10 +174,15 @@ public class FlatComboBoxUI
buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" );
buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" ); buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" );
// set maximumRowCount
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 )
comboBox.setMaximumRowCount( maximumRowCount );
// scale // scale
padding = UIScale.scale( padding ); padding = UIScale.scale( padding );
MigLayoutVisualPadding.install( comboBox, focusWidth ); MigLayoutVisualPadding.install( comboBox );
} }
@Override @Override
@@ -150,6 +192,7 @@ public class FlatComboBoxUI
borderColor = null; borderColor = null;
disabledBorderColor = null; disabledBorderColor = null;
editableBackground = null;
disabledBackground = null; disabledBackground = null;
disabledForeground = null; disabledForeground = null;
@@ -208,12 +251,20 @@ public class FlatComboBoxUI
String propertyName = e.getPropertyName(); String propertyName = e.getPropertyName();
if( editor != null && if( editor != null &&
((source == comboBox && (propertyName == "background" || propertyName == "foreground")) || ((source == comboBox && propertyName == "foreground") ||
(source == editor && propertyName == "enabled")) ) (source == editor && propertyName == "enabled")) )
{ {
// fix editor component colors // fix editor component colors
updateEditorColors(); updateEditorColors();
} } else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
editor.applyComponentOrientation( o );
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
editor.repaint();
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
comboBox.repaint();
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
comboBox.revalidate();
} }
}; };
} }
@@ -223,86 +274,110 @@ public class FlatComboBoxUI
return new FlatComboPopup( comboBox ); return new FlatComboPopup( comboBox );
} }
@Override
protected ComboBoxEditor createEditor() {
ComboBoxEditor comboBoxEditor = super.createEditor();
Component editor = comboBoxEditor.getEditorComponent();
if( editor instanceof JTextField ) {
JTextField textField = (JTextField) editor;
textField.setColumns( editorColumns );
// assign a non-null and non-javax.swing.plaf.UIResource border to the text field,
// otherwise it is replaced with default text field border when switching LaF
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
// instead of "border instanceof javax.swing.plaf.UIResource"
textField.setBorder( BorderFactory.createEmptyBorder() );
}
return comboBoxEditor;
}
@Override @Override
protected void configureEditor() { protected void configureEditor() {
super.configureEditor(); super.configureEditor();
// assign a non-javax.swing.plaf.UIResource border to the text field, // explicitly make non-opaque
// otherwise it is replaced with default text field border when switching LaF if( editor instanceof JComponent )
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder() ((JComponent)editor).setOpaque( false );
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
// instead of "border instanceof javax.swing.plaf.UIResource" editor.applyComponentOrientation( comboBox.getComponentOrientation() );
if( editor instanceof JTextComponent )
((JTextComponent)editor).setBorder( BorderFactory.createEmptyBorder() );
updateEditorColors(); updateEditorColors();
// macOS
if( SystemInfo.isMacOS && editor instanceof JTextComponent ) {
// delegate actions from editor text field to combobox, which is necessary
// because text field on macOS already handle those keys
InputMap inputMap = ((JTextComponent)editor).getInputMap();
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "UP" ) );
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_UP" ) );
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "DOWN" ) );
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_DOWN" ) );
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "HOME" ) );
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "END" ) );
}
} }
private void updateEditorColors() { private void updateEditorColors() {
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI() // use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
// is used, then the editor is updated after the combobox and the // is used, then the editor is updated after the combobox and the
// colors are again replaced with default colors // colors are again replaced with default colors
boolean enabled = editor.isEnabled(); boolean isTextComponent = editor instanceof JTextComponent;
editor.setBackground( FlatUIUtils.nonUIResource( enabled ? comboBox.getBackground() : disabledBackground ) ); editor.setForeground( FlatUIUtils.nonUIResource( getForeground( isTextComponent || editor.isEnabled() ) ) );
editor.setForeground( FlatUIUtils.nonUIResource( (enabled || editor instanceof JTextComponent)
? comboBox.getForeground() if( isTextComponent )
: disabledForeground ) ); ((JTextComponent)editor).setDisabledTextColor( FlatUIUtils.nonUIResource( getForeground( false ) ) );
if( editor instanceof JTextComponent )
((JTextComponent)editor).setDisabledTextColor( FlatUIUtils.nonUIResource( disabledForeground ) );
} }
@Override @Override
protected JButton createArrowButton() { protected JButton createArrowButton() {
return new FlatArrowButton( SwingConstants.SOUTH, arrowType, buttonArrowColor, return new FlatComboBoxButton();
buttonDisabledArrowColor, buttonHoverArrowColor, null )
{
@Override
protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false);
}
};
} }
@Override @Override
public void update( Graphics g, JComponent c ) { public void update( Graphics g, JComponent c ) {
if( c.isOpaque() ) { float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
float arc = FlatUIUtils.getBorderArc( c );
// fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
FlatUIUtils.paintParentBackground( g, c ); FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth(); int width = c.getWidth();
int height = c.getHeight(); int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0; int arrowX = arrowButton.getX();
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0; int arrowWidth = arrowButton.getWidth();
int arrowX = arrowButton.getX(); boolean paintButton = (comboBox.isEditable() || "button".equals( buttonStyle )) && !"none".equals( buttonStyle );
int arrowWidth = arrowButton.getWidth(); boolean enabled = comboBox.isEnabled();
boolean enabled = comboBox.isEnabled(); boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
// paint background // paint background
g2.setColor( enabled ? c.getBackground() : disabledBackground ); g2.setColor( getBackground( enabled ) );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc ); FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
// paint arrow button background // paint arrow button background
if( enabled ) { if( enabled ) {
g2.setColor( comboBox.isEditable() ? buttonEditableBackground : buttonBackground ); g2.setColor( paintButton ? buttonEditableBackground : buttonBackground );
Shape oldClip = g2.getClip(); Shape oldClip = g2.getClip();
if( isLeftToRight ) if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height ); g2.clipRect( arrowX, 0, width - arrowX, height );
else else
g2.clipRect( 0, 0, arrowX + arrowWidth, height ); g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc ); FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip ); g2.setClip( oldClip );
} }
if( comboBox.isEditable() ) { // paint vertical line between value and arrow button
// paint vertical line between value and arrow button if( paintButton ) {
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;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) ); g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
}
} }
paint( g, c ); paint( g, c );
@@ -312,14 +387,17 @@ public class FlatComboBoxUI
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) { public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
ListCellRenderer<Object> renderer = comboBox.getRenderer(); ListCellRenderer<Object> renderer = comboBox.getRenderer();
CellPaddingBorder.uninstall( renderer ); uninstallCellPaddingBorder( renderer );
if( renderer == null )
renderer = new DefaultListCellRenderer();
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false ); Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
c.setFont( comboBox.getFont() ); c.setFont( comboBox.getFont() );
CellPaddingBorder.uninstall( c ); c.applyComponentOrientation( comboBox.getComponentOrientation() );
uninstallCellPaddingBorder( c );
boolean enabled = comboBox.isEnabled(); boolean enabled = comboBox.isEnabled();
c.setForeground( enabled ? comboBox.getForeground() : disabledForeground ); c.setBackground( getBackground( enabled ) );
c.setBackground( enabled ? comboBox.getBackground() : disabledBackground ); c.setForeground( getForeground( enabled ) );
boolean shouldValidate = (c instanceof JPanel); boolean shouldValidate = (c instanceof JPanel);
if( padding != null ) if( padding != null )
@@ -336,8 +414,60 @@ public class FlatComboBoxUI
@Override @Override
public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) { public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
g.setColor( comboBox.isEnabled() ? comboBox.getBackground() : disabledBackground ); // not necessary because already painted in update()
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height ); }
protected Color getBackground( boolean enabled ) {
return enabled
? (editableBackground != null && comboBox.isEditable() ? editableBackground : comboBox.getBackground())
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground);
}
protected Color getForeground( boolean enabled ) {
return enabled ? comboBox.getForeground() : disabledForeground;
}
@Override
public Dimension getMinimumSize( JComponent c ) {
Dimension minimumSize = super.getMinimumSize( c );
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) );
return minimumSize;
}
@Override
protected Dimension getDefaultSize() {
@SuppressWarnings( "unchecked" )
ListCellRenderer<Object> renderer = comboBox.getRenderer();
uninstallCellPaddingBorder( renderer );
Dimension size = super.getDefaultSize();
uninstallCellPaddingBorder( renderer );
return size;
}
@Override
protected Dimension getDisplaySize() {
@SuppressWarnings( "unchecked" )
ListCellRenderer<Object> renderer = comboBox.getRenderer();
uninstallCellPaddingBorder( renderer );
Dimension displaySize = super.getDisplaySize();
// recalculate width without hardcoded 100 under special conditions
if( displaySize.width == 100 + padding.left + padding.right &&
comboBox.isEditable() &&
comboBox.getItemCount() == 0 &&
comboBox.getPrototypeDisplayValue() == null )
{
int width = getDefaultSize().width;
width = Math.max( width, editor.getPreferredSize().width );
width += padding.left + padding.right;
displaySize = new Dimension( width, displaySize.height );
}
uninstallCellPaddingBorder( renderer );
return displaySize;
} }
@Override @Override
@@ -363,31 +493,82 @@ public class FlatComboBoxUI
return null; return null;
} }
private void uninstallCellPaddingBorder( Object o ) {
CellPaddingBorder.uninstall( o );
if( lastRendererComponent != null ) {
CellPaddingBorder.uninstall( lastRendererComponent );
lastRendererComponent = null;
}
}
//---- class FlatComboBoxButton -------------------------------------------
protected class FlatComboBoxButton
extends FlatArrowButton
{
protected FlatComboBoxButton() {
this( SwingConstants.SOUTH, arrowType, buttonArrowColor, buttonDisabledArrowColor, buttonHoverArrowColor, null, null );
}
protected FlatComboBoxButton( int direction, String type, Color foreground, Color disabledForeground,
Color hoverForeground, Color hoverBackground, Color pressedBackground )
{
super( direction, type, foreground, disabledForeground, hoverForeground, hoverBackground, pressedBackground );
}
@Override
protected boolean isHover() {
return super.isHover() || (!comboBox.isEditable() ? hover : false);
}
}
//---- class FlatComboPopup ----------------------------------------------- //---- class FlatComboPopup -----------------------------------------------
@SuppressWarnings( { "rawtypes", "unchecked" } ) @SuppressWarnings( { "rawtypes", "unchecked" } )
private class FlatComboPopup protected class FlatComboPopup
extends BasicComboPopup extends BasicComboPopup
implements ListCellRenderer
{ {
private CellPaddingBorder paddingBorder; private CellPaddingBorder paddingBorder;
FlatComboPopup( JComboBox combo ) { protected FlatComboPopup( JComboBox combo ) {
super( combo ); super( combo );
// BasicComboPopup listens to JComboBox.componentOrientation and updates
// the component orientation of the list, scroller and popup, but when
// switching the LaF and a new combo popup is created, the component
// orientation is not applied.
ComponentOrientation o = comboBox.getComponentOrientation();
list.setComponentOrientation( o );
scroller.setComponentOrientation( o );
setComponentOrientation( o );
} }
@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, ignoring prototype value // get maximum display width of all items
Object prototype = comboBox.getPrototypeDisplayValue(); int displayWidth = getDisplaySize().width;
if( prototype != null )
comboBox.setPrototypeDisplayValue( null ); // add border insets
Dimension displaySize = getDisplaySize(); for( Border border : new Border[] { scroller.getViewportBorder(), scroller.getBorder() } ) {
if( prototype != null ) if( border != null ) {
comboBox.setPrototypeDisplayValue( prototype ); 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
pw = Math.max( pw, displaySize.width ); if( displayWidth > pw ) {
int diff = displayWidth - pw;
pw = displayWidth;
if( !comboBox.getComponentOrientation().isLeftToRight() )
px -= diff;
}
return super.computePopupBounds( px, py, pw, ph ); return super.computePopupBounds( px, py, pw, ph );
} }
@@ -405,7 +586,7 @@ public class FlatComboBoxUI
protected void configureList() { protected void configureList() {
super.configureList(); super.configureList();
list.setCellRenderer( this ); list.setCellRenderer( new PopupListCellRenderer() );
} }
@Override @Override
@@ -416,27 +597,39 @@ public class FlatComboBoxUI
super.propertyChange( e ); super.propertyChange( e );
if( e.getPropertyName() == "renderer" ) if( e.getPropertyName() == "renderer" )
list.setCellRenderer( FlatComboPopup.this ); list.setCellRenderer( new PopupListCellRenderer() );
} }
}; };
} }
@Override //---- class PopupListCellRenderer -----
public Component getListCellRendererComponent( JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus ) private class PopupListCellRenderer
implements ListCellRenderer
{ {
ListCellRenderer renderer = comboBox.getRenderer(); @Override
CellPaddingBorder.uninstall( renderer ); public Component getListCellRendererComponent( JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus )
{
ListCellRenderer renderer = comboBox.getRenderer();
CellPaddingBorder.uninstall( renderer );
CellPaddingBorder.uninstall( lastRendererComponent );
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); if( renderer == null )
renderer = new DefaultListCellRenderer();
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
if( c instanceof JComponent ) { if( c instanceof JComponent ) {
if( paddingBorder == null ) if( paddingBorder == null )
paddingBorder = new CellPaddingBorder( padding ); paddingBorder = new CellPaddingBorder( padding );
paddingBorder.install( (JComponent) c ); paddingBorder.install( (JComponent) c );
}
lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
return c;
} }
return c;
} }
} }
@@ -469,6 +662,9 @@ public class FlatComboBoxUI
} }
static void uninstall( Object o ) { static void uninstall( Object o ) {
if( o instanceof WeakReference )
o = ((WeakReference<?>)o).get();
if( !(o instanceof JComponent) ) if( !(o instanceof JComponent) )
return; return;
@@ -504,4 +700,31 @@ public class FlatComboBoxUI
rendererBorder.paintBorder( c, g, x, y, width, height ); rendererBorder.paintBorder( c, g, x, y, width, height );
} }
} }
//---- class EditorDelegateAction -----------------------------------------
/**
* Delegates actions from editor text field to combobox.
*/
private class EditorDelegateAction
extends AbstractAction
{
private final KeyStroke keyStroke;
EditorDelegateAction( InputMap inputMap, KeyStroke keyStroke ) {
this.keyStroke = keyStroke;
// add to input map
inputMap.put( keyStroke, this );
}
@Override
public void actionPerformed( ActionEvent e ) {
ActionListener action = comboBox.getActionForKeyStroke( keyStroke );
if( action != null ) {
action.actionPerformed( new ActionEvent( comboBox, e.getID(),
e.getActionCommand(), e.getWhen(), e.getModifiers() ) );
}
}
}
} }

View File

@@ -0,0 +1,308 @@
/*
* 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.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyVetoException;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.JToolTip;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicDesktopIconUI;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame.JDesktopIcon}.
*
* <!-- BasicDesktopIconUI -->
*
* @uiDefault DesktopIcon.border Border
*
* <!-- FlatDesktopIconUI -->
*
* @uiDefault DesktopIcon.background Color
* @uiDefault DesktopIcon.foreground Color
* @uiDefault DesktopIcon.iconSize Dimension
* @uiDefault DesktopIcon.closeSize Dimension
* @uiDefault DesktopIcon.closeIcon Icon
*
* @author Karl Tauber
*/
public class FlatDesktopIconUI
extends BasicDesktopIconUI
{
private Dimension iconSize;
private Dimension closeSize;
private JLabel dockIcon;
private JButton closeButton;
private JToolTip titleTip;
private ActionListener closeListener;
private MouseInputListener mouseInputListener;
public static ComponentUI createUI( JComponent c ) {
return new FlatDesktopIconUI();
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
dockIcon = null;
closeButton = null;
}
@Override
protected void installComponents() {
dockIcon = new JLabel();
dockIcon.setHorizontalAlignment( SwingConstants.CENTER );
closeButton = new JButton();
closeButton.setIcon( UIManager.getIcon( "DesktopIcon.closeIcon" ) );
closeButton.setFocusable( false );
closeButton.setBorder( BorderFactory.createEmptyBorder() );
closeButton.setOpaque( true );
closeButton.setBackground( FlatUIUtils.nonUIResource( desktopIcon.getBackground() ) );
closeButton.setForeground( FlatUIUtils.nonUIResource( desktopIcon.getForeground() ) );
closeButton.setVisible( false );
desktopIcon.setLayout( new FlatDesktopIconLayout() );
desktopIcon.add( closeButton );
desktopIcon.add( dockIcon );
}
@Override
protected void uninstallComponents() {
hideTitleTip();
desktopIcon.remove( dockIcon );
desktopIcon.remove( closeButton );
desktopIcon.setLayout( null );
}
@Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installColors( desktopIcon, "DesktopIcon.background", "DesktopIcon.foreground" );
iconSize = UIManager.getDimension( "DesktopIcon.iconSize" );
closeSize = UIManager.getDimension( "DesktopIcon.closeSize" );
}
@Override
protected void installListeners() {
super.installListeners();
closeListener = e -> {
if( frame.isClosable() )
frame.doDefaultCloseAction();
};
closeButton.addActionListener( closeListener );
closeButton.addMouseListener( mouseInputListener );
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
closeButton.removeActionListener( closeListener );
closeButton.removeMouseListener( mouseInputListener );
closeListener = null;
mouseInputListener = null;
}
@Override
protected MouseInputListener createMouseInputListener() {
mouseInputListener = new MouseInputAdapter() {
@Override
public void mouseReleased( MouseEvent e ) {
if( frame.isIcon() && desktopIcon.contains( e.getX(), e.getY() ) ) {
hideTitleTip();
closeButton.setVisible( false );
try {
frame.setIcon( false );
} catch( PropertyVetoException ex ) {
// ignore
}
}
}
@Override
public void mouseEntered( MouseEvent e ) {
showTitleTip();
if( frame.isClosable() )
closeButton.setVisible( true );
}
@Override
public void mouseExited( MouseEvent e ) {
hideTitleTip();
closeButton.setVisible( false );
}
};
return mouseInputListener;
}
private void showTitleTip() {
JRootPane rootPane = SwingUtilities.getRootPane( desktopIcon );
if( rootPane == null )
return;
if( titleTip == null ) {
titleTip = new JToolTip();
rootPane.getLayeredPane().add( titleTip, JLayeredPane.POPUP_LAYER );
}
titleTip.setTipText( frame.getTitle() );
titleTip.setSize( titleTip.getPreferredSize() );
int tx = (desktopIcon.getWidth() - titleTip.getWidth()) / 2;
int ty = -(titleTip.getHeight() + UIScale.scale( 4 ));
Point pt = SwingUtilities.convertPoint( desktopIcon, tx, ty, titleTip.getParent() );
if( pt.x + titleTip.getWidth() > rootPane.getWidth() )
pt.x = rootPane.getWidth() - titleTip.getWidth();
if( pt.x < 0 )
pt.x = 0;
titleTip.setLocation( pt );
titleTip.repaint();
}
private void hideTitleTip() {
if( titleTip == null )
return;
titleTip.setVisible( false );
titleTip.getParent().remove( titleTip );
titleTip = null;
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( iconSize );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return getPreferredSize( c );
}
@Override
public Dimension getMaximumSize( JComponent c ) {
return getPreferredSize( c );
}
void updateDockIcon() {
// use invoke later to make sure that components are updated when switching LaF
EventQueue.invokeLater( () -> {
if( dockIcon != null )
updateDockIconLater();
} );
}
private void updateDockIconLater() {
// make sure that frame is not selected
if( frame.isSelected() ) {
try {
frame.setSelected( false );
} catch( PropertyVetoException ex ) {
// ignore
}
}
// paint internal frame to buffered image
int frameWidth = Math.max( frame.getWidth(), 1 );
int frameHeight = Math.max( frame.getHeight(), 1 );
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = frameImage.createGraphics();
try {
//TODO fix missing internal frame header when switching LaF
frame.paint( g );
} finally {
g.dispose();
}
// compute preview size (keep ratio; also works with non-square preview)
Insets insets = desktopIcon.getInsets();
int previewWidth = UIScale.scale( iconSize.width ) - insets.left - insets.right;
int previewHeight = UIScale.scale( iconSize.height ) - insets.top - insets.bottom;
float frameRatio = ((float) frameHeight / (float) frameWidth);
if( ((float) previewWidth / (float) frameWidth) > ((float) previewHeight / (float) frameHeight) )
previewWidth = Math.round( previewHeight / frameRatio );
else
previewHeight = Math.round( previewWidth * frameRatio );
// scale preview
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
dockIcon.setIcon( new ImageIcon( previewImage ) );
}
//---- class DockIcon -----------------------------------------------------
private class FlatDesktopIconLayout
implements LayoutManager
{
@Override public void addLayoutComponent( String name, Component comp ) {}
@Override public void removeLayoutComponent( Component comp ) {}
@Override
public Dimension preferredLayoutSize( Container parent ) {
return dockIcon.getPreferredSize();
}
@Override
public Dimension minimumLayoutSize( Container parent ) {
return dockIcon.getMinimumSize();
}
@Override
public void layoutContainer( Container parent ) {
Insets insets = parent.getInsets();
// dock icon
dockIcon.setBounds( insets.left, insets.top,
parent.getWidth() - insets.left - insets.right,
parent.getHeight() - insets.top - insets.bottom );
// close button in upper right corner
Dimension cSize = UIScale.scale( closeSize );
closeButton.setBounds( parent.getWidth() - cSize.width, 0, cSize.width, cSize.height );
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.ui;
import javax.swing.DefaultDesktopManager;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicDesktopPaneUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JDesktopPane}.
*
* <!-- BasicDesktopPaneUI -->
*
* @uiDefault Desktop.background Color
* @uiDefault Desktop.minOnScreenInsets Insets
*
* @author Karl Tauber
*/
public class FlatDesktopPaneUI
extends BasicDesktopPaneUI
{
public static ComponentUI createUI( JComponent c ) {
return new FlatDesktopPaneUI();
}
@Override
protected void installDesktopManager() {
desktopManager = desktop.getDesktopManager();
if( desktopManager == null ) {
desktopManager = new FlatDesktopManager();
desktop.setDesktopManager( desktopManager );
}
}
//---- class FlatDesktopManager -------------------------------------------
private class FlatDesktopManager
extends DefaultDesktopManager
implements UIResource
{
@Override
public void iconifyFrame( JInternalFrame f ) {
super.iconifyFrame( f );
((FlatDesktopIconUI)f.getDesktopIcon().getUI()).updateDockIcon();
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* 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.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
* Paints a drop shadow border around the component.
* Supports 1-sided, 2-side, 3-sided or 4-sided drop shadows.
* <p>
* The shadow insets allow specifying drop shadow thickness for each side.
* A zero or negative value hides the drop shadow on that side.
* A negative value can be used to indent the drop shadow on corners.
* E.g. -4 on left indents drop shadow at top-left and bottom-left corners by 4 pixels.
*
* @author Karl Tauber
*/
public class FlatDropShadowBorder
extends FlatEmptyBorder
{
private final Color shadowColor;
private final Insets shadowInsets;
private final float shadowOpacity;
private final int shadowSize;
private Image shadowImage;
private Color lastShadowColor;
private double lastSystemScaleFactor;
private float lastUserScaleFactor;
public FlatDropShadowBorder() {
this( null );
}
public FlatDropShadowBorder( Color shadowColor ) {
this( shadowColor, 4, 0.5f );
}
public FlatDropShadowBorder( Color shadowColor, int shadowSize, float shadowOpacity ) {
this( shadowColor, new Insets( -shadowSize, -shadowSize, shadowSize, shadowSize ), shadowOpacity );
}
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
this.shadowColor = shadowColor;
this.shadowInsets = shadowInsets;
this.shadowOpacity = shadowOpacity;
shadowSize = Math.max(
Math.max( shadowInsets.left, shadowInsets.right ),
Math.max( shadowInsets.top, shadowInsets.bottom ) );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( shadowSize <= 0 )
return;
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
Color shadowColor = (this.shadowColor != null) ? this.shadowColor : g.getColor();
int shadowSize = scale( this.shadowSize, scaleFactor );
// create and cache shadow image
float userScaleFactor = UIScale.getUserScaleFactor();
if( shadowImage == null ||
!shadowColor.equals( lastShadowColor ) ||
lastSystemScaleFactor != scaleFactor ||
lastUserScaleFactor != userScaleFactor )
{
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
(float) (scaleFactor * userScaleFactor) );
lastShadowColor = shadowColor;
lastSystemScaleFactor = scaleFactor;
lastUserScaleFactor = userScaleFactor;
}
/*debug
int m = shadowImage.getWidth( null );
Color oldColor = g.getColor();
g.setColor( Color.lightGray );
g.drawRect( x - m - 1, y - m - 1, m + 1, m + 1 );
g.setColor( Color.white );
g.fillRect( x - m, y - m, m, m );
g.drawImage( shadowImage, x - m, y - m, null );
g.setColor( oldColor );
debug*/
int left = scale( shadowInsets.left, scaleFactor );
int right = scale( shadowInsets.right, scaleFactor );
int top = scale( shadowInsets.top, scaleFactor );
int bottom = scale( shadowInsets.bottom, scaleFactor );
// shadow outer coordinates
int x1o = x - Math.min( left, 0 );
int y1o = y - Math.min( top, 0 );
int x2o = x + width + Math.min( right, 0 );
int y2o = y + height + Math.min( bottom, 0 );
// shadow inner coordinates
int x1i = x1o + shadowSize;
int y1i = y1o + shadowSize;
int x2i = x2o - shadowSize;
int y2i = y2o - shadowSize;
int wh = (shadowSize * 2) - 1;
int center = shadowSize - 1;
// left-top edge
if( left > 0 || top > 0 ) {
g.drawImage( shadowImage, x1o, y1o, x1i, y1i,
0, 0, center, center, null );
}
// top shadow
if( top > 0 ) {
g.drawImage( shadowImage, x1i, y1o, x2i, y1i,
center, 0, center + 1, center, null );
}
// right-top edge
if( right > 0 || top > 0 ) {
g.drawImage( shadowImage, x2i, y1o, x2o, y1i,
center, 0, wh, center, null );
}
// left shadow
if( left > 0 ) {
g.drawImage( shadowImage, x1o, y1i, x1i, y2i,
0, center, center, center + 1, null );
}
// right shadow
if( right > 0 ) {
g.drawImage( shadowImage, x2i, y1i, x2o, y2i,
center, center, wh, center + 1, null );
}
// left-bottom edge
if( left > 0 || bottom > 0 ) {
g.drawImage( shadowImage, x1o, y2i, x1i, y2o,
0, center, center, wh, null );
}
// bottom shadow
if( bottom > 0 ) {
g.drawImage( shadowImage, x1i, y2i, x2i, y2o,
center, center, center + 1, wh, null );
}
// right-bottom edge
if( right > 0 || bottom > 0 ) {
g.drawImage( shadowImage, x2i, y2i, x2o, y2o,
center, center, wh, wh, null );
}
}
private int scale( int value, double scaleFactor ) {
return (int) Math.ceil( UIScale.scale( value ) * scaleFactor );
}
private static BufferedImage createShadowImage( Color shadowColor, int shadowSize,
float shadowOpacity, float scaleFactor )
{
int shadowRGB = shadowColor.getRGB() & 0xffffff;
int shadowAlpha = (int) (255 * shadowOpacity);
Color startColor = new Color( shadowRGB | ((shadowAlpha & 0xff) << 24), true );
Color midColor = new Color( shadowRGB | (((shadowAlpha / 2) & 0xff) << 24), true );
Color endColor = new Color( shadowRGB, true );
/*debug
startColor = Color.red;
midColor = Color.green;
endColor = Color.blue;
debug*/
int wh = (shadowSize * 2) - 1;
int center = shadowSize - 1;
RadialGradientPaint p = new RadialGradientPaint( center, center,
shadowSize - (0.75f * scaleFactor),
new float[] { 0, 0.35f, 1 },
new Color[] { startColor, midColor, endColor } );
BufferedImage image = new BufferedImage( wh, wh, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics();
try {
g.setPaint( p );
g.fillRect( 0, 0, wh, wh );
} finally {
g.dispose();
}
return image;
}
}

View File

@@ -18,17 +18,41 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicEditorPaneUI; import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.HiDPIUtils;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
* *
* TODO document used UI defaults of superclass * <!-- BasicEditorPaneUI -->
*
* @uiDefault EditorPane.font Font
* @uiDefault EditorPane.background Color also used if not editable
* @uiDefault EditorPane.foreground Color
* @uiDefault EditorPane.caretForeground Color
* @uiDefault EditorPane.selectionBackground Color
* @uiDefault EditorPane.selectionForeground Color
* @uiDefault EditorPane.disabledBackground Color used if not enabled
* @uiDefault EditorPane.inactiveBackground Color used if not editable
* @uiDefault EditorPane.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault EditorPane.border Border
* @uiDefault EditorPane.margin Insets
* @uiDefault EditorPane.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatEditorPaneUI -->
* *
* @uiDefault Component.minimumWidth int * @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -36,6 +60,9 @@ public class FlatEditorPaneUI
extends BasicEditorPaneUI extends BasicEditorPaneUI
{ {
protected int minimumWidth; protected int minimumWidth;
protected boolean isIntelliJTheme;
private Object oldHonorDisplayProperties;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatEditorPaneUI(); return new FlatEditorPaneUI();
@@ -46,24 +73,69 @@ public class FlatEditorPaneUI
super.installDefaults(); super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
// use component font and foreground for HTML text
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, true );
}
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
}
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
propertyChange( getComponent(), e );
}
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.MINIMUM_WIDTH:
c.revalidate();
break;
}
} }
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) ); return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
} }
@Override @Override
public Dimension getMinimumSize( JComponent c ) { public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ) ); return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
} }
private Dimension applyMinimumWidth( Dimension size ) { static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
// Assume that text area is in a scroll pane (that displays the border) // Assume that text area is in a scroll pane (that displays the border)
// and subtract 1px border line width. // and subtract 1px border line width.
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding // Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
// issues. E.g. at scale factor 1.5 the first returns 4, but the second 3. // issues. E.g. at scale factor 1.5 the first returns 4, but the second 3.
minimumWidth = FlatUIUtils.minimumWidth( c, minimumWidth );
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) ); size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size; return size;
} }
@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

@@ -50,10 +50,15 @@ public class FlatEmptyBorder
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
insets.left = scale( left ); boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
insets.left = scale( leftToRight ? left : right );
insets.top = scale( top ); insets.top = scale( top );
insets.right = scale( right ); insets.right = scale( leftToRight ? right : left );
insets.bottom = scale( bottom ); insets.bottom = scale( bottom );
return insets; return insets;
} }
public Insets getUnscaledBorderInsets() {
return super.getBorderInsets();
}
} }

View File

@@ -0,0 +1,248 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.io.File;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JFileChooser}.
*
* <!-- BasicFileChooserUI -->
*
* @uiDefault FileView.directoryIcon Icon
* @uiDefault FileView.fileIcon Icon
* @uiDefault FileView.computerIcon Icon
* @uiDefault FileView.hardDriveIcon Icon
* @uiDefault FileView.floppyDriveIcon Icon
*
* @uiDefault FileChooser.newFolderIcon Icon
* @uiDefault FileChooser.upFolderIcon Icon
* @uiDefault FileChooser.homeFolderIcon Icon
* @uiDefault FileChooser.detailsViewIcon Icon
* @uiDefault FileChooser.listViewIcon Icon
* @uiDefault FileChooser.viewMenuIcon Icon
*
* @uiDefault FileChooser.usesSingleFilePane boolean
* @uiDefault FileChooser.readOnly boolean if true, "New Folder" is disabled
*
* @uiDefault FileChooser.newFolderErrorText String
* @uiDefault FileChooser.newFolderErrorSeparator String
* @uiDefault FileChooser.newFolderParentDoesntExistTitleText String
* @uiDefault FileChooser.newFolderParentDoesntExistText String
* @uiDefault FileChooser.fileDescriptionText String
* @uiDefault FileChooser.directoryDescriptionText String
* @uiDefault FileChooser.saveButtonText String
* @uiDefault FileChooser.openButtonText String
* @uiDefault FileChooser.saveDialogTitleText String
* @uiDefault FileChooser.openDialogTitleText String
* @uiDefault FileChooser.cancelButtonText String
* @uiDefault FileChooser.updateButtonText String
* @uiDefault FileChooser.helpButtonText String
* @uiDefault FileChooser.directoryOpenButtonText String
*
* @uiDefault FileChooser.saveButtonMnemonic String
* @uiDefault FileChooser.openButtonMnemonic String
* @uiDefault FileChooser.cancelButtonMnemonic String
* @uiDefault FileChooser.updateButtonMnemonic String
* @uiDefault FileChooser.helpButtonMnemonic String
* @uiDefault FileChooser.directoryOpenButtonMnemonic String
*
* @uiDefault FileChooser.saveButtonToolTipText String
* @uiDefault FileChooser.openButtonToolTipText String
* @uiDefault FileChooser.cancelButtonToolTipText String
* @uiDefault FileChooser.updateButtonToolTipText String
* @uiDefault FileChooser.helpButtonToolTipText String
* @uiDefault FileChooser.directoryOpenButtonToolTipText String
*
* @uiDefault FileChooser.acceptAllFileFilterText String
*
* <!-- MetalFileChooserUI -->
*
* @uiDefault FileChooser.lookInLabelMnemonic String
* @uiDefault FileChooser.lookInLabelText String
* @uiDefault FileChooser.saveInLabelText String
* @uiDefault FileChooser.fileNameLabelMnemonic String
* @uiDefault FileChooser.fileNameLabelText String
* @uiDefault FileChooser.folderNameLabelMnemonic String
* @uiDefault FileChooser.folderNameLabelText String
* @uiDefault FileChooser.filesOfTypeLabelMnemonic String
* @uiDefault FileChooser.filesOfTypeLabelText String
*
* @uiDefault FileChooser.upFolderToolTipText String
* @uiDefault FileChooser.upFolderAccessibleName String
* @uiDefault FileChooser.homeFolderToolTipText String
* @uiDefault FileChooser.homeFolderAccessibleName String
* @uiDefault FileChooser.newFolderToolTipText String
* @uiDefault FileChooser.newFolderAccessibleName String
* @uiDefault FileChooser.listViewButtonToolTipText String
* @uiDefault FileChooser.listViewButtonAccessibleName String
* @uiDefault FileChooser.detailsViewButtonToolTipText String
* @uiDefault FileChooser.detailsViewButtonAccessibleName String
*
* <!-- FilePane -->
*
* @uiDefault FileChooser.fileNameHeaderText String
* @uiDefault FileChooser.fileSizeHeaderText String
* @uiDefault FileChooser.fileTypeHeaderText String
* @uiDefault FileChooser.fileDateHeaderText String
* @uiDefault FileChooser.fileAttrHeaderText String
*
* @uiDefault FileChooser.viewMenuLabelText String
* @uiDefault FileChooser.refreshActionLabelText String
* @uiDefault FileChooser.newFolderActionLabelText String
* @uiDefault FileChooser.listViewActionLabelText String
* @uiDefault FileChooser.detailsViewActionLabelText String
*
* @author Karl Tauber
*/
public class FlatFileChooserUI
extends MetalFileChooserUI
{
private final FlatFileView fileView = new FlatFileView();
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
}
public FlatFileChooserUI( JFileChooser filechooser ) {
super( filechooser );
}
@Override
public void installComponents( JFileChooser fc ) {
super.installComponents( fc );
patchUI( fc );
}
private void patchUI( JFileChooser fc ) {
// turn top-right buttons into toolbar buttons
Component topPanel = fc.getComponent( 0 );
if( (topPanel instanceof JPanel) &&
(((JPanel)topPanel).getLayout() instanceof BorderLayout) )
{
Component topButtonPanel = ((JPanel)topPanel).getComponent( 0 );
if( (topButtonPanel instanceof JPanel) &&
(((JPanel)topButtonPanel).getLayout() instanceof BoxLayout) )
{
Insets margin = UIManager.getInsets( "Button.margin" );
Component[] comps = ((JPanel)topButtonPanel).getComponents();
for( int i = comps.length - 1; i >= 0; i-- ) {
Component c = comps[i];
if( c instanceof JButton || c instanceof JToggleButton ) {
AbstractButton b = (AbstractButton)c;
b.putClientProperty( FlatClientProperties.BUTTON_TYPE,
FlatClientProperties.BUTTON_TYPE_TOOLBAR_BUTTON );
b.setMargin( margin );
b.setFocusable( false );
} else if( c instanceof Box.Filler )
((JPanel)topButtonPanel).remove( i );
}
}
}
// increase maximum row count of directory combo box popup list
try {
Component directoryComboBox = ((JPanel)topPanel).getComponent( 2 );
if( directoryComboBox instanceof JComboBox ) {
int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" );
if( maximumRowCount > 0 )
((JComboBox<?>)directoryComboBox).setMaximumRowCount( maximumRowCount );
}
} catch( ArrayIndexOutOfBoundsException ex ) {
// ignore
}
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return UIScale.scale( super.getMinimumSize( c ) );
}
@Override
public FileView getFileView( JFileChooser fc ) {
return fileView;
}
@Override
public void clearIconCache() {
fileView.clearIconCache();
}
//---- class FlatFileView -------------------------------------------------
private class FlatFileView
extends BasicFileView
{
@Override
public Icon getIcon( File f ) {
// get cached icon
Icon icon = getCachedIcon( f );
if( icon != null )
return icon;
// get system icon
if( f != null ) {
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
if( icon != null ) {
if( icon instanceof ImageIcon )
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
return icon;
}
}
// get default icon
icon = super.getIcon( f );
if( icon instanceof ImageIcon ) {
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
}
return icon;
}
}
}

View File

@@ -22,6 +22,30 @@ import javax.swing.plaf.ComponentUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JFormattedTextField}. * Provides the Flat LaF UI delegate for {@link javax.swing.JFormattedTextField}.
* *
* <!-- BasicTextFieldUI -->
*
* @uiDefault FormattedTextField.font Font
* @uiDefault FormattedTextField.background Color
* @uiDefault FormattedTextField.foreground Color also used if not editable
* @uiDefault FormattedTextField.caretForeground Color
* @uiDefault FormattedTextField.selectionBackground Color
* @uiDefault FormattedTextField.selectionForeground Color
* @uiDefault FormattedTextField.disabledBackground Color used if not enabled
* @uiDefault FormattedTextField.inactiveBackground Color used if not editable
* @uiDefault FormattedTextField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault FormattedTextField.border Border
* @uiDefault FormattedTextField.margin Insets
* @uiDefault FormattedTextField.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatTextFieldUI -->
*
* @uiDefault TextComponent.arc int
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault FormattedTextField.placeholderForeground Color
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatFormattedTextFieldUI public class FlatFormattedTextFieldUI

View File

@@ -0,0 +1,207 @@
/*
* 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.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF internal frame title bar.
*
* @author Karl Tauber
*/
public class FlatInternalFrameTitlePane
extends BasicInternalFrameTitlePane
{
private JLabel titleLabel;
private JPanel buttonPanel;
public FlatInternalFrameTitlePane( JInternalFrame f ) {
super( f );
}
@Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installBorder( this, "InternalFrameTitlePane.border" );
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new FlatPropertyChangeHandler();
}
@Override
protected LayoutManager createLayout() {
return new BorderLayout( UIScale.scale( 4 ), 0 );
}
@Override
protected void createButtons() {
super.createButtons();
iconButton.setContentAreaFilled( false );
maxButton.setContentAreaFilled( false );
closeButton.setContentAreaFilled( false );
Border emptyBorder = BorderFactory.createEmptyBorder();
iconButton.setBorder( emptyBorder );
maxButton.setBorder( emptyBorder );
closeButton.setBorder( emptyBorder );
updateButtonsVisibility();
}
@Override
protected void addSubComponents() {
titleLabel = new JLabel( frame.getTitle() );
titleLabel.setFont( FlatUIUtils.nonUIResource( getFont() ) );
titleLabel.setMinimumSize( new Dimension( UIScale.scale( 32 ), 1 ) );
updateFrameIcon();
updateColors();
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.setOpaque( false );
buttonPanel.add( iconButton );
buttonPanel.add( maxButton );
buttonPanel.add( closeButton );
add( titleLabel, BorderLayout.CENTER );
add( buttonPanel, BorderLayout.LINE_END );
}
protected void updateFrameIcon() {
Icon frameIcon = frame.getFrameIcon();
if( frameIcon != null && (frameIcon.getIconWidth() == 0 || frameIcon.getIconHeight() == 0) )
frameIcon = null;
else if( frameIcon instanceof ImageIcon )
frameIcon = new ScaledImageIcon( (ImageIcon) frameIcon );
titleLabel.setIcon( frameIcon );
}
protected void updateColors() {
Color background = FlatUIUtils.nonUIResource( frame.isSelected() ? selectedTitleColor : notSelectedTitleColor );
Color foreground = FlatUIUtils.nonUIResource( frame.isSelected() ? selectedTextColor : notSelectedTextColor );
titleLabel.setForeground( foreground );
iconButton.setBackground( background );
iconButton.setForeground( foreground );
maxButton.setBackground( background );
maxButton.setForeground( foreground );
closeButton.setBackground( background );
closeButton.setForeground( foreground );
}
protected void updateButtonsVisibility() {
iconButton.setVisible( frame.isIconifiable() );
maxButton.setVisible( frame.isMaximizable() );
closeButton.setVisible( frame.isClosable() );
}
/**
* Does nothing because FlatLaf internal frames do not have system menus.
*/
@Override
protected void assembleSystemMenu() {
}
/**
* Does nothing because FlatLaf internal frames do not have system menus.
*/
@Override
protected void showSystemMenu() {
}
@Override
public void paintComponent( Graphics g ) {
paintTitleBackground( g );
}
//---- class FlatPropertyChangeHandler ------------------------------------
protected class FlatPropertyChangeHandler
extends PropertyChangeHandler
{
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case JInternalFrame.TITLE_PROPERTY:
titleLabel.setText( frame.getTitle() );
break;
case JInternalFrame.FRAME_ICON_PROPERTY:
updateFrameIcon();
break;
case JInternalFrame.IS_SELECTED_PROPERTY:
updateColors();
break;
case "iconable":
case "maximizable":
case "closable":
updateButtonsVisibility();
enableActions();
revalidate();
repaint();
// do not invoke super.propertyChange() because this adds/removes the buttons
return;
case "componentOrientation":
applyComponentOrientation( frame.getComponentOrientation() );
break;
}
super.propertyChange( e );
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicInternalFrameUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
*
* <!-- BasicInternalFrameUI -->
*
* @uiDefault control Color
* @uiDefault InternalFrame.icon Icon
* @uiDefault InternalFrame.border Border
* @uiDefault InternalFrame.layoutTitlePaneAtOrigin boolean
*
* <!-- BasicInternalFrameTitlePane -->
*
* @uiDefault InternalFrame.titleFont Font
* @uiDefault InternalFrame.icon Icon
* @uiDefault InternalFrame.maximizeIcon Icon
* @uiDefault InternalFrame.minimizeIcon Icon
* @uiDefault InternalFrame.iconifyIcon Icon
* @uiDefault InternalFrame.closeIcon Icon
* @uiDefault InternalFrame.activeTitleBackground Color
* @uiDefault InternalFrame.activeTitleForeground Color
* @uiDefault InternalFrame.inactiveTitleBackground Color
* @uiDefault InternalFrame.inactiveTitleForeground Color
* @uiDefault InternalFrame.closeButtonToolTip String
* @uiDefault InternalFrame.iconButtonToolTip String
* @uiDefault InternalFrame.restoreButtonToolTip String
* @uiDefault InternalFrame.maxButtonToolTip String
* @uiDefault InternalFrameTitlePane.closeButtonText String
* @uiDefault InternalFrameTitlePane.minimizeButtonText String
* @uiDefault InternalFrameTitlePane.restoreButtonText String
* @uiDefault InternalFrameTitlePane.maximizeButtonText String
* @uiDefault InternalFrameTitlePane.moveButtonText String
* @uiDefault InternalFrameTitlePane.sizeButtonText String
* @uiDefault InternalFrameTitlePane.closeButton.mnemonic Integer
* @uiDefault InternalFrameTitlePane.minimizeButton.mnemonic Integer
* @uiDefault InternalFrameTitlePane.restoreButton.mnemonic Integer
* @uiDefault InternalFrameTitlePane.maximizeButton.mnemonic Integer
* @uiDefault InternalFrameTitlePane.moveButton.mnemonic Integer
* @uiDefault InternalFrameTitlePane.sizeButton.mnemonic Integer
*
* <!-- FlatInternalFrameUI -->
*
* @uiDefault InternalFrame.activeBorderColor Color
* @uiDefault InternalFrame.inactiveBorderColor Color
* @uiDefault InternalFrame.borderLineWidth int
* @uiDefault InternalFrame.borderMargins Insets
*
* <!-- FlatInternalFrameTitlePane -->
*
* @uiDefault InternalFrameTitlePane.border Border
*
* @author Karl Tauber
*/
public class FlatInternalFrameUI
extends BasicInternalFrameUI
{
protected FlatWindowResizer windowResizer;
public static ComponentUI createUI( JComponent c ) {
return new FlatInternalFrameUI( (JInternalFrame) c );
}
public FlatInternalFrameUI( JInternalFrame b ) {
super( b );
}
@Override
public void installUI( JComponent c ) {
super.installUI( c );
LookAndFeel.installProperty( frame, "opaque", false );
windowResizer = createWindowResizer();
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
if( windowResizer != null ) {
windowResizer.uninstall();
windowResizer = null;
}
}
@Override
protected JComponent createNorthPane( JInternalFrame w ) {
return new FlatInternalFrameTitlePane( w );
}
protected FlatWindowResizer createWindowResizer() {
return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager );
}
//---- class FlatInternalFrameBorder --------------------------------------
public static class FlatInternalFrameBorder
extends FlatEmptyBorder
{
private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
UIManager.getInsets( "InternalFrame.activeDropShadowInsets" ),
FlatUIUtils.getUIFloat( "InternalFrame.activeDropShadowOpacity", 0.5f ) );
private final FlatDropShadowBorder inactiveDropShadowBorder = new FlatDropShadowBorder(
UIManager.getColor( "InternalFrame.inactiveDropShadowColor" ),
UIManager.getInsets( "InternalFrame.inactiveDropShadowInsets" ),
FlatUIUtils.getUIFloat( "InternalFrame.inactiveDropShadowOpacity", 0.5f ) );
public FlatInternalFrameBorder() {
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
insets.left = scale( Math.min( borderLineWidth, left ) );
insets.top = scale( Math.min( borderLineWidth, top ) );
insets.right = scale( Math.min( borderLineWidth, right ) );
insets.bottom = scale( Math.min( borderLineWidth, bottom ) );
return insets;
}
return super.getBorderInsets( c, insets );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
JInternalFrame f = (JInternalFrame) c;
Insets insets = getBorderInsets( c );
float lineWidth = scale( (float) borderLineWidth );
float rx = x + insets.left - lineWidth;
float ry = y + insets.top - lineWidth;
float rwidth = width - insets.left - insets.right + (lineWidth * 2);
float rheight = height - insets.top - insets.bottom + (lineWidth * 2);
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( f.isSelected() ? activeBorderColor : inactiveBorderColor );
// paint drop shadow
if( dropShadowPainted ) {
FlatDropShadowBorder dropShadowBorder = f.isSelected()
? activeDropShadowBorder : inactiveDropShadowBorder;
Insets dropShadowInsets = dropShadowBorder.getBorderInsets();
dropShadowBorder.paintBorder( c, g2,
(int) rx - dropShadowInsets.left,
(int) ry - dropShadowInsets.top,
(int) rwidth + dropShadowInsets.left + dropShadowInsets.right,
(int) rheight + dropShadowInsets.top + dropShadowInsets.bottom );
}
// paint border
g2.fill( FlatUIUtils.createRectangle( rx, ry, rwidth, rheight, lineWidth ) );
} finally {
g2.dispose();
}
}
}
}

View File

@@ -19,24 +19,33 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.SwingUtilities; 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.BasicHTML;
import javax.swing.plaf.basic.BasicLabelUI; import javax.swing.plaf.basic.BasicLabelUI;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JLabel}. * Provides the Flat LaF UI delegate for {@link javax.swing.JLabel}.
* *
* <!-- BasicLabelUI -->
*
* @uiDefault Label.font Font
* @uiDefault Label.background Color only used if opaque * @uiDefault Label.background Color only used if opaque
* @uiDefault Label.foreground Color * @uiDefault Label.foreground Color
*
* <!-- FlatLabelUI -->
*
* @uiDefault Label.disabledForeground Color * @uiDefault Label.disabledForeground Color
* @uiDefault Label.font Font
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -72,6 +81,62 @@ public class FlatLabelUI
defaults_initialized = false; defaults_initialized = false;
} }
@Override
protected void installComponents( JLabel c ) {
super.installComponents( c );
// update HTML renderer if necessary
updateHTMLRenderer( c, c.getText(), false );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
String name = e.getPropertyName();
if( name == "text" || name == "font" || name == "foreground" ) {
JLabel label = (JLabel) e.getSource();
updateHTMLRenderer( label, label.getText(), true );
} else
super.propertyChange( e );
}
/**
* Checks whether text contains HTML headings and adds a special CSS rule to
* re-calculate heading font sizes based on current component font size.
*/
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
if( BasicHTML.isHTMLString( text ) &&
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
text.contains( "<h" ) &&
(text.contains( "<h1" ) || text.contains( "<h2" ) || text.contains( "<h3" ) ||
text.contains( "<h4" ) || text.contains( "<h5" ) || text.contains( "<h6" )) )
{
int headIndex = text.indexOf( "<head>" );
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
if( headIndex < 0 )
style = "<head>" + style + "</head>";
int insertIndex = headIndex >= 0 ? (headIndex + "<head>".length()) : "<html>".length();
text = text.substring( 0, insertIndex )
+ style
+ text.substring( insertIndex );
} else if( !always )
return; // not necessary to invoke BasicHTML.updateRenderer()
BasicHTML.updateRenderer( c, text );
}
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {
return (c.getClientProperty( BasicHTML.propertyKey ) != null)
? HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g )
: g;
}
@Override
public void paint( Graphics g, JComponent c ) {
super.paint( createGraphicsHTMLTextYCorrection( g, c ), c );
}
@Override @Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) { protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1; int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -26,9 +26,9 @@ import java.awt.Insets;
/** /**
* Line border for various components. * Line border for various components.
* *
* Paints a scaled 1px thick line around the component. * Paints a scaled (usually 1px thick) line around the component.
* The line thickness is not included in the border insets. * The line thickness is not added to the border insets.
* The insets should be at least 1,1,1,1. * The insets should be at least have line thickness (usually 1,1,1,1).
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
@@ -36,10 +36,24 @@ public class FlatLineBorder
extends FlatEmptyBorder extends FlatEmptyBorder
{ {
private final Color lineColor; private final Color lineColor;
private final float lineThickness;
public FlatLineBorder( Insets insets, Color lineColor ) { public FlatLineBorder( Insets insets, Color lineColor ) {
this( insets, lineColor, 1f );
}
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness ) {
super( insets ); super( insets );
this.lineColor = lineColor; this.lineColor = lineColor;
this.lineThickness = lineThickness;
}
public Color getLineColor() {
return lineColor;
}
public float getLineThickness() {
return lineThickness;
} }
@Override @Override
@@ -48,7 +62,7 @@ public class FlatLineBorder
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
g2.setColor( lineColor ); g2.setColor( lineColor );
FlatUIUtils.drawRoundRectangle( g2, x, y, width, height, 0f, scale( 1f ), 0f ); FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
} finally { } finally {
g2.dispose(); g2.dispose();
} }

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
* Cell border for {@link javax.swing.DefaultListCellRenderer}
* (used by {@link javax.swing.JList}).
* <p>
* Uses separate cell margins from UI defaults to allow easy customizing.
*
* @author Karl Tauber
*/
public class FlatListCellBorder
extends FlatLineBorder
{
final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
protected FlatListCellBorder() {
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
}
//---- class Default ------------------------------------------------------
/**
* Border for unselected cell that uses margins, but does not paint focus indicator border.
*/
public static class Default
extends FlatListCellBorder
{
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// do not paint focus indicator border
}
}
//---- class Focused ------------------------------------------------------
/**
* Border for focused unselected cell that uses margins and paints focus indicator border.
*/
public static class Focused
extends FlatListCellBorder
{
}
//---- class Selected -----------------------------------------------------
/**
* Border for selected cell that uses margins and paints focus indicator border
* if enabled (List.showCellFocusIndicator=true) and exactly one item is selected.
*/
public static class Selected
extends FlatListCellBorder
{
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( !showCellFocusIndicator )
return;
// paint focus indicator border only if exactly one item is selected
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
return;
super.paintBorder( c, g, x, y, width, height );
}
}
}

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -27,11 +28,37 @@ import javax.swing.plaf.basic.BasicListUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}. * Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
* *
* TODO document used UI defaults of superclass * <!-- BasicListUI -->
*
* @uiDefault List.font Font
* @uiDefault List.background Color
* @uiDefault List.foreground Color
* @uiDefault List.selectionBackground Color
* @uiDefault List.selectionForeground Color
* @uiDefault List.dropLineColor Color
* @uiDefault List.border Border
* @uiDefault List.cellRenderer ListCellRenderer
* @uiDefault FileChooser.listFont Font used if client property List.isFileList is true
*
* <!-- DefaultListCellRenderer -->
*
* @uiDefault List.cellNoFocusBorder Border
* @uiDefault List.focusCellHighlightBorder Border
* @uiDefault List.focusSelectedCellHighlightBorder Border
* @uiDefault List.dropCellBackground Color
* @uiDefault List.dropCellForeground Color
*
* <!-- FlatListUI -->
* *
* @uiDefault List.selectionInactiveBackground Color * @uiDefault List.selectionInactiveBackground Color
* @uiDefault List.selectionInactiveForeground Color * @uiDefault List.selectionInactiveForeground Color
* *
* <!-- FlatListCellBorder -->
*
* @uiDefault List.cellMargins Insets
* @uiDefault List.cellFocusColor Color
* @uiDefault List.showCellFocusIndicator boolean
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatListUI public class FlatListUI
@@ -55,7 +82,7 @@ public class FlatListUI
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" ); selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" ); selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
toggleSelectionColors( list.hasFocus() ); toggleSelectionColors();
} }
@Override @Override
@@ -74,13 +101,17 @@ public class FlatListUI
@Override @Override
public void focusGained( FocusEvent e ) { public void focusGained( FocusEvent e ) {
super.focusGained( e ); super.focusGained( e );
toggleSelectionColors( true ); toggleSelectionColors();
} }
@Override @Override
public void focusLost( FocusEvent e ) { public void focusLost( FocusEvent e ) {
super.focusLost( e ); super.focusLost( e );
toggleSelectionColors( false );
// use invokeLater for the case that the window is deactivated
EventQueue.invokeLater( () -> {
toggleSelectionColors();
} );
} }
}; };
} }
@@ -94,8 +125,11 @@ public class FlatListUI
* already used in applications. Then either the inactive colors are not used, * already used in applications. Then either the inactive colors are not used,
* 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( boolean focused ) { private void toggleSelectionColors() {
if( focused ) { if( list == null )
return;
if( FlatUIUtils.isPermanentFocusOwner( list ) ) {
if( list.getSelectionBackground() == selectionInactiveBackground ) if( list.getSelectionBackground() == selectionInactiveBackground )
list.setSelectionBackground( selectionBackground ); list.setSelectionBackground( selectionBackground );
if( list.getSelectionForeground() == selectionInactiveForeground ) if( list.getSelectionForeground() == selectionInactiveForeground )

View File

@@ -29,13 +29,26 @@ import javax.swing.plaf.basic.BasicBorders;
public class FlatMarginBorder public class FlatMarginBorder
extends BasicBorders.MarginBorder extends BasicBorders.MarginBorder
{ {
private final int left, right, top, bottom;
public FlatMarginBorder() {
left = right = top = bottom = 0;
}
public FlatMarginBorder( Insets insets ) {
left = insets.left;
top = insets.top;
right = insets.right;
bottom = insets.bottom;
}
@Override @Override
public Insets getBorderInsets( Component c, Insets insets ) { public Insets getBorderInsets( Component c, Insets insets ) {
insets = super.getBorderInsets( c, insets ); insets = super.getBorderInsets( c, insets );
insets.top = scale( insets.top ); insets.top = scale( insets.top + top );
insets.left = scale( insets.left ); insets.left = scale( insets.left + left );
insets.bottom = scale( insets.bottom ); insets.bottom = scale( insets.bottom + bottom );
insets.right = scale( insets.right ); insets.right = scale( insets.right + right );
return insets; return insets;
} }
} }

View File

@@ -16,13 +16,31 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuBarUI; import javax.swing.plaf.basic.BasicMenuBarUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.SystemInfo;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
* *
* <!-- BasicMenuBarUI -->
*
* @uiDefault MenuBar.font Font
* @uiDefault MenuBar.background Color
* @uiDefault MenuBar.foreground Color
* @uiDefault MenuBar.border Border
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatMenuBarUI public class FlatMenuBarUI
@@ -31,4 +49,45 @@ public class FlatMenuBarUI
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatMenuBarUI(); return new FlatMenuBarUI();
} }
/*
* WARNING: This class is not used on macOS if screen menu bar is enabled.
* Do not add any functionality here.
*/
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
if( map == null ) {
map = new ActionMapUIResource();
SwingUtilities.replaceUIActionMap( menuBar, map );
}
map.put( "takeFocus", new TakeFocus() );
}
//---- class TakeFocus ----------------------------------------------------
/**
* Activates the menu bar and shows mnemonics.
* On Windows, the popup of the first menu is not shown.
* On other platforms, the popup of the first menu is shown.
*/
private static class TakeFocus
extends AbstractAction
{
@Override
public void actionPerformed( ActionEvent e ) {
JMenuBar menuBar = (JMenuBar) e.getSource();
JMenu menu = menuBar.getMenu( 0 );
if( menu != null ) {
MenuSelectionManager.defaultManager().setSelectedPath( SystemInfo.isWindows
? new MenuElement[] { menuBar, menu }
: new MenuElement[] { menuBar, menu, menu.getPopupMenu() } );
FlatLaf.showMnemonics( menuBar );
}
}
}
} }

View File

@@ -0,0 +1,49 @@
/*
* 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.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.JMenuBar;
import javax.swing.UIManager;
/**
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
* {@link javax.swing.JCheckBoxMenuItem} and {@link javax.swing.JRadioButtonMenuItem}.
*
* @uiDefault MenuBar.itemMargins Insets
*
* @author Karl Tauber
*/
public class FlatMenuItemBorder
extends FlatMarginBorder
{
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( c.getParent() instanceof JMenuBar ) {
insets.top = scale( menuBarItemMargins.top );
insets.left = scale( menuBarItemMargins.left );
insets.bottom = scale( menuBarItemMargins.bottom );
insets.right = scale( menuBarItemMargins.right );
return insets;
} else
return super.getBorderInsets( c, insets );
}
}

View File

@@ -0,0 +1,574 @@
/*
* 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.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.text.AttributedCharacterIterator;
import javax.swing.Icon;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Renderer for menu items.
*
* @uiDefault MenuItem.minimumWidth int
* @uiDefault MenuItem.minimumIconSize Dimension
* @uiDefault MenuItem.textAcceleratorGap int
* @uiDefault MenuItem.textNoAcceleratorGap int
* @uiDefault MenuItem.acceleratorArrowGap int
* @uiDefault MenuItem.checkBackground Color
* @uiDefault MenuItem.underlineSelectionBackground Color
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
* @uiDefault MenuItem.underlineSelectionColor Color
* @uiDefault MenuItem.underlineSelectionHeight Color
*
* @author Karl Tauber
*/
public class FlatMenuItemRenderer
{
protected final JMenuItem menuItem;
protected final Icon checkIcon;
protected final Icon arrowIcon;
protected final Font acceleratorFont;
protected final String acceleratorDelimiter;
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
protected final Dimension minimumIconSize;
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
{
this.menuItem = menuItem;
this.checkIcon = checkIcon;
this.arrowIcon = arrowIcon;
this.acceleratorFont = acceleratorFont;
this.acceleratorDelimiter = acceleratorDelimiter;
Dimension minimumIconSize = UIManager.getDimension( "MenuItem.minimumIconSize" );
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
}
protected Dimension getPreferredMenuItemSize() {
int width = 0;
int height = 0;
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
Rectangle viewRect = new Rectangle( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE );
Rectangle iconRect = new Rectangle();
Rectangle textRect = new Rectangle();
// layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
// union icon and text rectangles
Rectangle labelRect = iconRect.union( textRect );
width += labelRect.width;
height = Math.max( labelRect.height, height );
// accelerator size
String accelText = getAcceleratorText();
if( accelText != null ) {
// gap between text and accelerator
width += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
FontMetrics accelFm = menuItem.getFontMetrics( acceleratorFont );
width += SwingUtilities.computeStringWidth( accelFm, accelText );
height = Math.max( accelFm.getHeight(), height );
}
// arrow size
if( !isTopLevelMenu && arrowIcon != null ) {
// gap between text and arrow
if( accelText == null )
width += scale( textNoAcceleratorGap );
// gap between accelerator and arrow
width += scale( acceleratorArrowGap );
width += arrowIcon.getIconWidth();
height = Math.max( arrowIcon.getIconHeight(), height );
}
// add insets
Insets insets = menuItem.getInsets();
width += insets.left + insets.right;
height += insets.top + insets.bottom;
// minimum width
if( !isTopLevelMenu ) {
int minimumWidth = FlatUIUtils.minimumWidth( menuItem, this.minimumWidth );
width = Math.max( width, scale( minimumWidth ) );
}
return new Dimension( width, height );
}
private void layout( Rectangle viewRect, Rectangle iconRect, Rectangle textRect,
Rectangle accelRect, Rectangle arrowRect, Rectangle labelRect )
{
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
// layout arrow
if( !isTopLevelMenu && arrowIcon != null ) {
arrowRect.width = arrowIcon.getIconWidth();
arrowRect.height = arrowIcon.getIconHeight();
} else
arrowRect.setSize( 0, 0 );
arrowRect.y = viewRect.y + centerOffset( viewRect.height, arrowRect.height );
// layout accelerator
String accelText = getAcceleratorText();
if( accelText != null ) {
FontMetrics accelFm = menuItem.getFontMetrics( acceleratorFont );
accelRect.width = SwingUtilities.computeStringWidth( accelFm, accelText );
accelRect.height = accelFm.getHeight();
accelRect.y = viewRect.y + centerOffset( viewRect.height, accelRect.height );
} else
accelRect.setBounds( 0, 0, 0, 0 );
// compute horizontal positions of accelerator and arrow
int accelArrowGap = !isTopLevelMenu ? scale( acceleratorArrowGap ) : 0;
if( menuItem.getComponentOrientation().isLeftToRight() ) {
// left-to-right
arrowRect.x = viewRect.x + viewRect.width - arrowRect.width;
accelRect.x = arrowRect.x - accelArrowGap - accelRect.width;
} else {
// right-to-left
arrowRect.x = viewRect.x;
accelRect.x = arrowRect.x + accelArrowGap + arrowRect.width;
}
// width of accelerator, arrow and gap
int accelArrowWidth = accelRect.width + arrowRect.width;
if( accelText != null )
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
if( !isTopLevelMenu && arrowIcon != null ) {
if( accelText == null )
accelArrowWidth += scale( textNoAcceleratorGap );
accelArrowWidth += scale( acceleratorArrowGap );
}
// label rectangle is view rectangle subtracted by accelerator, arrow and gap
labelRect.setBounds( viewRect );
labelRect.width -= accelArrowWidth;
if( !menuItem.getComponentOrientation().isLeftToRight() )
labelRect.x += accelArrowWidth;
// layout icon and text
SwingUtilities.layoutCompoundLabel( menuItem,
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
}
private static int centerOffset( int wh1, int wh2 ) {
return (wh1 / 2) - (wh2 / 2);
}
protected void paintMenuItem( Graphics g, Color selectionBackground, Color selectionForeground,
Color disabledForeground, Color acceleratorForeground, Color acceleratorSelectionForeground )
{
Rectangle viewRect = new Rectangle( menuItem.getWidth(), menuItem.getHeight() );
// subtract insets
Insets insets = menuItem.getInsets();
viewRect.x += insets.left;
viewRect.y += insets.top;
viewRect.width -= (insets.left + insets.right);
viewRect.height -= (insets.top + insets.bottom);
Rectangle iconRect = new Rectangle();
Rectangle textRect = new Rectangle();
Rectangle accelRect = new Rectangle();
Rectangle arrowRect = new Rectangle();
Rectangle labelRect = new Rectangle();
layout( viewRect, iconRect, textRect, accelRect, arrowRect, labelRect );
/*debug
g.setColor( Color.green ); g.drawRect( viewRect.x, viewRect.y, viewRect.width - 1, viewRect.height - 1 );
g.setColor( Color.red ); g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 );
g.setColor( Color.blue ); g.drawRect( iconRect.x, iconRect.y, iconRect.width - 1, iconRect.height - 1 );
g.setColor( Color.cyan ); g.drawRect( textRect.x, textRect.y, textRect.width - 1, textRect.height - 1 );
g.setColor( Color.magenta ); g.drawRect( accelRect.x, accelRect.y, accelRect.width - 1, accelRect.height - 1 );
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
debug*/
paintBackground( g, selectionBackground );
paintIcon( g, iconRect, getIconForPainting() );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) )
paintArrowIcon( g, arrowRect, arrowIcon );
}
protected void paintBackground( Graphics g, Color selectionBackground ) {
boolean armedOrSelected = isArmedOrSelected( menuItem );
if( menuItem.isOpaque() || armedOrSelected ) {
int width = menuItem.getWidth();
int height = menuItem.getHeight();
// paint background
g.setColor( armedOrSelected
? (isUnderlineSelection()
? deriveBackground( underlineSelectionBackground )
: selectionBackground)
: menuItem.getBackground() );
g.fillRect( 0, 0, width, height );
// paint underline
if( armedOrSelected && isUnderlineSelection() ) {
int underlineHeight = scale( underlineSelectionHeight );
g.setColor( underlineSelectionColor );
if( isTopLevelMenu( menuItem ) ) {
// paint underline at bottom
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
// paint underline at left side
g.fillRect( 0, 0, underlineHeight, height );
} else {
// paint underline at right side
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
}
}
}
}
protected Color deriveBackground( Color background ) {
Color baseColor = menuItem.isOpaque()
? menuItem.getBackground()
: FlatUIUtils.getParentBackground( menuItem );
return FlatUIUtils.deriveColor( background, baseColor );
}
protected void paintIcon( Graphics g, Rectangle iconRect, Icon 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)
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
g.setColor( deriveBackground( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground ) );
g.fillRect( r.x, r.y, r.width, r.height );
}
paintIcon( g, menuItem, icon, iconRect );
}
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
View htmlView = (View) menuItem.getClientProperty( BasicHTML.propertyKey );
if( htmlView != null ) {
paintHTMLText( g, menuItem, textRect, htmlView, isUnderlineSelection() ? null : selectionForeground );
return;
}
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
Color foreground = (isTopLevelMenu( menuItem ) ? menuItem.getParent() : menuItem).getForeground();
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(),
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
}
protected void paintAccelerator( Graphics g, Rectangle accelRect, String accelText,
Color foreground, Color selectionForeground, Color disabledForeground )
{
paintText( g, menuItem, accelRect, accelText, -1, acceleratorFont,
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
}
protected void paintArrowIcon( Graphics g, Rectangle arrowRect, Icon arrowIcon ) {
paintIcon( g, menuItem, arrowIcon, arrowRect );
}
protected static void paintIcon( Graphics g, JMenuItem menuItem, Icon icon, Rectangle iconRect ) {
if( icon == null )
return;
// center because the real icon may be smaller than dimension in iconRect
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
// paint
icon.paintIcon( menuItem, g, x, y );
}
protected static void paintText( Graphics g, JMenuItem menuItem,
Rectangle textRect, String text, int mnemonicIndex, Font font,
Color foreground, Color selectionForeground, Color disabledForeground )
{
if( text == null || text.isEmpty() )
return;
FontMetrics fm = menuItem.getFontMetrics( font );
Font oldFont = g.getFont();
g.setFont( font );
g.setColor( !menuItem.isEnabled()
? disabledForeground
: (isArmedOrSelected( menuItem )
? selectionForeground
: foreground) );
FlatUIUtils.drawStringUnderlineCharAt( menuItem, g, text, mnemonicIndex,
textRect.x, textRect.y + fm.getAscent() );
g.setFont( oldFont );
}
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
Rectangle textRect, View htmlView, Color selectionForeground )
{
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
htmlView.paint( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ), textRect );
}
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
}
protected static boolean isTopLevelMenu( JMenuItem menuItem ) {
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
}
protected boolean isUnderlineSelection() {
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
}
private Icon getIconForPainting() {
Icon icon = menuItem.getIcon();
if( icon == null && checkIcon != null && !isTopLevelMenu( menuItem ) )
return checkIcon;
if( icon == null )
return null;
if( !menuItem.isEnabled() )
return menuItem.getDisabledIcon();
if( menuItem.getModel().isPressed() && menuItem.isArmed() ) {
Icon pressedIcon = menuItem.getPressedIcon();
if( pressedIcon != null )
return pressedIcon;
}
return icon;
}
private Icon getIconForLayout() {
Icon icon = menuItem.getIcon();
if( isTopLevelMenu( menuItem ) )
return (icon != null) ? new MinSizeIcon( icon ) : null;
return new MinSizeIcon( (icon != null) ? icon : checkIcon );
}
private KeyStroke cachedAccelerator;
private String cachedAcceleratorText;
private boolean cachedAcceleratorLeftToRight;
private String getAcceleratorText() {
KeyStroke accelerator = menuItem.getAccelerator();
if( accelerator == null )
return null;
boolean leftToRight = menuItem.getComponentOrientation().isLeftToRight();
if( accelerator == cachedAccelerator && leftToRight == cachedAcceleratorLeftToRight )
return cachedAcceleratorText;
cachedAccelerator = accelerator;
cachedAcceleratorText = getTextForAccelerator( accelerator );
cachedAcceleratorLeftToRight = leftToRight;
return cachedAcceleratorText;
}
protected String getTextForAccelerator( KeyStroke accelerator ) {
StringBuilder buf = new StringBuilder();
boolean leftToRight = menuItem.getComponentOrientation().isLeftToRight();
// 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();
if( keyCode != 0 )
buf.append( KeyEvent.getKeyText( keyCode ) );
else
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();
}
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 --------------------------------------------------
private class MinSizeIcon
implements Icon
{
private final Icon delegate;
MinSizeIcon( Icon delegate ) {
this.delegate = delegate;
}
@Override
public int getIconWidth() {
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
return Math.max( iconWidth, scale( minimumIconSize.width ) );
}
@Override
public int getIconHeight() {
int iconHeight = (delegate != null) ? delegate.getIconHeight() : 0;
return Math.max( iconHeight, scale( minimumIconSize.height ) );
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
}
}
//---- class GraphicsProxyWithTextColor -----------------------------------
private static class GraphicsProxyWithTextColor
extends Graphics2DProxy
{
private final Color textColor;
GraphicsProxyWithTextColor( Graphics2D delegate, Color textColor ) {
super( delegate );
this.textColor = textColor;
}
@Override
public void drawString( String str, int x, int y ) {
Paint oldPaint = getPaint();
setPaint( textColor );
super.drawString( str, x, y );
setPaint( oldPaint );
}
@Override
public void drawString( String str, float x, float y ) {
Paint oldPaint = getPaint();
setPaint( textColor );
super.drawString( str, x, y );
setPaint( oldPaint );
}
@Override
public void drawString( AttributedCharacterIterator iterator, int x, int y ) {
Paint oldPaint = getPaint();
setPaint( textColor );
super.drawString( iterator, x, y );
setPaint( oldPaint );
}
@Override
public void drawString( AttributedCharacterIterator iterator, float x, float y ) {
Paint oldPaint = getPaint();
setPaint( textColor );
super.drawString( iterator, x, y );
setPaint( oldPaint );
}
@Override
public void drawChars( char[] data, int offset, int length, int x, int y ) {
Paint oldPaint = getPaint();
setPaint( textColor );
super.drawChars( data, offset, length, x, y );
setPaint( oldPaint );
}
}
}

View File

@@ -16,20 +16,47 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import java.awt.Dimension;
import java.beans.PropertyChangeListener; import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.basic.BasicMenuItemUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
* *
* <!-- BasicMenuItemUI -->
*
* @uiDefault MenuItem.font Font
* @uiDefault MenuItem.background Color
* @uiDefault MenuItem.foreground Color
* @uiDefault MenuItem.disabledForeground Color
* @uiDefault MenuItem.selectionBackground Color
* @uiDefault MenuItem.selectionForeground Color
* @uiDefault MenuItem.acceleratorForeground Color
* @uiDefault MenuItem.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault MenuItem.border Border
* @uiDefault MenuItem.borderPainted boolean
* @uiDefault MenuItem.margin Insets
* @uiDefault MenuItem.arrowIcon Icon
* @uiDefault MenuItem.checkIcon Icon
* @uiDefault MenuItem.opaque boolean
*
* <!-- FlatMenuItemUI -->
*
* @uiDefault MenuItem.iconTextGap int
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatMenuItemUI public class FlatMenuItemUI
extends BasicMenuItemUI extends BasicMenuItemUI
{ {
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatMenuItemUI(); return new FlatMenuItemUI();
} }
@@ -38,20 +65,30 @@ public class FlatMenuItemUI
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
// scale LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
defaultTextIconGap = scale( defaultTextIconGap );
renderer = createRenderer();
} }
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override @Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) { protected void uninstallDefaults() {
PropertyChangeListener superListener = super.createPropertyChangeListener( c ); super.uninstallDefaults();
return e -> {
superListener.propertyChange( e ); renderer = null;
if( e.getPropertyName() == "iconTextGap" ) }
defaultTextIconGap = scale( defaultTextIconGap );
}; protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
}
@Override
public void paint( Graphics g, JComponent c ) {
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
acceleratorForeground, acceleratorSelectionForeground );
} }
} }

View File

@@ -16,20 +16,60 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import java.awt.Color;
import java.beans.PropertyChangeListener; import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuUI; import javax.swing.plaf.basic.BasicMenuUI;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
* *
* <!-- BasicMenuUI -->
*
* @uiDefault Menu.font Font
* @uiDefault Menu.background Color
* @uiDefault Menu.foreground Color
* @uiDefault Menu.disabledForeground Color
* @uiDefault Menu.selectionBackground Color
* @uiDefault Menu.selectionForeground Color
* @uiDefault Menu.acceleratorForeground Color
* @uiDefault Menu.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault Menu.border Border
* @uiDefault Menu.borderPainted boolean
* @uiDefault Menu.margin Insets
* @uiDefault Menu.arrowIcon Icon
* @uiDefault Menu.checkIcon Icon
* @uiDefault Menu.opaque boolean
* @uiDefault Menu.crossMenuMnemonic boolean default is false
* @uiDefault Menu.useMenuBarBackgroundForTopLevel boolean default is false
* @uiDefault MenuBar.background Color used if Menu.useMenuBarBackgroundForTopLevel is true
*
* <!-- FlatMenuUI -->
*
* @uiDefault MenuItem.iconTextGap int
* @uiDefault MenuBar.hoverBackground Color
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatMenuUI public class FlatMenuUI
extends BasicMenuUI extends BasicMenuUI
{ {
private Color hoverBackground;
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatMenuUI(); return new FlatMenuUI();
} }
@@ -38,20 +78,91 @@ public class FlatMenuUI
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
// scale LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
defaultTextIconGap = scale( defaultTextIconGap );
menuItem.setRolloverEnabled( true );
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
renderer = createRenderer();
} }
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override @Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) { protected void uninstallDefaults() {
PropertyChangeListener superListener = super.createPropertyChangeListener( c ); super.uninstallDefaults();
return e -> {
superListener.propertyChange( e ); hoverBackground = null;
if( e.getPropertyName() == "iconTextGap" ) renderer = null;
defaultTextIconGap = scale( defaultTextIconGap ); }
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected MouseInputListener createMouseInputListener( JComponent c ) {
return new BasicMenuUI.MouseInputHandler() {
@Override
public void mouseEntered( MouseEvent e ) {
super.mouseEntered( e );
rollover( e, true );
}
@Override
public void mouseExited( MouseEvent e ) {
super.mouseExited( e );
rollover( e, false );
}
private void rollover( MouseEvent e, boolean rollover ) {
JMenu menu = (JMenu) e.getSource();
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
menu.getModel().setRollover( rollover );
menu.repaint();
}
}
}; };
} }
@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
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
}
@Override
public void paint( Graphics g, JComponent c ) {
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
acceleratorForeground, acceleratorSelectionForeground );
}
//---- class FlatMenuRenderer ---------------------------------------------
protected class FlatMenuRenderer
extends FlatMenuItemRenderer
{
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
{
super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected void paintBackground( Graphics g, Color selectionBackground ) {
ButtonModel model = menuItem.getModel();
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
{
g.setColor( deriveBackground( hoverBackground ) );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
} else
super.paintBackground( g, selectionBackground );
}
}
} }

View File

@@ -19,27 +19,35 @@ package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI; import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JOptionPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JOptionPane}.
* *
* <!-- BasicOptionPaneUI -->
*
* @uiDefault OptionPane.font Font unused
* @uiDefault OptionPane.background Color
* @uiDefault OptionPane.foreground Color unused
* @uiDefault OptionPane.border Border * @uiDefault OptionPane.border Border
* @uiDefault OptionPane.messageAreaBorder Border * @uiDefault OptionPane.messageAreaBorder Border
* @uiDefault OptionPane.buttonAreaBorder Border * @uiDefault OptionPane.buttonAreaBorder Border
* @uiDefault OptionPane.messageForeground Color * @uiDefault OptionPane.messageForeground Color optional; defaults to Label.foreground
* @uiDefault OptionPane.messageFont Font * @uiDefault OptionPane.messageFont Font optional; defaults to Label.font
* @uiDefault OptionPane.buttonFont Font * @uiDefault OptionPane.buttonFont Font optional; defaults to Button.font
* *
* @uiDefault OptionPane.minimumSize Dimension * @uiDefault OptionPane.minimumSize Dimension
* @uiDefault OptionPane.maxCharactersPerLine int
* @uiDefault OptionPane.iconMessageGap int
* @uiDefault OptionPane.messagePadding int
* @uiDefault OptionPane.buttonPadding int * @uiDefault OptionPane.buttonPadding int
* @uiDefault OptionPane.buttonMinimumWidth int -1=disabled * @uiDefault OptionPane.buttonMinimumWidth int -1=disabled
* @uiDefault OptionPane.sameSizeButtons boolean if true, gives all buttons same size * @uiDefault OptionPane.sameSizeButtons boolean if true, gives all buttons same size
@@ -52,6 +60,25 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault OptionPane.questionIcon Icon * @uiDefault OptionPane.questionIcon Icon
* @uiDefault OptionPane.warningIcon Icon * @uiDefault OptionPane.warningIcon Icon
* *
* @uiDefault OptionPane.okButtonText String
* @uiDefault OptionPane.okButtonMnemonic String
* @uiDefault OptionPane.okIcon Icon
* @uiDefault OptionPane.cancelButtonText String
* @uiDefault OptionPane.cancelButtonMnemonic String
* @uiDefault OptionPane.cancelIcon Icon
* @uiDefault OptionPane.yesButtonText String
* @uiDefault OptionPane.yesButtonMnemonic String
* @uiDefault OptionPane.yesIcon Icon
* @uiDefault OptionPane.noButtonText String
* @uiDefault OptionPane.noButtonMnemonic String
* @uiDefault OptionPane.noIcon Icon
*
* <!-- FlatOptionPaneUI -->
*
* @uiDefault OptionPane.iconMessageGap int
* @uiDefault OptionPane.messagePadding int
* @uiDefault OptionPane.maxCharactersPerLine int
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatOptionPaneUI public class FlatOptionPaneUI
@@ -76,6 +103,13 @@ public class FlatOptionPaneUI
focusWidth = UIManager.getInt( "Component.focusWidth" ); focusWidth = UIManager.getInt( "Component.focusWidth" );
} }
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override @Override
public Dimension getMinimumOptionPaneSize() { public Dimension getMinimumOptionPaneSize() {
return UIScale.scale( super.getMinimumOptionPaneSize() ); return UIScale.scale( super.getMinimumOptionPaneSize() );
@@ -122,9 +156,33 @@ public class FlatOptionPaneUI
if( messagePadding > 0 ) if( messagePadding > 0 )
cons.insets.bottom = UIScale.scale( messagePadding ); cons.insets.bottom = UIScale.scale( messagePadding );
// disable line wrapping for HTML
if( msg instanceof String && BasicHTML.isHTMLString( (String) msg ) )
maxll = Integer.MAX_VALUE;
super.addMessageComponents( container, cons, msg, maxll, internallyCreated ); super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
} }
private void updateChildPanels( Container c ) {
for( Component child : c.getComponents() ) {
if( child instanceof JPanel ) {
JPanel panel = (JPanel)child;
// make sub-panel non-opaque for OptionPane.background
panel.setOpaque( false );
// use non-UIResource borders to avoid that they are replaced when switching LaF
Border border = panel.getBorder();
if( border instanceof UIResource )
panel.setBorder( new NonUIResourceBorder( border ) );
}
if( child instanceof Container ) {
updateChildPanels( (Container) child );
}
}
}
private Component findByName( Container c, String name ) { private Component findByName( Container c, String name ) {
for( Component child : c.getComponents() ) { for( Component child : c.getComponents() ) {
if( name.equals( child.getName() ) ) if( name.equals( child.getName() ) )
@@ -138,4 +196,31 @@ public class FlatOptionPaneUI
} }
return null; return null;
} }
//---- class NonUIResourceBorder ------------------------------------------
private static class NonUIResourceBorder
implements Border
{
private final Border delegate;
NonUIResourceBorder( Border delegate ) {
this.delegate = delegate;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
delegate.paintBorder( c, g, x, y, width, height );
}
@Override
public Insets getBorderInsets( Component c ) {
return delegate.getBorderInsets( c );
}
@Override
public boolean isBorderOpaque() {
return delegate.isBorderOpaque();
}
}
} }

View File

@@ -0,0 +1,45 @@
/*
* 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.ui;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
*
* <!-- BasicPanelUI -->
*
* @uiDefault Panel.font Font unused
* @uiDefault Panel.background Color only used if opaque
* @uiDefault Panel.foreground Color
* @uiDefault Panel.border Border
*
* @author Karl Tauber
*/
public class FlatPanelUI
extends BasicPanelUI
{
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) {
if( instance == null )
instance = new FlatPanelUI();
return instance;
}
}

View File

@@ -16,37 +16,67 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; 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.Graphics2D;
import java.awt.event.FocusEvent; import java.awt.Toolkit;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPasswordFieldUI; import javax.swing.plaf.basic.BasicPasswordFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.HiDPIUtils;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}. * Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
* *
* TODO document used UI defaults of superclass * <!-- BasicPasswordFieldUI -->
* *
* @uiDefault Component.focusWidth int * @uiDefault PasswordField.font Font
* @uiDefault Component.minimumWidth int * @uiDefault PasswordField.background Color
* @uiDefault PasswordField.foreground Color also used if not editable
* @uiDefault PasswordField.caretForeground Color
* @uiDefault PasswordField.selectionBackground Color
* @uiDefault PasswordField.selectionForeground Color
* @uiDefault PasswordField.disabledBackground Color used if not enabled
* @uiDefault PasswordField.inactiveBackground Color used if not editable
* @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault PasswordField.border Border
* @uiDefault PasswordField.margin Insets
* @uiDefault PasswordField.echoChar character
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatPasswordFieldUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault PasswordField.placeholderForeground Color
* @uiDefault PasswordField.showCapsLock boolean
* @uiDefault PasswordField.capsLockIcon Icon
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* *
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatPasswordFieldUI public class FlatPasswordFieldUI
extends BasicPasswordFieldUI extends BasicPasswordFieldUI
{ {
protected int focusWidth;
protected int minimumWidth; protected int minimumWidth;
protected boolean isIntelliJTheme;
protected Color placeholderForeground;
protected boolean showCapsLock;
protected Icon capsLockIcon;
private Handler handler; private FocusListener focusListener;
private KeyListener capsLockListener;
public static ComponentUI createUI( JComponent c ) { public static ComponentUI createUI( JComponent c ) {
return new FlatPasswordFieldUI(); return new FlatPasswordFieldUI();
@@ -56,20 +86,25 @@ public class FlatPasswordFieldUI
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();
// use other echoChar on Mac because the default is too large in SF font String prefix = getPropertyPrefix();
if( SystemInfo.IS_MAC )
LookAndFeel.installProperty( getComponent(), "echoChar", '\u2022' );
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" ); minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
MigLayoutVisualPadding.install( getComponent(), focusWidth ); LookAndFeel.installProperty( getComponent(), "opaque", false );
MigLayoutVisualPadding.install( getComponent() );
} }
@Override @Override
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
placeholderForeground = null;
capsLockIcon = null;
MigLayoutVisualPadding.uninstall( getComponent() ); MigLayoutVisualPadding.uninstall( getComponent() );
} }
@@ -77,71 +112,82 @@ public class FlatPasswordFieldUI
protected void installListeners() { protected void installListeners() {
super.installListeners(); super.installListeners();
getComponent().addFocusListener( getHandler() ); focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
capsLockListener = new KeyAdapter() {
@Override
public void keyPressed( KeyEvent e ) {
repaint( e );
}
@Override
public void keyReleased( KeyEvent e ) {
repaint( e );
}
private void repaint( KeyEvent e ) {
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK )
e.getComponent().repaint();
}
};
getComponent().addFocusListener( focusListener );
getComponent().addKeyListener( capsLockListener );
} }
@Override @Override
protected void uninstallListeners() { protected void uninstallListeners() {
super.uninstallListeners(); super.uninstallListeners();
getComponent().removeFocusListener( getHandler() ); getComponent().removeFocusListener( focusListener );
getComponent().removeKeyListener( capsLockListener );
handler = null; focusListener = null;
capsLockListener = null;
} }
public Handler getHandler() { @Override
if( handler == null ) protected Caret createCaret() {
handler = new Handler(); return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
return handler; }
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatTextFieldUI.propertyChange( getComponent(), e );
}
@Override
protected void paintSafely( Graphics g ) {
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
paintCapsLock( g );
super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) );
}
protected void paintCapsLock( Graphics g ) {
if( !showCapsLock )
return;
JTextComponent c = getComponent();
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
return;
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
int x = c.getWidth() - capsLockIcon.getIconWidth() - y;
capsLockIcon.paintIcon( c, g, x, y );
} }
@Override @Override
protected void paintBackground( Graphics g ) { protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent(); // background is painted elsewhere
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
g2.setColor( c.getBackground() );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, 0 );
} finally {
g2.dispose();
}
} }
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) ); return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
} }
@Override @Override
public Dimension getMinimumSize( JComponent c ) { public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ) ); return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
}
private Dimension applyMinimumWidth( Dimension size ) {
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
return size;
}
//---- class Handler ------------------------------------------------------
private class Handler
implements FocusListener
{
@Override
public void focusGained( FocusEvent e ) {
getComponent().repaint();
}
@Override
public void focusLost( FocusEvent e ) {
getComponent().repaint();
}
} }
} }

View File

@@ -0,0 +1,502 @@
/*
* 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.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* A popup factory that adds drop shadows to popups on Windows.
* On macOS and Linux, heavy weight popups (without drop shadow) are produced and the
* operating system automatically adds drop shadows.
*
* @author Karl Tauber
*/
public class FlatPopupFactory
extends PopupFactory
{
private Method java8getPopupMethod;
private Method java9getPopupMethod;
@Override
public Popup getPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException
{
Point pt = fixToolTipLocation( owner, contents, x, y );
if( pt != null ) {
x = pt.x;
y = pt.y;
}
if( !isDropShadowPainted( owner, contents ) )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
Popup popup = getPopupForScreenOfOwner( owner, contents, x, y, true );
if( popup == null )
popup = getPopupForScreenOfOwner( owner, contents, x, y, false );
return new NonFlashingPopup( popup, contents );
}
// create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), owner, contents );
}
/**
* Creates a popup for the screen that the owner component is on.
* <p>
* 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;
for(;;) {
// create new or get cached popup
Popup popup = forceHeavyWeight
? getHeavyWeightPopup( owner, contents, x, y )
: super.getPopup( owner, contents, x, y );
// 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;
}
}
/**
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
* <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();
popup.show();
// 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 isDropShadowPainted( Component owner, Component contents ) {
Boolean b = isDropShadowPainted( owner );
if( b != null )
return b;
b = isDropShadowPainted( contents );
if( b != null )
return b;
return UIManager.getBoolean( "Popup.dropShadowPainted" );
}
private Boolean isDropShadowPainted( Component c ) {
if( !(c instanceof JComponent) )
return null;
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED );
return (value instanceof Boolean ) ? (Boolean) value : null;
}
/**
* There is no API in Java 8 to force creation of heavy weight popups,
* but it is possible with reflection. Java 9 provides a new method.
*
* When changing FlatLaf system requirements to Java 9+,
* then this method can be replaced with:
* return getPopup( owner, contents, x, y, true );
*/
private Popup getHeavyWeightPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException
{
try {
if( SystemInfo.isJava_9_orLater ) {
if( java9getPopupMethod == null ) {
java9getPopupMethod = PopupFactory.class.getDeclaredMethod(
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class );
}
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
} else {
// Java 8
if( java8getPopupMethod == null ) {
java8getPopupMethod = PopupFactory.class.getDeclaredMethod(
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
java8getPopupMethod.setAccessible( true );
}
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
}
} catch( NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex ) {
// ignore
return null;
}
}
/**
* 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) )
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 ) );
}
//---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup
extends Popup
{
private Popup delegate;
private Component contents;
// heavy weight
protected Window popupWindow;
private Color oldPopupWindowBackground;
NonFlashingPopup( Popup delegate, Component contents ) {
this.delegate = delegate;
this.contents = contents;
popupWindow = SwingUtilities.windowForComponent( contents );
if( popupWindow != null ) {
// heavy weight popup
// fix background flashing which may occur on some platforms
// (e.g. macOS and Linux) when using dark theme
oldPopupWindowBackground = popupWindow.getBackground();
popupWindow.setBackground( contents.getBackground() );
}
}
@Override
public void show() {
if( delegate != null ) {
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 ) {
Container parent = contents.getParent();
if( parent instanceof JPanel ) {
Dimension prefSize = parent.getPreferredSize();
if( !prefSize.equals( parent.getSize() ) ) {
Container panel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
if( panel != null )
panel.setSize( prefSize ); // for medium weight popup
else
parent.setSize( prefSize ); // for light weight popup
}
}
}
}
}
@Override
public void hide() {
if( delegate != null ) {
delegate.hide();
delegate = null;
contents = null;
}
if( popupWindow != null ) {
// restore background so that it can not affect other LaFs (when switching)
// because popup windows are cached and reused
popupWindow.setBackground( oldPopupWindowBackground );
popupWindow = null;
}
}
}
//---- class DropShadowPopup ----------------------------------------------
private class DropShadowPopup
extends NonFlashingPopup
{
private final Component owner;
// light weight
private JComponent lightComp;
private Border oldBorder;
private boolean oldOpaque;
// medium weight
private boolean mediumWeightShown;
private Panel mediumWeightPanel;
private JPanel dropShadowPanel;
private ComponentListener mediumPanelListener;
// heavy weight
private Popup dropShadowDelegate;
private Window dropShadowWindow;
private Color oldDropShadowWindowBackground;
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
super( delegate, contents );
this.owner = owner;
Dimension size = contents.getPreferredSize();
if( size.width <= 0 || size.height <= 0 )
return;
if( popupWindow != null ) {
// heavy weight popup
// Since Java has a problem with sub-pixel text rendering on translucent
// windows, we can not make the popup window translucent for the drop shadow.
// (see https://bugs.openjdk.java.net/browse/JDK-8215980)
// The solution is to create a second translucent window that paints
// the drop shadow and is positioned behind the popup window.
// create panel that paints the drop shadow
JPanel dropShadowPanel = new JPanel();
dropShadowPanel.setBorder( createDropShadowBorder() );
dropShadowPanel.setOpaque( false );
// set preferred size of drop shadow panel
Dimension prefSize = popupWindow.getPreferredSize();
Insets insets = dropShadowPanel.getInsets();
dropShadowPanel.setPreferredSize( new Dimension(
prefSize.width + insets.left + insets.right,
prefSize.height + insets.top + insets.bottom ) );
// create heavy weight popup for drop shadow
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
// make drop shadow popup window translucent
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
if( dropShadowWindow != null ) {
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
dropShadowWindow.setBackground( new Color( 0, true ) );
}
} else {
mediumWeightPanel = (Panel) SwingUtilities.getAncestorOfClass( Panel.class, contents );
if( mediumWeightPanel != null ) {
// medium weight popup
dropShadowPanel = new JPanel();
dropShadowPanel.setBorder( createDropShadowBorder() );
dropShadowPanel.setOpaque( false );
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
} else {
// light weight popup
Container p = contents.getParent();
if( !(p instanceof JComponent) )
return;
lightComp = (JComponent) p;
oldBorder = lightComp.getBorder();
oldOpaque = lightComp.isOpaque();
lightComp.setBorder( createDropShadowBorder() );
lightComp.setOpaque( false );
lightComp.setSize( lightComp.getPreferredSize() );
}
}
}
private Border createDropShadowBorder() {
return new FlatDropShadowBorder(
UIManager.getColor( "Popup.dropShadowColor" ),
UIManager.getInsets( "Popup.dropShadowInsets" ),
FlatUIUtils.getUIFloat( "Popup.dropShadowOpacity", 0.5f ) );
}
@Override
public void show() {
if( dropShadowDelegate != null )
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
if( mediumWeightPanel != null )
showMediumWeightDropShadow();
super.show();
// fix location of light weight popup in case it has left or top drop shadow
if( lightComp != null ) {
Insets insets = lightComp.getInsets();
if( insets.left != 0 || insets.top != 0 )
lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top );
}
}
@Override
public void hide() {
if( dropShadowDelegate != null ) {
dropShadowDelegate.hide();
dropShadowDelegate = null;
}
if( mediumWeightPanel != null ) {
hideMediumWeightDropShadow();
dropShadowPanel = null;
mediumWeightPanel = null;
}
super.hide();
if( dropShadowWindow != null ) {
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
dropShadowWindow = null;
}
if( lightComp != null ) {
lightComp.setBorder( oldBorder );
lightComp.setOpaque( oldOpaque );
lightComp = null;
}
}
private void showMediumWeightDropShadow() {
if( mediumWeightShown )
return;
mediumWeightShown = true;
Window window = SwingUtilities.windowForComponent( owner );
if( window == null )
return;
if( !(window instanceof RootPaneContainer) )
return;
dropShadowPanel.setVisible( false );
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
layeredPane.add( dropShadowPanel, JLayeredPane.POPUP_LAYER, 0 );
mediumPanelListener = new ComponentListener() {
@Override
public void componentShown( ComponentEvent e ) {
if( dropShadowPanel != null )
dropShadowPanel.setVisible( true );
}
@Override
public void componentHidden( ComponentEvent e ) {
if( dropShadowPanel != null )
dropShadowPanel.setVisible( false );
}
@Override
public void componentMoved( ComponentEvent e ) {
if( dropShadowPanel != null && mediumWeightPanel != null ) {
Point location = mediumWeightPanel.getLocation();
Insets insets = dropShadowPanel.getInsets();
dropShadowPanel.setLocation( location.x - insets.left, location.y - insets.top );
}
}
@Override
public void componentResized( ComponentEvent e ) {
if( dropShadowPanel != null )
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
}
};
mediumWeightPanel.addComponentListener( mediumPanelListener );
}
private void hideMediumWeightDropShadow() {
mediumWeightPanel.removeComponentListener( mediumPanelListener );
Container parent = dropShadowPanel.getParent();
if( parent != null ) {
Rectangle bounds = dropShadowPanel.getBounds();
parent.remove( dropShadowPanel );
parent.repaint( bounds.x, bounds.y, bounds.width, bounds.height );
}
}
}
}

View File

@@ -16,7 +16,12 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Insets;
import javax.swing.JScrollPane;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Border for {@link javax.swing.JPopupMenu}. * Border for {@link javax.swing.JPopupMenu}.
@@ -33,4 +38,18 @@ public class FlatPopupMenuBorder
super( UIManager.getInsets( "PopupMenu.borderInsets" ), super( UIManager.getInsets( "PopupMenu.borderInsets" ),
UIManager.getColor( "PopupMenu.borderColor" ) ); UIManager.getColor( "PopupMenu.borderColor" ) );
} }
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
if( c instanceof Container &&
((Container)c).getComponentCount() > 0 &&
((Container)c).getComponent( 0 ) instanceof JScrollPane )
{
// e.g. for combobox popups
insets.left = insets.top = insets.right = insets.bottom = UIScale.scale( 1 );
return insets;
}
return super.getBorderInsets( c, insets );
}
} }

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