Compare commits

..

149 Commits
0.33 ... 0.36

Author SHA1 Message Date
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
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
049dae6584 Button: support non-square icon-only buttons (issue #110) 2020-06-03 15:55:14 +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
e0b0617ad2 macOS Catalina: Use Helvetica Neue font 2020-05-30 21:44:52 +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
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
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
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
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
181 changed files with 11393 additions and 4465 deletions

View File

@@ -5,7 +5,7 @@ jdk:
- openjdk8
- openjdk9
- openjdk11
- openjdk13
- openjdk14
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock

View File

@@ -1,6 +1,68 @@
FlatLaf Change Log
==================
## 0.36
- ScrollBar: Made styling more flexible by supporting insets and arc for track
and thumb. (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

View File

@@ -69,9 +69,10 @@ docs).
Addons
------
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
- [Extras](flatlaf-extras)
- [SwingX](flatlaf-swingx)
- [JIDE Common Layer](flatlaf-jide-oss)
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
Projects using FlatLaf

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
val releaseVersion = "0.33"
val developmentVersion = "0.34-SNAPSHOT"
val releaseVersion = "0.36"
val developmentVersion = "0.37-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion

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

@@ -16,161 +16,46 @@
plugins {
`java-library`
`maven-publish`
id( "com.jfrog.bintray" )
id( "com.jfrog.artifactory" )
`flatlaf-module-info`
`flatlaf-java9`
`flatlaf-publish`
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets {
create( "java9" ) {
java {
setSrcDirs( listOf( "src/main/java9" ) )
}
}
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" ) )
}
}
}
java {
withSourcesJar()
withJavadocJar()
}
tasks {
assemble {
dependsOn(
"sourcesJar",
"javadocJar"
)
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
named<JavaCompile>( "compileModuleInfoJava" ) {
sourceCompatibility = "9"
targetCompatibility = "9"
}
}
jar {
archiveBaseName.set( "flatlaf" )
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
manifest.attributes( "Multi-Release" to "true" )
into( "META-INF/versions/9" ) {
from( sourceSets["java9"].output )
}
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}
doLast {
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
}
}
javadoc {
options {
this as StandardJavadocDocletOptions
use( true )
tags = listOf( "uiDefault", "clientProperty" )
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
}
isFailOnError = false
}
register( "sourcesJar", Jar::class ) {
named<Jar>("sourcesJar" ) {
archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "sources" )
from( sourceSets.main.get().allJava )
}
register( "javadocJar", Jar::class ) {
named<Jar>("javadocJar" ) {
archiveBaseName.set( "flatlaf" )
archiveClassifier.set( "javadoc" )
from( javadoc )
}
}
publishing {
publications {
create<MavenPublication>( "maven" ) {
artifactId = "flatlaf"
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( "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"
name = "flatlaf"
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" )
} )
flatlafPublish {
artifactId = "flatlaf"
name = "FlatLaf"
description = "Flat Look and Feel"
}

View File

@@ -30,7 +30,8 @@ public interface FlatClientProperties
* <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} and {@link #BUTTON_TYPE_HELP}
* <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";
@@ -43,6 +44,15 @@ public interface FlatClientProperties
*/
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>
@@ -61,6 +71,15 @@ public interface FlatClientProperties
*/
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>
@@ -80,7 +99,8 @@ public interface FlatClientProperties
/**
* Specifies minimum width of a component.
* <p>
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton} and {@link javax.swing.text.JTextComponent}<br>
* <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";
@@ -93,6 +113,52 @@ public interface FlatClientProperties
*/
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>
@@ -240,4 +306,14 @@ public interface FlatClientProperties
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

@@ -32,11 +32,11 @@ public class FlatDarculaLaf
@Override
public String getName() {
return "Flat Darcula";
return "FlatLaf Darcula";
}
@Override
public String getDescription() {
return "Flat Darcula Look and Feel";
return "FlatLaf Darcula Look and Feel";
}
}

View File

@@ -32,12 +32,12 @@ public class FlatDarkLaf
@Override
public String getName() {
return "Flat Dark";
return "FlatLaf Dark";
}
@Override
public String getDescription() {
return "Flat Dark Look and Feel";
return "FlatLaf Dark Look and Feel";
}
@Override

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

@@ -85,8 +85,12 @@ class FlatInputMaps
// swap to make it consistent with List and Tree
"HOME", "selectFirstRow",
"END", "selectLastRow",
"shift HOME", "selectFirstRowExtendSelection",
"shift END", "selectLastRowExtendSelection",
mac( "ctrl HOME", null ), "selectFirstColumn",
mac( "ctrl END", null ), "selectLastColumn"
mac( "ctrl END", null ), "selectLastColumn",
mac( "shift ctrl HOME", null ), "selectFirstColumnExtendSelection",
mac( "shift ctrl END", null ), "selectLastColumnExtendSelection"
);
if( !SystemInfo.IS_MAC ) {
@@ -501,6 +505,9 @@ class FlatInputMaps
"alt KP_LEFT", "selectParent",
"alt KP_RIGHT", "selectChild",
"shift HOME", "selectFirstExtendSelection",
"shift END", "selectLastExtendSelection",
"meta A", "selectAll",
"meta C", "copy",
"meta V", "paste",
@@ -545,8 +552,6 @@ class FlatInputMaps
"shift ctrl SPACE", null,
"shift ctrl UP", null,
"shift DELETE", null,
"shift END", null,
"shift HOME", null,
"shift INSERT", null,
"shift PAGE_DOWN", null,
"shift PAGE_UP", null,

View File

@@ -32,11 +32,11 @@ public class FlatIntelliJLaf
@Override
public String getName() {
return "Flat IntelliJ";
return "FlatLaf IntelliJ";
}
@Override
public String getDescription() {
return "Flat IntelliJ Look and Feel";
return "FlatLaf IntelliJ Look and Feel";
}
}

View File

@@ -18,22 +18,17 @@ package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Image;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -44,13 +39,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.LookAndFeel;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
@@ -64,6 +56,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.text.StyleContext;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo;
@@ -81,14 +74,14 @@ public abstract class FlatLaf
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
private String desktopPropertyName;
private String desktopPropertyName2;
private PropertyChangeListener desktopPropertyListener;
private static boolean aquaLoaded;
private static boolean updateUIPending;
private KeyEventPostProcessor mnemonicListener;
private static boolean showMnemonics;
private static WeakReference<Window> lastShowMnemonicWindow;
private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler;
private Consumer<UIDefaults> postInitialization;
@@ -131,14 +124,10 @@ public abstract class FlatLaf
public Icon getDisabledIcon( JComponent component, Icon icon ) {
if( icon instanceof ImageIcon ) {
Object grayFilter = UIManager.get( "Component.grayFilter" );
if( !(grayFilter instanceof ImageFilter) ) {
// fallback
grayFilter = isDark()
? new GrayFilter( -20, -70, 100 )
: new GrayFilter( 25, -25, 100 );
}
ImageFilter filter = (grayFilter instanceof ImageFilter)
? (ImageFilter) grayFilter
: GrayFilter.createDisabledIconFilter( isDark() ); // fallback
ImageFilter filter = (ImageFilter) grayFilter;
Function<Image, Image> mapper = img -> {
ImageProducer producer = new FilteredImageSource( img.getSource(), filter );
return Toolkit.getDefaultToolkit().createImage( producer );
@@ -158,12 +147,13 @@ public abstract class FlatLaf
super.initialize();
// add mnemonic listener
mnemonicListener = e -> {
checkShowMnemonics( e );
return false;
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor( mnemonicListener );
// install popup factory
oldPopupFactory = PopupFactory.getSharedInstance();
PopupFactory.setSharedInstance( new FlatPopupFactory() );
// install mnemonic handler
mnemonicHandler = new MnemonicHandler();
mnemonicHandler.install();
// listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.IS_WINDOWS ) {
@@ -171,15 +161,19 @@ public abstract class FlatLaf
// Settings > Ease of Access > Display > Make text bigger (100% - 225%)
desktopPropertyName = "win.messagebox.font";
} else if( SystemInfo.IS_LINUX ) {
// Linux/Gnome allows changing font in "Tweaks" app
desktopPropertyName = "gnome.Gtk/FontName";
// Linux/Gnome allows extra scaling and larger text:
// Settings > Devices > Displays > Scale (100% or 200%)
// Settings > Universal access > Large Text (off or on, 125%)
desktopPropertyName = "gnome.Xft/DPI";
// "Tweaks" app > Fonts > Scaling Factor (0,5 - 3)
desktopPropertyName2 = "gnome.Xft/DPI";
}
if( desktopPropertyName != null ) {
desktopPropertyListener = e -> {
String propertyName = e.getPropertyName();
if( desktopPropertyName.equals( propertyName ) )
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
reSetLookAndFeel();
else if( DESKTOPFONTHINTS.equals( propertyName ) ) {
if( UIManager.getLookAndFeel() instanceof FlatLaf ) {
@@ -190,6 +184,8 @@ public abstract class FlatLaf
};
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
if( desktopPropertyName2 != null )
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
toolkit.addPropertyChangeListener( DESKTOPFONTHINTS, desktopPropertyListener );
}
@@ -212,15 +208,24 @@ public abstract class FlatLaf
if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.removePropertyChangeListener( desktopPropertyName, desktopPropertyListener );
if( desktopPropertyName2 != null )
toolkit.removePropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
toolkit.removePropertyChangeListener( DESKTOPFONTHINTS, desktopPropertyListener );
desktopPropertyName = null;
desktopPropertyName2 = null;
desktopPropertyListener = null;
}
// remove mnemonic listener
if( mnemonicListener != null ) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor( mnemonicListener );
mnemonicListener = null;
// uninstall popup factory
if( oldPopupFactory != null ) {
PopupFactory.setSharedInstance( oldPopupFactory );
oldPopupFactory = null;
}
// uninstall mnemonic handler
if( mnemonicHandler != null ) {
mnemonicHandler.uninstall();
mnemonicHandler = null;
}
// restore default link color
@@ -273,8 +278,8 @@ public abstract class FlatLaf
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
// add Metal resource bundle, which is required for FlatFileChooserUI
defaults.addResourceBundle( "com.sun.swing.internal.plaf.metal.resources.metal" );
// add resource bundle for localized texts
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
// initialize some defaults (for overriding) that are used in UI delegates,
// but are not set in BasicLookAndFeel
@@ -321,9 +326,13 @@ public abstract class FlatLaf
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
// use Aqua MenuBarUI if Mac screen menubar is enabled
if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) )
if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
// add defaults necessary for AquaMenuBarUI
defaults.put( "MenuBar.backgroundPainter", BorderFactory.createEmptyBorder() );
}
// initialize text antialiasing
putAATextInfo( defaults );
@@ -334,6 +343,11 @@ public abstract class FlatLaf
for( FlatDefaultsAddon addon : addons )
addon.afterDefaultsLoading( this, defaults );
// add user scale factor to allow layout managers (e.g. MigLayout) to use it
defaults.put( "laf.scaleFactor", (ActiveValue) t -> {
return UIScale.getUserScaleFactor();
} );
if( postInitialization != null ) {
postInitialization.accept( defaults );
postInitialization = null;
@@ -363,7 +377,10 @@ public abstract class FlatLaf
} else if( SystemInfo.IS_MAC ) {
String fontName;
if( SystemInfo.IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER ) {
if( SystemInfo.IS_MAC_OS_10_15_CATALINA_OR_LATER ) {
// use Helvetica Neue font
fontName = "Helvetica Neue";
} else if( SystemInfo.IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER ) {
// use San Francisco Text font
fontName = ".SF NS Text";
} else {
@@ -405,7 +422,7 @@ public abstract class FlatLaf
// using StyleContext.getFont() here because it uses
// sun.font.FontUtilities.getCompositeFontUIResource()
// and creates a composite font that is able to display all Unicode characters
Font font = new StyleContext().getFont( family, style, size );
Font font = StyleContext.getDefaultStyleContext().getFont( family, style, size );
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
}
@@ -422,30 +439,14 @@ public abstract class FlatLaf
* <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.
*/
public static void initIconColors( UIDefaults defaults, boolean dark ) {
// colors for action icons
// see https://jetbrains.design/intellij/principles/icons/#action-icons
defaults.put( "Actions.Red", new ColorUIResource( !dark ? 0xDB5860 : 0xC75450 ) );
defaults.put( "Actions.Yellow", new ColorUIResource( !dark ? 0xEDA200 : 0xF0A732 ) );
defaults.put( "Actions.Green", new ColorUIResource( !dark ? 0x59A869 : 0x499C54 ) );
defaults.put( "Actions.Blue", new ColorUIResource( !dark ? 0x389FD6 : 0x3592C4 ) );
defaults.put( "Actions.Grey", new ColorUIResource( !dark ? 0x6E6E6E : 0xAFB1B3 ) );
defaults.put( "Actions.GreyInline", new ColorUIResource( !dark ? 0x7F8B91 : 0x7F8B91 ) );
// colors for object icons
// see https://jetbrains.design/intellij/principles/icons/#noun-icons
defaults.put( "Objects.Grey", new ColorUIResource( 0x9AA7B0 ) );
defaults.put( "Objects.Blue", new ColorUIResource( 0x40B6E0 ) );
defaults.put( "Objects.Green", new ColorUIResource( 0x62B543 ) );
defaults.put( "Objects.Yellow", new ColorUIResource( 0xF4AF3D ) );
defaults.put( "Objects.YellowDark", new ColorUIResource( 0xD9A343 ) );
defaults.put( "Objects.Purple", new ColorUIResource( 0xB99BF8 ) );
defaults.put( "Objects.Pink", new ColorUIResource( 0xF98B9E ) );
defaults.put( "Objects.Red", new ColorUIResource( 0xF26522 ) );
defaults.put( "Objects.RedStatus", new ColorUIResource( 0xE05555 ) );
defaults.put( "Objects.GreenAndroid", new ColorUIResource( 0xA4C639 ) );
defaults.put( "Objects.BlackText", new ColorUIResource( 0x231F20 ) );
for( FlatIconColors c : FlatIconColors.values() ) {
if( c.light == !dark || c.dark == dark )
defaults.put( c.key, new ColorUIResource( c.rgb ) );
}
}
private void putAATextInfo( UIDefaults defaults ) {
@@ -535,86 +536,15 @@ public abstract class FlatLaf
}
public static boolean isShowMnemonics() {
return showMnemonics || !UIManager.getBoolean( "Component.hideMnemonics" );
return MnemonicHandler.isShowMnemonics();
}
private static void checkShowMnemonics( KeyEvent e ) {
int keyCode = e.getKeyCode();
if( SystemInfo.IS_MAC ) {
// Ctrl+Alt keys must be pressed on Mac
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT )
showMnemonics( e.getID() == KeyEvent.KEY_PRESSED && e.isControlDown() && e.isAltDown(), e.getComponent() );
} else {
// Alt key must be pressed on Windows and Linux
if( keyCode == KeyEvent.VK_ALT )
showMnemonics( e.getID() == KeyEvent.KEY_PRESSED, e.getComponent() );
}
public static void showMnemonics( Component c ) {
MnemonicHandler.showMnemonics( true, c );
}
private 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 );
lastShowMnemonicWindow = new WeakReference<>( window );
} else if( lastShowMnemonicWindow != null ) {
Window window = lastShowMnemonicWindow.get();
if( window != null )
repaintMnemonics( window );
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;
public static void hideMnemonics() {
MnemonicHandler.showMnemonics( false, null );
}
//---- class ActiveFont ---------------------------------------------------

View File

@@ -32,12 +32,12 @@ public class FlatLightLaf
@Override
public String getName() {
return "Flat Light";
return "FlatLaf Light";
}
@Override
public String getDescription() {
return "Flat Light Look and Feel";
return "FlatLaf Light Look and Feel";
}
@Override

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

@@ -240,7 +240,7 @@ public class IntelliJTheme
if( color != null ) {
String key = e.getKey();
namedColors.put( key, color );
defaults.put( "ColorPalette." + e.getKey(), color );
defaults.put( "ColorPalette." + key, color );
}
}
}
@@ -467,6 +467,12 @@ public class IntelliJTheme
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" );

View File

@@ -74,6 +74,13 @@ class LinuxFontPolicy
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.IS_JETBRAINS_JVM &&
!Boolean.parseBoolean( System.getProperty( "flatlaf.useUbuntuFont" ) ) )
family = "Liberation Sans";
// scale font size
double dsize = size * getGnomeFontScale();
size = (int) (dsize + 0.5);

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.IS_MAC ) {
// 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.IS_WINDOWS )
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

@@ -40,6 +40,7 @@ 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;
@@ -63,8 +64,6 @@ class UIDefaultsLoader
private static final String TYPE_PREFIX = "{";
private static final String TYPE_PREFIX_END = "}";
private static final String VARIABLE_PREFIX = "@";
@Deprecated
private static final String REF_PREFIX = VARIABLE_PREFIX + "@";
private static final String PROPERTY_PREFIX = "$";
private static final String OPTIONAL_PREFIX = "?";
private static final String GLOBAL_PREFIX = "*.";
@@ -213,12 +212,6 @@ class UIDefaultsLoader
else if( !value.startsWith( VARIABLE_PREFIX ) )
return value;
// for compatibility
if( value.startsWith( REF_PREFIX ) ) {
FlatLaf.LOG.log( Level.WARNING, "FlatLaf: Usage of '@@' in .properties files is deprecated. Use '$' instead." );
value = value.substring( REF_PREFIX.length() );
}
boolean optional = false;
if( value.startsWith( OPTIONAL_PREFIX ) ) {
value = value.substring( OPTIONAL_PREFIX.length() );
@@ -515,29 +508,42 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
switch( function ) {
case "rgb": return parseColorRgbOrRgba( false, params );
case "rgba": return parseColorRgbOrRgba( true, params );
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 parseColorLightenOrDarken( true, params, resolver, reportError );
case "darken": return parseColorLightenOrDarken( false, params, resolver, reportError );
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)
* - red: an integer 0-255
* - green: an integer 0-255
* - blue: an integer 0-255
* - alpha: an integer 0-255
* 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 ) {
int red = parseInteger( params.get( 0 ), 0, 255 );
int green = parseInteger( params.get( 1 ), 0, 255 );
int blue = parseInteger( params.get( 2 ), 0, 255 );
int alpha = hasAlpha ? parseInteger( params.get( 3 ), 0, 255 ) : 255;
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 ) )
@@ -552,7 +558,7 @@ class UIDefaultsLoader
* - alpha: a percentage 0-100%
*/
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
int hue = parseInteger( params.get( 0 ), 0, 360 );
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;
@@ -562,13 +568,14 @@ class UIDefaultsLoader
}
/**
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options])
* 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] [lazy] [derived]
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
*/
private static Object parseColorLightenOrDarken( boolean lighten, List<String> params,
Function<String, String> resolver, boolean reportError )
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 ) );
@@ -583,16 +590,15 @@ class UIDefaultsLoader
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;
}
ColorFunctions.ColorFunction function = lighten
? new ColorFunctions.Lighten( amount, relative, autoInverse )
: new ColorFunctions.Darken( amount, relative, autoInverse );
if( derived ) {
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
return new DerivedColor( ColorFunctions.applyFunctions( color, function ), function );
}
// create function
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease(
hslIndex, increase, amount, relative, autoInverse );
if( lazy ) {
return (LazyValue) t -> {
@@ -603,8 +609,29 @@ class UIDefaultsLoader
};
}
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
return new ColorUIResource( ColorFunctions.applyFunctions( color, function ) );
// parse base color
String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
// 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 ) {
@@ -629,7 +656,12 @@ class UIDefaultsLoader
return value.charAt( 0 );
}
private static Integer parseInteger( String value, int min, int max ) {
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 + ')' );

View File

@@ -111,13 +111,13 @@ public class FlatCheckBoxIcon
paintBorder( g2 );
// paint background
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
selected ? selectedBackground : background,
disabledBackground,
focusedBackground,
selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground,
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ),
background );
background ) );
paintBackground( g2 );
// paint checkmark

View File

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

View File

@@ -100,12 +100,12 @@ public class FlatHelpButtonIcon
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
// paint background
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
background,
disabledBackground,
focusedBackground,
hoverBackground,
pressedBackground ), background );
pressedBackground ), background ) );
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
// paint question mark

View File

