Compare commits

..

211 Commits
0.42 ... 0.46

Author SHA1 Message Date
Karl Tauber
073a25f381 release 0.46 2020-12-20 18:42:23 +01:00
Karl Tauber
40592ab876 FlatUIUtils: fixed javadoc warnings 2020-12-20 18:34:13 +01:00
Karl Tauber
bbfe624b51 Merge pull request #222 into master
AnimatedIcon
2020-12-20 18:26:09 +01:00
Karl Tauber
a2af9e4c65 JIDE: RangeSlider: clicking on track now immediately moves the thumb to mouse location and starts dragging the thumb 2020-12-20 18:24:40 +01:00
Karl Tauber
0123a8895f JIDE: updated UI defaults dumps for commit ef065d31a0 (support TristateCheckBox) 2020-12-20 17:33:42 +01:00
Karl Tauber
53854a4d13 Slider: snap to ticks is now done while dragging the thumb 2020-12-20 17:32:01 +01:00
Karl Tauber
4fdd44858f Slider: clicking on track now immediately moves the thumb to mouse location and starts dragging the thumb 2020-12-20 13:32:10 +01:00
Karl Tauber
3c58879ce5 Slider: fixed painting of colored track if JSlider.inverted is true 2020-12-19 17:01:34 +01:00
Karl Tauber
a7c6a881b3 Extras: FlatTriStateCheckBox reworked 2020-12-19 16:13:12 +01:00
Karl Tauber
ef065d31a0 JIDE: support TristateCheckBox 2020-12-19 13:34:53 +01:00
Karl Tauber
d059d6b448 README.md: new projects using FlatLaf:
- jEnTunnel
- JPass
- Linotte
- MEKA
- Shutter Encoder
- ThunderFocus
- lectureStudio
2020-12-18 16:05:56 +01:00
Karl Tauber
2d0a6f1bec README.md: new projects using FlatLaf:
- JOSM
- Novel-Grabber
- Android Tool
2020-12-18 16:04:43 +01:00
Karl Tauber
a3cc5a1938 README.md: added descriptions to projects using FlatLaf 2020-12-18 14:34:40 +01:00
Karl Tauber
435068515a always reset our graphics rendering hints
(this is usually not necessary because each component gets its own instance of Graphics when painting, but resetting may avoid side effects if our paint methods are invoked directly)
2020-12-18 13:35:17 +01:00
Karl Tauber
956001dbd7 avoid painting text with our rendering hints enabled to avoid antialiased text in some components if text antialiasing is disabled in system (issue #227) 2020-12-18 12:22:27 +01:00
Karl Tauber
460f0d9dee UIScale: fixed NPE in getSystemScaleFactor(Graphics2D) when using Batik SVGGraphics2D (issue #226) 2020-12-15 11:25:00 +01:00
Karl Tauber
5155ec93c9 ToolTip: fixed drop shadow for wide tooltips (issue #224; regression since fixed issue #142) 2020-12-15 11:19:30 +01:00
Karl Tauber
8bb8883e22 IntelliJ Themes: added flag whether a theme is dark to FlatAllIJThemes.INFOS. (issue #221) 2020-12-12 18:54:42 +01:00
Karl Tauber
ffb7a6dfbb README.md:
- added demo download section
- added link to javadoc of extras components
2020-12-12 14:45:08 +01:00
Karl Tauber
176de6f245 README.md: simplified download sections of subprojects 2020-12-12 14:21:07 +01:00
Karl Tauber
11f9740dbf Extras: added support for JComponent.outline client property (issue #117) 2020-12-12 13:59:58 +01:00
Karl Tauber
42a91ba26c Extras: renamed SVG utility class from com.formdev.flatlaf.extras.SVGUtils to com.formdev.flatlaf.extras.FlatSVGUtils 2020-12-12 12:21:48 +01:00
Karl Tauber
234003e2b1 Extras: Renamed tri-state check box class from
`com.formdev.flatlaf.extras.TriStateCheckBox` to
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`
2020-12-12 00:33:51 +01:00
Karl Tauber
534384438b Extras: added extension class for JTabbedPane (issue #117) 2020-12-11 23:44:52 +01:00
Karl Tauber
ab51f35d5d Extras: added extension classes for JEditorPane, JSpinner, JTextArea and JTextPane; added minimumWidth and roundRect properties (issue #117) 2020-12-11 18:05:58 +01:00
Karl Tauber
511a4044d7 Extras: added extension classes for JButton and JToggleButton (issue #117) 2020-12-11 17:18:35 +01:00
Karl Tauber
821efaff40 Extras: removed duplicate enums in text components (issue #117) 2020-12-11 14:01:42 +01:00
Karl Tauber
91bc994532 Extras: made enums in text components public (issue #117) 2020-12-11 13:39:51 +01:00
Karl Tauber
1323b46ac7 Extras: added extension class for JProgressBar (issue #117) 2020-12-11 13:28:55 +01:00
Karl Tauber
3a8b30ca8e Extras: removed extension interfaces and moved methods to components classes because:
- Javadoc for components that implement extension interfaces are useless because they do not include default methods from the extension interface
- GUI builders do not recognize default methods from the extension interface and it is not possible to edit extension properties in GUI builder
- the idea of adding the extension interface to own components can be also achieved by changing superclass of own component

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

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

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

issue #175
2020-09-18 14:24:30 +02:00
323 changed files with 20719 additions and 2267 deletions

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

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

View File

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

View File

@@ -1,6 +1,165 @@
FlatLaf Change Log
==================
## 0.46
#### New features and improvements
- Slider and JIDE RangeSlider: Clicking on track now immediately moves the thumb
to mouse location and starts dragging the thumb. Use `UIManager.put(
"Slider.scrollOnTrackClick", true )` to enable old behavior that scrolls the
thumb when clicking on track.
- Slider: Snap to ticks is now done while dragging the thumb. Use
`UIManager.put( "Slider.snapToTicksOnReleased", true )` to enable old behavior
that snaps to ticks on mouse released.
- Extras: Added standard component extension classes that provides easy access
to FlatLaf specific client properties (see package
`com.formdev.flatlaf.extras.components`).
- Extras: Renamed tri-state check box class from
`com.formdev.flatlaf.extras.TriStateCheckBox` to
`com.formdev.flatlaf.extras.components.FlatTriStateCheckBox`. Also
changed/improved API and added javadoc.
- Extras: Renamed SVG utility class from `com.formdev.flatlaf.extras.SVGUtils`
to `com.formdev.flatlaf.extras.FlatSVGUtils`.
- IntelliJ Themes: Added flag whether a theme is dark to
`FlatAllIJThemes.INFOS`. (issue #221)
- JIDE Common Layer: Support `TristateCheckBox`.
#### Fixed bugs
- Slider: Fixed painting of colored track if `JSlider.inverted` is `true`.
- Table and TableHeader: Fixed missing right vertical grid line if using table
as row header in scroll pane. (issues #152 and #46)
- TableHeader: Fixed position of column separators in right-to-left component
orientation.
- ToolTip: Fixed drop shadow for wide tooltips on Windows and Java 9+. (issue
#224)
- SwingX: Fixed striping background highlighting color (e.g. alternating table
rows) in dark themes.
- Fixed: If text antialiasing is disabled (in OS system settings or via
`-Dawt.useSystemAAFontSettings=off`), then some components still did use
antialiasing to render text (not-editable ComboBox, ProgressBar, Slider,
TabbedPane and multiline ToolTip). (issue #227)
## 0.45
#### New features and improvements
- Slider: New design, added hover and pressed feedback and improved customizing.
(PR #214)
- JIDE Common Layer: Support `RangeSlider`. (PR #209)
- IntelliJ Themes:
- Added "Gradianto Nature Green" theme.
- Updated "Arc Dark", "Cyan", "Dark purple", "Gradianto", "Gray", "Gruvbox"
and "One Dark" themes.
- TabbedPane: Support hiding tab area if it contains only one tab. (set client
property `JTabbedPane.hideTabAreaWithOneTab` to `true`)
- MenuBar: Support different underline menu selection style UI defaults for
`MenuBar` and `MenuItem`. (PR #217; issue #216)
#### Fixed bugs
- Table: Do not paint last vertical grid line if auto-resize mode is not off.
(issue #46)
- Table: Fixed unstable grid line thickness when scaled on HiDPI screens. (issue
#152)
- TabbedPane: No longer add (internal) tab close button component as child to
`JTabbedPane`. (issue #219)
- Custom window decorations: Title bar was not hidden if window is in
full-screen mode. (issue #212)
## 0.44
#### New features and improvements
- TabbedPane: In scroll tab layout, added "Show Hidden Tabs" button to trailing
side of tab area. If pressed, it shows a popup menu that contains (partly)
hidden tabs and selecting one activates that tab. (PR #190; issue #40)
- TabbedPane: Support forward/backward scroll arrow buttons on both sides of tab
area. Backward button on left side, forward button on right side. Not
applicable scroll buttons are hidden. (PR #211; issue #40)
- TabbedPane: Support specifying default tab layout policy for all tabbed panes
in the application via UI value `TabbedPane.tabLayoutPolicy`. E.g. invoke
`UIManager.put( "TabbedPane.tabLayoutPolicy", "scroll" );` to use scroll
layout.
- TabbedPane: Support tab scrolling with mouse wheel (in scroll tab layout). (PR
#187; issue #40)
- TabbedPane: Repeat scrolling as long as scroll arrow buttons are pressed. (PR
#187; issue #40)
- TabbedPane: Support adding custom components to left and right sides of tab
area. (set client property `JTabbedPane.leadingComponent` or
`JTabbedPane.trailingComponent` to a `java.awt.Component`) (PR #192; issue
#40)
- TabbedPane: Support closable tabs. (PR #193; issues #31 and #40)
- TabbedPane: Support minimum or maximum tab widths. (set client property
`JTabbedPane.minimumTabWidth` or `JTabbedPane.maximumTabWidth` to an integer)
(PR #199)
- TabbedPane: Support alignment of tab area. (set client property
`JTabbedPane.tabAreaAlignment` to `"leading"`, `"trailing"`, `"center"` or
`"fill"`) (PR #199)
- TabbedPane: Support horizontal alignment of tab title and icon. (set client
property `JTabbedPane.tabAlignment` to `SwingConstants.LEADING`,
`SwingConstants.TRAILING` or `SwingConstants.CENTER`)
- TabbedPane: Support equal and compact tab width modes. (set client property
`JTabbedPane.tabWidthMode` to `"preferred"`, `"equal"` or `"compact"`) (PR
#199)
- TabbedPane: Support left, right, top and bottom tab icon placement. (set
client property `JTabbedPane.tabIconPlacement` to `SwingConstants.LEADING`,
`SwingConstants.TRAILING`, `SwingConstants.TOP` or `SwingConstants.BOTTOM`)
(PR #199)
- Support painting separator line between window title and content (use UI value
`TitlePane.borderColor`). (issue #184)
- Extras: `FlatSVGIcon` now allows specifying icon width and height in
constructors. (issue #196)
- SplitPane: Hide not applicable expand/collapse buttons. Added tooltips to
expand/collapse buttons. (issue #198)
- SplitPane: Added grip to divider. Can be disabled with `UIManager.put(
"SplitPaneDivider.style", "plain" )`. (issue #179)
#### Fixed bugs
- Custom window decorations: Not visible menu bar is now ignored in layout.
- Popups using `JToolTip` components did not respect their location. (issue
#188; regression in 0.42 in fix for #164)
- IntelliJ Themes: Added suffix "(Material)" to names of all Material UI Lite
themes to avoid duplicate theme names. (issue #201)
- Extras: `FlatSVGIcon` icons were not painted in disabled labels and disabled
tabs. (issue #205)
## 0.43
#### New features and improvements
- TabbedPane: Made tabs separator color lighter in dark themes so that it is
easier to recognize the tabbed pane.
- TabbedPane: Added top and bottom tab insets to avoid that large tab icons are
painted over active tab underline.
- TabbedPane: Support hiding separator between tabs and content area (set client
property `JTabbedPane.showContentSeparator` to `false`).
- CheckBoxMenuItem and RadioButtonMenuItem: Improved checkmark background colors
of selected menu items that have also an icon. This makes it is easier to
recognize selected menu items.
- Windows: Made scaling compatible with Windows OS scaling, which distinguish
between "screen scaling" and "text scaling". (issue #175)
#### Fixed bugs
- ComboBox: If using own `JTextField` as editor, default text field border is
now removed to avoid duplicate border.
- ComboBox: Limit popup width to screen width for very long items. (issue #182)
- FileChooser: Fixed localizing special Windows folders (e.g. "Documents") and
enabled hiding known file extensions (if enabled in Windows Explorer). (issue
#178)
- Spinner: Fixed `NullPointerException` in case that arrow buttons were removed
to create button-less spinner. (issue #181)
## 0.42
#### New features and improvements

View File

@@ -76,36 +76,76 @@ Addons
Projects using FlatLaf
----------------------
- [NetBeans](https://netbeans.apache.org/) 11.3
- [Apache NetBeans](https://netbeans.apache.org/) 11.3 - IDE for Java, PHP, HTML
and much more
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
- ![New](images/new.svg) [jAlbum](https://jalbum.net/) 21 (commercial)
- ![New](images/new.svg) [OWASP ZAP](https://www.zaproxy.org/) 2.10 - the worlds
most widely used web app scanner
- ![New](images/new.svg) [JOSM](https://josm.openstreetmap.de/) - an extensible
editor for [OpenStreetMap](https://www.openstreetmap.org/) (requires FlatLaf
JOSM plugin)
- [jAlbum](https://jalbum.net/) 21 (commercial) - creates photo album websites
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (commercial)
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial)
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org)
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial) - checks
your website
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org) - Kanzleisoftware
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5 - a turn-based sci-fi board
game
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
0.13.b024
- [Rest Suite](https://github.com/supanadit/restsuite)
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy)
- [SpringRemote](https://github.com/HaleyWang/SpringRemote)
0.13.b024 - GUI builder for
[GUIslice](https://github.com/ImpulseAdventure/GUIslice), a lightweight GUI
framework for embedded displays
- [Rest Suite](https://github.com/supanadit/restsuite) - Rest API testing
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy) - advanced
gamepad mapping software
- [SpringRemote](https://github.com/HaleyWang/SpringRemote) - remote Linux SSH
connections manager
- ![New](images/new.svg) [jEnTunnel](https://github.com/ggrandes/jentunnel) -
manage SSH Tunnels made easy
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
[mendelson AS2](https://mendelson-e-c.com/as2/),
[AS4](https://mendelson-e-c.com/as4/) and
[OFTP2](https://mendelson-e-c.com/oftp2) (commercial)
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2
- [lsfusion platform](https://github.com/lsfusion/platform)
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.2 - GIS and scientific
computation environment for meteorological community
- [lsfusion platform](https://github.com/lsfusion/platform) - information
systems development platform
- ![New](images/new.svg) [JPass](https://github.com/gaborbata/jpass) - password
manager with strong encryption
- [Jes - Die Java-E<>R](https://www.jes-eur.de)
- [Mapton](https://mapton.org/) 2.0
([source code](https://github.com/trixon/mapton)) based on NetBeans platform
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE)
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis)
- [RemoteLight](https://github.com/Drumber/RemoteLight) - Multifunctional LED
Control Software
([source code](https://github.com/trixon/mapton)) based on NetBeans platform -
some kind of map application
- [Pseudo Assembler IDE](https://github.com/tomasz-herman/PseudoAssemblerIDE) -
IDE for Pseudo-Assembler
- ![New](images/new.svg) [Linotte](https://github.com/cpc6128/LangageLinotte)
3.1 - French programming language created to learn programming
- ![New](images/new.svg) [MEKA](https://github.com/Waikato/meka) 1.9.3 -
multi-label classifiers and evaluation procedures using the Weka machine
learning framework
- ![New](images/new.svg) [Shutter Encoder](https://www.shutterencoder.com/) 14.2
([source code](https://github.com/paulpacifico/shutter-encoder)) -
professional video converter and compression tool (screenshots show **old**
look)
- [Sound Analysis](https://github.com/tomasz-herman/SoundAnalysis) - analyze
sound files in time or frequency domain
- [RemoteLight](https://github.com/Drumber/RemoteLight) - multifunctional LED
control software
- ![New](images/new.svg)
[ThunderFocus](https://github.com/marcocipriani01/ThunderFocus) -
Arduino-based telescope focuser
- ![New](images/new.svg)
[Novel-Grabber](https://github.com/Flameish/Novel-Grabber) - download novels
from any webnovel and lightnovel site
- ![New](images/new.svg) [lectureStudio](https://www.lecturestudio.org/)
4.3.1060 - digitize your lectures with ease
- ![New](images/new.svg)
[Android Tool](https://github.com/fast-geek/Android-Tool) - makes popular adb
and fastboot commands easier to use
- and more...

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
val releaseVersion = "0.42"
val developmentVersion = "0.43-SNAPSHOT"
val releaseVersion = "0.46"
val developmentVersion = "0.47-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
@@ -48,7 +48,7 @@ extra["bintray.dryRun"] = false
// if true, uploaded artifacts are visible to all
// if false, only visible to owner when logged into bintray
extra["bintray.publish"] = true
extra["bintray.publish"] = false
allprojects {

View File

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

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

View File

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

View File

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

View File

@@ -50,9 +50,9 @@ import javax.swing.LookAndFeel;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
@@ -94,6 +94,10 @@ public abstract class FlatLaf
private Boolean oldFrameWindowDecorated;
private Boolean oldDialogWindowDecorated;
/**
* Sets the application look and feel to the given LaF
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
*/
public static boolean install( LookAndFeel newLookAndFeel ) {
try {
UIManager.setLookAndFeel( newLookAndFeel );
@@ -104,6 +108,16 @@ public abstract class FlatLaf
}
}
/**
* Adds the given look and feel to the set of available look and feels.
* <p>
* Useful if your application uses {@link UIManager#getInstalledLookAndFeels()}
* to query available LaFs and display them to the user in a combobox.
*/
public static void installLafInfo( String lafName, Class<? extends LookAndFeel> lafClass ) {
UIManager.installLookAndFeel( new UIManager.LookAndFeelInfo( lafName, lafClass.getName() ) );
}
/**
* Returns the look and feel identifier.
* <p>
@@ -170,6 +184,9 @@ public abstract class FlatLaf
@Override
public Icon getDisabledIcon( JComponent component, Icon icon ) {
if( icon instanceof DisabledIconProvider )
return ((DisabledIconProvider)icon).getDisabledIcon();
if( icon instanceof ImageIcon ) {
Object grayFilter = UIManager.get( "Component.grayFilter" );
ImageFilter filter = (grayFilter instanceof ImageFilter)
@@ -344,6 +361,10 @@ public abstract class FlatLaf
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
// add flag that indicates whether the LaF is light or dark
// (can be queried without using FlatLaf API)
defaults.put( "laf.dark", isDark() );
// add resource bundle for localized texts
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
@@ -463,9 +484,11 @@ public abstract class FlatLaf
uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
}
// fallback
if( uiFont == null )
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
// increase font size if system property "flatlaf.uiScale" is set
uiFont = UIScale.applyCustomScaleFactor( uiFont );
// use active value for all fonts to allow changing fonts in all components
@@ -696,6 +719,18 @@ public abstract class FlatLaf
MnemonicHandler.showMnemonics( false, null );
}
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
@Override
public final boolean equals( Object obj ) {
return super.equals( obj );
}
// do not allow overriding to avoid issues in FlatUIUtils.createSharedUI()
@Override
public final int hashCode() {
return super.hashCode();
}
//---- class ActiveFont ---------------------------------------------------
private static class ActiveFont
@@ -744,4 +779,24 @@ public abstract class FlatLaf
super( image );
}
}
//---- interface DisabledIconProvider -------------------------------------
/**
* A provider for disabled icons.
* <p>
* This is intended to be implemented by {@link javax.swing.Icon} implementations
* that provide the ability to paint disabled state.
* <p>
* Used in {@link FlatLaf#getDisabledIcon(JComponent, Icon)} to create a disabled icon from an enabled icon.
*/
public interface DisabledIconProvider
{
/**
* Returns an icon with a disabled appearance.
*
* @return a disabled icon
*/
Icon getDisabledIcon();
}
}

View File

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

View File

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

View File

@@ -92,6 +92,14 @@ public interface FlatSystemProperties
*/
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
/**
* Specifies whether animations are enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String ANIMATION = "flatlaf.animation";
/**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p>

View File

@@ -475,7 +475,9 @@ public class IntelliJTheme
}
}
/** Rename UI default keys (key --> value). */
private static Map<String, String> uiKeyMapping = new HashMap<>();
/** Copy UI default keys (value --> key). */
private static Map<String, String> uiKeyCopying = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
@@ -529,6 +531,9 @@ public class IntelliJTheme
// Slider
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
uiKeyCopying.put( "Slider.trackValueColor", "ProgressBar.foreground" );
uiKeyCopying.put( "Slider.thumbColor", "ProgressBar.foreground" );
uiKeyCopying.put( "Slider.trackColor", "ProgressBar.background" );
// TitlePane
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
@@ -583,7 +588,7 @@ public class IntelliJTheme
@Override
public String getDescription() {
return theme.name;
return getName();
}
@Override

View File

@@ -211,14 +211,17 @@ class UIDefaultsLoader
}
// override UI defaults with globals
for( Object okey : defaults.keySet() ) {
if( okey instanceof String && ((String)okey).contains( "." ) ) {
String key = (String) okey;
String globalKey = key.substring( key.lastIndexOf( '.' ) + 1 );
String globalValue = globals.get( globalKey );
if( globalValue != null && !properties.containsKey( key ) )
properties.put( key, globalValue );
}
for( Object key : defaults.keySet() ) {
int dot;
if( !(key instanceof String) ||
properties.containsKey( key ) ||
(dot = ((String)key).lastIndexOf( '.' )) < 0 )
continue;
String globalKey = ((String)key).substring( dot + 1 );
String globalValue = globals.get( globalKey );
if( globalValue != null )
properties.put( key, globalValue );
}
Function<String, String> propertiesGetter = key -> {
@@ -583,13 +586,17 @@ class UIDefaultsLoader
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 );
case "fadein": return parseColorHSLIncreaseDecrease( 3, true, params, resolver, reportError );
case "fadeout": return parseColorHSLIncreaseDecrease( 3, false, params, resolver, reportError );
case "fade": return parseColorFade( params, resolver, reportError );
case "spin": return parseColorSpin( params, resolver, reportError );
}
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
}
/**
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha)
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha)
* - red: an integer 0-255 or a percentage 0-100%
* - green: an integer 0-255 or a percentage 0-100%
* - blue: an integer 0-255 or a percentage 0-100%
@@ -600,6 +607,8 @@ class UIDefaultsLoader
{
if( hasAlpha && params.size() == 2 ) {
// syntax rgba(color,alpha), which allows adding alpha to any color
// NOTE: this syntax is deprecated
// use fade(color,alpha) instead
String colorStr = params.get( 0 );
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
@@ -636,7 +645,8 @@ class UIDefaultsLoader
/**
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
* saturate(color,amount[,options]) or desaturate(color,amount[,options])
* saturate(color,amount[,options]) or desaturate(color,amount[,options]) or
* fadein(color,amount[,options]) or fadeout(color,amount[,options])
* - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100%
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
@@ -676,6 +686,59 @@ class UIDefaultsLoader
};
}
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
/**
* Syntax: fade(color,amount[,options])
* - color: a color (e.g. #f00) or a color function
* - amount: percentage 0-100%
* - options: [derived]
*/
private static Object parseColorFade( List<String> params, Function<String, String> resolver, boolean reportError ) {
String colorStr = params.get( 0 );
int amount = parsePercentage( params.get( 1 ) );
boolean derived = false;
if( params.size() > 2 ) {
String options = params.get( 2 );
derived = options.contains( "derived" );
}
// create function
ColorFunction function = new ColorFunctions.Fade( amount );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
/**
* Syntax: spin(color,angle[,options])
* - color: a color (e.g. #f00) or a color function
* - angle: number of degrees to rotate
* - options: [derived]
*/
private static Object parseColorSpin( List<String> params, Function<String, String> resolver, boolean reportError ) {
String colorStr = params.get( 0 );
int amount = parseInteger( params.get( 1 ), true );
boolean derived = false;
if( params.size() > 2 ) {
String options = params.get( 2 );
derived = options.contains( "derived" );
}
// create function
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease( 0, true, amount, false, false );
// parse base color, apply function and create derived color
return parseFunctionBaseColor( colorStr, function, derived, resolver, reportError );
}
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
boolean derived, Function<String, String> resolver, boolean reportError )
{
// parse base color
String resolvedColorStr = resolver.apply( colorStr );
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -132,12 +132,8 @@ public class FlatButtonUI
private boolean defaults_initialized = false;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) {
if( instance == null )
instance = new FlatButtonUI();
return instance;
return FlatUIUtils.createSharedUI( FlatButtonUI.class, FlatButtonUI::new );
}
@Override
@@ -258,15 +254,23 @@ public class FlatButtonUI
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
}
// same indices as in parameters to clientPropertyChoice()
static final int TYPE_OTHER = -1;
static final int TYPE_SQUARE = 0;
static final int TYPE_ROUND_RECT = 1;
static int getButtonType( Component c ) {
return (c instanceof AbstractButton)
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT )
: TYPE_OTHER;
if( !(c instanceof AbstractButton) )
return TYPE_OTHER;
Object value = ((AbstractButton)c).getClientProperty( BUTTON_TYPE );
if( !(value instanceof String) )
return TYPE_OTHER;
switch( (String) value ) {
case BUTTON_TYPE_SQUARE: return TYPE_SQUARE;
case BUTTON_TYPE_ROUND_RECT: return TYPE_ROUND_RECT;
default: return TYPE_OTHER;
}
}
static boolean isHelpButton( Component c ) {

View File

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

View File

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

View File

@@ -24,10 +24,12 @@ import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
@@ -83,7 +85,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ComboBox.editorColumns int
* @uiDefault ComboBox.maximumRowCount int
* @uiDefault ComboBox.buttonStyle String auto (default), button or none
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color
@@ -298,6 +300,10 @@ public class FlatComboBoxUI
protected void configureEditor() {
super.configureEditor();
// remove default text field border from editor
if( editor instanceof JTextField && ((JTextField)editor).getBorder() instanceof FlatTextBorder )
((JTextField)editor).setBorder( BorderFactory.createEmptyBorder() );
// explicitly make non-opaque
if( editor instanceof JComponent )
((JComponent)editor).setOpaque( false );
@@ -346,7 +352,7 @@ public class FlatComboBoxUI
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth();
int height = c.getHeight();
@@ -380,6 +386,9 @@ public class FlatComboBoxUI
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
}
// avoid that the "current value" renderer is invoked with enabled antialiasing
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
paint( g, c );
}
@@ -563,6 +572,17 @@ public class FlatComboBoxUI
// make popup wider if necessary
if( displayWidth > pw ) {
// limit popup width to screen width
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
if( gc != null ) {
Rectangle screenBounds = gc.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
displayWidth = Math.min( displayWidth, screenBounds.width - screenInsets.left - screenInsets.right );
} else {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
displayWidth = Math.min( displayWidth, screenSize.width );
}
int diff = displayWidth - pw;
pw = displayWidth;

View File

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

View File

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

View File

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

View File

@@ -39,6 +39,7 @@ 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.DerivedColor;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -55,7 +56,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault MenuItem.underlineSelectionBackground Color
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
* @uiDefault MenuItem.underlineSelectionColor Color
* @uiDefault MenuItem.underlineSelectionHeight Color
* @uiDefault MenuItem.underlineSelectionHeight int
*
* @author Karl Tauber
*/
@@ -246,8 +247,11 @@ public class FlatMenuItemRenderer
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
debug*/
paintBackground( g, selectionBackground );
paintIcon( g, iconRect, getIconForPainting() );
boolean underlineSelection = isUnderlineSelection();
paintBackground( g, underlineSelection ? underlineSelectionBackground : selectionBackground );
if( underlineSelection && isArmedOrSelected( menuItem ) )
paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) )
@@ -257,36 +261,36 @@ debug*/
protected void paintBackground( Graphics g, Color selectionBackground ) {
boolean armedOrSelected = isArmedOrSelected( menuItem );
if( menuItem.isOpaque() || armedOrSelected ) {
int width = menuItem.getWidth();
int height = menuItem.getHeight();
// paint background
g.setColor( armedOrSelected
? (isUnderlineSelection()
? deriveBackground( underlineSelectionBackground )
: selectionBackground)
? deriveBackground( selectionBackground )
: menuItem.getBackground() );
g.fillRect( 0, 0, width, height );
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
}
}
// 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 paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
int width = menuItem.getWidth();
int height = menuItem.getHeight();
int underlineHeight = scale( underlineSelectionHeight );
g.setColor( underlineSelectionColor );
if( isTopLevelMenu( menuItem ) ) {
// paint underline at bottom
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
// paint underline at left side
g.fillRect( 0, 0, underlineHeight, height );
} else {
// paint underline at right side
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
}
}
protected Color deriveBackground( Color background ) {
if( !(background instanceof DerivedColor) )
return background;
Color baseColor = menuItem.isOpaque()
? menuItem.getBackground()
: FlatUIUtils.getParentBackground( menuItem );
@@ -294,12 +298,12 @@ debug*/
return FlatUIUtils.deriveColor( background, baseColor );
}
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) {
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon, Color checkBackground ) {
// if checkbox/radiobutton menu item is selected and also has a custom icon,
// then use filled icon background to indicate selection (instead of using checkIcon)
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
g.setColor( deriveBackground( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground ) );
g.setColor( deriveBackground( checkBackground ) );
g.fillRect( r.x, r.y, r.width, r.height );
}

View File

@@ -62,6 +62,12 @@ import javax.swing.plaf.basic.BasicMenuUI;
* @uiDefault MenuItem.iconTextGap int
* @uiDefault MenuBar.hoverBackground Color
*
* <!-- FlatMenuRenderer -->
*
* @uiDefault MenuBar.underlineSelectionBackground Color
* @uiDefault MenuBar.underlineSelectionColor Color
* @uiDefault MenuBar.underlineSelectionHeight int
*
* @author Karl Tauber
*/
public class FlatMenuUI
@@ -147,6 +153,10 @@ public class FlatMenuUI
protected class FlatMenuRenderer
extends FlatMenuItemRenderer
{
protected final Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
protected final Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
protected final int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
Font acceleratorFont, String acceleratorDelimiter )
{
@@ -155,6 +165,9 @@ public class FlatMenuUI
@Override
protected void paintBackground( Graphics g, Color selectionBackground ) {
if( isUnderlineSelection() && ((JMenu)menuItem).isTopLevelMenu() )
selectionBackground = menuBarUnderlineSelectionBackground;
ButtonModel model = menuItem.getModel();
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
@@ -164,5 +177,15 @@ public class FlatMenuUI
} else
super.paintBackground( g, selectionBackground );
}
@Override
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
if( ((JMenu)menuItem).isTopLevelMenu() ) {
underlineSelectionColor = menuBarUnderlineSelectionColor;
underlineSelectionHeight = menuBarUnderlineSelectionHeight;
}
super.paintUnderlineSelection( g, underlineSelectionColor, underlineSelectionHeight );
}
}
}

View File

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

View File

@@ -63,6 +63,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* @uiDefault PasswordField.showCapsLock boolean
* @uiDefault PasswordField.capsLockIcon Icon
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* @uiDefault TextComponent.selectAllOnMouseClick boolean
*
* @author Karl Tauber
*/
@@ -144,7 +145,8 @@ public class FlatPasswordFieldUI
@Override
protected Caret createCaret() {
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ),
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
}
@Override

View File

@@ -68,19 +68,17 @@ public class FlatPopupFactory
y = pt.y;
}
if( !isDropShadowPainted( owner, contents ) )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), contents );
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
Popup popup = getPopupForScreenOfOwner( owner, contents, x, y, true );
if( popup == null )
popup = getPopupForScreenOfOwner( owner, contents, x, y, false );
return new NonFlashingPopup( popup, contents );
}
if( SystemInfo.isMacOS || SystemInfo.isLinux )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
// create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, false ), owner, contents );
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
}
/**
@@ -155,24 +153,20 @@ public class FlatPopupFactory
popup.show();
}
private boolean isDropShadowPainted( Component owner, Component contents ) {
Boolean b = isDropShadowPainted( owner );
if( b != null )
return b;
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
if( owner instanceof JComponent ) {
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) owner, clientKey, null );
if( b != null )
return b;
}
b = isDropShadowPainted( contents );
if( b != null )
return b;
if( contents instanceof JComponent ) {
Boolean b = FlatClientProperties.clientPropertyBooleanStrict( (JComponent) contents, clientKey, null );
if( b != null )
return b;
}
return UIManager.getBoolean( "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;
return UIManager.getBoolean( uiKey );
}
/**
@@ -218,7 +212,7 @@ public class FlatPopupFactory
* and corrects the y-location so that the tooltip is placed above the mouse location.
*/
private Point fixToolTipLocation( Component owner, Component contents, int x, int y ) {
if( !(contents instanceof JToolTip) )
if( !(contents instanceof JToolTip) || !wasInvokedFromToolTipManager() )
return null;
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
@@ -233,6 +227,16 @@ public class FlatPopupFactory
return new Point( x, mouseLocation.y - tipSize.height - UIScale.scale( 20 ) );
}
private boolean wasInvokedFromToolTipManager() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for( StackTraceElement stackTraceElement : stackTrace ) {
if( "javax.swing.ToolTipManager".equals( stackTraceElement.getClassName() ) &&
"showTipWindow".equals( stackTraceElement.getMethodName() ) )
return true;
}
return false;
}
//---- class NonFlashingPopup ---------------------------------------------
private class NonFlashingPopup
@@ -267,16 +271,17 @@ public class FlatPopupFactory
// increase tooltip size if necessary because it may be too small on HiDPI screens
// https://bugs.openjdk.java.net/browse/JDK-8213535
if( contents instanceof JToolTip ) {
if( contents instanceof JToolTip && popupWindow == null ) {
Container parent = contents.getParent();
if( parent instanceof JPanel ) {
Dimension prefSize = parent.getPreferredSize();
if( !prefSize.equals( parent.getSize() ) ) {
Container panel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
if( panel != null )
panel.setSize( prefSize ); // for medium weight popup
else
parent.setSize( prefSize ); // for light weight popup
Container mediumWeightPanel = SwingUtilities.getAncestorOfClass( Panel.class, parent );
Container c = (mediumWeightPanel != null)
? mediumWeightPanel // medium weight popup
: parent; // light weight popup
c.setSize( prefSize );
c.validate();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,6 @@ 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;
@@ -65,7 +64,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ScrollBar.pressedTrackColor Color optional
* @uiDefault ScrollBar.pressedThumbColor Color optional
* @uiDefault ScrollBar.pressedThumbWithTrack boolean
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault ScrollBar.showButtons boolean
* @uiDefault ScrollBar.buttonArrowColor Color
* @uiDefault ScrollBar.buttonDisabledArrowColor Color
@@ -142,6 +141,12 @@ public class FlatScrollBarUI
buttonDisabledArrowColor = UIManager.getColor( "ScrollBar.buttonDisabledArrowColor" );
hoverButtonBackground = UIManager.getColor( "ScrollBar.hoverButtonBackground" );
pressedButtonBackground = UIManager.getColor( "ScrollBar.pressedButtonBackground" );
// fallback (e.g. when used in NetBeans GUI builder)
if( trackInsets == null )
trackInsets = new Insets( 0, 0, 0, 0 );
if( thumbInsets == null )
thumbInsets = new Insets( 0, 0, 0, 0 );
}
@Override
@@ -215,8 +220,9 @@ public class FlatScrollBarUI
@Override
public void paint( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
@Override

View File

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

View File

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

View File

@@ -59,7 +59,7 @@ import com.formdev.flatlaf.FlatClientProperties;
*
* @uiDefault Component.minimumWidth int
* @uiDefault Spinner.buttonStyle String button (default) or none
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color
@@ -264,35 +264,38 @@ public class FlatSpinnerUI
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth();
int height = c.getHeight();
Component nextButton = getHandler().nextButton;
int arrowX = nextButton.getX();
int arrowWidth = nextButton.getWidth();
boolean paintButton = !"none".equals( buttonStyle );
boolean enabled = spinner.isEnabled();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
// paint background
g2.setColor( getBackground( enabled ) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
// paint arrow buttons background
if( paintButton && enabled ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint button background and separator
boolean paintButton = !"none".equals( buttonStyle );
Handler handler = getHandler();
if( paintButton && (handler.nextButton != null || handler.previousButton != null) ) {
Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
int arrowX = button.getX();
int arrowWidth = button.getWidth();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
// paint vertical line between value and arrow buttons
if( paintButton ) {
// paint arrow buttons background
if( enabled ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint vertical line between value and arrow buttons
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
@@ -300,6 +303,8 @@ public class FlatSpinnerUI
}
paint( g, c );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
//---- class Handler ------------------------------------------------------
@@ -359,7 +364,7 @@ public class FlatSpinnerUI
if( nextButton == null && previousButton == null ) {
if( editor != null )
editor.setBounds( r );
editor.setBounds( FlatUIUtils.subtractInsets( r, padding ) );
return;
}

View File

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

View File

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

View File

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

View File

@@ -64,6 +64,7 @@ import com.formdev.flatlaf.util.HiDPIUtils;
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextField.placeholderForeground Color
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
* @uiDefault TextComponent.selectAllOnMouseClick boolean
*
* @author Karl Tauber
*/
@@ -121,7 +122,8 @@ public class FlatTextFieldUI
@Override
protected Caret createCaret() {
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy"),
UIManager.getBoolean( "TextComponent.selectAllOnMouseClick" ) );
}
@Override

View File

@@ -76,6 +76,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.foreground Color
* @uiDefault TitlePane.inactiveForeground Color
* @uiDefault TitlePane.embeddedForeground Color
* @uiDefault TitlePane.borderColor Color optional
* @uiDefault TitlePane.iconSize Dimension
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
@@ -97,6 +98,7 @@ public class FlatTitlePane
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
protected final Color inactiveForeground = UIManager.getColor( "TitlePane.inactiveForeground" );
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
protected final Insets menuBarMargins = UIManager.getInsets( "TitlePane.menuBarMargins" );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
@@ -157,7 +159,7 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && isMenuBarEmbedded())
return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded())
? FlatUIUtils.addInsets( menuBar.getPreferredSize(), UIScale.scale( menuBarMargins ) )
: new Dimension();
}
@@ -238,7 +240,7 @@ public class FlatTitlePane
}
protected void activeChanged( boolean active ) {
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && isMenuBarEmbedded();
boolean hasEmbeddedMenuBar = rootPane.getJMenuBar() != null && rootPane.getJMenuBar().isVisible() && isMenuBarEmbedded();
Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground );
Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground );
Color titleForeground = (hasEmbeddedMenuBar && active) ? FlatUIUtils.nonUIResource( embeddedForeground ) : foreground;
@@ -422,6 +424,10 @@ public class FlatTitlePane
protected void menuBarChanged() {
menuBarPlaceholder.invalidate();
// necessary for the case that an embedded menu bar is made invisible
// and a border color is specified
repaint();
// update title foreground color
EventQueue.invokeLater( () -> {
activeChanged( window == null || window.isActive() );
@@ -662,12 +668,13 @@ debug*/
public Insets getBorderInsets( Component c, Insets insets ) {
super.getBorderInsets( c, insets );
// if menu bar is embedded, add bottom insets of menu bar border
Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) {
// if menu bar is embedded, add bottom insets of menu bar border
Insets menuBarInsets = menuBarBorder.getBorderInsets( c );
insets.bottom += menuBarInsets.bottom;
}
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 );
if( hasJBRCustomDecoration() )
insets = FlatUIUtils.addInsets( insets, JBRWindowTopBorder.getInstance().getBorderInsets() );
@@ -677,10 +684,16 @@ debug*/
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// if menu bar is embedded, paint menu bar border
// paint bottom border
Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null )
if( menuBarBorder != null ) {
// if menu bar is embedded, paint menu bar border
menuBarBorder.paintBorder( c, g, x, y, width, height );
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) {
// paint border between title pane and content if border color is specified
float lineHeight = UIScale.scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
if( hasJBRCustomDecoration() )
JBRWindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
@@ -688,7 +701,7 @@ debug*/
protected Border getMenuBarBorder() {
JMenuBar menuBar = rootPane.getJMenuBar();
return (menuBar != null && isMenuBarEmbedded()) ? menuBar.getBorder() : null;
return (menuBar != null && menuBar.isVisible() && isMenuBarEmbedded()) ? menuBar.getBorder() : null;
}
}

View File

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

View File

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

View File

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

View File

@@ -52,12 +52,8 @@ public class FlatToolTipUI
{
private static PropertyChangeListener sharedPropertyChangedListener;
private static ComponentUI instance;
public static ComponentUI createUI( JComponent c ) {
if( instance == null )
instance = new FlatToolTipUI();
return instance;
return FlatUIUtils.createSharedUI( FlatToolTipUI.class, FlatToolTipUI::new );
}
@Override
@@ -120,7 +116,6 @@ public class FlatToolTipUI
FontMetrics fm = c.getFontMetrics( c.getFont() );
Insets insets = c.getInsets();
FlatUIUtils.setRenderingHints( (Graphics2D) g );
g.setColor( c.getForeground() );
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );

View File

@@ -23,19 +23,26 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.IdentityHashMap;
import java.util.WeakHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.LookAndFeel;
@@ -43,6 +50,7 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.DerivedColor;
@@ -59,6 +67,8 @@ public class FlatUIUtils
{
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
private static WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
public static Rectangle addInsets( Rectangle r, Insets insets ) {
return new Rectangle(
r.x - insets.left,
@@ -121,6 +131,10 @@ public class FlatUIUtils
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
}
public static boolean isChevron( String arrowType ) {
return !"triangle".equals( arrowType );
}
public static Color nonUIResource( Color c ) {
return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c;
}
@@ -170,6 +184,16 @@ public class FlatUIUtils
keyboardFocusManager.getActiveWindow() == SwingUtilities.windowForComponent( c );
}
/**
* Returns whether the given component is in a window that is in full-screen mode.
*/
public static boolean isFullScreen( Component c ) {
GraphicsConfiguration gc = c.getGraphicsConfiguration();
GraphicsDevice gd = (gc != null) ? gc.getDevice() : null;
Window fullScreenWindow = (gd != null) ? gd.getFullScreenWindow() : null;
return (fullScreenWindow != null && fullScreenWindow == SwingUtilities.windowForComponent( c ));
}
public static Boolean isRoundRect( Component c ) {
return (c instanceof JComponent)
? FlatClientProperties.clientPropertyBooleanStrict(
@@ -216,10 +240,57 @@ public class FlatUIUtils
/**
* Sets rendering hints used for painting.
*/
public static void setRenderingHints( Graphics2D g ) {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
public static Object[] setRenderingHints( Graphics g ) {
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
return oldRenderingHints;
}
/**
* Resets rendering hints previously set with {@link #setRenderingHints}.
*/
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
}
/**
* Temporary resets rendering hints set with {@link #setRenderingHints}
* and runs the given runnable.
* <p>
* This is intended for painting text while rendering hints are set.
* <p>
* If text antialiasing is disabled (in OS system settings or via
* {@code -Dawt.useSystemAAFontSettings=off}), but general antialiasing is enabled,
* then text is still painted using some kind of "grayscale" antialiasing,
* which may make the text look bold (depends on font and font size).
* To avoid this, temporary disable general antialiasing.
* This does not affect text rendering if text antialiasing is enabled (usually the default).
*/
public static void runWithoutRenderingHints( Graphics g, Object[] oldRenderingHints, Runnable runnable ) {
if( oldRenderingHints == null ) {
runnable.run();
return;
}
Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints2 = new Object[] {
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
};
resetRenderingHints( g2, oldRenderingHints );
runnable.run();
resetRenderingHints( g2, oldRenderingHints2 );
}
public static Color deriveColor( Color color, Color baseColor ) {
@@ -373,6 +444,52 @@ public class FlatUIUtils
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
}
static void paintFilledRectangle( Graphics g, Color color, float x, float y, float w, float h ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( color );
g2.fill( new Rectangle2D.Float( x, y, w, h ) );
} finally {
g2.dispose();
}
}
public static void paintGrip( Graphics g, int x, int y, int width, int height,
boolean horizontal, int dotCount, int dotSize, int gap, boolean centerPrecise )
{
dotSize = UIScale.scale( dotSize );
gap = UIScale.scale( gap );
int gripSize = (dotSize * dotCount) + ((gap * (dotCount - 1)));
// calculate grip position
float gx;
float gy;
if( horizontal ) {
gx = x + Math.round( (width - gripSize) / 2f );
gy = y + ((height - dotSize) / 2f);
if( !centerPrecise )
gy = Math.round( gy );
} else {
// vertical
gx = x + ((width - dotSize) / 2f);
gy = y + Math.round( (height - gripSize) / 2f );
if( !centerPrecise )
gx = Math.round( gx );
}
// paint dots
for( int i = 0; i < dotCount; i++ ) {
((Graphics2D)g).fill( new Ellipse2D.Float( gx, gy, dotSize, dotSize ) );
if( horizontal )
gx += dotSize + gap;
else
gy += dotSize + gap;
}
}
/**
* Fill background with parent's background color because the visible component
* is smaller than its bounds (for the focus decoration).
@@ -449,15 +566,31 @@ public class FlatUIUtils
float x2 = x + width;
float y2 = y + height;
// same constant as in java.awt.geom.EllipseIterator.CtrlVal used to paint circles
double c = 0.5522847498307933;
double ci = 1. - c;
double ciTopLeft = arcTopLeft * ci;
double ciTopRight = arcTopRight * ci;
double ciBottomLeft = arcBottomLeft * ci;
double ciBottomRight = arcBottomRight * ci;
Path2D rect = new Path2D.Float();
rect.moveTo( x2 - arcTopRight, y );
rect.quadTo( x2, y, x2, y + arcTopRight );
rect.lineTo( x2, y2 - arcBottomRight );
rect.quadTo( x2, y2, x2 - arcBottomRight, y2 );
rect.lineTo( x + arcBottomLeft, y2 );
rect.quadTo( x, y2, x, y2 - arcBottomLeft );
rect.lineTo( x, y + arcTopLeft );
rect.quadTo( x, y, x + arcTopLeft, y );
rect.moveTo( x2 - arcTopRight, y );
rect.curveTo( x2 - ciTopRight, y,
x2, y + ciTopRight,
x2, y + arcTopRight );
rect.lineTo( x2, y2 - arcBottomRight );
rect.curveTo( x2, y2 - ciBottomRight,
x2 - ciBottomRight, y2,
x2 - arcBottomRight, y2 );
rect.lineTo( x + arcBottomLeft, y2 );
rect.curveTo( x + ciBottomLeft, y2,
x, y2 - ciBottomLeft,
x, y2 - arcBottomLeft );
rect.lineTo( x, y + arcTopLeft );
rect.curveTo( x, y + ciTopLeft,
x + ciTopLeft, y,
x + arcTopLeft, y );
rect.closePath();
return rect;
@@ -534,6 +667,19 @@ public class FlatUIUtils
return explicitlySet;
}
/**
* Creates a shared component UI for the given key and the current Laf.
* Each Laf instance has its own shared component UI instance.
* <p>
* This is for GUI builders that support Laf switching and
* may use multiple Laf instances at the same time.
*/
public static ComponentUI createSharedUI( Object key, Supplier<ComponentUI> newInstanceSupplier ) {
return sharedUIinstances
.computeIfAbsent( UIManager.getLookAndFeel(), k -> new IdentityHashMap<>() )
.computeIfAbsent( key, k -> newInstanceSupplier.get() );
}
//---- class HoverListener ------------------------------------------------
public static class HoverListener

View File

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

View File

@@ -256,6 +256,8 @@ public abstract class FlatWindowResizer
@Override
protected boolean isWindowResizable() {
if( FlatUIUtils.isFullScreen( resizeComp ) )
return false;
if( window instanceof Frame )
return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
if( window instanceof Dialog )
@@ -429,9 +431,9 @@ public abstract class FlatWindowResizer
protected void paintComponent( Graphics g ) {
super.paintChildren( g );
// this is necessary because Dialog.setResizable() does not fire events
if( isDialog() )
updateVisibility();
// for dialogs: necessary because Dialog.setResizable() does not fire events
// for frames: necessary because GraphicsDevice.setFullScreenWindow() does not fire events
updateVisibility();
/*debug
int width = getWidth();

View File

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

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.util;
import java.util.ArrayList;
import javax.swing.Timer;
import com.formdev.flatlaf.FlatSystemProperties;
/**
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
@@ -39,6 +40,15 @@ public class Animator
private long startTime;
private Timer timer;
/**
* Checks whether animations are enabled (the default) or disabled via
* system property {@code flatlaf.animation} set to {@code false}.
* This allows disabling all animations at command line with {@code -Dflatlaf.animation=false}.
*/
public static boolean useAnimation() {
return FlatSystemProperties.getBoolean( FlatSystemProperties.ANIMATION, true );
}
/**
* Creates an animation that runs duration milliseconds.
* Use {@link #addTarget(TimingTarget)} to receive timing events
@@ -86,7 +96,7 @@ public class Animator
* Sets the duration of the animation in milliseconds.
*
* @throws IllegalStateException if animation is running
* @throws IllegalArgumentException if duration is <= zero
* @throws IllegalArgumentException if duration is &lt;= zero
*/
public void setDuration( int duration ) {
throwExceptionIfRunning();
@@ -108,7 +118,7 @@ public class Animator
*
* @param resolution the resolution of the animation in milliseconds
* @throws IllegalStateException if animation is running
* @throws IllegalArgumentException if resolution is <= zero
* @throws IllegalArgumentException if resolution is &lt;= zero
*/
public void setResolution( int resolution ) {
throwExceptionIfRunning();
@@ -174,14 +184,17 @@ public class Animator
timeToStop = false;
startTime = System.nanoTime() / 1000000;
timer = new Timer( resolution, e -> {
if( !hasBegun ) {
begin();
hasBegun = true;
}
if( timer == null ) {
timer = new Timer( resolution, e -> {
if( !hasBegun ) {
begin();
hasBegun = true;
}
timingEvent( getTimingFraction() );
} );
timingEvent( getTimingFraction() );
} );
} else
timer.setDelay( resolution );
timer.setInitialDelay( 0 );
timer.start();
}
@@ -203,10 +216,11 @@ public class Animator
}
private void stop( boolean cancel ) {
if( timer != null ) {
if( !running )
return;
if( timer != null )
timer.stop();
timer = null;
}
if( !cancel )
end();
@@ -215,6 +229,15 @@ public class Animator
timeToStop = false;
}
/**
* Restarts the animation.
* Invokes {@link #cancel()} and {@link #start()}.
*/
public void restart() {
cancel();
start();
}
/**
* Returns whether this animation is running.
*/

View File

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

View File

@@ -24,6 +24,13 @@ package com.formdev.flatlaf.util;
public class CubicBezierEasing
implements Animator.Interpolator
{
/**
* Standard easing as specified in Material design (0.4, 0, 0.2, 1).
*
* @see <a href="https://material.io/design/motion/speed.html#easing">https://material.io/design/motion/speed.html#easing</a>
*/
public static final CubicBezierEasing STANDARD_EASING = new CubicBezierEasing( 0.4f, 0f, 0.2f, 1f );
// common cubic-bezier easing functions (same as in CSS)
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function
public static final CubicBezierEasing EASE = new CubicBezierEasing( 0.25f, 0.1f, 0.25f, 1f );

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Toolkit;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
@@ -112,7 +113,7 @@ public class UIScale
}
public static double getSystemScaleFactor( Graphics2D g ) {
return isSystemScalingEnabled() ? g.getDeviceConfiguration().getDefaultTransform().getScaleX() : 1;
return isSystemScalingEnabled() ? getSystemScaleFactor( g.getDeviceConfiguration() ) : 1;
}
public static double getSystemScaleFactor( GraphicsConfiguration gc ) {
@@ -162,6 +163,13 @@ public class UIScale
if( !isUserScalingEnabled() )
return;
// apply custom scale factor specified in system property "flatlaf.uiScale"
float customScaleFactor = getCustomScaleFactor();
if( customScaleFactor > 0 ) {
setUserScaleFactor( customScaleFactor );
return;
}
// use font size to calculate scale factor (instead of DPI)
// because even if we are on a HiDPI display it is not sure
// that a larger font size is set by the current LaF
@@ -170,7 +178,45 @@ public class UIScale
if( font == null )
font = UIManager.getFont( "Label.font" );
setUserScaleFactor( computeScaleFactor( font ) );
float newScaleFactor;
if( SystemInfo.isWindows ) {
// Special handling for Windows to be compatible with OS scaling,
// which distinguish between "screen scaling" and "text scaling".
// - Windows "screen scaling" scales everything (text, icon, gaps, etc)
// and may have different scaling factors for each screen.
// - Windows "text scaling" increases only the font size, but on all screens.
//
// Both can be changed by the user in the Windows 10 Settings:
// - Settings > Display > Scale and layout
// - Settings > Ease of Access > Display > Make text bigger (100% - 225%)
if( font instanceof UIResource ) {
if( isSystemScalingEnabled() ) {
// Do not apply own scaling if the JRE scales using Windows screen scale factor.
// If user increases font size in Windows 10 settings, desktop property
// "win.messagebox.font" is changed and FlatLaf uses the larger font.
newScaleFactor = 1;
} else {
// If the JRE does not scale (Java 8), the size of the UI font
// (usually from desktop property "win.messagebox.font")
// combines the Windows screen and text scale factors.
// But the font in desktop property "win.defaultGUI.font" is only
// scaled with the Windows screen scale factor. So use it to compute
// our scale factor that is equal to Windows screen scale factor.
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.defaultGUI.font" );
newScaleFactor = computeScaleFactor( (winFont != null) ? winFont : font );
}
} else {
// If font was explicitly set from outside (is not a UIResource)
// use it to compute scale factor. This allows applications to
// use custom fonts (e.g. that the user can change in UI) and
// get scaling if a larger font size is used.
// E.g. FlatLaf Demo supports increasing font size in "Font" menu and UI scales.
newScaleFactor = computeScaleFactor( font );
}
} else
newScaleFactor = computeScaleFactor( font );
setUserScaleFactor( newScaleFactor );
}
private static float computeScaleFactor( Font font ) {
@@ -206,8 +252,7 @@ public class UIScale
if( !isUserScalingEnabled() )
return font;
String uiScale = System.getProperty( FlatSystemProperties.UI_SCALE );
float scaleFactor = parseScaleFactor( uiScale );
float scaleFactor = getCustomScaleFactor();
if( scaleFactor <= 0 )
return font;
@@ -219,6 +264,13 @@ public class UIScale
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
}
/**
* Get custom scale factor specified in system property "flatlaf.uiScale".
*/
private static float getCustomScaleFactor() {
return parseScaleFactor( System.getProperty( FlatSystemProperties.UI_SCALE ) );
}
/**
* Similar to sun.java2d.SunGraphicsEnvironment.getScaleFactor(String)
*/

View File

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

View File

@@ -30,8 +30,7 @@
@textComponentBackground=#45494A
@menuBackground=darken(@background,5%)
@menuHoverBackground=lighten(@menuBackground,10%,derived)
@menuCheckBackground=lighten(@menuBackground,10%,derived)
@menuCheckHoverBackground=lighten(@menuBackground,20%,derived)
@menuCheckBackground=darken(@selectionBackground,10%)
@menuAcceleratorForeground=darken(@foreground,15%)
@menuAcceleratorSelectionForeground=@selectionForeground
@cellFocusColor=#000
@@ -238,11 +237,15 @@ Separator.foreground=#515151
#---- Slider ----
Slider.trackValueColor=#4A88C7
Slider.trackColor=#646464
Slider.thumbColor=#A6A6A6
Slider.thumbColor=$Slider.trackValueColor
Slider.tickColor=#888
Slider.hoverColor=darken($Slider.thumbColor,15%,derived)
Slider.disabledForeground=#4c5052
Slider.focusedColor=fade($Component.focusColor,70%,derived)
Slider.hoverThumbColor=darken($Slider.thumbColor,10%,derived)
Slider.pressedThumbColor=darken($Slider.thumbColor,15%,derived)
Slider.disabledTrackColor=#4c5052
Slider.disabledThumbColor=$Slider.disabledTrackColor
#---- SplitPane ----
@@ -255,14 +258,24 @@ SplitPaneDivider.oneTouchHoverArrowColor=#7A7D81
TabbedPane.underlineColor=#4A88C7
TabbedPane.disabledUnderlineColor=#7a7a7a
TabbedPane.hoverColor=#2e3133
TabbedPane.hoverColor=darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.focusColor=#3d4b5c
TabbedPane.contentAreaColor=#323232
TabbedPane.contentAreaColor=#646464
TabbedPane.buttonHoverBackground=darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.buttonPressedBackground=darken($TabbedPane.background,8%,derived noAutoInverse)
TabbedPane.closeBackground=null
TabbedPane.closeForeground=@disabledText
TabbedPane.closeHoverBackground=lighten($TabbedPane.background,5%,derived)
TabbedPane.closeHoverForeground=@foreground
TabbedPane.closePressedBackground=lighten($TabbedPane.background,10%,derived)
TabbedPane.closePressedForeground=$TabbedPane.closeHoverForeground
#---- Table ----
Table.gridColor=lighten($Table.background,3%)
Table.gridColor=lighten($Table.background,5%)
#---- TableHeader ----

View File

@@ -199,6 +199,7 @@ ComboBox.minimumWidth=72
ComboBox.editorColumns=0
ComboBox.maximumRowCount=15
[mac]ComboBox.showPopupOnNavigation=true
# allowed values: auto, button or none
ComboBox.buttonStyle=auto
ComboBox.background=@textComponentBackground
ComboBox.buttonBackground=@textComponentBackground
@@ -211,6 +212,7 @@ Component.innerFocusWidth={float}0.5
Component.innerOutlineWidth={float}1
Component.arc=5
Component.minimumWidth=64
# allowed values: chevron or triangle
Component.arrowType=chevron
Component.hideMnemonics=true
@@ -238,6 +240,7 @@ FileChooser.homeFolderIcon=com.formdev.flatlaf.icons.FlatFileChooserHomeFolderIc
FileChooser.detailsViewIcon=com.formdev.flatlaf.icons.FlatFileChooserDetailsViewIcon
FileChooser.listViewIcon=com.formdev.flatlaf.icons.FlatFileChooserListViewIcon
FileChooser.usesSingleFilePane=true
[win]FileChooser.useSystemExtensionHiding=true
#---- FileView ----
@@ -358,7 +361,7 @@ MenuItem.acceleratorDelimiter=-
# for MenuItem.selectionType=underline
MenuItem.underlineSelectionBackground=@menuHoverBackground
MenuItem.underlineSelectionCheckBackground=@menuCheckHoverBackground
MenuItem.underlineSelectionCheckBackground=@menuCheckBackground
MenuItem.underlineSelectionColor=$TabbedPane.underlineColor
MenuItem.underlineSelectionHeight=3
@@ -503,8 +506,9 @@ Separator.stripeIndent=1
#---- Slider ----
Slider.focusInsets=0,0,0,0
Slider.trackWidth=3
Slider.thumbWidth=11
Slider.trackWidth=2
Slider.thumbSize=12,12
Slider.focusWidth=4
#---- Spinner ----
@@ -517,6 +521,7 @@ Spinner.buttonDisabledArrowColor=$ComboBox.buttonDisabledArrowColor
Spinner.buttonHoverArrowColor=$ComboBox.buttonHoverArrowColor
Spinner.padding=@textComponentMargin
Spinner.editorBorderPainted=false
# allowed values: button or none
Spinner.buttonStyle=button
@@ -531,6 +536,12 @@ SplitPane.oneTouchButtonOffset={scaledInteger}2
SplitPaneDivider.border=null
SplitPaneDivider.oneTouchArrowColor=$ComboBox.buttonArrowColor
# allowed values: grip or plain
SplitPaneDivider.style=grip
SplitPaneDivider.gripColor=@icon
SplitPaneDivider.gripDotCount=3
SplitPaneDivider.gripDotSize={integer}3
SplitPaneDivider.gripGap=2
#---- TabbedPane ----
@@ -538,15 +549,46 @@ SplitPaneDivider.oneTouchArrowColor=$ComboBox.buttonArrowColor
TabbedPane.tabHeight=32
TabbedPane.tabSelectionHeight=3
TabbedPane.contentSeparatorHeight=1
TabbedPane.showTabSeparators=false
TabbedPane.tabSeparatorsFullHeight=false
TabbedPane.hasFullBorder=false
TabbedPane.tabInsets=0,12,0,12
TabbedPane.tabInsets=4,12,4,12
TabbedPane.tabAreaInsets=0,0,0,0
TabbedPane.selectedTabPadInsets=0,0,0,0
TabbedPane.tabRunOverlay=0
TabbedPane.tabsOverlapBorder=true
TabbedPane.tabsOverlapBorder=false
TabbedPane.disabledForeground=@disabledText
TabbedPane.shadow=@background
TabbedPane.contentBorderInsets=null
# allowed values: moreTabsButton or arrowButtons
TabbedPane.hiddenTabsNavigation=moreTabsButton
# allowed values: leading, trailing, center or fill
TabbedPane.tabAreaAlignment=leading
# allowed values: leading, trailing or center
TabbedPane.tabAlignment=center
# allowed values: preferred, equal or compact
TabbedPane.tabWidthMode=preferred
# allowed values: chevron or triangle
TabbedPane.arrowType=chevron
TabbedPane.buttonInsets=2,1,2,1
TabbedPane.buttonArc=$Button.arc
# allowed values: wrap or scroll
#TabbedPane.tabLayoutPolicy=scroll
# allowed values: never or asNeeded
TabbedPane.tabsPopupPolicy=asNeeded
# allowed values: never, asNeeded or asNeededSingle
TabbedPane.scrollButtonsPolicy=asNeededSingle
# allowed values: both or trailing
TabbedPane.scrollButtonsPlacement=both
TabbedPane.closeIcon=com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon
TabbedPane.closeSize=16,16
TabbedPane.closeArc=4
TabbedPane.closeCrossPlainSize={float}7.5
TabbedPane.closeCrossFilledSize=$TabbedPane.closeCrossPlainSize
TabbedPane.closeCrossLineWidth={float}1
#---- Table ----
@@ -593,8 +635,9 @@ TextArea.background=@textComponentBackground
#---- TextComponent ----
# allowed values: "never", "once" (default) or "always"
# allowed values: never, once or always
TextComponent.selectAllOnFocusPolicy=once
TextComponent.selectAllOnMouseClick=false
TextComponent.arc=0
@@ -639,7 +682,7 @@ TitlePane.foreground=@foreground
TitlePane.inactiveForeground=@disabledText
TitlePane.closeHoverBackground=#e81123
TitlePane.closePressedBackground=rgba($TitlePane.closeHoverBackground,60%)
TitlePane.closePressedBackground=fade($TitlePane.closeHoverBackground,60%)
TitlePane.closeHoverForeground=#fff
TitlePane.closePressedForeground=#fff

View File

@@ -30,8 +30,7 @@
@textComponentBackground=#fff
@menuBackground=#fff
@menuHoverBackground=darken(@menuBackground,10%,derived)
@menuCheckBackground=darken(@menuBackground,10%,derived)
@menuCheckHoverBackground=darken(@menuBackground,20%,derived)
@menuCheckBackground=lighten(@selectionBackground,40%)
@menuAcceleratorForeground=lighten(@foreground,30%)
@menuAcceleratorSelectionForeground=@selectionForeground
@cellFocusColor=#000
@@ -250,11 +249,15 @@ Separator.foreground=#d1d1d1
#---- Slider ----
Slider.trackValueColor=#1E82E6
Slider.trackColor=#c4c4c4
Slider.thumbColor=#6e6e6e
Slider.thumbColor=$Slider.trackValueColor
Slider.tickColor=#888
Slider.hoverColor=lighten($Slider.thumbColor,15%,derived)
Slider.disabledForeground=#c0c0c0
Slider.focusedColor=fade($Component.focusColor,50%,derived)
Slider.hoverThumbColor=lighten($Slider.thumbColor,10%,derived)
Slider.pressedThumbColor=lighten($Slider.thumbColor,15%,derived)
Slider.disabledTrackColor=#c0c0c0
Slider.disabledThumbColor=$Slider.disabledTrackColor
#---- SplitPane ----
@@ -267,14 +270,24 @@ SplitPaneDivider.oneTouchHoverArrowColor=#333
TabbedPane.underlineColor=#4083C9
TabbedPane.disabledUnderlineColor=#ababab
TabbedPane.hoverColor=#d9d9d9
TabbedPane.hoverColor=darken($TabbedPane.background,7%,derived)
TabbedPane.focusColor=#dae4ed
TabbedPane.contentAreaColor=#bfbfbf
TabbedPane.buttonHoverBackground=darken($TabbedPane.background,7%,derived)
TabbedPane.buttonPressedBackground=darken($TabbedPane.background,10%,derived)
TabbedPane.closeBackground=null
TabbedPane.closeForeground=@disabledText
TabbedPane.closeHoverBackground=darken($TabbedPane.background,20%,derived)
TabbedPane.closeHoverForeground=@foreground
TabbedPane.closePressedBackground=darken($TabbedPane.background,25%,derived)
TabbedPane.closePressedForeground=$TabbedPane.closeHoverForeground
#---- Table ----
Table.gridColor=darken($Table.background,3%)
Table.gridColor=darken($Table.background,5%)
#---- TableHeader ----

View File

@@ -35,6 +35,11 @@ Button.default.hoverBorderColor=null
HelpButton.hoverBorderColor=null
#---- Slider ----
Slider.focusedColor=fade($Component.focusColor,40%,derived)
#---- ToggleButton ----
ToggleButton.startBackground=$ToggleButton.background
@@ -60,6 +65,8 @@ ToggleButton.endBackground=$ToggleButton.background
[Cobalt_2]CheckBox.icon.background=#002946
[Cobalt_2]CheckBox.icon.checkmarkColor=#002946
[Dark_purple]Slider.focusedColor=fade($Component.focusColor,70%,derived)
[Dracula]ProgressBar.selectionBackground=#fff
[Dracula]ProgressBar.selectionForeground=#fff
@@ -81,6 +88,15 @@ ToggleButton.endBackground=$ToggleButton.background
[High_contrast]ToggleButton.disabledSelectedBackground=#444
[High_contrast]ToggleButton.toolbar.selectedBackground=#fff
[One_Dark]Slider.focusedColor=fade(#568af2,40%)
[Solarized_Dark]Slider.focusedColor=fade($Component.focusColor,80%,derived)
[vuesion-theme]Slider.trackValueColor=#ececee
[vuesion-theme]Slider.trackColor=#303a45
[vuesion-theme]Slider.thumbColor=#ececee
[vuesion-theme]Slider.focusedColor=fade(#ececee,20%)
# Material Theme UI Lite

View File

@@ -46,3 +46,20 @@ FileChooser.refreshActionLabelText=Refresh
FileChooser.newFolderActionLabelText=New Folder
FileChooser.listViewActionLabelText=List
FileChooser.detailsViewActionLabelText=Details
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText=Collapse Left Pane
SplitPaneDivider.collapseRightToolTipText=Collapse Right Pane
SplitPaneDivider.collapseTopToolTipText=Collapse Top Pane
SplitPaneDivider.collapseBottomToolTipText=Collapse Bottom Pane
SplitPaneDivider.expandLeftToolTipText=Expand Left Pane
SplitPaneDivider.expandRightToolTipText=Expand Right Pane
SplitPaneDivider.expandTopToolTipText=Expand Top Pane
SplitPaneDivider.expandBottomToolTipText=Expand Bottom Pane
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText=Show Hidden Tabs

View File

@@ -46,3 +46,8 @@ FileChooser.refreshActionLabelText=Aktualisieren
FileChooser.newFolderActionLabelText=Neuer Ordner
FileChooser.listViewActionLabelText=Liste
FileChooser.detailsViewActionLabelText=Details
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText=Verdeckte Tabs anzeigen

View File

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

View File

@@ -136,6 +136,16 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
// register Alt+UP and Alt+DOWN to switch to previous/next theme
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> frame.themesPanel.selectPreviousTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> frame.themesPanel.selectNextTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
// register ESC key to close frame
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> {

View File

@@ -32,7 +32,7 @@ import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.SVGUtils;
import com.formdev.flatlaf.extras.FlatSVGUtils;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
@@ -59,7 +59,7 @@ class DemoFrame
updateFontMenuItems();
controlBar.initialize( this, tabbedPane );
setIconImages( SVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() )
tabbedPane.setSelectedIndex( tabIndex );
@@ -141,11 +141,16 @@ class DemoFrame
private void windowDecorationsChanged() {
boolean windowDecorations = windowDecorationsCheckBoxMenuItem.isSelected();
// change window decoration of demo main frame
dispose();
setUndecorated( windowDecorations );
getRootPane().setWindowDecorationStyle( windowDecorations ? JRootPane.FRAME : JRootPane.NONE );
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
setVisible( true );
// enable/disable window decoration for later created frames/dialogs
JFrame.setDefaultLookAndFeelDecorated( windowDecorations );
JDialog.setDefaultLookAndFeelDecorated( windowDecorations );
}
private void menuBarEmbeddedChanged() {
@@ -675,11 +680,12 @@ class DemoFrame
//======== tabbedPane ========
{
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
tabbedPane.addChangeListener(e -> selectedTabChanged());
tabbedPane.addTab("Basic Components", basicComponentsPanel);
tabbedPane.addTab("More Components", moreComponentsPanel);
tabbedPane.addTab("Data Components", dataComponentsPanel);
tabbedPane.addTab("SplitPane & Tabs", tabsPanel);
tabbedPane.addTab("Tabs", tabsPanel);
tabbedPane.addTab("Option Pane", optionPanePanel);
tabbedPane.addTab("Extras", extrasPanel1);
}
@@ -743,6 +749,6 @@ class DemoFrame
private JCheckBoxMenuItem animatedLafChangeMenuItem;
private JTabbedPane tabbedPane;
private ControlBar controlBar;
private IJThemesPanel themesPanel;
IJThemesPanel themesPanel;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -64,6 +64,7 @@ new FormModel {
name: "contentPanel"
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane"
"tabLayoutPolicy": 1
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -86,7 +87,7 @@ new FormModel {
add( new FormComponent( "com.formdev.flatlaf.demo.TabsPanel" ) {
name: "tabsPanel"
}, new FormLayoutConstraints( null ) {
"title": "SplitPane & Tabs"
"title": "Tabs"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.OptionPanePanel" ) {
name: "optionPanePanel"
@@ -116,6 +117,7 @@ new FormModel {
name: "themesPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.variableModifiers": 0
}
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "East"

View File

@@ -101,6 +101,18 @@ class MoreComponentsPanel
JButton button8 = new JButton();
JToggleButton toggleButton6 = new JToggleButton();
JButton button1 = new JButton();
JLabel splitPaneLabel = new JLabel();
JSplitPane splitPane3 = new JSplitPane();
JSplitPane splitPane1 = new JSplitPane();
JPanel panel10 = new JPanel();
JLabel label1 = new JLabel();
JPanel panel11 = new JPanel();
JLabel label2 = new JLabel();
JSplitPane splitPane2 = new JSplitPane();
JPanel panel12 = new JPanel();
JLabel label5 = new JLabel();
JPanel panel13 = new JPanel();
JLabel label6 = new JLabel();
//======== this ========
setLayout(new MigLayout(
@@ -122,7 +134,8 @@ class MoreComponentsPanel
"[]" +
"[]" +
"[]" +
"[]"));
"[]" +
"[100,top]"));
//---- scrollPaneLabel ----
scrollPaneLabel.setText("JScrollPane:");
@@ -386,6 +399,81 @@ class MoreComponentsPanel
toolBar1.add(button1);
}
add(toolBar1, "cell 1 10 3 1,growx");
//---- splitPaneLabel ----
splitPaneLabel.setText("JSplitPane:");
add(splitPaneLabel, "cell 0 11");
//======== splitPane3 ========
{
splitPane3.setResizeWeight(0.5);
//======== splitPane1 ========
{
splitPane1.setResizeWeight(0.5);
//======== panel10 ========
{
panel10.setBackground(new Color(217, 163, 67));
panel10.setLayout(new BorderLayout());
//---- label1 ----
label1.setText("LEFT");
label1.setHorizontalAlignment(SwingConstants.CENTER);
label1.setForeground(Color.white);
panel10.add(label1, BorderLayout.CENTER);
}
splitPane1.setLeftComponent(panel10);
//======== panel11 ========
{
panel11.setBackground(new Color(98, 181, 67));
panel11.setLayout(new BorderLayout());
//---- label2 ----
label2.setText("RIGHT");
label2.setHorizontalAlignment(SwingConstants.CENTER);
label2.setForeground(Color.white);
panel11.add(label2, BorderLayout.CENTER);
}
splitPane1.setRightComponent(panel11);
}
splitPane3.setLeftComponent(splitPane1);
//======== splitPane2 ========
{
splitPane2.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitPane2.setResizeWeight(0.5);
//======== panel12 ========
{
panel12.setBackground(new Color(242, 101, 34));
panel12.setLayout(new BorderLayout());
//---- label5 ----
label5.setText("TOP");
label5.setHorizontalAlignment(SwingConstants.CENTER);
label5.setForeground(Color.white);
panel12.add(label5, BorderLayout.CENTER);
}
splitPane2.setTopComponent(panel12);
//======== panel13 ========
{
panel13.setBackground(new Color(64, 182, 224));
panel13.setLayout(new BorderLayout());
//---- label6 ----
label6.setText("BOTTOM");
label6.setHorizontalAlignment(SwingConstants.CENTER);
label6.setForeground(Color.white);
panel13.add(label6, BorderLayout.CENTER);
}
splitPane2.setBottomComponent(panel13);
}
splitPane3.setRightComponent(splitPane2);
}
add(splitPane3, "cell 1 11 4 1,grow");
// JFormDesigner - End of component initialization //GEN-END:initComponents
if( FlatLafDemo.screenshotsMode ) {

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" 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": "insets dialog,hidemode 3"
"$columnConstraints": "[][][][][]"
"$rowConstraints": "[][][][][][][][][][][]"
"$rowConstraints": "[][][][][][][][][][][][100,top]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -358,9 +358,90 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 10 3 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "splitPaneLabel"
"text": "JSplitPane:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane3"
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane1"
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel10"
"background": new java.awt.Color( 217, 163, 67, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "LEFT"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel11"
"background": new java.awt.Color( 98, 181, 67, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "RIGHT"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
} )
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane2"
"orientation": 0
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel12"
"background": new java.awt.Color( 242, 101, 34, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"text": "TOP"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel13"
"background": new java.awt.Color( 64, 182, 224, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "BOTTOM"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11 4 1,grow"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 700, 420 )
"size": new java.awt.Dimension( 700, 550 )
} )
}
}

View File

@@ -1,205 +1,606 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
"$setComponentNames": true
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[grow,fill]"
"$columnConstraints": "[grow,fill]para[fill]para[fill]"
"$rowConstraints": "[grow,fill]para[][]"
} ) {
name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class com.jgoodies.forms.layout.FormLayout ) {
"$columnSpecs": "70dlu:grow, unrelgap, 70dlu:grow"
"$rowSpecs": "default, linegap, fill:70dlu, pargap, pref, linegap, fill:80dlu:grow, unrelgap, fill:80dlu:grow, unrelgap, pref"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[][fill]para[]0[][]para[][]para[][]"
} ) {
name: "panel9"
name: "panel1"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "splitPaneLabel"
"text": "JSplitPane:"
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) )
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane3"
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane1"
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel10"
"background": new java.awt.Color( 217, 163, 67, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "LEFT"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel11"
"background": new java.awt.Color( 98, 181, 67, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "RIGHT"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
name: "tabPlacementLabel"
"text": "Tab placement"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "tabPlacementToolBar"
"floatable": false
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "topPlacementButton"
"text": "top"
"selected": true
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": &SwingDerivedFont0 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) {
name: "splitPane2"
"orientation": 0
"resizeWeight": 0.5
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel12"
"background": new java.awt.Color( 242, 101, 34, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "TOP"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "left"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel13"
"background": new java.awt.Color( 64, 182, 224, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"text": "BOTTOM"
"horizontalAlignment": 0
"foreground": sfield java.awt.Color white
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "right"
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "bottomPlacementButton"
"text": "bottom"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 3
"gridWidth": 3
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "leftPlacementButton"
"text": "left"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "rightPlacementButton"
"text": "right"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator1"
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollButton"
"text": "scroll"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "borderButton"
"text": "border"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "borderChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0,alignx right,growx 0"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabPlacementTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,width 300:300,height 100:100"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabbedPaneLabel"
"text": "JTabbedPane:"
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 1
"gridY": 5
name: "tabLayoutLabel"
"text": "Tab layout"
"font": &SwingDerivedFont1 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "tabLayoutToolBar"
"floatable": false
"border": &EmptyBorder0 new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollTabLayoutButton"
"text": "scroll"
"$buttonGroup": new FormReference( "tabLayoutButtonGroup" )
"font": &SwingDerivedFont2 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"selected": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabLayoutChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "wrapTabLayoutButton"
"text": "wrap"
"$buttonGroup": new FormReference( "tabLayoutButtonGroup" )
"font": #SwingDerivedFont2
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabLayoutChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,alignx right,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scrollLayoutNoteLabel"
"text": "(use mouse wheel to scroll; arrow button shows hidden tabs)"
"enabled": false
"font": &SwingDerivedFont3 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "wrapLayoutNoteLabel"
"text": "(probably better to use scroll layout?)"
"enabled": false
"font": #SwingDerivedFont3
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane1"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 1
"gridY": 7
name: "scrollLayoutTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane3"
"tabPlacement": 2
name: "wrapLayoutTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4,width 100:100,height pref*2px"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "closableTabsLabel"
"text": "Closable tabs"
"font": #SwingDerivedFont1
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 3
"gridY": 7
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "closableTabsToolBar"
"floatable": false
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "squareCloseButton"
"text": "square"
"font": &SwingDerivedFont4 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
"selected": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "circleCloseButton"
"text": "circle"
"font": #SwingDerivedFont4
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "redCrossCloseButton"
"text": "red cross"
"font": #SwingDerivedFont4
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5,alignx right,growx 0"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane2"
"tabPlacement": 3
name: "closableTabsTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAreaComponentsLabel"
"text": "Custom tab area components"
"font": #SwingDerivedFont1
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 9
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "tabAreaComponentsToolBar"
"floatable": false
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "leadingComponentButton"
"text": "leading"
"font": &SwingDerivedFont5 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"selected": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customComponentsChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "trailingComponentButton"
"text": "trailing"
"font": #SwingDerivedFont5
"selected": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customComponentsChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7,alignx right,growx 0"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane4"
"tabPlacement": 4
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 3
"gridY": 9
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[][][][]"
"$rowConstraints": "[center]"
} ) {
name: "panel14"
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "moreTabsCheckBox"
"text": "More tabs"
"mnemonic": 77
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "moreTabsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabScrollCheckBox"
"text": "Use scroll layout"
"mnemonic": 83
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabScrollChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox"
"text": "Show tab separators"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hasFullBorderCheckBox"
"text": "Show content border"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hasFullBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0,alignx left,growx 0"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 11
"gridWidth": 3
name: "customComponentsTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[]0[][fill][center][center][center]para[center]0[][center][center][center][]"
} ) {
name: "panel2"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabIconPlacementLabel"
"text": "Tab icon placement"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabIconPlacementNodeLabel"
"text": "(top/bottom/leading/trailing)"
"enabled": false
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "iconTopTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "iconBottomTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "iconLeadingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "iconTrailingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAreaAlignmentLabel"
"text": "Tab area alignment"
"font": &SwingDerivedFont6 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAreaAlignmentNoteLabel"
"text": "(leading/center/trailing/fill)"
"enabled": false
"font": &SwingDerivedFont7 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "alignLeadingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "alignCenterTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "alignTrailingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 10"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "alignFillTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,growy"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[]0[][][][]para[][][]para[]0[]"
} ) {
name: "panel3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabWidthModeLabel"
"text": "Tab width mode"
"font": #SwingDerivedFont6
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabWidthModeNoteLabel"
"text": "(preferred/equal/compact)"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"enabled": false
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "widthPreferredTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "widthEqualTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "widthCompactTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "minMaxTabWidthLabel"
"text": "Minimum/maximum tab width"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "minimumTabWidthTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "maximumTabWidthTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAlignmentLabel"
"text": "Tab title alignment"
"font": #SwingDerivedFont6
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$columnConstraints": "[grow,fill]para[fill]"
"$rowConstraints": "[][][][]"
"$layoutConstraints": "insets 0,hidemode 3"
} ) {
name: "panel5"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAlignmentNoteLabel"
"text": "(leading/center/trailing)"
"enabled": false
"font": #SwingDerivedFont7
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAlignmentNoteLabel2"
"text": "(trailing)"
"enabled": false
"font": #SwingDerivedFont7
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx right,growx 0"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabAlignLeadingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabAlignVerticalTabbedPane"
"tabPlacement": 2
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1 1 3,growy"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabAlignCenterTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabAlignTrailingTabbedPane"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
add( new FormComponent( "javax.swing.JSeparator" ) {
name: "separator2"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1 3 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[][fill]para[fill][fill]para"
"$rowConstraints": "[][center]"
} ) {
name: "panel4"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scrollButtonsPolicyLabel"
"text": "Scroll buttons policy:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "scrollButtonsPolicyToolBar"
"floatable": false
"border": #EmptyBorder0
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollAsNeededSingleButton"
"text": "asNeededSingle"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollAsNeededButton"
"text": "asNeeded"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollNeverButton"
"text": "never"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scrollButtonsPlacementLabel"
"text": "Scroll buttons placement:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "scrollButtonsPlacementToolBar"
"floatable": false
"border": #EmptyBorder0
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollBothButton"
"text": "both"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "scrollButtonsPlacementButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollTrailingButton"
"text": "trailing"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPlacementButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPlacementChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabsPopupPolicyLabel"
"text": "Tabs popup policy:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "tabsPopupPolicyToolBar"
"floatable": false
"border": #EmptyBorder0
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "popupAsNeededButton"
"text": "asNeeded"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "tabsPopupPolicyButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabsPopupPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "popupNeverButton"
"text": "never"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "tabsPopupPolicyButtonGroup" )
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabsPopupPolicyChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox"
"text": "Show tab separators"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1 2 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 3 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 625, 515 )
"size": new java.awt.Dimension( 1075, 895 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "tabPlacementButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 915 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "closableTabsButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 970 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "tabLayoutButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 1020 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "tabsPopupPolicyButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 200, 915 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "scrollButtonsPolicyButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 200, 965 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "scrollButtonsPlacementButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 200, 1020 )
} )
}
}

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.demo.extras;
import javax.swing.*;
import com.formdev.flatlaf.extras.*;
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
import net.miginfocom.swing.*;
/**
@@ -63,7 +64,7 @@ public class ExtrasPanel
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
label4 = new JLabel();
label1 = new JLabel();
triStateCheckBox1 = new TriStateCheckBox();
triStateCheckBox1 = new FlatTriStateCheckBox();
triStateLabel1 = new JLabel();
label2 = new JLabel();
svgIconsPanel = new JPanel();
@@ -124,7 +125,7 @@ public class ExtrasPanel
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel label4;
private JLabel label1;
private TriStateCheckBox triStateCheckBox1;
private FlatTriStateCheckBox triStateCheckBox1;
private JLabel triStateLabel1;
private JLabel label2;
private JPanel svgIconsPanel;

View File

@@ -21,7 +21,7 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "com.formdev.flatlaf.extras.TriStateCheckBox" ) {
add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTriStateCheckBox" ) {
name: "triStateCheckBox1"
"text": "Three States"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "triStateCheckBox1Changed", false ) )

View File

@@ -49,7 +49,9 @@ public class IJThemesClassGenerator
}
Path out = new File( toPath, "FlatAllIJThemes.java" ).toPath();
String allThemes = CLASS_HEADER + ALL_THEMES_TEMPLATE.replace( "${allInfos}", allInfos );
String allThemes = (CLASS_HEADER + ALL_THEMES_TEMPLATE)
.replace( "${subPackage}", "" )
.replace( "${allInfos}", allInfos );
writeFile( out, allThemes );
System.out.println( markdownTable );
@@ -69,6 +71,10 @@ public class IJThemesClassGenerator
if( nameSep >= 0 )
name = name.substring( nameSep + 1 ).trim();
String themeName = name;
if( "material-theme-ui-lite".equals( resourcePath ) )
themeName += " (Material)";
StringBuilder buf = new StringBuilder();
for( String n : name.split( " " ) ) {
if( n.length() == 0 || n.equals( "-" ) )
@@ -84,10 +90,11 @@ public class IJThemesClassGenerator
String themeClass = "Flat" + buf + "IJTheme";
String themeFile = resourceName;
String classBody = CLASS_HEADER + CLASS_TEMPLATE
String classBody = (CLASS_HEADER + CLASS_TEMPLATE)
.replace( "${subPackage}", subPackage )
.replace( "${themeClass}", themeClass )
.replace( "${themeFile}", themeFile );
.replace( "${themeFile}", themeFile )
.replace( "${themeName}", themeName );
File toDir = new File( toPath );
if( resourcePath != null )
@@ -101,10 +108,11 @@ public class IJThemesClassGenerator
allInfos.append( THEME_TEMPLATE
.replace( "${subPackage}", subPackage )
.replace( "${themeClass}", themeClass )
.replace( "${themeName}", name ) );
.replace( "${themeName}", themeName )
.replace( "${dark}", Boolean.toString( ti.dark ) ) );
markdownTable.append( String.format( "[%s](%s) | `com.formdev.flatlaf.intellijthemes%s.%s`\n",
name, ti.sourceCodeUrl, subPackage, themeClass ) );
themeName, ti.sourceCodeUrl, subPackage, themeClass ) );
}
private static void writeFile( Path out, String content ) {
@@ -132,11 +140,16 @@ public class IJThemesClassGenerator
" * See the License for the specific language governing permissions and\n" +
" * limitations under the License.\n" +
" */\n" +
"\n" +
"package com.formdev.flatlaf.intellijthemes${subPackage};\n" +
"\n" +
"//\n" +
"// DO NOT MODIFY\n" +
"// Generated with com.formdev.flatlaf.demo.intellijthemes.IJThemesClassGenerator\n" +
"//\n" +
"\n";
private static final String CLASS_TEMPLATE =
"package com.formdev.flatlaf.intellijthemes${subPackage};\n" +
"\n" +
"import com.formdev.flatlaf.IntelliJTheme;\n" +
"\n" +
"/**\n" +
@@ -145,7 +158,9 @@ public class IJThemesClassGenerator
"public class ${themeClass}\n" +
" extends IntelliJTheme.ThemeLaf\n" +
"{\n" +
" public static boolean install( ) {\n" +
" public static final String NAME = \"${themeName}\";\n" +
"\n" +
" public static boolean install() {\n" +
" try {\n" +
" return install( new ${themeClass}() );\n" +
" } catch( RuntimeException ex ) {\n" +
@@ -153,14 +168,21 @@ public class IJThemesClassGenerator
" }\n" +
" }\n" +
"\n" +
" public static void installLafInfo() {\n" +
" installLafInfo( NAME, ${themeClass}.class );\n" +
" }\n" +
"\n" +
" public ${themeClass}() {\n" +
" super( Utils.loadTheme( \"${themeFile}\" ) );\n" +
" }\n" +
"\n" +
" @Override\n" +
" public String getName() {\n" +
" return NAME;\n" +
" }\n" +
"}\n";
private static final String ALL_THEMES_TEMPLATE =
"package com.formdev.flatlaf.intellijthemes;\n" +
"\n" +
"import javax.swing.UIManager.LookAndFeelInfo;\n" +
"\n" +
"/**\n" +
@@ -168,11 +190,28 @@ public class IJThemesClassGenerator
" */\n" +
"public class FlatAllIJThemes\n" +
"{\n" +
" public static final LookAndFeelInfo[] INFOS = {\n" +
" public static final FlatIJLookAndFeelInfo[] INFOS = {\n" +
"${allInfos}\n" +
" };\n" +
"\n" +
" //---- class FlatIJLookAndFeelInfo ----------------------------------------\n" +
"\n" +
" public static class FlatIJLookAndFeelInfo\n" +
" extends LookAndFeelInfo\n" +
" {\n" +
" private final boolean dark;\n" +
"\n" +
" public FlatIJLookAndFeelInfo( String name, String className, boolean dark ) {\n" +
" super( name, className );\n" +
" this.dark = dark;\n" +
" }\n" +
"\n" +
" public boolean isDark() {\n" +
" return dark;\n" +
" }\n" +
" }\n" +
"}\n";
private static final String THEME_TEMPLATE =
" new LookAndFeelInfo( \"${themeName}\", \"com.formdev.flatlaf.intellijthemes${subPackage}.${themeClass}\" ),";
" new FlatIJLookAndFeelInfo( \"${themeName}\", \"com.formdev.flatlaf.intellijthemes${subPackage}.${themeClass}\", ${dark} ),";
}

View File

@@ -220,6 +220,17 @@ public class IJThemesPanel
}
}
public void selectPreviousTheme() {
int sel = themesList.getSelectedIndex();
if( sel > 0 )
themesList.setSelectedIndex( sel - 1 );
}
public void selectNextTheme() {
int sel = themesList.getSelectedIndex();
themesList.setSelectedIndex( sel + 1 );
}
private void themesListValueChanged( ListSelectionEvent e ) {
IJThemeInfo themeInfo = themesList.getSelectedValue();
boolean bundledTheme = (themeInfo != null && themeInfo.resourceName != null);

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 0C8.02219 0 6.08879 0.58649 4.4443 1.6853C2.79981 2.78412 1.51809 4.3459 0.761209 6.17317C0.00433284 8.00043 -0.193701 10.0111 0.192152 11.9509C0.578004 13.8907 1.53041 15.6725 2.92894 17.0711C4.32746 18.4696 6.10929 19.422 8.0491 19.8079C9.98891 20.1937 11.9996 19.9957 13.8268 19.2388C15.6541 18.4819 17.2159 17.2002 18.3147 15.5557C19.4135 13.9112 20 11.9778 20 10C20 7.34784 18.9464 4.8043 17.0711 2.92893C15.1957 1.05357 12.6522 0 10 0ZM10 18.2C8.3782 18.2 6.79281 17.7191 5.44433 16.8181C4.09585 15.917 3.04483 14.6364 2.42419 13.138C1.80355 11.6397 1.64117 9.9909 1.95757 8.40026C2.27396 6.80961 3.05494 5.34852 4.20173 4.20172C5.34852 3.05493 6.80962 2.27396 8.40026 1.95756C9.99091 1.64116 11.6397 1.80355 13.138 2.42419C14.6364 3.04483 15.917 4.09584 16.8181 5.44432C17.7191 6.79281 18.2 8.37819 18.2 10C18.1974 12.174 17.3326 14.2581 15.7954 15.7954C14.2581 17.3326 12.174 18.1974 10 18.2ZM10.9 9.43L14.46 11.09L13.7 12.72L9.10001 10.57V4H10.9V9.43Z" fill="#9AA7B0" fill-opacity="0.8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#389FD6" fill-rule="evenodd" d="M14.0431479,8.44999981 L14.3048222,13.4430431 L12.574725,11.885269 C11.4741571,13.1817306 9.83248424,14.0044386 7.99865879,14.0044386 C5.03921121,14.0044386 2.58020312,11.8617896 2.08827651,9.04314243 L3.94798986,9.10858763 C4.43427312,10.8903005 6.0642585,12.2000519 8.00015362,12.2000519 C9.30169687,12.2000519 10.4649641,11.6080237 11.2353504,10.6785714 L9.05000019,8.71167959 L14.0431479,8.44999981 Z M7.99865879,2.00443857 C10.9184669,2.00443857 13.3511517,4.09007371 13.888219,6.85284133 L12.0226275,6.7880427 C11.5024638,5.05933714 9.89836709,3.8000774 8.00015362,3.8000774 C6.69883692,3.8000774 5.53574811,4.39189956 4.76535914,5.32107268 L6.95482203,7.29304326 L1.96167436,7.55472304 L1.70000005,2.56167973 L3.42768119,4.11762166 C4.52821742,2.82461451 6.1676575,2.00443857 7.99865879,2.00443857 Z" transform="rotate(3 8.002 8.004)"/>
</svg>

After

Width:  |  Height:  |  Size: 981 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" points="13.789 2.09 15.535 3.837 6.292 13.08 1.95 8.738 3.698 6.99 6.293 9.585"/>
</svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="#389FD6" fill-rule="evenodd" transform="translate(1)">
<path d="M10.9000003,4.89999992 L14.0999999,4.89999992 L9.9000001,9.09999973 L5.70000029,4.89999992 L8.90000027,4.89999992 L8.90000027,0.899999917 L10.9000003,0.899999917 L10.9000003,4.89999992 Z" transform="rotate(90 9.9 5)"/>
<path d="M5.0999999,10.9000001 L8.29999971,10.9000001 L4.0999999,15.0999999 L-0.0999999046,10.9000001 L3.0999999,10.9000001 L3.0999999,6.9000001 L5.0999999,6.9000001 L5.0999999,10.9000001 Z" transform="matrix(0 1 1 0 -6.9 6.9)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 630 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="#F4AF3D" fill-rule="evenodd" points="7.998 11.522 3.673 14.311 4.989 9.336 1 6.084 6.138 5.798 7.998 1 9.858 5.798 14.996 6.084 11.007 9.336 12.323 14.311"/>
</svg>

After

Width:  |  Height:  |  Size: 266 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="12" height="2" x="2" y="3" fill="#6E6E6E"/>
<rect width="12" height="2" x="2" y="7" fill="#6E6E6E"/>
<rect width="12" height="2" x="2" y="11" fill="#6E6E6E"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -0,0 +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="#9AA7B0" fill-opacity=".8" d="M1,1 L15,1 L15,14 L1,14 L1,1 Z M2,4 L2,13 L14,13 L14,4 L2,4 Z"/>
<rect width="10" height="7" x="3" y="5" fill="#40B6E0" fill-opacity=".6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#7F8B91" fill-opacity=".9" fill-rule="evenodd" d="M11.038136,9.94904865 L13.9980971,12.9090097 L12.9374369,13.9696699 L9.98176525,11.0139983 C9.14925083,11.6334368 8.11743313,12 7,12 C4.23857625,12 2,9.76142375 2,7 C2,4.23857625 4.23857625,2 7,2 C9.76142375,2 12,4.23857625 12,7 C12,8.1028408 11.642948,9.12228765 11.038136,9.94904865 Z M7,11 C9.209139,11 11,9.209139 11,7 C11,4.790861 9.209139,3 7,3 C4.790861,3 3,4.790861 3,7 C3,9.209139 4.790861,11 7,11 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 567 B

View File

@@ -4,14 +4,14 @@
"license": "MIT",
"licenseFile": "arc-themes.LICENSE.txt",
"sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea",
"sourceCodePath": "blob/master/resources/arc-theme.theme.json"
"sourceCodePath": "blob/master/arc-theme-idea-light/resources/arc-theme.theme.json"
},
"arc-theme-orange.theme.json": {
"name": "Arc - Orange",
"license": "MIT",
"licenseFile": "arc-themes.LICENSE.txt",
"sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea",
"sourceCodePath": "blob/master/resources/arc-theme-orange.theme.json"
"sourceCodePath": "blob/master/arc-theme-idea-light/resources/arc-theme-orange.theme.json"
},
"arc_theme_dark.theme.json": {
"name": "Arc Dark",
@@ -19,7 +19,7 @@
"license": "MIT",
"licenseFile": "arc-themes.LICENSE.txt",
"sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea",
"sourceCodePath": "blob/master/resources/arc_theme_dark.theme.json"
"sourceCodePath": "blob/master/arc-theme-idea-dark/resources/arc_theme_dark.theme.json"
},
"arc_theme_dark_orange.theme.json": {
"name": "Arc Dark - Orange",
@@ -27,7 +27,7 @@
"license": "MIT",
"licenseFile": "arc-themes.LICENSE.txt",
"sourceCodeUrl": "https://gitlab.com/zlamalp/arc-theme-idea",
"sourceCodePath": "blob/master/resources/arc_theme_dark_orange.theme.json"
"sourceCodePath": "blob/master/arc-theme-idea-dark/resources/arc_theme_dark_orange.theme.json"
},
"Carbon.theme.json": {
"name": "Carbon",
@@ -74,7 +74,7 @@
"license": "MIT",
"licenseFile": "Dracula.LICENSE.txt",
"sourceCodeUrl": "https://github.com/dracula/jetbrains",
"sourceCodePath": "blob/master/src/main/resources/themes/Dracula.theme.json"
"sourceCodePath": "blob/master/src/main/resources/themes/dracula.theme.json"
},
"Gradianto_dark_fuchsia.theme.json": {
"name": "Gradianto Dark Fuchsia",
@@ -100,6 +100,14 @@
"sourceCodeUrl": "https://github.com/thvardhan/Gradianto",
"sourceCodePath": "blob/master/src/main/resources/Gradianto_midnight_blue.theme.json"
},
"Gradianto_Nature_Green.theme.json": {
"name": "Gradianto Nature Green",
"dark": true,
"license": "MIT",
"licenseFile": "Gradianto.LICENSE.txt",
"sourceCodeUrl": "https://github.com/thvardhan/Gradianto",
"sourceCodePath": "blob/master/src/main/resources/Gradianto_Nature_Green.theme.json"
},
"Gray.theme.json": {
"name": "Gray",
"license": "MIT",

View File

@@ -7,9 +7,12 @@ This sub-project provides some additional components and classes:
An icon that displays SVG using
[svgSalamander](https://github.com/JFormDesigner/svgSalamander).\
![FlatSVGIcon.png](../images/extras-FlatSVGIcon.png)
- [TriStateCheckBox](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/TriStateCheckBox.html):
- [FlatTriStateCheckBox](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/components/FlatTriStateCheckBox.html):
A tri-state check box.\
![TriStateCheckBox.png](../images/extras-TriStateCheckBox.png)
- Extension classes of standard Swing components that provide easy access to
FlatLaf specific client properties (see package
[com.formdev.flatlaf.extras.components](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/components/package-summary.html)).
- [FlatAnimatedLafChange](https://www.javadoc.io/doc/com.formdev/flatlaf-extras/latest/com/formdev/flatlaf/extras/FlatAnimatedLafChange.html):
Animated Laf (theme) changing.
- [FlatInspector](#ui-inspector): A simple UI inspector that shows information
@@ -35,10 +38,8 @@ Otherwise download `flatlaf-extras-<version>.jar` here:
[![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf-extras/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf-extras/_latestVersion)
You also need `flatlaf-<version>.jar` and `svgSalamander-<version>.jar`, which
you can download here:
If SVG classes are used, `svgSalamander-<version>.jar` is also required:
[![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
[![Download](https://api.bintray.com/packages/jformdesigner/svgSalamander/svgSalamander/images/download.svg)](https://bintray.com/jformdesigner/svgSalamander/svgSalamander/_latestVersion)
@@ -49,7 +50,7 @@ Tools
A simple UI inspector that shows information about UI component at mouse
location in a tooltip, which may be useful while developing an application.
Should be not installed in released applications.
Should not be installed in released applications.
Once installed with following code (e.g. in method `main`), it can be activated
for the active window with the given keystroke:

View File

@@ -22,7 +22,7 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "com.formdev:svgSalamander:1.1.2.3" )
implementation( "com.formdev:svgSalamander:1.1.2.4" )
}
flatlafModuleInfo {
@@ -40,6 +40,7 @@ tasks {
this as StandardJavadocDocletOptions
use( true )
tags = listOf( "uiDefault", "clientProperty" )
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
}
isFailOnError = false
}

View File

@@ -24,7 +24,6 @@ import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
@@ -73,10 +72,10 @@ import com.formdev.flatlaf.util.UIScale;
* <p>
* When the UI inspector is active some additional keys are available:
* <ul>
* <li>press <kbd>Esc</kbd> key to disable UI inspector</li>
* <li>press <kbd>Ctrl</kbd> key to increase inspection level, which shows
* <li>press {@code Esc} key to disable UI inspector</li>
* <li>press {@code Ctrl} key to increase inspection level, which shows
* information about parent of UI component at mouse location</li>
* <li>press <kbd>Shift</kbd> key to decrease inspection level</li>
* <li>press {@code Shift} key to decrease inspection level</li>
* </ul>
*
* @author Karl Tauber
@@ -106,6 +105,8 @@ public class FlatInspector
/**
* Installs a key listener into the application that allows enabling and disabling
* the UI inspector with the given keystroke (e.g. "ctrl shift alt X").
*
* @param activationKeys a keystroke (e.g. "ctrl shift alt X")
*/
public static void install( String activationKeys ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( activationKeys );
@@ -216,6 +217,9 @@ public class FlatInspector
this.enabled = enabled;
// make sure that glass pane is not opaque, which is not the case in WebLaF
((JComponent)rootPane.getGlassPane()).setOpaque( false );
rootPane.getGlassPane().setVisible( enabled );
Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -343,8 +347,9 @@ public class FlatInspector
@Override
protected void paintBorder( Graphics g ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
super.paintBorder( g );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
};
c.setBackground( new Color( 255, 0, 0, 32 ) );

View File

@@ -18,50 +18,203 @@ package com.formdev.flatlaf.extras;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.RGBImageFilter;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatIconColors;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLaf.DisabledIconProvider;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.UIScale;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
/**
* An icon that loads and paints SVG.
*
* @author Karl Tauber
*/
public class FlatSVGIcon
implements Icon
extends ImageIcon
implements DisabledIconProvider
{
// use own SVG universe so that it can not be cleared from anywhere
private static final SVGUniverse svgUniverse = new SVGUniverse();
private final String name;
private final int width;
private final int height;
private final float scale;
private final boolean disabled;
private final ClassLoader classLoader;
private SVGDiagram diagram;
private boolean dark;
/**
* Creates an SVG icon from the given resource name.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name ) {
this( name, null );
this( name, -1, -1, 1, false, null );
}
/**
* Creates an SVG icon from the given resource name.
* The SVG file is loaded from the given class loader.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, ClassLoader classLoader ) {
this( name, -1, -1, 1, false, classLoader );
}
/**
* Creates an SVG icon from the given resource name with the given width and height.
* <p>
* The icon is scaled if the given size is different to the size specified in the SVG file.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param width the width of the icon
* @param height the height of the icon
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, int width, int height ) {
this( name, width, height, 1, false, null );
}
/**
* Creates an SVG icon from the given resource name with the given width and height.
* The SVG file is loaded from the given class loader.
* <p>
* The icon is scaled if the given size is different to the size specified in the SVG file.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param width the width of the icon
* @param height the height of the icon
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, int width, int height, ClassLoader classLoader ) {
this( name, width, height, 1, false, classLoader );
}
/**
* Creates an SVG icon from the given resource name that is scaled by the given amount.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as base icon size, which is multiplied
* by the given scale factor.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param scale the amount by which the icon size is scaled
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, float scale ) {
this( name, -1, -1, scale, false, null );
}
/**
* Creates an SVG icon from the given resource name that is scaled by the given amount.
* The SVG file is loaded from the given class loader.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as base icon size, which is multiplied
* by the given scale factor.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param scale the amount by which the icon size is scaled
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, float scale, ClassLoader classLoader ) {
this( name, -1, -1, scale, false, classLoader );
}
private FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader ) {
this.name = name;
this.classLoader = classLoader;
this.width = width;
this.height = height;
this.scale = scale;
this.disabled = disabled;
}
/**
* Creates a new icon with given width and height, which is derived from this icon.
*
* @param width the width of the new icon
* @param height the height of the new icon
* @return a new icon
*/
public FlatSVGIcon derive( int width, int height ) {
if( width == this.width && height == this.height )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, false, classLoader );
icon.diagram = diagram;
icon.dark = dark;
return icon;
}
/**
* Creates a new icon with given scaling, which is derived from this icon.
*
* @param scale the amount by which the icon size is scaled
* @return a new icon
*/
public FlatSVGIcon derive( float scale ) {
if( scale == this.scale )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, false, classLoader );
icon.diagram = diagram;
icon.dark = dark;
return icon;
}
/**
* Creates a new icon with disabled appearance, which is derived from this icon.
*
* @return a new icon
*/
@Override
public Icon getDisabledIcon() {
if( disabled )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader );
icon.diagram = diagram;
icon.dark = dark;
return icon;
}
private void update() {
@@ -91,34 +244,59 @@ public class FlatSVGIcon
return cl.getResource( name );
}
/**
* Returns whether the SVG file was found.
*
* @return whether the SVG file was found
*/
public boolean hasFound() {
update();
return diagram != null;
}
/**
* Returns the scaled width of the icon.
*/
@Override
public int getIconWidth() {
if( width > 0 )
return scaleSize( width );
update();
return (int) UIScale.scale( (diagram != null) ? diagram.getWidth() : 16 );
return scaleSize( (diagram != null) ? Math.round( diagram.getWidth() ) : 16 );
}
/**
* Returns the scaled height of the icon.
*/
@Override
public int getIconHeight() {
if( height > 0 )
return scaleSize( height );
update();
return (int) UIScale.scale( (diagram != null) ? diagram.getHeight() : 16 );
return scaleSize( (diagram != null) ? Math.round( diagram.getHeight() ) : 16 );
}
private int scaleSize( int size ) {
int scaledSize = UIScale.scale( size );
if( scale != 1 )
scaledSize = Math.round( scaledSize * scale );
return scaledSize;
}
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
update();
// check whether icon is outside of clipping area
Rectangle clipBounds = g.getClipBounds();
if( clipBounds != null && !clipBounds.intersects( new Rectangle( x, y, getIconWidth(), getIconHeight() ) ) )
return;
// get gray filter
RGBImageFilter grayFilter = null;
if( c != null && !c.isEnabled() ) {
if( disabled ) {
Object grayFilterObj = UIManager.get( "Component.grayFilter" );
grayFilter = (grayFilterObj instanceof RGBImageFilter)
? (RGBImageFilter) grayFilterObj
@@ -147,6 +325,14 @@ public class FlatSVGIcon
g.clipRect( 0, 0, getIconWidth(), getIconHeight() );
UIScale.scaleGraphics( g );
if( width > 0 || height > 0 ) {
double sx = (width > 0) ? width / diagram.getWidth() : 1;
double sy = (height > 0) ? height / diagram.getHeight() : 1;
if( sx != 1 || sy != 1 )
g.scale( sx, sy );
}
if( scale != 1 )
g.scale( scale, scale );
diagram.setIgnoringClipHeuristic( true );
@@ -162,6 +348,39 @@ public class FlatSVGIcon
g.fillRect( x, y, getIconWidth(), getIconHeight() );
}
@Override
public Image getImage() {
update();
// base size
int iconWidth = getIconWidth();
int iconHeight = getIconHeight();
Dimension[] dimensions = new Dimension[] {
new Dimension( iconWidth, iconHeight ),
new Dimension( iconWidth * 2, iconHeight * 2 ),
};
Function<Dimension, Image> producer = size -> {
BufferedImage image = new BufferedImage( size.width, size.height, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics();
try {
// scale from base size to passed size
double sx = (size.width > 0) ? (float) size.width / iconWidth : 1;
double sy = (size.height > 0) ? (float) size.height / iconHeight : 1;
if( sx != 1 || sy != 1 )
g.scale( sx, sy );
paintIcon( null, g, 0, 0 );
} finally {
g.dispose();
}
return image;
};
return MultiResolutionImageSupport.create( 0, dimensions, producer );
}
private static Boolean darkLaf;
private static boolean isDarkLaf() {

View File

@@ -0,0 +1,140 @@
/*
* 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.extras;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import javax.swing.JWindow;
import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
/**
* Utility methods for SVG.
*
* @author Karl Tauber
*/
public class FlatSVGUtils
{
/**
* Creates from the given SVG a list of icon images with different sizes that
* can be used for windows headers. The SVG should have a size of 16x16,
* otherwise it is scaled.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @return list of icon images with different sizes (16x16, 24x24, 32x32, 48x48 and 64x64)
* @throws RuntimeException if failed to load or render SVG file
* @see JWindow#setIconImages(List)
*/
public static List<Image> createWindowIconImages( String svgName ) {
SVGDiagram diagram = loadSVG( svgName );
return Arrays.asList(
svg2image( diagram, 16, 16 ),
svg2image( diagram, 24, 24 ),
svg2image( diagram, 32, 32 ),
svg2image( diagram, 48, 48 ),
svg2image( diagram, 64, 64 )
);
}
/**
* Creates a buffered image and renders the given SVG into it.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @param width the width of the image
* @param height the height of the image
* @return the image
* @throws RuntimeException if failed to load or render SVG file
*/
public static BufferedImage svg2image( String svgName, int width, int height ) {
return svg2image( loadSVG( svgName ), width, height );
}
/**
* Creates a buffered image and renders the given SVG into it.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @param scaleFactor the amount by which the SVG size is scaled
* @return the image
* @throws RuntimeException if failed to load or render SVG file
*/
public static BufferedImage svg2image( String svgName, float scaleFactor ) {
SVGDiagram diagram = loadSVG( svgName );
int width = (int) (diagram.getWidth() * scaleFactor);
int height = (int) (diagram.getHeight() * scaleFactor);
return svg2image( diagram, width, height );
}
/**
* Creates a buffered image and renders the given SVGDiagram into it.
*
* @param diagram the SVG diagram
* @param width the width of the image
* @param height the height of the image
* @return the image
* @throws RuntimeException if failed to render SVG file
*/
public static BufferedImage svg2image( SVGDiagram diagram, int width, int height ) {
try {
BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics();
try {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
double sx = width / diagram.getWidth();
double sy = height / diagram.getHeight();
if( sx != 1 || sy != 1 )
g.scale( sx, sy );
diagram.setIgnoringClipHeuristic( true );
diagram.render( g );
} finally {
g.dispose();
}
return image;
} catch( SVGException ex ) {
throw new RuntimeException( ex );
}
}
/**
* Loads a SVG file.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @return the SVG diagram
* @throws RuntimeException if failed to load SVG file
*/
private static SVGDiagram loadSVG( String svgName ) {
try {
URL url = FlatSVGUtils.class.getResource( svgName );
return SVGCache.getSVGUniverse().getDiagram( url.toURI() );
} catch( URISyntaxException ex ) {
throw new RuntimeException( ex );
}
}
}

View File

@@ -76,6 +76,8 @@ public class FlatUIDefaultsInspector
/**
* Installs a key listener into the application that allows enabling and disabling
* the UI inspector with the given keystroke (e.g. "ctrl shift alt Y").
*
* @param activationKeys a keystroke (e.g. "ctrl shift alt Y")
*/
public static void install( String activationKeys ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( activationKeys );
@@ -746,7 +748,8 @@ public class FlatUIDefaultsInspector
int dot = key.indexOf( '.' );
if( dot > 0 && !selected ) {
g.setColor( UIManager.getColor( "Label.disabledForeground" ) );
g.setColor( FlatUIUtils.getUIColor( "Label.disabledForeground",
FlatUIUtils.getUIColor( "Label.disabledText", Color.gray ) ) );
if( dot >= clippedText.length() )
FlatUIUtils.drawString( this, g, clippedText, x, y );

View File

@@ -1,86 +0,0 @@
/*
* 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.extras;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import javax.swing.JWindow;
import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
/**
* @author Karl Tauber
*/
public class SVGUtils
{
/**
* Creates from the given SVG a list of icon images with different sizes that
* can be used for windows headers. The SVG should have a size of 16 x 16.
*
* @see JWindow#setIconImages(List)
*/
public static List<Image> createWindowIconImages( String svgName ) {
return Arrays.asList(
svg2image( svgName, 1f ), // 16 x 16
svg2image( svgName, 1.5f ), // 24 x 24
svg2image( svgName, 2f ), // 32 x 32
svg2image( svgName, 3f ), // 48 x 48
svg2image( svgName, 4f ) // 64 x 64
);
}
/**
* Creates a buffered image and renders the given SVG into it.
*/
public static BufferedImage svg2image( String svgName, float scaleFactor ) {
try {
URL url = SVGUtils.class.getResource( svgName );
SVGDiagram diagram = SVGCache.getSVGUniverse().getDiagram( url.toURI() );
BufferedImage image = new BufferedImage(
(int) (diagram.getWidth() * scaleFactor),
(int) (diagram.getHeight() * scaleFactor),
BufferedImage.TYPE_INT_ARGB );
Graphics2D g = image.createGraphics();
try {
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
if( scaleFactor != 1f )
g.scale( scaleFactor, scaleFactor );
diagram.setIgnoringClipHeuristic( true );
diagram.render( g );
} finally {
g.dispose();
}
return image;
} catch( URISyntaxException | SVGException ex ) {
throw new RuntimeException( ex );
}
}
}

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