@@ -27,6 +27,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* Base class for internal frame icons.
*
* @uiDefault InternalFrame.buttonSize Dimension
* @uiDefault InternalFrame.buttonHoverBackground Color
* @uiDefault InternalFrame.buttonPressedBackground Color
*
@@ -53,7 +54,7 @@ public abstract class FlatInternalFrameAbstractIcon
protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) {
FlatUIUtils.setColor( g, background, c.getBackground() );
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
g.fillRect( 0, 0, width, height );
}
}

View File

@@ -28,8 +28,11 @@ import com.formdev.flatlaf.ui.FlatButtonUI;
/**
* "close" icon for {@link javax.swing.JInternalFrame}.
*
* @uiDefault InternalFrame.buttonHoverBackground Color
* @uiDefault InternalFrame.buttonPressedBackground Color
* @uiDefault InternalFrame.buttonSize Dimension
* @uiDefault InternalFrame.closeHoverBackground Color
* @uiDefault InternalFrame.closePressedBackground Color
* @uiDefault InternalFrame.closeHoverForeground Color
* @uiDefault InternalFrame.closePressedForeground Color
*
* @author Karl Tauber
*/

View File

@@ -24,14 +24,14 @@ import java.awt.geom.Rectangle2D;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* "minimize" (actually "restore") icon for {@link javax.swing.JInternalFrame}.
* "restore" (or "minimize") icon for {@link javax.swing.JInternalFrame}.
*
* @author Karl Tauber
*/
public class FlatInternalFrameMinimizeIcon
public class FlatInternalFrameRestoreIcon
extends FlatInternalFrameAbstractIcon
{
public FlatInternalFrameMinimizeIcon() {
public FlatInternalFrameRestoreIcon() {
}
@Override

View File

@@ -32,6 +32,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
* @uiDefault Menu.icon.arrowColor Color
* @uiDefault Menu.icon.disabledArrowColor Color
* @uiDefault Menu.selectionForeground Color
* @uiDefault MenuItem.selectionType String
*
* @author Karl Tauber
*/
@@ -65,9 +66,14 @@ public class FlatMenuArrowIcon
}
private Color getArrowColor( Component c ) {
if( c instanceof JMenu && ((JMenu)c).isSelected() )
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
return selectionForeground;
return c.isEnabled() ? arrowColor : disabledArrowColor;
}
private boolean isUnderlineSelection() {
// not storing value of "MenuItem.selectionType" in class to allow changing at runtime
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
}
}

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -26,6 +27,7 @@ import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import javax.swing.JComponent;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicArrowButton;
@@ -111,6 +113,10 @@ public class FlatArrowButton
this.yOffset = yOffset;
}
protected Color deriveHoverBackground( Color hoverBackground ) {
return hoverBackground;
}
@Override
public Dimension getPreferredSize() {
return scale( super.getPreferredSize() );
@@ -132,31 +138,44 @@ public class FlatArrowButton
// paint hover background
if( enabled && isHover() && hoverBackground != null ) {
g.setColor( hoverBackground );
g.setColor( deriveHoverBackground( hoverBackground ) );
g.fillRect( 0, 0, width, height );
}
int direction = getDirection();
boolean vert = (direction == NORTH || direction == SOUTH);
// compute width/height
int w = scale( arrowWidth + (chevron ? 0 : 1) );
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
// rotate width/height
int rw = vert ? w : h;
int rh = vert ? h : w;
// chevron lines end 1px outside of width/height
if( chevron ) {
// add 1px to width/height for position calculation only
rw++;
rh++;
}
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
// optimization for small chevron arrows (e.g. OneTouchButtons in SplitPane)
if( x + rw >= width && x > 0 )
x--;
if( y + rh >= height && y > 0 )
y--;
// move arrow for round borders
Container parent = getParent();
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
// paint arrow
g.setColor( enabled
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
: disabledForeground );
g.translate( x, y );
/*debug
debugPaint( g2, vert, rw, rh );
debug*/
Shape arrowShape = createArrowShape( direction, chevron, w, h );
if( chevron ) {
g2.setStroke( new BasicStroke( scale( 1f ) ) );
@@ -177,4 +196,22 @@ public class FlatArrowButton
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,6 +36,8 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders;
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}).
@@ -54,6 +56,12 @@ import javax.swing.text.JTextComponent;
* @uiDefault Component.disabledBorderColor 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
*/
public class FlatBorder
@@ -61,11 +69,18 @@ public class FlatBorder
{
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
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 borderColor = UIManager.getColor( "Component.borderColor" );
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
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
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create();
@@ -73,25 +88,53 @@ public class FlatBorder
FlatUIUtils.setRenderingHints( g2 );
boolean isCellEditor = isTableCellEditor( c );
float focusWidth = isCellEditor ? 0 : getFocusWidth( c );
float borderWidth = getBorderWidth( c );
float arc = isCellEditor ? 0 : getArc( c );
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
float borderWidth = scale( (float) getBorderWidth( c ) );
float arc = isCellEditor ? 0 : scale( (float) getArc( c ) );
Color outlineColor = getOutlineColor( c );
if( isFocused( c ) ) {
float innerFocusWidth = !(c instanceof JScrollPane) ? this.innerFocusWidth : 0;
if( outlineColor != null || isFocused( c ) ) {
float innerFocusWidth = !(c instanceof JScrollPane)
? (outlineColor != null ? innerOutlineWidth : this.innerFocusWidth)
: 0;
g2.setColor( getFocusColor( c ) );
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height, focusWidth,
getLineWidth( c ) + scale( innerFocusWidth ), arc );
scale( (float) getLineWidth( c ) ) + scale( innerFocusWidth ), arc );
}
g2.setPaint( getBorderColor( c ) );
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
} finally {
g2.dispose();
}
}
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 ) {
return focusColor;
}
@@ -135,6 +178,9 @@ public class FlatBorder
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
} else if( c instanceof JSpinner ) {
if( FlatUIUtils.isPermanentFocusOwner( c ) )
return true;
JComponent editor = ((JSpinner)c).getEditor();
if( editor instanceof JSpinner.DefaultEditor ) {
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
@@ -153,7 +199,8 @@ public class FlatBorder
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
boolean isCellEditor = isTableCellEditor( c );
float ow = (isCellEditor ? 0 : getFocusWidth( c )) + getLineWidth( c );
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
float ow = focusWidth + scale( (float) getLineWidth( c ) );
insets = super.getBorderInsets( c, insets );
insets.top = Math.round( scale( (float) insets.top ) + ow );
@@ -163,19 +210,33 @@ public class FlatBorder
return insets;
}
protected float getFocusWidth( Component c ) {
return scale( (float) focusWidth );
/**
* Returns the (unscaled) thickness of the outer focus border.
*/
protected int getFocusWidth( Component c ) {
return focusWidth;
}
protected float getLineWidth( Component c ) {
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 ) {
/**
* 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( Component c ) {
/**
* Returns the (unscaled) arc diameter of the border.
*/
protected int getArc( Component c ) {
return 0;
}
}

View File

@@ -16,7 +16,6 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
@@ -115,8 +114,8 @@ public class FlatButtonBorder
} else {
insets = super.getBorderInsets( c, insets );
// use smaller left and right insets for icon-only buttons (so that they are square)
if( FlatButtonUI.isIconOnlyButton( c ) && ((AbstractButton)c).getMargin() instanceof UIResource )
// 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 );
}
@@ -124,17 +123,21 @@ public class FlatButtonBorder
}
@Override
protected float getFocusWidth( Component c ) {
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth(c );
protected int getFocusWidth( Component c ) {
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
}
@Override
protected float getBorderWidth( Component c ) {
return FlatButtonUI.isDefaultButton( c ) ? scale( (float) defaultBorderWidth ) : super.getBorderWidth( c );
protected int getBorderWidth( Component c ) {
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : super.getBorderWidth( c );
}
@Override
protected float getArc( Component c ) {
return FlatButtonUI.isSquareButton( c ) ? 0 : scale( (float) arc );
protected int getArc( Component c ) {
switch( FlatButtonUI.getButtonType( c ) ) {
case FlatButtonUI.TYPE_SQUARE: return 0;
case FlatButtonUI.TYPE_ROUND_RECT: return Short.MAX_VALUE;
default: return arc;
}
}
}

View File

@@ -39,7 +39,6 @@ import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
@@ -61,8 +60,6 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatButtonUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Button.arc int
* @uiDefault Button.minimumWidth int
* @uiDefault Button.iconTextGap int
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
@@ -92,8 +89,6 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatButtonUI
extends BasicButtonUI
{
protected int focusWidth;
protected int arc;
protected int minimumWidth;
protected int iconTextGap;
@@ -139,8 +134,6 @@ public class FlatButtonUI
if( !defaults_initialized ) {
String prefix = getPropertyPrefix();
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( "Button.arc" );
minimumWidth = UIManager.getInt( prefix + "minimumWidth" );
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
@@ -187,7 +180,7 @@ public class FlatButtonUI
LookAndFeel.installProperty( b, "opaque", false );
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
MigLayoutVisualPadding.install( b, getFocusWidth( b ) );
MigLayoutVisualPadding.install( b );
}
@Override
@@ -215,6 +208,11 @@ public class FlatButtonUI
case MINIMUM_HEIGHT:
b.revalidate();
break;
case BUTTON_TYPE:
b.revalidate();
b.repaint();
break;
}
}
@@ -226,7 +224,11 @@ public class FlatButtonUI
return c instanceof JButton && ((JButton)c).isDefaultButton();
}
static boolean isIconOnlyButton( Component c ) {
/**
* Returns true if the button has an icon but no text,
* or it it does not have an icon and the text is either "..." or one character.
*/
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
return false;
@@ -236,8 +238,15 @@ public class FlatButtonUI
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
}
static boolean isSquareButton( Component c ) {
return c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE );
// 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 ) {
@@ -245,7 +254,8 @@ public class FlatButtonUI
}
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
@@ -272,11 +282,10 @@ public class FlatButtonUI
try {
FlatUIUtils.setRenderingHints( g2 );
Border border = c.getBorder();
boolean isToolBarButton = isToolBarButton( c );
float focusWidth = (border instanceof FlatBorder && !isToolBarButton) ? scale( (float) getFocusWidth( c ) ) : 0;
float arc = ((border instanceof FlatButtonBorder && !isSquareButton( c )) || isToolBarButton)
? scale( (float) this.arc ) : 0;
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
float arc = FlatUIUtils.getBorderArc( c );
boolean def = isDefaultButton( c );
int x = 0;
@@ -308,7 +317,7 @@ public class FlatButtonUI
if( background == startBg && endBg != null && !startBg.equals( endBg ) )
g2.setPaint( new GradientPaint( 0, 0, startBg, 0, height, endBg ) );
else
FlatUIUtils.setColor( g2, background, def ? defaultBackground : c.getBackground() );
g2.setColor( FlatUIUtils.deriveColor( background, def ? defaultBackground : c.getBackground() ) );
FlatUIUtils.paintComponentBackground( g2, x, y, width, height, focusWidth, arc );
} finally {
@@ -401,23 +410,21 @@ public class FlatButtonUI
return new Dimension( helpButtonIcon.getIconWidth(), helpButtonIcon.getIconHeight() );
Dimension prefSize = super.getPreferredSize( c );
if ( prefSize == null )
if( prefSize == null )
return null;
// make button square if it is a icon-only button
// or apply minimum width, if not in toolbar and not a icon-only button
if( isIconOnlyButton( c ) )
prefSize.width = Math.max( prefSize.width, prefSize.height );
else if( !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
int focusWidth = getFocusWidth( c );
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) + (focusWidth * 2) ) );
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) + (focusWidth * 2) ) );
// make button square if it is a single-character button
// or apply minimum width, if not in toolbar and not a icon-only or single-character button
if( isIconOnlyOrSingleCharacterButton( c ) ) {
// make only single-character buttons square to allow non-square icon-only buttons
if( ((AbstractButton)c).getIcon() == null )
prefSize.width = Math.max( prefSize.width, prefSize.height );
} else if( !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
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;
}
protected int getFocusWidth( JComponent c ) {
return focusWidth;
}
}

View File

@@ -16,12 +16,11 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
@@ -30,29 +29,34 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
*
* <!-- 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
* @uiDefault CheckBoxMenuItem.evenHeight boolean
* @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
*/
public class FlatCheckBoxMenuItemUI
extends BasicCheckBoxMenuItemUI
{
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) {
return new FlatCheckBoxMenuItemUI();
}
@@ -61,25 +65,30 @@ public class FlatCheckBoxMenuItemUI
protected void installDefaults() {
super.installDefaults();
// scale
defaultTextIconGap = scale( defaultTextIconGap );
}
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
return e -> {
superListener.propertyChange( e );
if( e.getPropertyName() == "iconTextGap" )
defaultTextIconGap = scale( defaultTextIconGap );
};
renderer = createRenderer();
}
@Override
protected void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect, String text ) {
FlatMenuItemUI.paintText( g, menuItem, textRect, text, disabledForeground, selectionForeground );
protected void uninstallDefaults() {
super.uninstallDefaults();
renderer = null;
}
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

@@ -39,6 +39,7 @@ import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.InputMap;
import javax.swing.JButton;
@@ -46,6 +47,7 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
import javax.swing.LookAndFeel;
@@ -76,8 +78,8 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatComboBoxUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.arc int
* @uiDefault ComboBox.minimumWidth int
* @uiDefault ComboBox.editorColumns int
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color
@@ -96,8 +98,8 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatComboBoxUI
extends BasicComboBoxUI
{
protected int focusWidth;
protected int arc;
protected int minimumWidth;
protected int editorColumns;
protected String arrowType;
protected boolean isIntelliJTheme;
protected Color borderColor;
@@ -150,8 +152,8 @@ public class FlatComboBoxUI
LookAndFeel.installProperty( comboBox, "opaque", false );
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( "Component.arc" );
minimumWidth = UIManager.getInt( "ComboBox.minimumWidth" );
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
borderColor = UIManager.getColor( "Component.borderColor" );
@@ -170,7 +172,7 @@ public class FlatComboBoxUI
// scale
padding = UIScale.scale( padding );
MigLayoutVisualPadding.install( comboBox, focusWidth );
MigLayoutVisualPadding.install( comboBox );
}
@Override
@@ -249,6 +251,10 @@ public class FlatComboBoxUI
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();
}
};
}
@@ -258,18 +264,30 @@ public class FlatComboBoxUI
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
protected void configureEditor() {
super.configureEditor();
// assign a 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"
if( editor instanceof JTextComponent )
((JTextComponent)editor).setBorder( BorderFactory.createEmptyBorder() );
// explicitly make non-opaque
if( editor instanceof JComponent )
((JComponent)editor).setOpaque( false );
@@ -318,8 +336,11 @@ public class FlatComboBoxUI
@Override
public void update( Graphics g, JComponent c ) {
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) )
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
@@ -327,8 +348,6 @@ public class FlatComboBoxUI
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
int arrowX = arrowButton.getX();
int arrowWidth = arrowButton.getWidth();
boolean enabled = comboBox.isEnabled();
@@ -394,14 +413,20 @@ public class FlatComboBoxUI
@Override
public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
g.setColor( comboBox.isEnabled() ? comboBox.getBackground() : getDisabledBackground( comboBox ) );
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height );
// not necessary because already painted in update()
}
private Color getDisabledBackground( JComponent c ) {
return isIntelliJTheme ? FlatUIUtils.getParentBackground( c ) : disabledBackground;
}
@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" )
@@ -422,6 +447,18 @@ public class FlatComboBoxUI
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;
}

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

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
@@ -26,6 +27,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
@@ -83,22 +85,36 @@ public class FlatEditorPaneUI
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
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) );
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
}
@Override
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)
// and subtract 1px border line width.
// 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.
int minimumWidth = FlatUIUtils.minimumWidth( getComponent(), this.minimumWidth );
minimumWidth = FlatUIUtils.minimumWidth( c, minimumWidth );
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size;
}

View File

@@ -16,11 +16,27 @@
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.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;
/**
@@ -86,6 +102,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault FileChooser.folderNameLabelText String
* @uiDefault FileChooser.filesOfTypeLabelMnemonic String
* @uiDefault FileChooser.filesOfTypeLabelText String
*
* @uiDefault FileChooser.upFolderToolTipText String
* @uiDefault FileChooser.upFolderAccessibleName String
* @uiDefault FileChooser.homeFolderToolTipText String
@@ -97,11 +114,27 @@ import com.formdev.flatlaf.util.UIScale;
* @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 );
}
@@ -110,6 +143,40 @@ public class FlatFileChooserUI
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 );
}
}
}
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );
@@ -119,4 +186,50 @@ public class FlatFileChooserUI
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

@@ -112,6 +112,16 @@ public class FlatInternalFrameUI
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" ) );
@@ -137,16 +147,31 @@ public class FlatInternalFrameUI
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 );
g2.fill( FlatUIUtils.createRectangle(
x + insets.left - lineWidth,
y + insets.top - lineWidth,
width - insets.left - insets.right + (lineWidth * 2),
height - insets.top - insets.bottom + (lineWidth * 2),
lineWidth ) );
// 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

@@ -48,6 +48,14 @@ public class FlatLineBorder
this.lineThickness = lineThickness;
}
public Color getLineColor() {
return lineColor;
}
public float getLineThickness() {
return lineThickness;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create();

View File

@@ -16,9 +16,20 @@
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.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.basic.BasicMenuBarUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -38,4 +49,45 @@ public class FlatMenuBarUI
public static ComponentUI createUI( JComponent c ) {
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.IS_WINDOWS
? new MenuElement[] { menuBar, menu }
: new MenuElement[] { menuBar, menu, menu.getPopupMenu() } );
FlatLaf.showMnemonics( menuBar );
}
}
}
}

View File

@@ -0,0 +1,516 @@
/*
* 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;
/**
* 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() ? 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 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( 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 = 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( 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();
}
private 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 String getAcceleratorText() {
KeyStroke accelerator = menuItem.getAccelerator();
if( accelerator == null )
return null;
if( accelerator == cachedAccelerator )
return cachedAcceleratorText;
StringBuilder buf = new StringBuilder();
int modifiers = accelerator.getModifiers();
if( modifiers != 0 )
buf.append( InputEvent.getModifiersExText( modifiers ) ).append( acceleratorDelimiter );
int keyCode = accelerator.getKeyCode();
if( keyCode != 0 )
buf.append( KeyEvent.getKeyText( keyCode ) );
else
buf.append( accelerator.getKeyChar() );
cachedAccelerator = accelerator;
cachedAcceleratorText = buf.toString();
return cachedAcceleratorText;
}
//---- 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,48 +16,47 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import javax.swing.ButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
import com.formdev.flatlaf.FlatLaf;
/**
* 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
* @uiDefault MenuItem.evenHeight boolean
* @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
*/
public class FlatMenuItemUI
extends BasicMenuItemUI
{
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) {
return new FlatMenuItemUI();
}
@@ -66,42 +65,30 @@ public class FlatMenuItemUI
protected void installDefaults() {
super.installDefaults();
// scale
defaultTextIconGap = scale( defaultTextIconGap );
}
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
return e -> {
superListener.propertyChange( e );
if( e.getPropertyName() == "iconTextGap" )
defaultTextIconGap = scale( defaultTextIconGap );
};
renderer = createRenderer();
}
@Override
protected void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect, String text ) {
paintText( g, menuItem, textRect, text, disabledForeground, selectionForeground );
protected void uninstallDefaults() {
super.uninstallDefaults();
renderer = null;
}
public static void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect,
String text, Color disabledForeground, Color selectionForeground )
{
FontMetrics fm = menuItem.getFontMetrics( menuItem.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
ButtonModel model = menuItem.getModel();
g.setColor( !model.isEnabled()
? disabledForeground
: (model.isArmed() || (menuItem instanceof JMenu && model.isSelected())
? selectionForeground
: menuItem.getForeground()) );
@Override
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
}
FlatUIUtils.drawStringUnderlineCharAt( menuItem, g, text, mnemonicIndex,
textRect.x, textRect.y + fm.getAscent() );
@Override
public void paint( Graphics g, JComponent c ) {
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
acceleratorForeground, acceleratorSelectionForeground );
}
}

View File

@@ -16,16 +16,17 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import javax.swing.ButtonModel;
import javax.swing.Icon;
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;
@@ -36,30 +37,30 @@ import javax.swing.plaf.basic.BasicMenuUI;
*
* <!-- 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.evenHeight 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
* @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 MenuBar.hoverBackground Color
* @uiDefault MenuItem.iconTextGap int
* @uiDefault MenuBar.hoverBackground Color
*
* @author Karl Tauber
*/
@@ -67,6 +68,7 @@ public class FlatMenuUI
extends BasicMenuUI
{
private Color hoverBackground;
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) {
return new FlatMenuUI();
@@ -76,12 +78,12 @@ public class FlatMenuUI
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
menuItem.setRolloverEnabled( true );
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
// scale
defaultTextIconGap = scale( defaultTextIconGap );
renderer = createRenderer();
}
@Override
@@ -89,19 +91,11 @@ public class FlatMenuUI
super.uninstallDefaults();
hoverBackground = null;
renderer = null;
}
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
return e -> {
superListener.propertyChange( e );
if( e.getPropertyName() == "iconTextGap" )
defaultTextIconGap = scale( defaultTextIconGap );
};
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
@@ -130,19 +124,37 @@ public class FlatMenuUI
}
@Override
protected void paintBackground( Graphics g, JMenuItem menuItem, Color bgColor ) {
ButtonModel model = menuItem.getModel();
if( model.isArmed() || model.isSelected() ) {
super.paintBackground( g, menuItem, bgColor );
} else if( model.isRollover() && model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() ) {
FlatUIUtils.setColor( g, hoverBackground, menuItem.getBackground() );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
} else
super.paintBackground( g, menuItem, bgColor );
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
return renderer.getPreferredMenuItemSize();
}
@Override
protected void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect, String text ) {
FlatMenuItemUI.paintText( g, menuItem, textRect, text, disabledForeground, selectionForeground );
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( FlatUIUtils.deriveColor( hoverBackground, menuItem.getBackground() ) );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
} else
super.paintBackground( g, selectionBackground );
}
}
}

View File

@@ -16,7 +16,6 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -34,7 +33,6 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPasswordFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
@@ -57,8 +55,6 @@ import com.formdev.flatlaf.FlatClientProperties;
*
* <!-- FlatPasswordFieldUI -->
*
* @uiDefault TextComponent.arc int
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault PasswordField.placeholderForeground Color
@@ -70,8 +66,6 @@ import com.formdev.flatlaf.FlatClientProperties;
public class FlatPasswordFieldUI
extends BasicPasswordFieldUI
{
protected int arc;
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
protected Color placeholderForeground;
@@ -89,16 +83,14 @@ public class FlatPasswordFieldUI
super.installDefaults();
String prefix = getPropertyPrefix();
arc = UIManager.getInt( "TextComponent.arc" );
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
LookAndFeel.installProperty( getComponent(), "opaque", false );
MigLayoutVisualPadding.install( getComponent(), focusWidth );
MigLayoutVisualPadding.install( getComponent() );
}
@Override
@@ -153,14 +145,12 @@ public class FlatPasswordFieldUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
getComponent().repaint();
FlatTextFieldUI.propertyChange( getComponent(), e );
}
@Override
protected void paintSafely( Graphics g ) {
FlatTextFieldUI.paintBackground( g, getComponent(), focusWidth, arc, isIntelliJTheme );
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
paintCapsLock( g );
super.paintSafely( g );
@@ -184,18 +174,11 @@ public class FlatPasswordFieldUI
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ), c );
return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ), c );
}
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
int minimumWidth = FlatUIUtils.minimumWidth( getComponent(), this.minimumWidth );
int focusWidth = (c.getBorder() instanceof FlatBorder) ? this.focusWidth : 0;
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
return size;
return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
}
}

View File

@@ -0,0 +1,375 @@
/*
* 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.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.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;
/**
* 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
{
if( !isDropShadowPainted( owner, contents ) )
return new NonFlashingPopup( super.getPopup( owner, contents, x, y ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.IS_MAC || SystemInfo.IS_LINUX ) {
Popup popup = getHeavyWeightPopup( owner, contents, x, y );
if( popup == null )
popup = super.getPopup( owner, contents, x, y );
return new NonFlashingPopup( popup, contents );
}
// create drop shadow popup
return new DropShadowPopup( super.getPopup( owner, contents, x, y ), owner, contents );
}
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.IS_JAVA_9_OR_LATER ) {
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;
}
}
//---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup
extends Popup
{
private Popup delegate;
// heavy weight
protected Window popupWindow;
private Color oldPopupWindowBackground;
NonFlashingPopup( Popup delegate, Component contents ) {
this.delegate = delegate;
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 )
delegate.show();
}
@Override
public void hide() {
if( delegate != null ) {
delegate.hide();
delegate = 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 = getHeavyWeightPopup( owner, dropShadowPanel, x, y );
// 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 )
dropShadowDelegate.show();
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,12 +16,11 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
@@ -46,13 +45,18 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
* @uiDefault RadioButtonMenuItem.arrowIcon Icon
* @uiDefault RadioButtonMenuItem.checkIcon Icon
* @uiDefault RadioButtonMenuItem.opaque boolean
* @uiDefault RadioButtonMenuItem.evenHeight boolean
*
* <!-- FlatRadioButtonMenuItemUI -->
*
* @uiDefault MenuItem.iconTextGap int
*
* @author Karl Tauber
*/
public class FlatRadioButtonMenuItemUI
extends BasicRadioButtonMenuItemUI
{
private FlatMenuItemRenderer renderer;
public static ComponentUI createUI( JComponent c ) {
return new FlatRadioButtonMenuItemUI();
}
@@ -61,25 +65,30 @@ public class FlatRadioButtonMenuItemUI
protected void installDefaults() {
super.installDefaults();
// scale
defaultTextIconGap = scale( defaultTextIconGap );
}
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
/**
* Scale defaultTextIconGap again if iconTextGap property has changed.
*/
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
return e -> {
superListener.propertyChange( e );
if( e.getPropertyName() == "iconTextGap" )
defaultTextIconGap = scale( defaultTextIconGap );
};
renderer = createRenderer();
}
@Override
protected void paintText( Graphics g, JMenuItem menuItem, Rectangle textRect, String text ) {
FlatMenuItemUI.paintText( g, menuItem, textRect, text, disabledForeground, selectionForeground );
protected void uninstallDefaults() {
super.uninstallDefaults();
renderer = null;
}
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

@@ -23,7 +23,6 @@ import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.AbstractButton;
import javax.swing.CellRendererPane;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
@@ -122,11 +121,10 @@ public class FlatRadioButtonUI
public void paint( Graphics g, JComponent c ) {
// fill background even if not opaque if
// - contentAreaFilled is true and
// - used as cell renderer (because of selection background)
// - or if background was explicitly set to a non-UIResource color
// - if background was explicitly set to a non-UIResource color
if( !c.isOpaque() &&
((AbstractButton)c).isContentAreaFilled() &&
(c.getParent() instanceof CellRendererPane || !(c.getBackground() instanceof UIResource)))
!(c.getBackground() instanceof UIResource) )
{
g.setColor( c.getBackground() );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );

View File

@@ -16,8 +16,15 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Container;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicRootPaneUI;
/**
@@ -35,4 +42,21 @@ public class FlatRootPaneUI
instance = new FlatRootPaneUI();
return instance;
}
@Override
protected void installDefaults( JRootPane c ) {
super.installDefaults( c );
// Update background color of JFrame or JDialog parent to avoid bad border
// on HiDPI screens when switching from light to dark Laf.
// The background of JFrame is initialized in JFrame.frameInit() and
// the background of JDialog in JDialog.dialogInit(),
// but it was not updated when switching Laf.
Container parent = c.getParent();
if( parent instanceof JFrame || parent instanceof JDialog ) {
Color background = parent.getBackground();
if( background == null || background instanceof UIResource )
parent.setBackground( UIManager.getColor( "control" ) );
}
}
}

View File

@@ -16,7 +16,6 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Component;
import javax.swing.UIManager;
@@ -33,7 +32,7 @@ public class FlatRoundBorder
protected final int arc = UIManager.getInt( "Component.arc" );
@Override
protected float getArc( Component c ) {
return scale( (float) arc );
protected int getArc( Component c ) {
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc;
}
}

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -28,6 +30,7 @@ import java.util.Objects;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@@ -52,8 +55,13 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatScrollBarUI -->
*
* @uiDefault ScrollBar.trackInsets Insets
* @uiDefault ScrollBar.thumbInsets Insets
* @uiDefault ScrollBar.trackArc int
* @uiDefault ScrollBar.thumbArc int
* @uiDefault ScrollBar.hoverTrackColor Color
* @uiDefault ScrollBar.hoverThumbColor Color
* @uiDefault ScrollBar.hoverThumbWithTrack boolean
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault ScrollBar.showButtons boolean
* @uiDefault ScrollBar.buttonArrowColor Color
@@ -64,8 +72,13 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatScrollBarUI
extends BasicScrollBarUI
{
protected Insets trackInsets;
protected Insets thumbInsets;
protected int trackArc;
protected int thumbArc;
protected Color hoverTrackColor;
protected Color hoverThumbColor;
protected boolean hoverThumbWithTrack;
protected boolean showButtons;
protected String arrowType;
@@ -73,8 +86,8 @@ public class FlatScrollBarUI
protected Color buttonDisabledArrowColor;
private MouseAdapter hoverListener;
private boolean hoverTrack;
private boolean hoverThumb;
protected boolean hoverTrack;
protected boolean hoverThumb;
public static ComponentUI createUI( JComponent c ) {
return new FlatScrollBarUI();
@@ -102,8 +115,13 @@ public class FlatScrollBarUI
protected void installDefaults() {
super.installDefaults();
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
thumbArc = UIManager.getInt( "ScrollBar.thumbArc" );
hoverTrackColor = UIManager.getColor( "ScrollBar.hoverTrackColor" );
hoverThumbColor = UIManager.getColor( "ScrollBar.hoverThumbColor" );
hoverThumbWithTrack = UIManager.getBoolean( "ScrollBar.hoverThumbWithTrack" );
showButtons = UIManager.getBoolean( "ScrollBar.showButtons" );
arrowType = UIManager.getString( "Component.arrowType" );
@@ -115,6 +133,8 @@ public class FlatScrollBarUI
protected void uninstallDefaults() {
super.uninstallDefaults();
trackInsets = null;
thumbInsets = null;
hoverTrackColor = null;
hoverThumbColor = null;
@@ -171,6 +191,11 @@ public class FlatScrollBarUI
FlatArrowButton button = new FlatArrowButton( orientation,
arrowType, buttonArrowColor, buttonDisabledArrowColor, null, hoverTrackColor )
{
@Override
protected Color deriveHoverBackground( Color hoverBackground ) {
return getTrackColor( scrollbar, true ) ;
}
@Override
public Dimension getPreferredSize() {
if( isShowButtons() ) {
@@ -203,6 +228,45 @@ public class FlatScrollBarUI
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
}
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
super.paint( g, c );
}
@Override
protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) {
g.setColor( getTrackColor( c, hoverTrack ) );
paintTrackOrThumb( g, c, trackBounds, trackInsets, trackArc );
}
@Override
protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds ) {
if( thumbBounds.isEmpty() || !scrollbar.isEnabled() )
return;
g.setColor( getThumbColor( c, hoverThumb ) );
paintTrackOrThumb( g, c, thumbBounds, thumbInsets, thumbArc );
}
protected void paintTrackOrThumb( Graphics g, JComponent c, Rectangle bounds, Insets insets, int arc ) {
// rotate insets for horizontal orientation because they are given for vertical orientation
if( scrollbar.getOrientation() == JScrollBar.HORIZONTAL )
insets = new Insets( insets.right, insets.top, insets.left, insets.bottom );
// subtract insets from bounds
bounds = FlatUIUtils.subtractInsets( bounds, UIScale.scale( insets ) );
if( arc <= 0 ) {
// paint rectangle
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height );
} else {
// paint round rectangle
arc = Math.min( UIScale.scale( arc ), Math.min( bounds.width, bounds.height ) );
g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, arc, arc );
}
}
@Override
protected void paintDecreaseHighlight( Graphics g ) {
// do not paint
@@ -213,19 +277,15 @@ public class FlatScrollBarUI
// do not paint
}
@Override
protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) {
g.setColor( hoverTrack ? hoverTrackColor : trackColor );
g.fillRect( trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height );
protected Color getTrackColor( JComponent c, boolean hover ) {
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
return hover ? FlatUIUtils.deriveColor( hoverTrackColor, trackColor ) : trackColor;
}
@Override
protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds ) {
if( thumbBounds.isEmpty() || !scrollbar.isEnabled() )
return;
g.setColor( hoverThumb ? hoverThumbColor : thumbColor );
g.fillRect( thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height );
protected Color getThumbColor( JComponent c, boolean hover ) {
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
return hover ? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor ) : thumbColor;
}
@Override
@@ -276,7 +336,7 @@ public class FlatScrollBarUI
boolean inThumb = getThumbBounds().contains( x, y );
if( inTrack != hoverTrack || inThumb != hoverThumb ) {
hoverTrack = inTrack;
hoverThumb = inThumb;
hoverThumb = inThumb || (hoverThumbWithTrack && inTrack);
repaint();
}
}

View File

@@ -83,7 +83,7 @@ public class FlatScrollPaneUI
int focusWidth = UIManager.getInt( "Component.focusWidth" );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
MigLayoutVisualPadding.install( scrollpane, focusWidth );
MigLayoutVisualPadding.install( scrollpane );
}
@Override

View File

@@ -201,7 +201,7 @@ public class FlatSliderUI
}
if( coloredTrack != null ) {
FlatUIUtils.setColor( g, FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor );
g.setColor( FlatUIUtils.deriveColor( FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor ) );
((Graphics2D)g).fill( coloredTrack );
}
@@ -211,10 +211,10 @@ public class FlatSliderUI
@Override
public void paintThumb( Graphics g ) {
FlatUIUtils.setColor( g, slider.isEnabled()
g.setColor( FlatUIUtils.deriveColor( slider.isEnabled()
? (FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor))
: disabledForeground,
thumbColor );
thumbColor ) );
if( isRoundThumb() )
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );

View File

@@ -40,6 +40,7 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSpinnerUI;
import com.formdev.flatlaf.FlatClientProperties;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
@@ -56,8 +57,6 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
*
* <!-- FlatSpinnerUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.arc int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.isIntelliJTheme boolean
@@ -78,8 +77,6 @@ public class FlatSpinnerUI
{
private Handler handler;
protected int focusWidth;
protected int arc;
protected int minimumWidth;
protected String arrowType;
protected boolean isIntelliJTheme;
@@ -103,8 +100,6 @@ public class FlatSpinnerUI
LookAndFeel.installProperty( spinner, "opaque", false );
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( "Component.arc" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
@@ -121,7 +116,7 @@ public class FlatSpinnerUI
// scale
padding = scale( padding );
MigLayoutVisualPadding.install( spinner, focusWidth );
MigLayoutVisualPadding.install( spinner );
}
@Override
@@ -146,6 +141,7 @@ public class FlatSpinnerUI
super.installListeners();
addEditorFocusListener( spinner.getEditor() );
spinner.addFocusListener( getHandler() );
spinner.addPropertyChangeListener( getHandler() );
}
@@ -154,6 +150,7 @@ public class FlatSpinnerUI
super.uninstallListeners();
removeEditorFocusListener( spinner.getEditor() );
spinner.removeFocusListener( getHandler() );
spinner.removePropertyChangeListener( getHandler() );
handler = null;
@@ -211,7 +208,7 @@ public class FlatSpinnerUI
}
}
private JTextField getEditorTextField( JComponent editor ) {
private static JTextField getEditorTextField( JComponent editor ) {
return editor instanceof JSpinner.DefaultEditor
? ((JSpinner.DefaultEditor)editor).getTextField()
: null;
@@ -246,8 +243,11 @@ public class FlatSpinnerUI
@Override
public void update( Graphics g, JComponent c ) {
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) )
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
@@ -255,8 +255,6 @@ public class FlatSpinnerUI
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
Component nextButton = getHandler().nextButton;
int arrowX = nextButton.getX();
int arrowWidth = nextButton.getWidth();
@@ -328,8 +326,9 @@ public class FlatSpinnerUI
// the arrows width is the same as the inner height so that the arrows area is square
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
int innerHeight = editorSize.height + padding.top + padding.bottom;
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
return new Dimension(
Math.max( insets.left + insets.right + editorSize.width + padding.left + padding.right + innerHeight, scale( minimumWidth + (focusWidth * 2) ) ),
Math.max( insets.left + insets.right + editorSize.width + padding.left + padding.right + innerHeight, scale( minimumWidth ) + Math.round( focusWidth * 2 ) ),
insets.top + insets.bottom + innerHeight );
}
@@ -368,12 +367,14 @@ public class FlatSpinnerUI
if( editor != null )
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
int nextHeight = Math.round( buttonsRect.height / 2f );
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
if( nextButton != null )
nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight );
if( previousButton != null ) {
previousButton.setBounds( buttonsRect.x, buttonsRect.y + nextHeight,
buttonsRect.width, buttonsRect.height - nextHeight );
// for precise layout of arrows, the "previous" button has the same height
// as the "next" button and may overlap on uneven buttonsRect.height
int previousY = buttonsRect.y + buttonsRect.height - nextHeight;
previousButton.setBounds( buttonsRect.x, previousY, buttonsRect.width, nextHeight );
}
}
@@ -382,6 +383,13 @@ public class FlatSpinnerUI
@Override
public void focusGained( FocusEvent e ) {
spinner.repaint();
// if spinner gained focus, transfer it to the editor text field
if( e.getComponent() == spinner ) {
JTextField textField = getEditorTextField( spinner.getEditor() );
if( textField != null )
textField.requestFocusInWindow();
}
}
@Override
@@ -398,6 +406,14 @@ public class FlatSpinnerUI
case "enabled":
updateEditorColors();
break;
case FlatClientProperties.COMPONENT_ROUND_RECT:
spinner.repaint();
break;
case FlatClientProperties.MINIMUM_WIDTH:
spinner.revalidate();
break;
}
}
}

View File

@@ -20,11 +20,13 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -132,6 +134,12 @@ public class FlatTableUI
oldIntercellSpacing = table.getIntercellSpacing();
table.setIntercellSpacing( intercellSpacing );
}
// checkbox is non-opaque in FlatLaf and therefore would not paint selection
// --> make checkbox renderer opaque (but opaque in Metal or Windows LaF)
TableCellRenderer booleanRenderer = table.getDefaultRenderer( Boolean.class );
if( booleanRenderer instanceof JCheckBox )
((JCheckBox)booleanRenderer).setOpaque( true );
}
@Override

View File

@@ -16,10 +16,10 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.UIManager;
@@ -83,6 +83,12 @@ public class FlatTextAreaUI
inactiveBackground = null;
}
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatEditorPaneUI.propertyChange( getComponent(), e );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
@@ -100,25 +106,19 @@ public class FlatTextAreaUI
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ), c );
return applyMinimumWidth( c, super.getPreferredSize( c ) );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ), c );
return applyMinimumWidth( c, super.getMinimumSize( c ) );
}
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
private Dimension applyMinimumWidth( JComponent c, Dimension size ) {
// do not apply minimum width if JTextArea.columns is set
if( c instanceof JTextArea && ((JTextArea)c).getColumns() > 0 )
return size;
// Assume that text area is in a scroll pane (that displays the border)
// and subtract 1px border line width.
// 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.
int minimumWidth = FlatUIUtils.minimumWidth( getComponent(), this.minimumWidth );
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size;
return FlatEditorPaneUI.applyMinimumWidth( c, size, minimumWidth );
}
}

View File

@@ -16,14 +16,13 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Component;
import javax.swing.UIManager;
/**
* Border for various text components (e.g. {@link javax.swing.JTextField}).
*
* @uiDefault Component.arc int
* @uiDefault TextComponent.arc int
*
* @author Karl Tauber
*/
@@ -33,7 +32,7 @@ public class FlatTextBorder
protected final int arc = UIManager.getInt( "TextComponent.arc" );
@Override
protected float getArc( Component c ) {
return scale( (float) arc );
protected int getArc( Component c ) {
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc;
}
}

View File

@@ -32,7 +32,6 @@ import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextFieldUI;
@@ -60,8 +59,6 @@ import com.formdev.flatlaf.FlatClientProperties;
*
* <!-- FlatTextFieldUI -->
*
* @uiDefault TextComponent.arc int
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextField.placeholderForeground Color
@@ -72,8 +69,6 @@ import com.formdev.flatlaf.FlatClientProperties;
public class FlatTextFieldUI
extends BasicTextFieldUI
{
protected int arc;
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
protected Color placeholderForeground;
@@ -89,15 +84,13 @@ public class FlatTextFieldUI
super.installDefaults();
String prefix = getPropertyPrefix();
arc = UIManager.getInt( "TextComponent.arc" );
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
LookAndFeel.installProperty( getComponent(), "opaque", false );
MigLayoutVisualPadding.install( getComponent(), focusWidth );
MigLayoutVisualPadding.install( getComponent() );
}
@Override
@@ -133,14 +126,25 @@ public class FlatTextFieldUI
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
propertyChange( getComponent(), e );
}
if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
getComponent().repaint();
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case FlatClientProperties.PLACEHOLDER_TEXT:
case FlatClientProperties.COMPONENT_ROUND_RECT:
c.repaint();
break;
case FlatClientProperties.MINIMUM_WIDTH:
c.revalidate();
break;
}
}
@Override
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), focusWidth, arc, isIntelliJTheme );
paintBackground( g, getComponent(), isIntelliJTheme );
paintPlaceholder( g, getComponent(), placeholderForeground );
super.paintSafely( g );
}
@@ -150,19 +154,20 @@ public class FlatTextFieldUI
// background is painted elsewhere
}
static void paintBackground( Graphics g, JTextComponent c, int focusWidth, int arc, boolean isIntelliJTheme ) {
Border border = c.getBorder();
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme ) {
// do not paint background if:
// - not opaque and
// - border is not a flat border and
// - opaque was explicitly set (to false)
// (same behaviour as in AquaTextFieldUI)
if( !c.isOpaque() && !(border instanceof FlatBorder) && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
if( !c.isOpaque() && FlatUIUtils.getOutsideFlatBorder( c ) == null && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
return;
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 )
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
FlatUIUtils.paintParentBackground( g, c );
// paint background
@@ -170,16 +175,13 @@ public class FlatTextFieldUI
try {
FlatUIUtils.setRenderingHints( g2 );
float fFocusWidth = (border instanceof FlatBorder) ? scale( (float) focusWidth ) : 0;
float fArc = (border instanceof FlatTextBorder) ? scale( (float) arc ) : 0;
Color background = c.getBackground();
g2.setColor( !(background instanceof UIResource)
? background
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
? FlatUIUtils.getParentBackground( c )
: background) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), fFocusWidth, fArc );
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
} finally {
g2.dispose();
}
@@ -212,28 +214,29 @@ public class FlatTextFieldUI
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ), c );
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ), c );
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
}
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
// do not apply minimum width if JTextField.columns is set
if( c instanceof JTextField && ((JTextField)c).getColumns() > 0 )
return size;
// do not apply minimum width if used in combobox or spinner
Container parent = c.getParent();
if( parent instanceof JComboBox ||
parent instanceof JSpinner ||
(parent != null && parent.getParent() instanceof JSpinner) )
return size;
int minimumWidth = FlatUIUtils.minimumWidth( getComponent(), this.minimumWidth );
int focusWidth = (c.getBorder() instanceof FlatBorder) ? this.focusWidth : 0;
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
minimumWidth = FlatUIUtils.minimumWidth( c, minimumWidth );
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
size.width = Math.max( size.width, scale( minimumWidth ) + Math.round( focusWidth * 2 ) );
return size;
}
}

View File

@@ -16,9 +16,9 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
@@ -83,24 +83,20 @@ public class FlatTextPaneUI
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
}
@Override
protected void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatEditorPaneUI.propertyChange( getComponent(), e );
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) );
return FlatEditorPaneUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ) );
}
private Dimension applyMinimumWidth( Dimension size ) {
// Assume that text area is in a scroll pane (that displays the border)
// and subtract 1px border line width.
// 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.
int minimumWidth = FlatUIUtils.minimumWidth( getComponent(), this.minimumWidth );
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size;
return FlatEditorPaneUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
}
@Override

View File

@@ -138,7 +138,7 @@ public class FlatToggleButtonUI
case BUTTON_TYPE:
if( BUTTON_TYPE_TAB.equals( e.getOldValue() ) || BUTTON_TYPE_TAB.equals( e.getNewValue() ) ) {
MigLayoutVisualPadding.uninstall( b );
MigLayoutVisualPadding.install( b, getFocusWidth( b ) );
MigLayoutVisualPadding.install( b );
b.revalidate();
}
@@ -212,9 +212,4 @@ public class FlatToggleButtonUI
return super.getForeground( c );
}
@Override
protected int getFocusWidth( JComponent c ) {
return isTabButton( c ) ? 0 : super.getFocusWidth( c );
}
}

View File

@@ -27,6 +27,7 @@ import javax.swing.JComponent;
import javax.swing.JToolTip;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicToolTipUI;
import com.formdev.flatlaf.util.StringUtils;
@@ -102,7 +103,7 @@ public class FlatToolTipUI
for( String line : lines )
width = Math.max( width, SwingUtilities.computeStringWidth( fm, line ) );
return new Dimension( insets.left + width + insets.right, insets.top + height + insets.bottom );
return new Dimension( insets.left + width + insets.right + 6, insets.top + height + insets.bottom );
} else
return super.getPreferredSize( c );
}
@@ -118,8 +119,8 @@ public class FlatToolTipUI
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
int x = insets.left;
int x2 = c.getWidth() - insets.right;
int x = insets.left + 3;
int x2 = c.getWidth() - insets.right - 3;
int y = insets.top - fm.getDescent();
int lineHeight = fm.getHeight();
JComponent comp = ((JToolTip)c).getComponent();
@@ -134,6 +135,6 @@ public class FlatToolTipUI
private boolean isMultiLine( JComponent c ) {
String text = ((JToolTip)c).getTipText();
return c.getClientProperty( "html" ) == null && text != null && text.indexOf( '\n' ) >= 0;
return c.getClientProperty( BasicHTML.propertyKey ) == null && text != null && text.indexOf( '\n' ) >= 0;
}
}

View File

@@ -39,9 +39,12 @@ import java.util.function.Consumer;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.UIScale;
@@ -141,6 +144,47 @@ public class FlatUIUtils
return (KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner() == c);
}
public static boolean isRoundRect( Component c ) {
return c instanceof JComponent && FlatClientProperties.clientPropertyBoolean(
(JComponent) c, FlatClientProperties.COMPONENT_ROUND_RECT, false );
}
/**
* Returns the scaled thickness of the outer focus border for the given component.
*/
public static float getBorderFocusWidth( JComponent c ) {
FlatBorder border = getOutsideFlatBorder( c );
return (border != null)
? UIScale.scale( (float) border.getFocusWidth( c ) )
: 0;
}
/**
* Returns the scaled arc diameter of the border for the given component.
*/
public static float getBorderArc( JComponent c ) {
FlatBorder border = getOutsideFlatBorder( c );
return (border != null)
? UIScale.scale( (float) border.getArc( c ) )
: 0;
}
public static boolean hasRoundBorder( JComponent c ) {
return getBorderArc( c ) >= c.getHeight();
}
public static FlatBorder getOutsideFlatBorder( JComponent c ) {
Border border = c.getBorder();
for(;;) {
if( border instanceof FlatBorder )
return (FlatBorder) border;
else if( border instanceof CompoundBorder )
border = ((CompoundBorder)border).getOutsideBorder();
else
return null;
}
}
/**
* Sets rendering hints used for painting.
*/
@@ -150,10 +194,10 @@ public class FlatUIUtils
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
}
public static void setColor( Graphics g, Color color, Color baseColor ) {
if( color instanceof DerivedColor )
color = ((DerivedColor)color).derive( baseColor );
g.setColor( color );
public static Color deriveColor( Color color, Color baseColor ) {
return (color instanceof DerivedColor)
? ((DerivedColor)color).derive( baseColor )
: color;
}
/**
@@ -194,14 +238,9 @@ public class FlatUIUtils
if( arc > 0 && arc < UIScale.scale( 10 ) )
outerArc -= UIScale.scale( 2f );
if( outerArc < 0 )
outerArc = 0;
if( innerArc < 0 )
innerArc = 0;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new RoundRectangle2D.Float( x, y, width, height, outerArc, outerArc ), false );
path.append( new RoundRectangle2D.Float( x + ow, y + ow, width - (ow * 2), height - (ow * 2), innerArc, innerArc ), false );
path.append( createComponentRectangle( x, y, width, height, outerArc ), false );
path.append( createComponentRectangle( x + ow, y + ow, width - (ow * 2), height - (ow * 2), innerArc ), false );
g.fill( path );
}
@@ -235,19 +274,16 @@ public class FlatUIUtils
private static void paintComponentBorderImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
float x1 = x + focusWidth;
float y1 = y + focusWidth;
float width1 = width - focusWidth * 2;
float height1 = height - focusWidth * 2;
float arc2 = arc - (lineWidth * 2);
if( arc < 0 )
arc = 0;
if( arc2 < 0 )
arc2 = 0;
RoundRectangle2D.Float r1 = new RoundRectangle2D.Float(
x + focusWidth, y + focusWidth,
width - focusWidth * 2, height - focusWidth * 2, arc, arc );
RoundRectangle2D.Float r2 = new RoundRectangle2D.Float(
r1.x + lineWidth, r1.y + lineWidth,
r1.width - lineWidth * 2, r1.height - lineWidth * 2, arc2, arc2 );
Shape r1 = createComponentRectangle( x1, y1, width1, height1, arc );
Shape r2 = createComponentRectangle(
x1 + lineWidth, y1 + lineWidth,
width1 - lineWidth * 2, height1 - lineWidth * 2, arc2 );
Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
border.append( r1, false );
@@ -285,12 +321,21 @@ public class FlatUIUtils
private static void paintComponentBackgroundImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float arc )
{
if( arc < 0 )
arc = 0;
g.fill( new RoundRectangle2D.Float(
g.fill( createComponentRectangle(
x + focusWidth, y + focusWidth,
width - focusWidth * 2, height - focusWidth * 2, arc, arc ) );
width - focusWidth * 2, height - focusWidth * 2, arc ) );
}
/**
* Creates a (rounded) rectangle used to paint components (border, background, etc).
* The given arc diameter is limited to min(width,height).
*/
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {
if( arc <= 0 )
return new Rectangle2D.Float( x, y, w, h );
arc = Math.min( arc, Math.min( w, h ) );
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
}
/**
@@ -359,14 +404,12 @@ public class FlatUIUtils
if( arcTopLeft <= 0 && arcTopRight <= 0 && arcBottomLeft <= 0 && arcBottomRight <= 0 )
return new Rectangle2D.Float( x, y, width, height );
if( arcTopLeft < 0 )
arcTopLeft = 0;
if( arcTopRight < 0 )
arcTopRight = 0;
if( arcBottomLeft < 0 )
arcBottomLeft = 0;
if( arcBottomRight < 0 )
arcBottomRight = 0;
// limit arcs to min(width,height)
float maxArc = Math.min( width, height ) / 2;
arcTopLeft = (arcTopLeft > 0) ? Math.min( arcTopLeft, maxArc ) : 0;
arcTopRight = (arcTopRight > 0) ? Math.min( arcTopRight, maxArc ) : 0;
arcBottomLeft = (arcBottomLeft > 0) ? Math.min( arcBottomLeft, maxArc ) : 0;
arcBottomRight = (arcBottomRight > 0) ? Math.min( arcBottomRight, maxArc ) : 0;
float x2 = x + width;
float y2 = y + height;
@@ -427,6 +470,23 @@ public class FlatUIUtils
public static void drawStringUnderlineCharAt( JComponent c, Graphics g,
String text, int underlinedIndex, int x, int y )
{
// scale underline height if necessary
if( underlinedIndex >= 0 && UIScale.getUserScaleFactor() > 1 ) {
g = new Graphics2DProxy( (Graphics2D) g ) {
@Override
public void fillRect( int x, int y, int width, int height ) {
if( height == 1 ) {
// scale height and correct y position
// (using 0.9f so that underline height is 1 at scale factor 1.5x)
height = Math.round( UIScale.scale( 0.9f ) );
y += height - 1;
}
super.fillRect( x, y, width, height );
}
};
}
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
}

View File

@@ -69,14 +69,17 @@ public class MigLayoutVisualPadding
/**
* Convenience method that checks whether component border is a FlatBorder.
*/
public static void install( JComponent c, int focusWidth ) {
public static void install( JComponent c ) {
if( !migLayoutAvailable )
return;
install( c, c2 -> {
return (c2.getBorder() instanceof FlatBorder)
? new Insets( focusWidth, focusWidth, focusWidth, focusWidth )
: null;
FlatBorder border = FlatUIUtils.getOutsideFlatBorder( c2 );
if( border != null ) {
int focusWidth = border.getFocusWidth( c2 );
return new Insets( focusWidth, focusWidth, focusWidth, focusWidth );
} else
return null;
}, "border" );
}

View File

@@ -49,20 +49,26 @@ public class ColorFunctions
void apply( float[] hsl );
}
//---- class Lighten ------------------------------------------------------
//---- class HSLIncreaseDecrease ------------------------------------------
/**
* Increase the lightness of a color in the HSL color space by an absolute
* or relative amount.
* Increase or decrease hue, saturation or luminance of a color in the HSL color space
* by an absolute or relative amount.
*/
public static class Lighten
public static class HSLIncreaseDecrease
implements ColorFunction
{
private final float amount;
private final boolean relative;
private final boolean autoInverse;
public final int hslIndex;
public final boolean increase;
public final float amount;
public final boolean relative;
public final boolean autoInverse;
public Lighten( float amount, boolean relative, boolean autoInverse ) {
public HSLIncreaseDecrease( int hslIndex, boolean increase,
float amount, boolean relative, boolean autoInverse )
{
this.hslIndex = hslIndex;
this.increase = increase;
this.amount = amount;
this.relative = relative;
this.autoInverse = autoInverse;
@@ -70,33 +76,17 @@ public class ColorFunctions
@Override
public void apply( float[] hsl ) {
float amount2 = autoInverse && shouldInverse( hsl ) ? -amount : amount;
hsl[2] = clamp( relative
? (hsl[2] * ((100 + amount2) / 100))
: (hsl[2] + amount2) );
float amount2 = increase ? amount : -amount;
amount2 = autoInverse && shouldInverse( hsl ) ? -amount2 : amount2;
hsl[hslIndex] = clamp( relative
? (hsl[hslIndex] * ((100 + amount2) / 100))
: (hsl[hslIndex] + amount2) );
}
protected boolean shouldInverse( float[] hsl ) {
return hsl[2] >= 50;
}
}
//---- class Darken -------------------------------------------------------
/**
* Decrease the lightness of a color in the HSL color space by an absolute
* or relative amount.
*/
public static class Darken
extends Lighten
{
public Darken( float amount, boolean relative, boolean autoInverse ) {
super( -amount, relative, autoInverse );
}
@Override
protected boolean shouldInverse( float[] hsl ) {
return hsl[2] < 50;
return increase
? hsl[hslIndex] >= 50
: hsl[hslIndex] < 50;
}
}
}

View File

@@ -32,12 +32,31 @@ public class DerivedColor
{
private final ColorFunction[] functions;
private boolean hasBaseOfDefaultColor;
private int baseOfDefaultColorRGB;
public DerivedColor( Color defaultColor, ColorFunction... functions ) {
super( (defaultColor != null) ? defaultColor : Color.red );
this.functions = functions;
}
public Color derive( Color baseColor ) {
return ColorFunctions.applyFunctions( baseColor, functions );
if( (hasBaseOfDefaultColor && baseOfDefaultColorRGB == baseColor.getRGB()) || baseColor == this )
return this; // return default color
Color result = ColorFunctions.applyFunctions( baseColor, functions );
// if the result is equal to the default color, then the original base color
// was passed and we can cache this to avoid color calculations
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
hasBaseOfDefaultColor = true;
baseOfDefaultColorRGB = baseColor.getRGB();
}
return result;
}
public ColorFunction[] getFunctions() {
return functions;
}
}

View File

@@ -0,0 +1,479 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.Map;
/**
* A proxy for {@link Graphics2D}.
*
* @author Karl Tauber
*/
public class Graphics2DProxy
extends Graphics2D
{
private final Graphics2D delegate;
public Graphics2DProxy( Graphics2D delegate ) {
this.delegate = delegate;
}
@Override
public Graphics create() {
return delegate.create();
}
@Override
public Graphics create( int x, int y, int width, int height ) {
return delegate.create( x, y, width, height );
}
@Override
public Color getColor() {
return delegate.getColor();
}
@Override
public void setColor( Color c ) {
delegate.setColor( c );
}
@Override
public void setPaintMode() {
delegate.setPaintMode();
}
@Override
public void setXORMode( Color c1 ) {
delegate.setXORMode( c1 );
}
@Override
public Font getFont() {
return delegate.getFont();
}
@Override
public void setFont( Font font ) {
delegate.setFont( font );
}
@Override
public FontMetrics getFontMetrics() {
return delegate.getFontMetrics();
}
@Override
public FontMetrics getFontMetrics( Font f ) {
return delegate.getFontMetrics( f );
}
@Override
public Rectangle getClipBounds() {
return delegate.getClipBounds();
}
@Override
public void clipRect( int x, int y, int width, int height ) {
delegate.clipRect( x, y, width, height );
}
@Override
public void setClip( int x, int y, int width, int height ) {
delegate.setClip( x, y, width, height );
}
@Override
public Shape getClip() {
return delegate.getClip();
}
@Override
public void setClip( Shape clip ) {
delegate.setClip( clip );
}
@Override
public void copyArea( int x, int y, int width, int height, int dx, int dy ) {
delegate.copyArea( x, y, width, height, dx, dy );
}
@Override
public void drawLine( int x1, int y1, int x2, int y2 ) {
delegate.drawLine( x1, y1, x2, y2 );
}
@Override
public void fillRect( int x, int y, int width, int height ) {
delegate.fillRect( x, y, width, height );
}
@Override
public void drawRect( int x, int y, int width, int height ) {
delegate.drawRect( x, y, width, height );
}
@Override
public void clearRect( int x, int y, int width, int height ) {
delegate.clearRect( x, y, width, height );
}
@Override
public void drawRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight ) {
delegate.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
}
@Override
public void fillRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight ) {
delegate.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
}
@Override
public void drawOval( int x, int y, int width, int height ) {
delegate.drawOval( x, y, width, height );
}
@Override
public void fillOval( int x, int y, int width, int height ) {
delegate.fillOval( x, y, width, height );
}
@Override
public void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle ) {
delegate.drawArc( x, y, width, height, startAngle, arcAngle );
}
@Override
public void fillArc( int x, int y, int width, int height, int startAngle, int arcAngle ) {
delegate.fillArc( x, y, width, height, startAngle, arcAngle );
}
@Override
public void drawPolyline( int[] xPoints, int[] yPoints, int nPoints ) {
delegate.drawPolyline( xPoints, yPoints, nPoints );
}
@Override
public void drawPolygon( int[] xPoints, int[] yPoints, int nPoints ) {
delegate.drawPolygon( xPoints, yPoints, nPoints );
}
@Override
public void drawPolygon( Polygon p ) {
delegate.drawPolygon( p );
}
@Override
public void fillPolygon( int[] xPoints, int[] yPoints, int nPoints ) {
delegate.fillPolygon( xPoints, yPoints, nPoints );
}
@Override
public void fillPolygon( Polygon p ) {
delegate.fillPolygon( p );
}
@Override
public void drawChars( char[] data, int offset, int length, int x, int y ) {
delegate.drawChars( data, offset, length, x, y );
}
@Override
public void drawBytes( byte[] data, int offset, int length, int x, int y ) {
delegate.drawBytes( data, offset, length, x, y );
}
@Override
public boolean drawImage( Image img, int x, int y, ImageObserver observer ) {
return delegate.drawImage( img, x, y, observer );
}
@Override
public boolean drawImage( Image img, int x, int y, int width, int height, ImageObserver observer ) {
return delegate.drawImage( img, x, y, width, height, observer );
}
@Override
public boolean drawImage( Image img, int x, int y, Color bgcolor, ImageObserver observer ) {
return delegate.drawImage( img, x, y, bgcolor, observer );
}
@Override
public boolean drawImage( Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer ) {
return delegate.drawImage( img, x, y, width, height, bgcolor, observer );
}
@Override
public boolean drawImage( Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer ) {
return delegate.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
}
@Override
public boolean drawImage( Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer ) {
return delegate.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
}
@Override
public void dispose() {
delegate.dispose();
}
@Override
public void finalize() {
delegate.finalize();
}
@Override
public String toString() {
return delegate.toString();
}
@SuppressWarnings( "deprecation" )
@Override
public Rectangle getClipRect() {
return delegate.getClipRect();
}
@Override
public boolean hitClip( int x, int y, int width, int height ) {
return delegate.hitClip( x, y, width, height );
}
@Override
public Rectangle getClipBounds( Rectangle r ) {
return delegate.getClipBounds( r );
}
@Override
public void draw3DRect( int x, int y, int width, int height, boolean raised ) {
delegate.draw3DRect( x, y, width, height, raised );
}
@Override
public void fill3DRect( int x, int y, int width, int height, boolean raised ) {
delegate.fill3DRect( x, y, width, height, raised );
}
@Override
public void draw( Shape s ) {
delegate.draw( s );
}
@Override
public boolean drawImage( Image img, AffineTransform xform, ImageObserver obs ) {
return delegate.drawImage( img, xform, obs );
}
@Override
public void drawImage( BufferedImage img, BufferedImageOp op, int x, int y ) {
delegate.drawImage( img, op, x, y );
}
@Override
public void drawRenderedImage( RenderedImage img, AffineTransform xform ) {
delegate.drawRenderedImage( img, xform );
}
@Override
public void drawRenderableImage( RenderableImage img, AffineTransform xform ) {
delegate.drawRenderableImage( img, xform );
}
@Override
public void drawString( String str, int x, int y ) {
delegate.drawString( str, x, y );
}
@Override
public void drawString( String str, float x, float y ) {
delegate.drawString( str, x, y );
}
@Override
public void drawString( AttributedCharacterIterator iterator, int x, int y ) {
delegate.drawString( iterator, x, y );
}
@Override
public void drawString( AttributedCharacterIterator iterator, float x, float y ) {
delegate.drawString( iterator, x, y );
}
@Override
public void drawGlyphVector( GlyphVector g, float x, float y ) {
delegate.drawGlyphVector( g, x, y );
}
@Override
public void fill( Shape s ) {
delegate.fill( s );
}
@Override
public boolean hit( Rectangle rect, Shape s, boolean onStroke ) {
return delegate.hit( rect, s, onStroke );
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return delegate.getDeviceConfiguration();
}
@Override
public void setComposite( Composite comp ) {
delegate.setComposite( comp );
}
@Override
public void setPaint( Paint paint ) {
delegate.setPaint( paint );
}
@Override
public void setStroke( Stroke s ) {
delegate.setStroke( s );
}
@Override
public void setRenderingHint( Key hintKey, Object hintValue ) {
delegate.setRenderingHint( hintKey, hintValue );
}
@Override
public Object getRenderingHint( Key hintKey ) {
return delegate.getRenderingHint( hintKey );
}
@Override
public void setRenderingHints( Map<?, ?> hints ) {
delegate.setRenderingHints( hints );
}
@Override
public void addRenderingHints( Map<?, ?> hints ) {
delegate.addRenderingHints( hints );
}
@Override
public RenderingHints getRenderingHints() {
return delegate.getRenderingHints();
}
@Override
public void translate( int x, int y ) {
delegate.translate( x, y );
}
@Override
public void translate( double tx, double ty ) {
delegate.translate( tx, ty );
}
@Override
public void rotate( double theta ) {
delegate.rotate( theta );
}
@Override
public void rotate( double theta, double x, double y ) {
delegate.rotate( theta, x, y );
}
@Override
public void scale( double sx, double sy ) {
delegate.scale( sx, sy );
}
@Override
public void shear( double shx, double shy ) {
delegate.shear( shx, shy );
}
@Override
public void transform( AffineTransform Tx ) {
delegate.transform( Tx );
}
@Override
public void setTransform( AffineTransform Tx ) {
delegate.setTransform( Tx );
}
@Override
public AffineTransform getTransform() {
return delegate.getTransform();
}
@Override
public Paint getPaint() {
return delegate.getPaint();
}
@Override
public Composite getComposite() {
return delegate.getComposite();
}
@Override
public void setBackground( Color color ) {
delegate.setBackground( color );
}
@Override
public Color getBackground() {
return delegate.getBackground();
}
@Override
public Stroke getStroke() {
return delegate.getStroke();
}
@Override
public void clip( Shape s ) {
delegate.clip( s );
}
@Override
public FontRenderContext getFontRenderContext() {
return delegate.getFontRenderContext();
}
}

View File

@@ -19,6 +19,12 @@ public class GrayFilter
private final int origContrast;
private final int origBrightness;
public static GrayFilter createDisabledIconFilter( boolean dark ) {
return dark
? new GrayFilter( -20, -70, 100 )
: new GrayFilter( 25, -25, 100 );
}
/**
* @param brightness in range [-100..100] where 0 has no effect
* @param contrast in range [-100..100] where 0 has no effect

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf.util;
import java.awt.Image;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
/**
@@ -41,4 +43,12 @@ public class MultiResolutionImageSupport
public static Image map( Image image, Function<Image, Image> mapper ) {
return mapper.apply( image );
}
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return image;
}
public static List<Image> getResolutionVariants( Image image ) {
return Collections.singletonList( image );
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.ImageIcon;
/**
* Scales the given image icon using the system and user scale factors and
* paints the icon at system scale factor 1x. This gives best scaling quality.
* If the given image icon supports multiple resolutions, the best resolution
* variant is used. The last scaled image is cached for faster repainting.
*
* @author Karl Tauber
*/
public class ScaledImageIcon
implements Icon
{
private final ImageIcon imageIcon;
private double lastSystemScaleFactor;
private float lastUserScaleFactor;
private Image lastImage;
public ScaledImageIcon( ImageIcon imageIcon ) {
this.imageIcon = imageIcon;
}
@Override
public int getIconWidth() {
return UIScale.scale( imageIcon.getIconWidth() );
}
@Override
public int getIconHeight() {
return UIScale.scale( imageIcon.getIconHeight() );
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
/*debug
g.setColor( Color.red );
g.drawRect( x, y, getIconWidth(), getIconHeight() );
debug*/
// scale factor
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
float userScaleFactor = UIScale.getUserScaleFactor();
double scaleFactor = systemScaleFactor * userScaleFactor;
// paint input image icon if not necessary to scale
if( scaleFactor == 1 ) {
imageIcon.paintIcon( c, g, x, y );
return;
}
// paint cached scaled icon
if( systemScaleFactor == lastSystemScaleFactor &&
userScaleFactor == lastUserScaleFactor &&
lastImage != null )
{
paintLastImage( g, x, y );
return;
}
// destination image size
int destImageWidth = (int) Math.round( imageIcon.getIconWidth() * scaleFactor );
int destImageHeight = (int) Math.round( imageIcon.getIconHeight() * scaleFactor );
// get resolution variant of image if it is a multi-resolution image
Image image = MultiResolutionImageSupport.getResolutionVariant(
imageIcon.getImage(), destImageWidth, destImageHeight );
// size of image
int imageWidth = image.getWidth( null );
int imageHeight = image.getHeight( null );
// scale image if necessary to destination size
if( imageWidth != destImageWidth || imageHeight != destImageHeight ) {
// determine scaling method; default is "quality"
Object scalingInterpolation = RenderingHints.VALUE_INTERPOLATION_BICUBIC;
float imageScaleFactor = (float) destImageWidth / (float) imageWidth;
if( ((int) imageScaleFactor) == imageScaleFactor &&
imageScaleFactor > 1f &&
imageWidth <= 16 &&
imageHeight <= 16 )
{
// use "speed" scaling for small icons if the scale factor is an integer
// to avoid blurred icons
scalingInterpolation = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
}
// scale image
BufferedImage bufferedImage = image2bufferedImage( image );
image = scaleImage( bufferedImage, destImageWidth, destImageHeight, scalingInterpolation );
}
// cache image
lastSystemScaleFactor = systemScaleFactor;
lastUserScaleFactor = userScaleFactor;
lastImage = image;
// paint image
paintLastImage( g, x, y );
}
private void paintLastImage( Graphics g, int x, int y ) {
if( lastSystemScaleFactor > 1 ) {
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, 100, 100, // width and height are not used
(g2, x2, y2, width2, height2, scaleFactor2) -> {
g2.drawImage( lastImage, x2, y2, null );
} );
} else
g.drawImage( lastImage, x, y, null );
}
/**
* Scales the given image to the target dimensions.
*
* This is the same what imgscalr library (https://github.com/rkalla/imgscalr)
* would do when invoking Scalr.resize().
*/
private BufferedImage scaleImage( BufferedImage image, int targetWidth, int targetHeight,
Object scalingInterpolation )
{
BufferedImage bufferedImage = new BufferedImage( targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = bufferedImage.createGraphics();
try {
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, scalingInterpolation );
g.drawImage( image, 0, 0, targetWidth, targetHeight, null );
} finally {
g.dispose();
}
return bufferedImage;
}
private BufferedImage image2bufferedImage( Image image ) {
if( image instanceof BufferedImage )
return (BufferedImage) image;
BufferedImage bufferedImage = new BufferedImage( image.getWidth( null ),
image.getHeight( null ), BufferedImage.TYPE_INT_ARGB );
Graphics2D g = bufferedImage.createGraphics();
try {
g.drawImage( image, 0, 0, null );
} finally {
g.dispose();
}
return bufferedImage;
}
}

View File

@@ -33,6 +33,7 @@ public class SystemInfo
// OS versions
public static final boolean IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER;
public static final boolean IS_MAC_OS_10_15_CATALINA_OR_LATER;
// Java versions
public static final boolean IS_JAVA_9_OR_LATER;
@@ -53,6 +54,7 @@ public class SystemInfo
// OS versions
long osVersion = scanVersion( System.getProperty( "os.version" ) );
IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER = (IS_MAC && osVersion >= toVersion( 10, 11, 0, 0 ));
IS_MAC_OS_10_15_CATALINA_OR_LATER = (IS_MAC && osVersion >= toVersion( 10, 15, 0, 0 ));
// Java versions
long javaVersion = scanVersion( System.getProperty( "java.version" ) );

View File

@@ -21,6 +21,7 @@ import java.awt.image.AbstractMultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.MultiResolutionImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Function;
@@ -51,6 +52,18 @@ public class MultiResolutionImageSupport
: mapper.apply( image );
}
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return (image instanceof MultiResolutionImage)
? ((MultiResolutionImage)image).getResolutionVariant( destImageWidth, destImageHeight )
: image;
}
public static List<Image> getResolutionVariants( Image image ) {
return (image instanceof MultiResolutionImage)
? ((MultiResolutionImage)image).getResolutionVariants()
: Collections.singletonList( image );
}
//---- class MappedMultiResolutionImage -----------------------------------
private static class MappedMultiResolutionImage

View File

@@ -26,5 +26,7 @@ module com.formdev.flatlaf {
exports com.formdev.flatlaf.ui;
exports com.formdev.flatlaf.util;
opens com.formdev.flatlaf.resources;
uses com.formdev.flatlaf.FlatDefaultsAddon;
}

View File

@@ -27,6 +27,7 @@ Button.default.boldText=true
Component.focusWidth=2
Component.innerFocusWidth=0
Component.innerOutlineWidth=0
Component.arrowType=triangle

View File

@@ -29,6 +29,9 @@
@disabledText=#777777
@textComponentBackground=#45494A
@menuBackground=darken(@background,5%)
@menuHoverBackground=lighten(@menuBackground,10%)
@menuCheckBackground=lighten(@menuBackground,10%)
@menuCheckHoverBackground=lighten(@menuBackground,20%)
@cellFocusColor=#000000
@icon=#adadad
@@ -53,7 +56,7 @@
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=#bbbbbb
*.acceleratorForeground=darken(@foreground,15%)
*.acceleratorSelectionForeground=@selectionForeground
@@ -69,8 +72,8 @@ controlDkShadow=lighten($controlShadow,10%)
#---- Button ----
Button.background=#4c5052
Button.hoverBackground=lighten($Button.background,3%,derived autoInverse)
Button.pressedBackground=lighten($Button.background,6%,derived autoInverse)
Button.hoverBackground=lighten($Button.background,3%,derived)
Button.pressedBackground=lighten($Button.background,6%,derived)
Button.borderColor=#5e6060
Button.disabledBorderColor=#5e6060
@@ -79,16 +82,16 @@ Button.hoverBorderColor=$Button.focusedBorderColor
Button.default.background=#365880
Button.default.foreground=#bbbbbb
Button.default.hoverBackground=lighten($Button.default.background,3%,derived autoInverse)
Button.default.pressedBackground=lighten($Button.default.background,6%,derived autoInverse)
Button.default.hoverBackground=lighten($Button.default.background,3%,derived)
Button.default.pressedBackground=lighten($Button.default.background,6%,derived)
Button.default.borderColor=#4c708c
Button.default.hoverBorderColor=#537699
Button.default.focusedBorderColor=#537699
Button.default.focusColor=#43688c
Button.default.boldText=true
Button.toolbar.hoverBackground=lighten($Button.background,1%,derived autoInverse)
Button.toolbar.pressedBackground=lighten($Button.background,4%,derived autoInverse)
Button.toolbar.hoverBackground=lighten($Button.background,1%,derived)
Button.toolbar.pressedBackground=lighten($Button.background,4%,derived)
#---- CheckBox ----
@@ -101,8 +104,8 @@ CheckBox.icon.hoverBorderColor=$CheckBox.icon.focusedBorderColor
CheckBox.icon.selectedFocusedBorderColor=#466D94
CheckBox.icon.background=#43494A
CheckBox.icon.disabledBackground=@background
CheckBox.icon.hoverBackground=lighten($CheckBox.icon.background,3%,derived autoInverse)
CheckBox.icon.pressedBackground=lighten($CheckBox.icon.background,6%,derived autoInverse)
CheckBox.icon.hoverBackground=lighten($CheckBox.icon.background,3%,derived)
CheckBox.icon.pressedBackground=lighten($CheckBox.icon.background,6%,derived)
CheckBox.icon.selectedBackground=#43494A
CheckBox.icon.checkmarkColor=#A7A7A7
CheckBox.icon.disabledCheckmarkColor=#606060
@@ -127,6 +130,12 @@ Component.focusColor=#3d6185
Component.linkColor=#589df6
Component.grayFilter=-20,-70,100
Component.error.borderColor=desaturate($Component.error.focusedBorderColor,25%)
Component.error.focusedBorderColor=#8b3c3c
Component.warning.borderColor=darken(desaturate($Component.warning.focusedBorderColor,20%),10%)
Component.warning.focusedBorderColor=#ac7920
Component.custom.borderColor=desaturate(#f00,50%,relative derived noAutoInverse)
#---- Desktop ----
@@ -145,16 +154,19 @@ InternalFrame.activeTitleForeground=@foreground
InternalFrame.inactiveTitleBackground=darken(@background,5%)
InternalFrame.inactiveTitleForeground=@disabledText
InternalFrame.activeBorderColor=lighten($Component.borderColor,10%)
InternalFrame.inactiveBorderColor=$Component.borderColor
InternalFrame.activeBorderColor=darken(@background,7%)
InternalFrame.inactiveBorderColor=darken(@background,3%)
InternalFrame.buttonHoverBackground=lighten($InternalFrame.activeTitleBackground,10%,derived autoInverse)
InternalFrame.buttonPressedBackground=lighten($InternalFrame.activeTitleBackground,20%,derived autoInverse)
InternalFrame.buttonHoverBackground=lighten($InternalFrame.activeTitleBackground,10%,derived)
InternalFrame.buttonPressedBackground=lighten($InternalFrame.activeTitleBackground,20%,derived)
InternalFrame.closeHoverBackground=lazy(Actions.Red)
InternalFrame.closePressedBackground=darken(Actions.Red,10%,lazy)
InternalFrame.closeHoverForeground=#fff
InternalFrame.closePressedForeground=#fff
InternalFrame.activeDropShadowOpacity=0.5
InternalFrame.inactiveDropShadowOpacity=0.75
#---- List ----
@@ -170,7 +182,7 @@ Menu.icon.disabledArrowColor=#606060
#---- MenuBar ----
MenuBar.borderColor=#515151
MenuBar.hoverBackground=lighten($MenuBar.background,10%)
MenuBar.hoverBackground=@menuHoverBackground
#---- MenuItemCheckBox ----
@@ -184,6 +196,12 @@ MenuItemCheckBox.icon.disabledCheckmarkColor=#606060
PasswordField.capsLockIconColor=#ffffff64
#---- Popup ----
Popup.dropShadowColor=#000
Popup.dropShadowOpacity=0.25
#---- PopupMenu ----
PopupMenu.borderColor=#5e5e5e
@@ -199,10 +217,10 @@ ProgressBar.selectionBackground=@foreground
#---- ScrollBar ----
ScrollBar.track=#3F4244
ScrollBar.thumb=lighten($ScrollBar.track,10%)
ScrollBar.hoverTrackColor=lighten($ScrollBar.track,4%)
ScrollBar.hoverThumbColor=lighten($ScrollBar.thumb,10%)
ScrollBar.track=lighten(@background,1%,derived noAutoInverse)
ScrollBar.thumb=lighten($ScrollBar.track,10%,derived noAutoInverse)
ScrollBar.hoverTrackColor=lighten($ScrollBar.track,4%,derived noAutoInverse)
ScrollBar.hoverThumbColor=lighten($ScrollBar.thumb,10%,derived noAutoInverse)
#---- Separator ----
@@ -215,7 +233,7 @@ Separator.foreground=#515151
Slider.trackColor=#646464
Slider.thumbColor=#A6A6A6
Slider.tickColor=#888888
Slider.hoverColor=darken($Slider.thumbColor,15%,derived autoInverse)
Slider.hoverColor=darken($Slider.thumbColor,15%,derived)
Slider.disabledForeground=#4c5052
@@ -250,11 +268,11 @@ TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
#---- ToggleButton ----
ToggleButton.selectedBackground=lighten($ToggleButton.background,10%,derived autoInverse)
ToggleButton.selectedBackground=lighten($ToggleButton.background,10%,derived)
ToggleButton.selectedForeground=@foreground
ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,3%,derived autoInverse)
ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,3%,derived)
ToggleButton.toolbar.selectedBackground=lighten($ToggleButton.background,7%,derived autoInverse)
ToggleButton.toolbar.selectedBackground=lighten($ToggleButton.background,7%,derived)
#---- ToolTip ----

View File

@@ -48,6 +48,7 @@ CheckBox.icon.selectedPressedBackground=#72A1D4
Component.focusWidth=2
Component.innerFocusWidth=0
Component.innerOutlineWidth=0
Component.arrowType=triangle

View File

@@ -64,7 +64,7 @@ ViewportUI=com.formdev.flatlaf.ui.FlatViewportUI
#---- variables ----
@textComponentMargin=2,6,2,6
@menuItemMargin=2,8,2,8
@menuItemMargin=3,6,3,6
#---- system colors ----
@@ -178,6 +178,8 @@ ColorChooser.swatchesDefaultRecentColor=$control
ComboBox.border=com.formdev.flatlaf.ui.FlatRoundBorder
ComboBox.padding=2,6,2,6
ComboBox.minimumWidth=72
ComboBox.editorColumns=0
[mac]ComboBox.showPopupOnNavigation=true
@@ -185,6 +187,7 @@ ComboBox.padding=2,6,2,6
Component.focusWidth=0
Component.innerFocusWidth={float}0.5
Component.innerOutlineWidth={float}1
Component.arc=5
Component.minimumWidth=64
Component.arrowType=chevron
@@ -258,9 +261,16 @@ InternalFrame.buttonSize=24,24
InternalFrame.closeIcon=com.formdev.flatlaf.icons.FlatInternalFrameCloseIcon
InternalFrame.iconifyIcon=com.formdev.flatlaf.icons.FlatInternalFrameIconifyIcon
InternalFrame.maximizeIcon=com.formdev.flatlaf.icons.FlatInternalFrameMaximizeIcon
InternalFrame.minimizeIcon=com.formdev.flatlaf.icons.FlatInternalFrameMinimizeIcon
InternalFrame.minimizeIcon=com.formdev.flatlaf.icons.FlatInternalFrameRestoreIcon
InternalFrame.windowBindings=null
# drop shadow
InternalFrame.dropShadowPainted=true
InternalFrame.activeDropShadowColor=null
InternalFrame.activeDropShadowInsets=5,5,6,6
InternalFrame.inactiveDropShadowColor=null
InternalFrame.inactiveDropShadowInsets=3,3,4,4
#---- InternalFrameTitlePane ----
@@ -299,7 +309,7 @@ Menu.background=@menuBackground
MenuBar.border=com.formdev.flatlaf.ui.FlatMenuBarBorder
MenuBar.background=@menuBackground
MenuBar.itemMargins=3,3,3,3
MenuBar.itemMargins=3,8,3,8
#---- MenuItem ----
@@ -311,9 +321,23 @@ MenuItem.margin=@menuItemMargin
MenuItem.opaque=false
MenuItem.borderPainted=true
MenuItem.background=@menuBackground
MenuItem.checkBackground=@menuCheckBackground
MenuItem.checkMargins=2,2,2,2
MenuItem.minimumWidth=72
MenuItem.minimumIconSize=16,16
MenuItem.iconTextGap=6
MenuItem.textAcceleratorGap=24
MenuItem.textNoAcceleratorGap=6
MenuItem.acceleratorArrowGap=2
MenuItem.acceleratorDelimiter=-
[mac]MenuItem.acceleratorDelimiter=
# for MenuItem.selectionType=underline
MenuItem.underlineSelectionBackground=@menuHoverBackground
MenuItem.underlineSelectionCheckBackground=@menuCheckHoverBackground
MenuItem.underlineSelectionColor=$TabbedPane.underlineColor
MenuItem.underlineSelectionHeight=3
#---- OptionPane ----
@@ -348,6 +372,12 @@ PasswordField.echoChar=\u2022
PasswordField.capsLockIcon=com.formdev.flatlaf.icons.FlatCapsLockIcon
#---- Popup ----
Popup.dropShadowPainted=true
Popup.dropShadowInsets=-4,-4,4,4
#---- PopupMenu ----
PopupMenu.border=com.formdev.flatlaf.ui.FlatPopupMenuBorder
@@ -396,12 +426,24 @@ RadioButtonMenuItem.background=@menuBackground
#---- ScrollBar ----
ScrollBar.width=10
ScrollBar.trackInsets=0,0,0,0
ScrollBar.thumbInsets=0,0,0,0
ScrollBar.trackArc=0
ScrollBar.thumbArc=0
ScrollBar.hoverThumbWithTrack=false
ScrollBar.showButtons=false
ScrollBar.squareButtons=false
ScrollBar.buttonArrowColor=$ComboBox.buttonArrowColor
ScrollBar.buttonDisabledArrowColor=$ComboBox.buttonDisabledArrowColor
ScrollBar.allowsAbsolutePositioning=true
[mac]ScrollBar.thumbInsets=2,2,2,2
[mac]ScrollBar.thumbArc=999
[mac]ScrollBar.hoverThumbWithTrack=true
[linux]ScrollBar.thumbInsets=2,2,2,2
[linux]ScrollBar.thumbArc=999
#---- ScrollPane ----
@@ -577,14 +619,6 @@ ToolBar.spacingBorder=$Button.toolbar.spacingInsets
ToolTipManager.enableToolTipMode=activeApplication
#---- ToolTip ----
ToolTip.border=4,6,4,6,$Component.borderColor
ToolTip.borderInactive=null
ToolTip.backgroundInactive=$ToolTip.background
ToolTip.foregroundInactive=@disabledText
#---- Tree ----
Tree.border=1,1,1,1

View File

@@ -29,6 +29,9 @@
@disabledText=#8C8C8C
@textComponentBackground=#ffffff
@menuBackground=#fff
@menuHoverBackground=darken(@menuBackground,10%)
@menuCheckBackground=darken(@menuBackground,10%)
@menuCheckHoverBackground=darken(@menuBackground,20%)
@cellFocusColor=#000000
@icon=#afafaf
@@ -53,7 +56,7 @@
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=#505050
*.acceleratorForeground=lighten(@foreground,30%)
*.acceleratorSelectionForeground=@selectionForeground
@@ -70,8 +73,8 @@ controlDkShadow=darken($controlShadow,15%)
Button.background=#ffffff
Button.focusedBackground=#e3f1fa
Button.hoverBackground=darken($Button.background,3%,derived autoInverse)
Button.pressedBackground=darken($Button.background,10%,derived autoInverse)
Button.hoverBackground=darken($Button.background,3%,derived)
Button.pressedBackground=darken($Button.background,10%,derived)
Button.borderColor=$Component.borderColor
Button.disabledBorderColor=$Component.disabledBorderColor
@@ -89,8 +92,8 @@ Button.default.focusedBorderColor=$Button.focusedBorderColor
Button.default.focusColor=$Component.focusColor
Button.default.borderWidth=2
Button.toolbar.hoverBackground=darken($Button.background,12%,derived autoInverse)
Button.toolbar.pressedBackground=darken($Button.background,15%,derived autoInverse)
Button.toolbar.hoverBackground=darken($Button.background,12%,derived)
Button.toolbar.pressedBackground=darken($Button.background,15%,derived)
#---- CheckBox ----
@@ -129,6 +132,12 @@ Component.focusColor=#97c3f3
Component.linkColor=#2470B3
Component.grayFilter=25,-25,100
Component.error.borderColor=lighten(desaturate($Component.error.focusedBorderColor,20%),25%)
Component.error.focusedBorderColor=#e53e4d
Component.warning.borderColor=lighten(saturate($Component.warning.focusedBorderColor,25%),20%)
Component.warning.focusedBorderColor=#e2a53a
Component.custom.borderColor=lighten(desaturate(#f00,20%,derived noAutoInverse),25%,derived noAutoInverse)
#---- Desktop ----
@@ -155,13 +164,16 @@ InternalFrame.inactiveTitleForeground=@disabledText
InternalFrame.activeBorderColor=darken($Component.borderColor,20%)
InternalFrame.inactiveBorderColor=$Component.borderColor
InternalFrame.buttonHoverBackground=darken($InternalFrame.activeTitleBackground,10%,derived autoInverse)
InternalFrame.buttonPressedBackground=darken($InternalFrame.activeTitleBackground,20%,derived autoInverse)
InternalFrame.buttonHoverBackground=darken($InternalFrame.activeTitleBackground,10%,derived)
InternalFrame.buttonPressedBackground=darken($InternalFrame.activeTitleBackground,20%,derived)
InternalFrame.closeHoverBackground=lazy(Actions.Red)
InternalFrame.closePressedBackground=darken(Actions.Red,10%,lazy)
InternalFrame.closeHoverForeground=#fff
InternalFrame.closePressedForeground=#fff
InternalFrame.activeDropShadowOpacity=0.25
InternalFrame.inactiveDropShadowOpacity=0.5
#---- List ----
@@ -177,7 +189,7 @@ Menu.icon.disabledArrowColor=#ABABAB
#---- MenuBar ----
MenuBar.borderColor=#cdcdcd
MenuBar.hoverBackground=darken($MenuBar.background,10%)
MenuBar.hoverBackground=@menuHoverBackground
#---- MenuItemCheckBox ----
@@ -191,6 +203,12 @@ MenuItemCheckBox.icon.disabledCheckmarkColor=#ABABAB
PasswordField.capsLockIconColor=#00000064
#---- Popup ----
Popup.dropShadowColor=#000
Popup.dropShadowOpacity=0.15
#---- PopupMenu ----
PopupMenu.borderColor=#adadad
@@ -206,10 +224,10 @@ ProgressBar.selectionBackground=@foreground
#---- ScrollBar ----
ScrollBar.track=#F5F5F5
ScrollBar.thumb=darken($ScrollBar.track,10%)
ScrollBar.hoverTrackColor=darken($ScrollBar.track,3%)
ScrollBar.hoverThumbColor=darken($ScrollBar.thumb,10%)
ScrollBar.track=lighten(@background,1%,derived noAutoInverse)
ScrollBar.thumb=darken($ScrollBar.track,10%,derived noAutoInverse)
ScrollBar.hoverTrackColor=darken($ScrollBar.track,3%,derived noAutoInverse)
ScrollBar.hoverThumbColor=darken($ScrollBar.thumb,10%,derived noAutoInverse)
#---- Separator ----
@@ -222,7 +240,7 @@ Separator.foreground=#d1d1d1
Slider.trackColor=#c4c4c4
Slider.thumbColor=#6e6e6e
Slider.tickColor=#888888
Slider.hoverColor=lighten($Slider.thumbColor,15%,derived autoInverse)
Slider.hoverColor=lighten($Slider.thumbColor,15%,derived)
Slider.disabledForeground=#c0c0c0
@@ -257,15 +275,16 @@ TableHeader.bottomSeparatorColor=$TableHeader.separatorColor
#---- ToggleButton ----
ToggleButton.selectedBackground=darken($ToggleButton.background,20%,derived autoInverse)
ToggleButton.selectedBackground=darken($ToggleButton.background,20%,derived)
ToggleButton.selectedForeground=@foreground
ToggleButton.disabledSelectedBackground=darken($ToggleButton.background,13%,derived autoInverse)
ToggleButton.disabledSelectedBackground=darken($ToggleButton.background,13%,derived)
ToggleButton.toolbar.selectedBackground=$ToggleButton.selectedBackground
#---- ToolTip ----
ToolTip.border=4,6,4,6,$InternalFrame.activeBorderColor
ToolTip.background=#fafafa

View File

@@ -39,8 +39,8 @@ HelpButton.hoverBorderColor=null
ToggleButton.startBackground=$ToggleButton.background
ToggleButton.endBackground=$ToggleButton.background
[dark]ToggleButton.selectedBackground=lighten($ToggleButton.background,15%,derived autoInverse)
[dark]ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,5%,derived autoInverse)
[dark]ToggleButton.selectedBackground=lighten($ToggleButton.background,15%,derived)
[dark]ToggleButton.disabledSelectedBackground=lighten($ToggleButton.background,5%,derived)
#---- theme specific ----

View File

@@ -0,0 +1,48 @@
#
# 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.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic=Look &In:
FileChooser.saveInLabelText=Save In:
FileChooser.fileNameLabel.textAndMnemonic=File &Name:
FileChooser.folderNameLabel.textAndMnemonic=Folder &name:
FileChooser.filesOfTypeLabel.textAndMnemonic=Files of &Type:
# toolbar
FileChooser.upFolderToolTipText=Up One Level
FileChooser.upFolderAccessibleName=Up
FileChooser.homeFolderToolTipText=Home
FileChooser.homeFolderAccessibleName=Home
FileChooser.newFolderToolTipText=Create New Folder
FileChooser.newFolderAccessibleName=New Folder
FileChooser.listViewButtonToolTipText=List
FileChooser.listViewButtonAccessibleName=List
FileChooser.detailsViewButtonToolTipText=Details
FileChooser.detailsViewButtonAccessibleName=Details
# details table header
FileChooser.fileNameHeaderText=Name
FileChooser.fileSizeHeaderText=Size
FileChooser.fileTypeHeaderText=Type
FileChooser.fileDateHeaderText=Modified
FileChooser.fileAttrHeaderText=Attributes
# popup menu
FileChooser.viewMenuLabelText=View
FileChooser.refreshActionLabelText=Refresh
FileChooser.newFolderActionLabelText=New Folder
FileChooser.listViewActionLabelText=List
FileChooser.detailsViewActionLabelText=Details

View File

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

View File

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

View File

@@ -27,11 +27,18 @@ plugins {
id( "com.jfrog.artifactory" )
}
repositories {
maven {
// for using MigLayout snapshot
url = uri( "https://oss.sonatype.org/content/repositories/snapshots/" )
}
}
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( project( ":flatlaf-extras" ) )
implementation( project( ":flatlaf-intellij-themes" ) )
implementation( "com.miglayout:miglayout-swing:5.2" )
implementation( "com.miglayout:miglayout-swing:5.3-SNAPSHOT" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
}
@@ -49,6 +56,7 @@ tasks {
}
exclude( "module-info.class" )
exclude( "META-INF/versions/*/module-info.class" )
// include all dependencies in jar
from( {

View File

@@ -114,6 +114,14 @@ class BasicComponentsPanel
JScrollPane scrollPane12 = new JScrollPane();
JTextPane textPane4 = new JTextPane();
JTextPane textPane5 = new JTextPane();
JLabel label3 = new JLabel();
JTextField textField5 = new JTextField();
JComboBox<String> comboBox7 = new JComboBox<>();
JSpinner spinner3 = new JSpinner();
JLabel label4 = new JLabel();
JTextField textField7 = new JTextField();
JComboBox<String> comboBox8 = new JComboBox<>();
JSpinner spinner4 = new JSpinner();
JPopupMenu popupMenu1 = new JPopupMenu();
JMenuItem cutMenuItem = new JMenuItem();
JMenuItem copyMenuItem = new JMenuItem();
@@ -141,6 +149,8 @@ class BasicComponentsPanel
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]"));
//---- labelLabel ----
@@ -179,9 +189,8 @@ class BasicComponentsPanel
add(button5, "cell 3 1");
//---- button6 ----
button6.setText("square");
button6.setEnabled(false);
button6.putClientProperty("JButton.buttonType", "square");
button6.setText("round");
button6.putClientProperty("JButton.buttonType", "roundRect");
add(button6, "cell 4 1");
//---- button3 ----
@@ -597,19 +606,61 @@ class BasicComponentsPanel
textPane5.setText("no scroll pane");
add(textPane5, "cell 5 11,growx");
//---- label3 ----
label3.setText("Error hints:");
add(label3, "cell 0 12");
//---- textField5 ----
textField5.putClientProperty("JComponent.outline", "error");
add(textField5, "cell 1 12,growx");
//---- comboBox7 ----
comboBox7.putClientProperty("JComponent.outline", "error");
comboBox7.setModel(new DefaultComboBoxModel<>(new String[] {
"editable"
}));
comboBox7.setEditable(true);
add(comboBox7, "cell 2 12,growx");
//---- spinner3 ----
spinner3.putClientProperty("JComponent.outline", "error");
add(spinner3, "cell 3 12,growx");
//---- label4 ----
label4.setText("Warning hints:");
add(label4, "cell 0 13");
//---- textField7 ----
textField7.putClientProperty("JComponent.outline", "warning");
add(textField7, "cell 1 13,growx");
//---- comboBox8 ----
comboBox8.putClientProperty("JComponent.outline", "warning");
comboBox8.setModel(new DefaultComboBoxModel<>(new String[] {
"not editable"
}));
add(comboBox8, "cell 2 13,growx");
//---- spinner4 ----
spinner4.putClientProperty("JComponent.outline", "warning");
add(spinner4, "cell 3 13,growx");
//======== popupMenu1 ========
{
//---- cutMenuItem ----
cutMenuItem.setText("Cut");
cutMenuItem.setMnemonic('C');
popupMenu1.add(cutMenuItem);
//---- copyMenuItem ----
copyMenuItem.setText("Copy");
copyMenuItem.setMnemonic('O');
popupMenu1.add(copyMenuItem);
//---- pasteMenuItem ----
pasteMenuItem.setText("Paste");
pasteMenuItem.setMnemonic('P');
popupMenu1.add(pasteMenuItem);
}
// JFormDesigner - End of component initialization //GEN-END:initComponents

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.0.0.194" Java: "13.0.1" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.1.0.272" Java: "13.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[][][][][][]"
"$rowConstraints": "[][][][][][][][][][][][]"
"$rowConstraints": "[][][][][][][][][][][][]para[][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -63,9 +63,8 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button6"
"text": "square"
"enabled": false
"$client.JButton.buttonType": "square"
"text": "round"
"$client.JButton.buttonType": "roundRect"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 1"
} )
@@ -592,23 +591,83 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 11,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "Error hints:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField5"
"$client.JComponent.outline": "error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12,growx"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox7"
"$client.JComponent.outline": "error"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "editable"
addElement( "editable" )
}
"editable": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 12,growx"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner3"
"$client.JComponent.outline": "error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 12,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"text": "Warning hints:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField7"
"$client.JComponent.outline": "warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13,growx"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox8"
"$client.JComponent.outline": "warning"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "not editable"
addElement( "not editable" )
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 13,growx"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner4"
"$client.JComponent.outline": "warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 13,growx"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 790, 440 )
"size": new java.awt.Dimension( 920, 440 )
} )
add( new FormContainer( "javax.swing.JPopupMenu", new FormLayoutManager( class javax.swing.JPopupMenu ) ) {
name: "popupMenu1"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "cutMenuItem"
"text": "Cut"
"mnemonic": 67
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "copyMenuItem"
"text": "Copy"
"mnemonic": 79
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "pasteMenuItem"
"text": "Paste"
"mnemonic": 80
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 500 )

View File

@@ -37,7 +37,7 @@ import net.miginfocom.swing.*;
class ControlBar
extends JPanel
{
private JFrame frame;
private DemoFrame frame;
private JTabbedPane tabbedPane;
ControlBar() {
@@ -63,7 +63,7 @@ class ControlBar
(SystemInfo.IS_LINUX && className.equals( "com.sun.java.swing.plaf.gtk.GTKLookAndFeel") ) )
name += " (F9)";
else if( className.equals( MetalLookAndFeel.class.getName() ) )
name += " (F10)";
name += " (F12)";
else if( className.equals( NimbusLookAndFeel.class.getName() ) )
name += " (F11)";
@@ -78,6 +78,9 @@ class ControlBar
// update info label because user scale factor may change
updateInfoLabel();
// update "Font" menu
frame.updateFontMenuItems();
// this is necessary because embedded JOptionPane's "steal" the default button
frame.getRootPane().setDefaultButton( closeButton );
} );
@@ -90,7 +93,15 @@ class ControlBar
} );
}
void initialize( JFrame frame, JTabbedPane tabbedPane ) {
@Override
public void updateUI() {
super.updateUI();
if( infoLabel != null )
updateInfoLabel();
}
void initialize( DemoFrame frame, JTabbedPane tabbedPane ) {
this.frame = frame;
this.tabbedPane = tabbedPane;
@@ -106,7 +117,7 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.IS_LINUX )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( KeyEvent.VK_F10, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
// register ESC key to close frame
@@ -139,12 +150,20 @@ class ControlBar
}
private void updateInfoLabel() {
String javaVendor = System.getProperty( "java.vendor" );
if( "Oracle Corporation".equals( javaVendor ) )
javaVendor = null;
double systemScaleFactor = UIScale.getSystemScaleFactor( getGraphicsConfiguration() );
float userScaleFactor = UIScale.getUserScaleFactor();
Font font = UIManager.getFont( "Label.font" );
String newInfo = "(Java " + System.getProperty( "java.version" )
+ (javaVendor != null ? ("; " + javaVendor) : "")
+ (systemScaleFactor != 1 ? ("; system scale factor " + systemScaleFactor) : "")
+ (userScaleFactor != 1 ? ("; user scale factor " + userScaleFactor) : "")
+ (systemScaleFactor == 1 && userScaleFactor == 1 ? "; no scaling" : "")
+ "; " + font.getFamily() + " " + font.getSize()
+ (font.isBold() ? " BOLD" : "")
+ (font.isItalic() ? " ITALIC" : "")
+ ")";
if( !newInfo.equals( infoLabel.getText() ) )
@@ -177,6 +196,10 @@ class ControlBar
// change look and feel
UIManager.setLookAndFeel( lafClassName );
// clear custom default font when switching to non-FlatLaf LaF
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) )
UIManager.put( "defaultFont", null );
// update all components
FlatLaf.updateUI();
@@ -208,6 +231,9 @@ class ControlBar
private void enabledChanged() {
enabledDisable( tabbedPane, enabledCheckBox.isSelected() );
// repainting whole tabbed pane is faster than repainting many individual components
tabbedPane.repaint();
}
private void enabledDisable( Container container, boolean enabled ) {

View File

@@ -18,9 +18,13 @@ package com.formdev.flatlaf.demo;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import net.miginfocom.swing.*;
@@ -31,10 +35,18 @@ import net.miginfocom.swing.*;
class DemoFrame
extends JFrame
{
private final String[] availableFontFamilyNames;
private int initialFontMenuItemCount = -1;
DemoFrame() {
int tabIndex = DemoPrefs.getState().getInt( FlatLafDemo.KEY_TAB, 0 );
availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames().clone();
Arrays.sort( availableFontFamilyNames );
initComponents();
updateFontMenuItems();
controlBar.initialize( this, tabbedPane );
if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() )
@@ -59,11 +71,20 @@ class DemoFrame
} );
}
private void underlineMenuSelection() {
UIManager.put( "MenuItem.selectionType", underlineMenuSelectionMenuItem.isSelected() ? "underline" : null );
}
private void alwaysShowMnemonics() {
UIManager.put( "Component.hideMnemonics", !alwaysShowMnemonicsMenuItem.isSelected() );
repaint();
}
private void fontFamilyChanged( ActionEvent e ) {
String fontFamily = e.getActionCommand();
Font font = UIManager.getFont( "defaultFont" );
Font newFont = new Font( fontFamily, font.getStyle(), font.getSize() );
Font newFont = StyleContext.getDefaultStyleContext().getFont( fontFamily, font.getStyle(), font.getSize() );
UIManager.put( "defaultFont", newFont );
FlatLaf.updateUI();
@@ -81,6 +102,7 @@ class DemoFrame
private void restoreFont() {
UIManager.put( "defaultFont", null );
updateFontMenuItems();
FlatLaf.updateUI();
}
@@ -89,17 +111,80 @@ class DemoFrame
Font newFont = font.deriveFont( (float) (font.getSize() + 1) );
UIManager.put( "defaultFont", newFont );
updateFontMenuItems();
FlatLaf.updateUI();
}
private void decrFont() {
Font font = UIManager.getFont( "defaultFont" );
Font newFont = font.deriveFont( (float) Math.max( font.getSize() - 1, 8 ) );
Font newFont = font.deriveFont( (float) Math.max( font.getSize() - 1, 10 ) );
UIManager.put( "defaultFont", newFont );
updateFontMenuItems();
FlatLaf.updateUI();
}
void updateFontMenuItems() {
if( initialFontMenuItemCount < 0 )
initialFontMenuItemCount = fontMenu.getItemCount();
else {
// remove old font items
for( int i = fontMenu.getItemCount() - 1; i >= initialFontMenuItemCount; i-- )
fontMenu.remove( i );
}
// get current font
Font currentFont = UIManager.getFont( "Label.font" );
String currentFamily = currentFont.getFamily();
String currentSize = Integer.toString( currentFont.getSize() );
// add font families
fontMenu.addSeparator();
ArrayList<String> families = new ArrayList<>( Arrays.asList(
"Arial", "Cantarell", "Comic Sans MS", "Courier New", "DejaVu Sans",
"Dialog", "Liberation Sans", "Monospaced", "Noto Sans", "Roboto",
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
if( !families.contains( currentFamily ) )
families.add( currentFamily );
families.sort( String.CASE_INSENSITIVE_ORDER );
ButtonGroup familiesGroup = new ButtonGroup();
for( String family : families ) {
if( Arrays.binarySearch( availableFontFamilyNames, family ) < 0 )
continue; // not available
JCheckBoxMenuItem item = new JCheckBoxMenuItem( family );
item.setSelected( family.equals( currentFamily ) );
item.addActionListener( this::fontFamilyChanged );
fontMenu.add( item );
familiesGroup.add( item );
}
// add font sizes
fontMenu.addSeparator();
ArrayList<String> sizes = new ArrayList<>( Arrays.asList(
"10", "12", "14", "16", "18", "20", "24", "28" ) );
if( !sizes.contains( currentSize ) )
sizes.add( currentSize );
sizes.sort( String.CASE_INSENSITIVE_ORDER );
ButtonGroup sizesGroup = new ButtonGroup();
for( String size : sizes ) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem( size );
item.setSelected( size.equals( currentSize ) );
item.addActionListener( this::fontSizeChanged );
fontMenu.add( item );
sizesGroup.add( item );
}
// enabled/disable items
boolean enabled = UIManager.getLookAndFeel() instanceof FlatLaf;
for( Component item : fontMenu.getMenuComponents() )
item.setEnabled( enabled );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JMenuBar menuBar1 = new JMenuBar();
@@ -125,6 +210,7 @@ class DemoFrame
JMenuItem projectViewMenuItem = new JMenuItem();
JMenuItem structureViewMenuItem = new JMenuItem();
JMenuItem propertiesViewMenuItem = new JMenuItem();
JMenuItem menuItem1 = new JMenuItem();
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem2 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem3 = new JRadioButtonMenuItem();
@@ -132,6 +218,9 @@ class DemoFrame
JMenuItem restoreFontMenuItem = new JMenuItem();
JMenuItem incrFontMenuItem = new JMenuItem();
JMenuItem decrFontMenuItem = new JMenuItem();
JMenu optionsMenu = new JMenu();
underlineMenuSelectionMenuItem = new JCheckBoxMenuItem();
alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem();
JMenu helpMenu = new JMenu();
JMenuItem aboutMenuItem = new JMenuItem();
JToolBar toolBar1 = new JToolBar();
@@ -149,6 +238,7 @@ class DemoFrame
DataComponentsPanel dataComponentsPanel = new DataComponentsPanel();
TabsPanel tabsPanel = new TabsPanel();
OptionPanePanel optionPanePanel = new OptionPanePanel();
ExtrasPanel extrasPanel1 = new ExtrasPanel();
controlBar = new ControlBar();
IJThemesPanel themesPanel = new IJThemesPanel();
@@ -308,6 +398,10 @@ class DemoFrame
menu1.add(propertiesViewMenuItem);
}
viewMenu.add(menu1);
//---- menuItem1 ----
menuItem1.setText("<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
viewMenu.add(menuItem1);
viewMenu.addSeparator();
//---- radioButtonMenuItem1 ----
@@ -337,24 +431,40 @@ class DemoFrame
//---- restoreFontMenuItem ----
restoreFontMenuItem.setText("Restore Font");
restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.CTRL_MASK));
restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
restoreFontMenuItem.addActionListener(e -> restoreFont());
fontMenu.add(restoreFontMenuItem);
//---- incrFontMenuItem ----
incrFontMenuItem.setText("Increase Font Size");
incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_MASK));
incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
incrFontMenuItem.addActionListener(e -> incrFont());
fontMenu.add(incrFontMenuItem);
//---- decrFontMenuItem ----
decrFontMenuItem.setText("Decrease Font Size");
decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_MASK));
decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
decrFontMenuItem.addActionListener(e -> decrFont());
fontMenu.add(decrFontMenuItem);
}
menuBar1.add(fontMenu);
//======== optionsMenu ========
{
optionsMenu.setText("Options");
//---- underlineMenuSelectionMenuItem ----
underlineMenuSelectionMenuItem.setText("Use underline menu selection");
underlineMenuSelectionMenuItem.addActionListener(e -> underlineMenuSelection());
optionsMenu.add(underlineMenuSelectionMenuItem);
//---- alwaysShowMnemonicsMenuItem ----
alwaysShowMnemonicsMenuItem.setText("Always show mnemonics");
alwaysShowMnemonicsMenuItem.addActionListener(e -> alwaysShowMnemonics());
optionsMenu.add(alwaysShowMnemonicsMenuItem);
}
menuBar1.add(optionsMenu);
//======== helpMenu ========
{
helpMenu.setText("Help");
@@ -425,6 +535,7 @@ class DemoFrame
tabbedPane.addTab("Data Components", dataComponentsPanel);
tabbedPane.addTab("SplitPane & Tabs", tabsPanel);
tabbedPane.addTab("Option Pane", optionPanePanel);
tabbedPane.addTab("Extras", extrasPanel1);
}
contentPanel.add(tabbedPane, "cell 0 0");
}
@@ -457,30 +568,12 @@ class DemoFrame
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
// add font families
fontMenu.addSeparator();
String[] fontFamilies = { "Arial", "Comic Sans MS", "Courier New", "Dialog",
"Monospaced", "SansSerif", "Serif", "Tahoma", "Verdana" };
for( String fontFamily : fontFamilies ) {
JMenuItem fontItem = new JMenuItem( fontFamily );
fontItem.addActionListener( this::fontFamilyChanged );
fontMenu.add( fontItem );
}
// add font sizes
fontMenu.addSeparator();
int[] fontSizes = { 8, 10, 12, 14, 16, 18, 20, 24, 28 };
for( int fontSize : fontSizes ) {
String fontSizeStr = Integer.toString( fontSize );
JMenuItem fontItem = new JMenuItem( fontSizeStr );
fontItem.addActionListener( this::fontSizeChanged );
fontMenu.add( fontItem );
}
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenu fontMenu;
private JCheckBoxMenuItem underlineMenuSelectionMenuItem;
private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem;
private JTabbedPane tabbedPane;
private ControlBar controlBar;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -93,6 +93,11 @@ new FormModel {
}, new FormLayoutConstraints( null ) {
"title": "Option Pane"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) {
name: "extrasPanel1"
}, new FormLayoutConstraints( null ) {
"title": "Extras"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
@@ -259,6 +264,10 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) )
} )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem1"
"text": "<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
name: "separator8"
} )
@@ -294,22 +303,42 @@ new FormModel {
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "restoreFontMenuItem"
"text": "Restore Font"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 48, 130, false )
"accelerator": static javax.swing.KeyStroke getKeyStroke( 48, 4226, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "restoreFont", false ) )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "incrFontMenuItem"
"text": "Increase Font Size"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 521, 130, false )
"accelerator": static javax.swing.KeyStroke getKeyStroke( 521, 4226, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "incrFont", false ) )
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "decrFontMenuItem"
"text": "Decrease Font Size"
"accelerator": static javax.swing.KeyStroke getKeyStroke( 45, 130, false )
"accelerator": static javax.swing.KeyStroke getKeyStroke( 45, 4226, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "decrFont", false ) )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "optionsMenu"
"text": "Options"
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "underlineMenuSelectionMenuItem"
"text": "Use underline menu selection"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "underlineMenuSelection", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "alwaysShowMnemonicsMenuItem"
"text": "Always show mnemonics"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alwaysShowMnemonics", false ) )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "helpMenu"
"text": "Help"

View File

@@ -22,9 +22,9 @@ import java.util.prefs.Preferences;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatPropertiesLaf;
import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel.PropertiesLaf;
import com.formdev.flatlaf.util.StringUtils;
/**
@@ -68,12 +68,12 @@ public class DemoPrefs
if( !theme.isEmpty() )
UIManager.getLookAndFeelDefaults().put( THEME_UI_KEY, theme );
} else if( IJThemesPanel.PropertiesLaf.class.getName().equals( lafClassName ) ) {
} else if( FlatPropertiesLaf.class.getName().equals( lafClassName ) ) {
String theme = state.get( KEY_LAF_THEME, "" );
if( theme.startsWith( FILE_PREFIX ) ) {
File themeFile = new File( theme.substring( FILE_PREFIX.length() ) );
String themeName = StringUtils.removeTrailing( themeFile.getName(), ".properties" );
FlatLaf.install( new PropertiesLaf( themeName, themeFile ) );
FlatLaf.install( new FlatPropertiesLaf( themeName, themeFile ) );
} else
FlatLightLaf.install();
@@ -82,7 +82,7 @@ public class DemoPrefs
} else
UIManager.setLookAndFeel( lafClassName );
}
} catch( Exception ex ) {
} catch( Throwable ex ) {
ex.printStackTrace();
// fallback

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf.demo;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
@@ -27,12 +29,19 @@ public class FlatLafDemo
static final String KEY_TAB = "tab";
public static void main( String[] args ) {
// on macOS enable screen menu bar
if( SystemInfo.IS_MAC && System.getProperty( "apple.laf.useScreenMenuBar" ) == null )
System.setProperty( "apple.laf.useScreenMenuBar", "true" );
SwingUtilities.invokeLater( () -> {
DemoPrefs.init( PREFS_ROOT_PATH );
// set look and feel
DemoPrefs.initLaf( args );
// install inspector
FlatInspector.install( "ctrl shift alt X" );
// create frame
DemoFrame frame = new DemoFrame();

View File

@@ -27,7 +27,7 @@ import net.miginfocom.swing.*;
* @author Karl Tauber
*/
class OptionPanePanel
extends JScrollPane
extends JPanel
{
OptionPanePanel() {
initComponents();
@@ -48,6 +48,7 @@ class OptionPanePanel
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JScrollPane scrollPane1 = new JScrollPane();
ScrollablePanel panel9 = new ScrollablePanel();
JLabel plainLabel = new JLabel();
JPanel panel1 = new JPanel();
@@ -83,200 +84,206 @@ class OptionPanePanel
OptionPanePanel.ShowDialogLinkLabel customShowDialogLabel = new OptionPanePanel.ShowDialogLinkLabel();
//======== this ========
setBorder(BorderFactory.createEmptyBorder());
setLayout(new BorderLayout());
//======== panel9 ========
//======== scrollPane1 ========
{
panel9.setLayout(new MigLayout(
"flowy,hidemode 3",
// columns
"[]" +
"[]" +
"[fill]",
// rows
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]"));
scrollPane1.setBorder(BorderFactory.createEmptyBorder());
//---- plainLabel ----
plainLabel.setText("Plain");
panel9.add(plainLabel, "cell 0 0");
//======== panel1 ========
//======== panel9 ========
{
panel1.setBorder(LineBorder.createGrayLineBorder());
panel1.setLayout(new BorderLayout());
panel9.setLayout(new MigLayout(
"flowy,hidemode 3",
// columns
"[]" +
"[]" +
"[fill]",
// rows
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]" +
"[top]"));
//---- plainOptionPane ----
plainOptionPane.setMessage("Hello world.");
panel1.add(plainOptionPane, BorderLayout.CENTER);
//---- plainLabel ----
plainLabel.setText("Plain");
panel9.add(plainLabel, "cell 0 0");
//======== panel1 ========
{
panel1.setBorder(LineBorder.createGrayLineBorder());
panel1.setLayout(new BorderLayout());
//---- plainOptionPane ----
plainOptionPane.setMessage("Hello world.");
panel1.add(plainOptionPane, BorderLayout.CENTER);
}
panel9.add(panel1, "cell 1 0");
//---- plainShowDialogLabel ----
plainShowDialogLabel.setOptionPane(plainOptionPane);
plainShowDialogLabel.setTitleLabel(plainLabel);
panel9.add(plainShowDialogLabel, "cell 2 0");
//---- errorLabel ----
errorLabel.setText("Error");
panel9.add(errorLabel, "cell 0 1");
//======== panel2 ========
{
panel2.setBorder(LineBorder.createGrayLineBorder());
panel2.setLayout(new BorderLayout());
//---- errorOptionPane ----
errorOptionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
errorOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
errorOptionPane.setMessage("Your PC ran into a problem. Buy a new one.");
panel2.add(errorOptionPane, BorderLayout.CENTER);
}
panel9.add(panel2, "cell 1 1");
//---- errorShowDialogLabel ----
errorShowDialogLabel.setTitleLabel(errorLabel);
errorShowDialogLabel.setOptionPane(errorOptionPane);
panel9.add(errorShowDialogLabel, "cell 2 1");
//---- informationLabel ----
informationLabel.setText("Information");
panel9.add(informationLabel, "cell 0 2");
//======== panel3 ========
{
panel3.setBorder(LineBorder.createGrayLineBorder());
panel3.setLayout(new BorderLayout());
//---- informationOptionPane ----
informationOptionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
informationOptionPane.setOptionType(JOptionPane.YES_NO_OPTION);
informationOptionPane.setMessage("Text with\nmultiple lines\n(use \\n to separate lines)");
panel3.add(informationOptionPane, BorderLayout.CENTER);
}
panel9.add(panel3, "cell 1 2");
//---- informationShowDialogLabel ----
informationShowDialogLabel.setOptionPane(informationOptionPane);
informationShowDialogLabel.setTitleLabel(informationLabel);
panel9.add(informationShowDialogLabel, "cell 2 2");
//---- questionLabel ----
questionLabel.setText("Question");
panel9.add(questionLabel, "cell 0 3");
//======== panel4 ========
{
panel4.setBorder(LineBorder.createGrayLineBorder());
panel4.setLayout(new BorderLayout());
//---- questionOptionPane ----
questionOptionPane.setMessageType(JOptionPane.QUESTION_MESSAGE);
questionOptionPane.setOptionType(JOptionPane.YES_NO_CANCEL_OPTION);
questionOptionPane.setMessage("Answer the question. What question? Don't know. Just writing useless text to make this longer than 80 characters.");
panel4.add(questionOptionPane, BorderLayout.CENTER);
}
panel9.add(panel4, "cell 1 3");
//---- questionShowDialogLabel ----
questionShowDialogLabel.setOptionPane(questionOptionPane);
questionShowDialogLabel.setTitleLabel(questionLabel);
panel9.add(questionShowDialogLabel, "cell 2 3");
//---- warningLabel ----
warningLabel.setText("Warning");
panel9.add(warningLabel, "cell 0 4");
//======== panel5 ========
{
panel5.setBorder(LineBorder.createGrayLineBorder());
panel5.setLayout(new BorderLayout());
//---- warningOptionPane ----
warningOptionPane.setMessageType(JOptionPane.WARNING_MESSAGE);
warningOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
warningOptionPane.setMessage("<html>I like <b>bold</b>,<br> and I like <i>italic</i>,<br> and I like to have<br> many lines.<br> Lots of lines.");
panel5.add(warningOptionPane, BorderLayout.CENTER);
}
panel9.add(panel5, "cell 1 4");
//---- warningShowDialogLabel ----
warningShowDialogLabel.setOptionPane(warningOptionPane);
warningShowDialogLabel.setTitleLabel(warningLabel);
panel9.add(warningShowDialogLabel, "cell 2 4");
//---- inputLabel ----
inputLabel.setText("Input");
panel9.add(inputLabel, "cell 0 5");
//======== panel7 ========
{
panel7.setBorder(LineBorder.createGrayLineBorder());
panel7.setLayout(new BorderLayout());
//---- inputOptionPane ----
inputOptionPane.setWantsInput(true);
inputOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
inputOptionPane.setMessage("Enter whatever you want:");
panel7.add(inputOptionPane, BorderLayout.CENTER);
}
panel9.add(panel7, "cell 1 5");
//---- inputShowDialogLabel ----
inputShowDialogLabel.setOptionPane(inputOptionPane);
inputShowDialogLabel.setTitleLabel(inputLabel);
panel9.add(inputShowDialogLabel, "cell 2 5");
//---- inputIconLabel ----
inputIconLabel.setText("Input + icon");
panel9.add(inputIconLabel, "cell 0 6");
//======== panel8 ========
{
panel8.setBorder(LineBorder.createGrayLineBorder());
panel8.setLayout(new BorderLayout());
//---- inputIconOptionPane ----
inputIconOptionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
inputIconOptionPane.setWantsInput(true);
inputIconOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
inputIconOptionPane.setMessage("Enter something:");
panel8.add(inputIconOptionPane, BorderLayout.CENTER);
}
panel9.add(panel8, "cell 1 6");
//---- inputIconShowDialogLabel ----
inputIconShowDialogLabel.setTitleLabel(inputIconLabel);
inputIconShowDialogLabel.setOptionPane(inputIconOptionPane);
panel9.add(inputIconShowDialogLabel, "cell 2 6");
//---- customLabel ----
customLabel.setText("Custom");
panel9.add(customLabel, "cell 0 7");
//======== panel6 ========
{
panel6.setBorder(LineBorder.createGrayLineBorder());
panel6.setLayout(new BorderLayout());
//---- customOptionPane ----
customOptionPane.setIcon(UIManager.getIcon("Tree.leafIcon"));
panel6.add(customOptionPane, BorderLayout.CENTER);
}
panel9.add(panel6, "cell 1 7");
//---- customShowDialogLabel ----
customShowDialogLabel.setOptionPane(customOptionPane);
customShowDialogLabel.setTitleLabel(customLabel);
panel9.add(customShowDialogLabel, "cell 2 7");
}
panel9.add(panel1, "cell 1 0");
//---- plainShowDialogLabel ----
plainShowDialogLabel.setOptionPane(plainOptionPane);
plainShowDialogLabel.setTitleLabel(plainLabel);
panel9.add(plainShowDialogLabel, "cell 2 0");
//---- errorLabel ----
errorLabel.setText("Error");
panel9.add(errorLabel, "cell 0 1");
//======== panel2 ========
{
panel2.setBorder(LineBorder.createGrayLineBorder());
panel2.setLayout(new BorderLayout());
//---- errorOptionPane ----
errorOptionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
errorOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
errorOptionPane.setMessage("Your PC ran into a problem. Buy a new one.");
panel2.add(errorOptionPane, BorderLayout.CENTER);
}
panel9.add(panel2, "cell 1 1");
//---- errorShowDialogLabel ----
errorShowDialogLabel.setTitleLabel(errorLabel);
errorShowDialogLabel.setOptionPane(errorOptionPane);
panel9.add(errorShowDialogLabel, "cell 2 1");
//---- informationLabel ----
informationLabel.setText("Information");
panel9.add(informationLabel, "cell 0 2");
//======== panel3 ========
{
panel3.setBorder(LineBorder.createGrayLineBorder());
panel3.setLayout(new BorderLayout());
//---- informationOptionPane ----
informationOptionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
informationOptionPane.setOptionType(JOptionPane.YES_NO_OPTION);
informationOptionPane.setMessage("Text with\nmultiple lines\n(use \\n to separate lines)");
panel3.add(informationOptionPane, BorderLayout.CENTER);
}
panel9.add(panel3, "cell 1 2");
//---- informationShowDialogLabel ----
informationShowDialogLabel.setOptionPane(informationOptionPane);
informationShowDialogLabel.setTitleLabel(informationLabel);
panel9.add(informationShowDialogLabel, "cell 2 2");
//---- questionLabel ----
questionLabel.setText("Question");
panel9.add(questionLabel, "cell 0 3");
//======== panel4 ========
{
panel4.setBorder(LineBorder.createGrayLineBorder());
panel4.setLayout(new BorderLayout());
//---- questionOptionPane ----
questionOptionPane.setMessageType(JOptionPane.QUESTION_MESSAGE);
questionOptionPane.setOptionType(JOptionPane.YES_NO_CANCEL_OPTION);
questionOptionPane.setMessage("Answer the question. What question? Don't know. Just writing useless text to make this longer than 80 characters.");
panel4.add(questionOptionPane, BorderLayout.CENTER);
}
panel9.add(panel4, "cell 1 3");
//---- questionShowDialogLabel ----
questionShowDialogLabel.setOptionPane(questionOptionPane);
questionShowDialogLabel.setTitleLabel(questionLabel);
panel9.add(questionShowDialogLabel, "cell 2 3");
//---- warningLabel ----
warningLabel.setText("Warning");
panel9.add(warningLabel, "cell 0 4");
//======== panel5 ========
{
panel5.setBorder(LineBorder.createGrayLineBorder());
panel5.setLayout(new BorderLayout());
//---- warningOptionPane ----
warningOptionPane.setMessageType(JOptionPane.WARNING_MESSAGE);
warningOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
warningOptionPane.setMessage("<html>I like <b>bold</b>,<br> and I like <i>italic</i>,<br> and I like to have<br> many lines.<br> Lots of lines.");
panel5.add(warningOptionPane, BorderLayout.CENTER);
}
panel9.add(panel5, "cell 1 4");
//---- warningShowDialogLabel ----
warningShowDialogLabel.setOptionPane(warningOptionPane);
warningShowDialogLabel.setTitleLabel(warningLabel);
panel9.add(warningShowDialogLabel, "cell 2 4");
//---- inputLabel ----
inputLabel.setText("Input");
panel9.add(inputLabel, "cell 0 5");
//======== panel7 ========
{
panel7.setBorder(LineBorder.createGrayLineBorder());
panel7.setLayout(new BorderLayout());
//---- inputOptionPane ----
inputOptionPane.setWantsInput(true);
inputOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
inputOptionPane.setMessage("Enter whatever you want:");
panel7.add(inputOptionPane, BorderLayout.CENTER);
}
panel9.add(panel7, "cell 1 5");
//---- inputShowDialogLabel ----
inputShowDialogLabel.setOptionPane(inputOptionPane);
inputShowDialogLabel.setTitleLabel(inputLabel);
panel9.add(inputShowDialogLabel, "cell 2 5");
//---- inputIconLabel ----
inputIconLabel.setText("Input + icon");
panel9.add(inputIconLabel, "cell 0 6");
//======== panel8 ========
{
panel8.setBorder(LineBorder.createGrayLineBorder());
panel8.setLayout(new BorderLayout());
//---- inputIconOptionPane ----
inputIconOptionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
inputIconOptionPane.setWantsInput(true);
inputIconOptionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
inputIconOptionPane.setMessage("Enter something:");
panel8.add(inputIconOptionPane, BorderLayout.CENTER);
}
panel9.add(panel8, "cell 1 6");
//---- inputIconShowDialogLabel ----
inputIconShowDialogLabel.setTitleLabel(inputIconLabel);
inputIconShowDialogLabel.setOptionPane(inputIconOptionPane);
panel9.add(inputIconShowDialogLabel, "cell 2 6");
//---- customLabel ----
customLabel.setText("Custom");
panel9.add(customLabel, "cell 0 7");
//======== panel6 ========
{
panel6.setBorder(LineBorder.createGrayLineBorder());
panel6.setLayout(new BorderLayout());
//---- customOptionPane ----
customOptionPane.setIcon(UIManager.getIcon("Tree.leafIcon"));
panel6.add(customOptionPane, BorderLayout.CENTER);
}
panel9.add(panel6, "cell 1 7");
//---- customShowDialogLabel ----
customShowDialogLabel.setOptionPane(customOptionPane);
customShowDialogLabel.setTitleLabel(customLabel);
panel9.add(customShowDialogLabel, "cell 2 7");
scrollPane1.setViewportView(panel9);
}
setViewportView(panel9);
add(scrollPane1, BorderLayout.CENTER);
// JFormDesigner - End of component initialization //GEN-END:initComponents
}

View File

@@ -6,240 +6,245 @@ new FormModel {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "this"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormContainer( "com.formdev.flatlaf.demo.ScrollablePanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "flowy,hidemode 3"
"$columnConstraints": "[][][fill]"
"$rowConstraints": "[top][top][top][top][top][top][top][top]"
} ) {
name: "panel9"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "plainLabel"
"text": "Plain"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel1"
"border": &LineBorder0 new javax.swing.border.LineBorder( sfield java.awt.Color gray, 1, false )
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "plainOptionPane"
"message": "Hello world."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
name: "scrollPane1"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormContainer( "com.formdev.flatlaf.demo.ScrollablePanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "flowy,hidemode 3"
"$columnConstraints": "[][][fill]"
"$rowConstraints": "[top][top][top][top][top][top][top][top]"
} ) {
name: "panel9"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "plainLabel"
"text": "Plain"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "plainShowDialogLabel"
"optionPane": new FormReference( "plainOptionPane" )
"titleLabel": new FormReference( "plainLabel" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "errorLabel"
"text": "Error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel2"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "errorOptionPane"
"messageType": 0
"optionType": 2
"message": "Your PC ran into a problem. Buy a new one."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel1"
"border": &LineBorder0 new javax.swing.border.LineBorder( sfield java.awt.Color gray, 1, false )
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "plainOptionPane"
"message": "Hello world."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "errorShowDialogLabel"
"titleLabel": new FormReference( "errorLabel" )
"optionPane": new FormReference( "errorOptionPane" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "informationLabel"
"text": "Information"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel3"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "informationOptionPane"
"messageType": 1
"optionType": 0
"message": "Text with\nmultiple lines\n(use \\n to separate lines)"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "informationShowDialogLabel"
"optionPane": new FormReference( "informationOptionPane" )
"titleLabel": new FormReference( "informationLabel" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "questionLabel"
"text": "Question"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel4"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "questionOptionPane"
"messageType": 3
"optionType": 1
"message": "Answer the question. What question? Don't know. Just writing useless text to make this longer than 80 characters."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "questionShowDialogLabel"
"optionPane": new FormReference( "questionOptionPane" )
"titleLabel": new FormReference( "questionLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "warningLabel"
"text": "Warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel5"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "warningOptionPane"
"messageType": 2
"optionType": 2
"message": "<html>I like <b>bold</b>,<br> and I like <i>italic</i>,<br> and I like to have<br> many lines.<br> Lots of lines."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "warningShowDialogLabel"
"optionPane": new FormReference( "warningOptionPane" )
"titleLabel": new FormReference( "warningLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputLabel"
"text": "Input"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel7"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "inputOptionPane"
"wantsInput": true
"optionType": 2
"message": "Enter whatever you want:"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "inputShowDialogLabel"
"optionPane": new FormReference( "inputOptionPane" )
"titleLabel": new FormReference( "inputLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputIconLabel"
"text": "Input + icon"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel8"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "inputIconOptionPane"
"messageType": 1
"wantsInput": true
"optionType": 2
"message": "Enter something:"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "inputIconShowDialogLabel"
"titleLabel": new FormReference( "inputIconLabel" )
"optionPane": new FormReference( "inputIconOptionPane" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "customLabel"
"text": "Custom"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel6"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "customOptionPane"
"icon": new com.jformdesigner.model.SwingIcon( 2, "Tree.leafIcon" )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "plainShowDialogLabel"
"optionPane": new FormReference( "plainOptionPane" )
"titleLabel": new FormReference( "plainLabel" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "errorLabel"
"text": "Error"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel2"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "errorOptionPane"
"messageType": 0
"optionType": 2
"message": "Your PC ran into a problem. Buy a new one."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "errorShowDialogLabel"
"titleLabel": new FormReference( "errorLabel" )
"optionPane": new FormReference( "errorOptionPane" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "informationLabel"
"text": "Information"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel3"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "informationOptionPane"
"messageType": 1
"optionType": 0
"message": "Text with\nmultiple lines\n(use \\n to separate lines)"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "informationShowDialogLabel"
"optionPane": new FormReference( "informationOptionPane" )
"titleLabel": new FormReference( "informationLabel" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "questionLabel"
"text": "Question"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel4"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "questionOptionPane"
"messageType": 3
"optionType": 1
"message": "Answer the question. What question? Don't know. Just writing useless text to make this longer than 80 characters."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "questionShowDialogLabel"
"optionPane": new FormReference( "questionOptionPane" )
"titleLabel": new FormReference( "questionLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "warningLabel"
"text": "Warning"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel5"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "warningOptionPane"
"messageType": 2
"optionType": 2
"message": "<html>I like <b>bold</b>,<br> and I like <i>italic</i>,<br> and I like to have<br> many lines.<br> Lots of lines."
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "warningShowDialogLabel"
"optionPane": new FormReference( "warningOptionPane" )
"titleLabel": new FormReference( "warningLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputLabel"
"text": "Input"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel7"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "inputOptionPane"
"wantsInput": true
"optionType": 2
"message": "Enter whatever you want:"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "inputShowDialogLabel"
"optionPane": new FormReference( "inputOptionPane" )
"titleLabel": new FormReference( "inputLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputIconLabel"
"text": "Input + icon"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel8"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "inputIconOptionPane"
"messageType": 1
"wantsInput": true
"optionType": 2
"message": "Enter something:"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "inputIconShowDialogLabel"
"titleLabel": new FormReference( "inputIconLabel" )
"optionPane": new FormReference( "inputIconOptionPane" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "customLabel"
"text": "Custom"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel6"
"border": #LineBorder0
add( new FormComponent( "javax.swing.JOptionPane" ) {
name: "customOptionPane"
"icon": new com.jformdesigner.model.SwingIcon( 2, "Tree.leafIcon" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "customShowDialogLabel"
"optionPane": new FormReference( "customOptionPane" )
"titleLabel": new FormReference( "customLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel$ShowDialogLinkLabel" ) {
name: "customShowDialogLabel"
"optionPane": new FormReference( "customOptionPane" )
"titleLabel": new FormReference( "customLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )

View File

@@ -0,0 +1,132 @@
/*
* 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.demo.extras;
import javax.swing.*;
import com.formdev.flatlaf.extras.*;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class ExtrasPanel
extends JPanel
{
public ExtrasPanel() {
initComponents();
triStateLabel1.setText( triStateCheckBox1.getState().toString() );
addSVGIcon( "actions/copy.svg" );
addSVGIcon( "actions/colors.svg" );
addSVGIcon( "actions/execute.svg" );
addSVGIcon( "actions/suspend.svg" );
addSVGIcon( "actions/intentionBulb.svg" );
addSVGIcon( "actions/quickfixOffBulb.svg" );
addSVGIcon( "objects/abstractClass.svg" );
addSVGIcon( "objects/abstractMethod.svg" );
addSVGIcon( "objects/annotationtype.svg" );
addSVGIcon( "objects/annotationtype.svg" );
addSVGIcon( "objects/css.svg" );
addSVGIcon( "objects/javaScript.svg" );
addSVGIcon( "objects/xhtml.svg" );
addSVGIcon( "errorDialog.svg" );
addSVGIcon( "informationDialog.svg" );
addSVGIcon( "warningDialog.svg" );
}
private void addSVGIcon( String name ) {
svgIconsPanel.add( new JLabel( new FlatSVGIcon( "com/formdev/flatlaf/demo/extras/svg/" + name ) ) );
}
private void triStateCheckBox1Changed() {
triStateLabel1.setText( triStateCheckBox1.getState().toString() );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
label4 = new JLabel();
label1 = new JLabel();
triStateCheckBox1 = new TriStateCheckBox();
triStateLabel1 = new JLabel();
label2 = new JLabel();
svgIconsPanel = new JPanel();
label3 = new JLabel();
//======== this ========
setLayout(new MigLayout(
"hidemode 3",
// columns
"[]" +
"[]" +
"[left]",
// rows
"[]para" +
"[]" +
"[]" +
"[]"));
//---- label4 ----
label4.setText("Note: Components on this page require the flatlaf-extras library.");
add(label4, "cell 0 0 3 1");
//---- label1 ----
label1.setText("TriStateCheckBox:");
add(label1, "cell 0 1");
//---- triStateCheckBox1 ----
triStateCheckBox1.setText("three states");
triStateCheckBox1.addActionListener(e -> triStateCheckBox1Changed());
add(triStateCheckBox1, "cell 1 1");
//---- triStateLabel1 ----
triStateLabel1.setText("text");
add(triStateLabel1, "cell 2 1");
//---- label2 ----
label2.setText("SVG Icons:");
add(label2, "cell 0 2");
//======== svgIconsPanel ========
{
svgIconsPanel.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[fill]",
// rows
"[grow,center]"));
}
add(svgIconsPanel, "cell 1 2 2 1");
//---- label3 ----
label3.setText("The icons may change colors when switching to another theme.");
add(label3, "cell 1 3 2 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel label4;
private JLabel label1;
private TriStateCheckBox triStateCheckBox1;
private JLabel triStateLabel1;
private JLabel label2;
private JPanel svgIconsPanel;
private JLabel label3;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -0,0 +1,63 @@
JFDML JFormDesigner: "7.0.1.0.272" Java: "13.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[][][left]"
"$rowConstraints": "[]para[][][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"text": "Note: Components on this page require the flatlaf-extras library."
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0 3 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "TriStateCheckBox:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "com.formdev.flatlaf.extras.TriStateCheckBox" ) {
name: "triStateCheckBox1"
"text": "three states"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "triStateCheckBox1Changed", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "triStateLabel1"
"text": "text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "SVG Icons:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[grow,center]"
} ) {
name: "svgIconsPanel"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "The icons may change colors when switching to another theme."
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 500, 300 )
} )
}
}

View File

@@ -28,7 +28,6 @@ import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
@@ -38,7 +37,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
@@ -48,6 +46,7 @@ import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatPropertiesLaf;
import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.demo.DemoPrefs;
import com.formdev.flatlaf.extras.FlatSVGIcon;
@@ -198,14 +197,14 @@ public class IJThemesPanel
}
private void themesListValueChanged( ListSelectionEvent e ) {
if( e.getValueIsAdjusting() || isAdjustingThemesList )
return;
IJThemeInfo themeInfo = themesList.getSelectedValue();
boolean bundledTheme = (themeInfo != null && themeInfo.resourceName != null);
saveButton.setEnabled( bundledTheme );
sourceCodeButton.setEnabled( bundledTheme );
if( e.getValueIsAdjusting() || isAdjustingThemesList )
return;
EventQueue.invokeLater( () -> {
setTheme( themeInfo );
} );
@@ -229,7 +228,7 @@ public class IJThemesPanel
} else if( themeInfo.themeFile != null ) {
try {
if( themeInfo.themeFile.getName().endsWith( ".properties" ) ) {
FlatLaf.install( new PropertiesLaf( themeInfo.name, themeInfo.themeFile ) );
FlatLaf.install( new FlatPropertiesLaf( themeInfo.name, themeInfo.themeFile ) );
} else
FlatLaf.install( IntelliJTheme.createLaf( new FileInputStream( themeInfo.themeFile ) ) );
@@ -342,7 +341,7 @@ public class IJThemesPanel
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
String theme = UIManager.getLookAndFeelDefaults().getString( DemoPrefs.THEME_UI_KEY );
if( theme == null && (lookAndFeel instanceof IntelliJTheme.ThemeLaf || lookAndFeel instanceof PropertiesLaf) )
if( theme == null && (lookAndFeel instanceof IntelliJTheme.ThemeLaf || lookAndFeel instanceof FlatPropertiesLaf) )
return;
Predicate<IJThemeInfo> test;
@@ -431,78 +430,4 @@ public class IJThemesPanel
private JScrollPane themesScrollPane;
private JList<IJThemeInfo> themesList;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class PropertiesLaf ------------------------------------------------
public static class PropertiesLaf
extends FlatLaf
{
private final String name;
private final String baseTheme;
private final boolean dark;
private final Properties properties;
public PropertiesLaf( String name, File propertiesFile )
throws IOException
{
this.name = name;
properties = new Properties();
try( InputStream in = new FileInputStream( propertiesFile ) ) {
if( in != null )
properties.load( in );
}
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;
}
lafClasses.add( PropertiesLaf.class );
return lafClasses;
}
@Override
protected Properties getAdditionalDefaults() {
return properties;
}
}
}

View File

@@ -0,0 +1,3 @@
The icons in this folder are from IntelliJ IDEA Community Edition,
which is licensed under the Apache 2.0 license. Copyright 2000-2019 JetBrains s.r.o.
See: https://github.com/JetBrains/intellij-community/

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd" transform="translate(2 2)">
<rect width="5.5" height="5.5" fill="#59A869"/>
<rect width="5.5" height="5.5" x="6.5" fill="#EDA200"/>
<rect width="5.5" height="5.5" y="6.5" fill="#389FD6"/>
<rect width="5.5" height="5.5" x="6.5" y="6.5" fill="#DB5860"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#AFB1B3" d="M11,3 L4,3 L4,11 L2,11 L2,1 L11,1 L11,3 Z"/>
<path fill="#AFB1B3" d="M5,4 L14,4 L14,14 L5,14 L5,4 Z M7,6 L7,7 L12,7 L12,6 L7,6 Z M7,10 L7,11 L12,11 L12,10 L7,10 Z M7,8 L7,9 L12,9 L12,8 L7,8 Z"/>
<path fill="#6E6E6E" d="M11,3 L4,3 L4,11 L2,11 L2,1 L11,1 L11,3 Z"/>
<path fill="#6E6E6E" d="M5,4 L14,4 L14,14 L5,14 L5,4 Z M7,6 L7,7 L12,7 L12,6 L7,6 Z M7,10 L7,11 L12,11 L12,10 L7,10 Z M7,8 L7,9 L12,9 L12,8 L7,8 Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon fill="#59A869" fill-rule="evenodd" points="4 2 14 8 4 14"/>
</svg>

After

Width:  |  Height:  |  Size: 162 B

View File

@@ -0,0 +1,7 @@
<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="6" height="1" x="5" y="12" fill="#6E6E6E"/>
<path fill="#6E6E6E" d="M5.5,14 L10.5,14 L10.5,14 C10.5,14.5522847 10.0522847,15 9.5,15 L6.5,15 C5.94771525,15 5.5,14.5522847 5.5,14 Z"/>
<path fill="#EDA200" d="M13,5.2 C13,9.2 11,8.96875 11,11 L5,11 C5,9.03125 3,9.2 3,5.2 C3,2.991 5.23878906,1 8,1 C10.76125,1 13,2.99103125 13,5.2 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 493 B

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