Compare commits

...

101 Commits
0.14 ... 0.20

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

(issue #18)
2019-10-21 22:12:51 +02:00
Karl Tauber
fff0e5e946 fixed FlatTestLaf (broken by commit 342b932f9e) 2019-10-21 20:11:35 +02:00
Karl Tauber
be88eeb343 release 0.15 2019-10-21 18:29:40 +02:00
Karl Tauber
342b932f9e ToolTip:
- Improved styling of dark tooltips (darker background, no border).
- increased top and bottom margins
- use brighter color in light theme
- Fixed colors in tooltips of disabled components. (issue #15)
2019-10-21 18:07:43 +02:00
Karl Tauber
964dc14a8a ComboBox: fixed NPE in combobox with custom renderer after switching to FlatLaf (#16; regression in 0.14) 2019-10-21 17:04:23 +02:00
Karl Tauber
b56f462626 SwingX: added screenshots 2019-10-21 13:42:22 +02:00
242 changed files with 41677 additions and 1702 deletions

View File

@@ -1,6 +1,72 @@
FlatLaf Change Log
==================
## 0.20
- Support using IntelliJ platform themes (.theme.json files).
- Support `JFileChooser`. (issue #5)
- Look and feel identifier returned by `FlatLaf.getID()` now always starts with
"FlatLaf". Use `UIManager.getLookAndFeel().getID().startsWith( "FlatLaf" )` to
check whether the current look and feel is FlatLaf.
- Fixed selection background of checkbox in table cell.
- Fixed color of links in HTML text.
- Fixed jittery submenu rendering on Mac. (issue #10)
- Fixed "cannot find symbol" error in NetBeans editor, when source/binary format
is set to JDK 9 (or later) in NetBeans project. (issue #13)
- Button: Make button square if button text is "..." or a single character.
- ComboBox: Fixed issues with NetBeans `org.openide.awt.ColorComboBox`
component.
- Hex color values in `.properties` files now must start with a `#` character.
- SwingX: Support `JXTitledPanel`. (issue #22)
- SwingX: Fixed too wide border when using date picker as table cell editor.
(issue #24)
- JIDE Common Layer: Fixed `JidePopup` border.
## 0.18
- TextField and TextArea: Do not apply minimum width if `columns` property is
greater than zero.
- TabbedPane: In scroll-tab-layout, the separator line now spans the whole width
and is no longer interrupted by the scroll buttons.
- TabbedPane: Content pane is no longer opaque. Use antialiasing for painting
separator and content border.
- ToolTip: Use anti-aliasing to render multi-line tooltips.
- JIDE Common Layer: Support `JideTabbedPane`.
## 0.17
- CheckBox: Support painting a third state (set client property
"JButton.selectedState" to "indeterminate").
- `TriStateCheckBox` component added (see [FlatLaf Extras](flatlaf-extras)).
- Made `JComboBox`, `JProgressBar`, `JSpinner` and `JXDatePicker` non-opaque.
`JPasswordField`, `JScrollPane` and `JTextField` are non-opaque if they have
an outside focus border (e.g. IntelliJ and Darcula themes). (issues #20 and
#17)
- Button: Hover and pressed background colors are now derived from actual button
background color. (issue #21)
- Table: Fixed missing upper right corner (e.g. in SwingX JXTable with column
control visible).
## 0.16
- Made some fixes for right-to-left support in ComboBox, Slider and ToolTip.
(issue #18)
- Fixed Java 9 module descriptor (broken since 0.14).
- Made `JButton`, `JCheckBox`, `JRadioButton`, `JToggleButton` and `JSlider`
non-opaque. (issue #20)
## 0.15
- ToolTip: Improved styling of dark tooltips (darker background, no border).
- ToolTip: Fixed colors in tooltips of disabled components. (issue #15)
- ComboBox: Fixed NPE in combobox with custom renderer after switching to
FlatLaf. (issue #16; regression in 0.14)
## 0.14
- ComboBox: Use small border if used as table editor.

View File

@@ -16,6 +16,16 @@ IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
![Flat Dark Demo](images/FlatDarkDemo.png)
IntelliJ Platform Themes
------------------------
FlatLaf can use 3rd party themes created for IntelliJ Platform:
![Cyan Light Demo](images/CyanLightDemo.png)
![Dark Purple Demo](images/DarkPurpleDemo.png)
Demo
----
@@ -35,7 +45,7 @@ build script:
groupId: com.formdev
artifactId: flatlaf
version: 0.14
version: 0.20
Otherwise download `flatlaf-<version>.jar` here:
@@ -46,6 +56,7 @@ Addons
------
- [SwingX](flatlaf-swingx)
- [JIDE Common Layer](flatlaf-jide-oss)
Documentation

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
version = "0.14"
version = "0.20"
allprojects {
repositories {

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

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

View File

@@ -34,17 +34,19 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
}
}
dependencies {
testImplementation( "com.miglayout:miglayout-swing:5.2" )
testImplementation( "com.jgoodies:jgoodies-forms:1.9.0" )
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks {
assemble {
dependsOn(
"sourcesJar",
"javadocJar"
)
}
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
named<JavaCompile>( "compileModuleInfoJava" ) {
sourceCompatibility = "9"
@@ -56,12 +58,7 @@ tasks {
archiveBaseName.set( "flatlaf" )
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
manifest.attributes(
"Multi-Release" to "true"
)
into( "META-INF/versions/9" ) {
from( sourceSets["module-info"].output )
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}
}
@@ -88,19 +85,6 @@ tasks {
from( javadoc )
}
register( "testJar", Jar::class ) {
archiveBaseName.set( "flatlaf-test" )
from( sourceSets.test.get().output )
}
}
configurations {
create( "testArtifacts" )
}
artifacts {
add( "testArtifacts", tasks.getByPath( "testJar" ) )
}
publishing {

View File

@@ -27,6 +27,11 @@ public interface FlatClientProperties
String BUTTON_TYPE = "JButton.buttonType";
String BUTTON_TYPE_HELP = "help";
String SELECTED_STATE = "JButton.selectedState";
String SELECTED_STATE_INDETERMINATE = "indeterminate";
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
/**
* Checks whether a client property of a component has the given value.
*/

View File

@@ -19,6 +19,8 @@ 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
*
* @author Karl Tauber
*/
public class FlatDarculaLaf

View File

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

View File

@@ -19,6 +19,8 @@ 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
*
* @author Karl Tauber
*/
public class FlatIntelliJLaf

View File

@@ -19,10 +19,8 @@ package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Insets;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
@@ -30,34 +28,21 @@ import java.awt.Window;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Function;
import javax.swing.AbstractButton;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.LookAndFeel;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.plaf.metal.MetalLookAndFeel;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.util.ScaledNumber;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -69,13 +54,6 @@ import com.formdev.flatlaf.util.UIScale;
public abstract class FlatLaf
extends BasicLookAndFeel
{
private static final String TYPE_PREFIX = "{";
private static final String TYPE_PREFIX_END = "}";
private static final String VARIABLE_PREFIX = "@";
private static final String REF_PREFIX = VARIABLE_PREFIX + "@";
private static final String OPTIONAL_PREFIX = "?";
private static final String GLOBAL_PREFIX = "*.";
private BasicLookAndFeel base;
private String desktopPropertyName;
@@ -94,11 +72,21 @@ public abstract class FlatLaf
}
}
/**
* Returns the look and feel identifier.
* <p>
* Syntax: "FlatLaf - ${theme-name}"
* <p>
* Use {@code UIManager.getLookAndFeel().getID().startsWith( "FlatLaf" )}
* to check whether the current look and feel is FlatLaf.
*/
@Override
public String getID() {
return getName();
return "FlatLaf - " + getName();
}
public abstract boolean isDark();
@Override
public boolean isNativeLookAndFeel() {
return true;
@@ -115,6 +103,11 @@ public abstract class FlatLaf
super.initialize();
// make sure that a plain popup factory is used (otherwise sub-menu rendering
// is "jittery" on Mac, where AquaLookAndFeel installs its own popup factory)
if( PopupFactory.getSharedInstance().getClass() != PopupFactory.class )
PopupFactory.setSharedInstance( new PopupFactory() );
// add mnemonic listener
mnemonicListener = e -> {
if( e.getKeyCode() == KeyEvent.VK_ALT )
@@ -157,6 +150,9 @@ public abstract class FlatLaf
mnemonicListener = null;
}
// restore default link color
new HTMLEditorKit().getStyleSheet().addRule( "a { color: blue; }" );
if( base != null )
base.uninitialize();
@@ -187,6 +183,9 @@ public abstract class FlatLaf
public UIDefaults getDefaults() {
UIDefaults defaults = getBase().getDefaults();
// add Metal resource bundle, which is required for FlatFileChooserUI
defaults.addResourceBundle( "com.sun.swing.internal.plaf.metal.resources.metal" );
// initialize some defaults (for overriding) that are used in basic UI delegates,
// but are not set in MetalLookAndFeel or BasicLookAndFeel
Color control = defaults.getColor( "control" );
@@ -209,15 +208,33 @@ public abstract class FlatLaf
Object aquaMenuBarUI = useScreenMenuBar ? defaults.get( "MenuBarUI" ) : null;
initFonts( defaults );
loadDefaultsFromProperties( defaults );
initIconColors( defaults, isDark() );
// load defaults from properties
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
if( lafClassesForDefaultsLoading != null )
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, defaults );
else
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), defaults );
// use Aqua MenuBarUI if Mac screen menubar is enabled
if( useScreenMenuBar )
defaults.put( "MenuBarUI", aquaMenuBarUI );
// update link color in HTML text
Color linkColor = defaults.getColor( "Component.linkColor" );
if( linkColor != null ) {
new HTMLEditorKit().getStyleSheet().addRule(
String.format( "a { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
}
return defaults;
}
List<Class<?>> getLafClassesForDefaultsLoading() {
return null;
}
private void initFonts( UIDefaults defaults ) {
FontUIResource uiFont = null;
@@ -255,290 +272,42 @@ public abstract class FlatLaf
}
/**
* Load properties associated to Flat LaF classes and add to UI defaults.
*
* Each class that extend this class may have its own .properties file
* in the same package as the class. Properties from superclasses are loaded
* first to give subclasses a chance to override defaults.
* E.g. if running FlatDarkLaf, then the FlatLaf.properties is loaded first
* and FlatDarkLaf.properties loaded second.
* Adds the default color palette for action icons and object icons to the given UIDefaults.
* <p>
* This method is public and static to allow using the color palette with
* other LaFs (e.g. Windows LaF). To do so invoke:
* {@code FlatLaf.initIconColors( UIManager.getLookAndFeelDefaults(), false );}
* after
* {@code UIManager.setLookAndFeel( ... );}.
* <p>
* The colors are based on IntelliJ Platform
* <a href="https://jetbrains.design/intellij/principles/icons/#action-icons">Action icons</a>
* and
* <a href="https://jetbrains.design/intellij/principles/icons/#noun-icons">Noun icons</a>
*/
private void loadDefaultsFromProperties( UIDefaults defaults ) {
// determine classes in class hierarchy in reverse order
ArrayList<Class<?>> lafClasses = new ArrayList<>();
for( Class<?> lafClass = getClass();
FlatLaf.class.isAssignableFrom( lafClass );
lafClass = lafClass.getSuperclass() )
{
lafClasses.add( 0, lafClass );
}
public static void initIconColors( UIDefaults defaults, boolean dark ) {
// colors for action icons
// see https://jetbrains.design/intellij/principles/icons/#action-icons
defaults.put( "Actions.Red", new ColorUIResource( !dark ? 0xDB5860 : 0xC75450 ) );
defaults.put( "Actions.Yellow", new ColorUIResource( !dark ? 0xEDA200 : 0xF0A732 ) );
defaults.put( "Actions.Green", new ColorUIResource( !dark ? 0x59A869 : 0x499C54 ) );
defaults.put( "Actions.Blue", new ColorUIResource( !dark ? 0x389FD6 : 0x3592C4 ) );
defaults.put( "Actions.Grey", new ColorUIResource( !dark ? 0x6E6E6E : 0xAFB1B3 ) );
defaults.put( "Actions.GreyInline", new ColorUIResource( !dark ? 0x7F8B91 : 0x7F8B91 ) );
try {
// load properties files
Properties properties = new Properties();
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
for( Class<?> lafClass : lafClasses ) {
// load core properties
String propertiesName = "/" + lafClass.getName().replace( '.', '/' ) + ".properties";
try( InputStream in = lafClass.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
}
// load properties from addons
for( FlatDefaultsAddon addon : addonLoader ) {
try( InputStream in = addon.getDefaults( lafClass ) ) {
if( in != null )
properties.load( in );
}
}
}
Function<String, String> resolver = value -> {
return resolveValue( properties, value );
};
// get globals, which override all other defaults that end with same suffix
HashMap<String, Object> globals = new HashMap<>();
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( !key.startsWith( GLOBAL_PREFIX ) )
continue;
String value = resolveValue( properties, (String) e.getValue() );
globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver ) );
}
// override UI defaults with globals
for( Object key : defaults.keySet() ) {
if( key instanceof String && ((String)key).contains( "." ) ) {
String skey = (String) key;
String globalKey = skey.substring( skey.lastIndexOf( '.' ) + 1 );
Object globalValue = globals.get( globalKey );
if( globalValue != null )
defaults.put( key, globalValue );
}
}
// add non-global properties to UI defaults
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( key.startsWith( VARIABLE_PREFIX ) || key.startsWith( GLOBAL_PREFIX ) )
continue;
String value = resolveValue( properties, (String) e.getValue() );
defaults.put( key, parseValue( key, value, resolver ) );
}
} catch( IOException ex ) {
ex.printStackTrace();
}
}
private String resolveValue( Properties properties, String value ) {
if( !value.startsWith( VARIABLE_PREFIX ) )
return value;
if( value.startsWith( REF_PREFIX ) )
value = value.substring( REF_PREFIX.length() );
boolean optional = false;
if( value.startsWith( OPTIONAL_PREFIX ) ) {
value = value.substring( OPTIONAL_PREFIX.length() );
optional = true;
}
String newValue = properties.getProperty( value );
if( newValue == null ) {
if( optional )
return "null";
System.err.println( "variable or reference '" + value + "' not found" );
throw new IllegalArgumentException( value );
}
return resolveValue( properties, newValue );
}
private enum ValueType { UNKNOWN, STRING, INTEGER, BORDER, ICON, INSETS, SIZE, COLOR, SCALEDNUMBER }
private Object parseValue( String key, String value, Function<String, String> resolver ) {
value = value.trim();
// null, false, true
switch( value ) {
case "null": return null;
case "false": return false;
case "true": return true;
}
ValueType valueType = ValueType.UNKNOWN;
// check whether value type is specified in the value
if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
String typeStr = value.substring( TYPE_PREFIX.length(), end );
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
// remove type from value
value = value.substring( end + TYPE_PREFIX_END.length() );
} catch( IllegalArgumentException ex ) {
// ignore
}
}
}
// determine value type from key
if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER;
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
valueType = ValueType.ICON;
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
valueType = ValueType.INSETS;
else if( key.endsWith( "Size" ) )
valueType = ValueType.SIZE;
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
valueType = ValueType.INTEGER;
}
// parse value
switch( valueType ) {
case STRING: return value;
case INTEGER: return parseInteger( value, true );
case BORDER: return parseBorder( value, resolver );
case ICON: return parseInstance( value );
case INSETS: return parseInsets( value );
case SIZE: return parseSize( value );
case COLOR: return parseColor( value, true );
case SCALEDNUMBER: return parseScaledNumber( value );
case UNKNOWN:
default:
// colors
ColorUIResource color = parseColor( value, false );
if( color != null )
return color;
// integer
Integer integer = parseInteger( value, false );
if( integer != null )
return integer;
// string
return value;
}
}
private Object parseBorder( String value, Function<String, String> resolver ) {
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor]
List<String> parts = split( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() == 5)
? parseColor( resolver.apply( parts.get( 4 ) ), true )
: null;
return (LazyValue) t -> {
return (lineColor != null)
? new FlatLineBorder( insets, lineColor )
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value );
}
private Object parseInstance( String value ) {
return (LazyValue) t -> {
try {
return Class.forName( value ).newInstance();
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
ex.printStackTrace();
return null;
}
};
}
private Insets parseInsets( String value ) {
List<String> numbers = split( value, ',' );
try {
return new InsetsUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ),
Integer.parseInt( numbers.get( 2 ) ),
Integer.parseInt( numbers.get( 3 ) ) );
} catch( NumberFormatException ex ) {
System.err.println( "invalid insets '" + value + "'" );
throw ex;
}
}
private Dimension parseSize( String value ) {
List<String> numbers = split( value, ',' );
try {
return new DimensionUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ) );
} catch( NumberFormatException ex ) {
System.err.println( "invalid size '" + value + "'" );
throw ex;
}
}
private ColorUIResource parseColor( String value, boolean reportError ) {
try {
int rgb = Integer.parseInt( value, 16 );
if( value.length() == 6 )
return new ColorUIResource( rgb );
if( value.length() == 8 )
return new ColorUIResource( new Color( rgb, true ) );
if( reportError )
throw new NumberFormatException( value );
} catch( NumberFormatException ex ) {
if( reportError ) {
System.err.println( "invalid color '" + value + "'" );
throw ex;
}
// not a color --> ignore
}
return null;
}
private Integer parseInteger( String value, boolean reportError ) {
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
if( reportError ) {
System.err.println( "invalid integer '" + value + "'" );
throw ex;
}
}
return null;
}
private ScaledNumber parseScaledNumber( String value ) {
try {
return new ScaledNumber( Integer.parseInt( value ) );
} catch( NumberFormatException ex ) {
System.err.println( "invalid integer '" + value + "'" );
throw ex;
}
}
public static List<String> split( String str, char delim ) {
ArrayList<String> strs = new ArrayList<>();
int delimIndex = str.indexOf( delim );
int index = 0;
while( delimIndex >= 0 ) {
strs.add( str.substring( index, delimIndex ) );
index = delimIndex + 1;
delimIndex = str.indexOf( delim, index );
}
strs.add( str.substring( index ) );
return strs;
// colors for object icons
// see https://jetbrains.design/intellij/principles/icons/#noun-icons
defaults.put( "Objects.Grey", new ColorUIResource( 0x9AA7B0 ) );
defaults.put( "Objects.Blue", new ColorUIResource( 0x40B6E0 ) );
defaults.put( "Objects.Green", new ColorUIResource( 0x62B543 ) );
defaults.put( "Objects.Yellow", new ColorUIResource( 0xF4AF3D ) );
defaults.put( "Objects.YellowDark", new ColorUIResource( 0xD9A343 ) );
defaults.put( "Objects.Purple", new ColorUIResource( 0xB99BF8 ) );
defaults.put( "Objects.Pink", new ColorUIResource( 0xF98B9E ) );
defaults.put( "Objects.Red", new ColorUIResource( 0xF26522 ) );
defaults.put( "Objects.RedStatus", new ColorUIResource( 0xE05555 ) );
defaults.put( "Objects.GreenAndroid", new ColorUIResource( 0xA4C639 ) );
defaults.put( "Objects.BlackText", new ColorUIResource( 0x231F20 ) );
}
private static void reSetLookAndFeel() {

View File

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

View File

@@ -0,0 +1,488 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.UIDefaults;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.json.Json;
import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.StringUtils;
/**
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
*
* .theme.json files are used by Theme plugins for IntelliJ IDEA and other
* JetBrains IDEs that are based on IntelliJ platform.
*
* Here you can find IntelliJ Theme plugins:
* https://plugins.jetbrains.com/search?tags=Theme
*
* The IntelliJ .theme.json file are documented here:
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html
*
* @author Karl Tauber
*/
public class IntelliJTheme
{
public final String name;
public final boolean dark;
public final String author;
private final Map<String, String> colors;
private final Map<String, Object> ui;
private final Map<String, Object> icons;
private Map<String, ColorUIResource> namedColors = Collections.emptyMap();
/**
* Loads a IntelliJ .theme.json file from the given input stream,
* creates a Laf instance for it and installs it.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
public static boolean install( InputStream in ) {
try {
return FlatLaf.install( createLaf( in ) );
} catch( Exception ex ) {
System.err.println( "Failed to load IntelliJ theme" );
ex.printStackTrace();
return false;
}
}
/**
* Loads a IntelliJ .theme.json file from the given input stream and
* creates a Laf instance for it.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
public static FlatLaf createLaf( InputStream in )
throws IOException, ParseException
{
return createLaf( new IntelliJTheme( in ) );
}
/**
* Creates a Laf instance for the given IntelliJ theme.
*/
public static FlatLaf createLaf( IntelliJTheme theme ) {
return new ThemeLaf( theme );
}
/**
* Loads a IntelliJ .theme.json file from the given input stream.
*
* The input stream is automatically closed.
* Using a buffered input stream is not necessary.
*/
@SuppressWarnings( "unchecked" )
public IntelliJTheme( InputStream in )
throws IOException, ParseException
{
Map<String, Object> json;
try( Reader reader = new InputStreamReader( in, StandardCharsets.UTF_8 ) ) {
json = (Map<String, Object>) Json.parse( reader );
}
name = (String) json.get( "name" );
dark = Boolean.parseBoolean( (String) json.get( "dark" ) );
author = (String) json.get( "author" );
colors = (Map<String, String>) json.get( "colors" );
ui = (Map<String, Object>) json.get( "ui" );
icons = (Map<String, Object>) json.get( "icons" );
}
private void applyProperties( UIDefaults defaults ) {
if( ui == null )
return;
defaults.put( "Component.isIntelliJTheme", true );
loadNamedColors( defaults );
// convert Json "ui" structure to UI defaults
ArrayList<Object> defaultsKeysCache = new ArrayList<>();
Set<String> uiKeys = new HashSet<>();
for( Map.Entry<String, Object> e : ui.entrySet() )
apply( e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
applyColorPalette( defaults );
applyCheckBoxColors( defaults );
// IDEA uses TextField.background for editable ComboBox and Spinner
defaults.put( "ComboBox.editableBackground", defaults.get( "TextField.background" ) );
defaults.put( "Spinner.background", defaults.get( "TextField.background" ) );
// Spinner arrow button always has same colors as ComboBox arrow button
defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonEditableBackground" ) );
defaults.put( "Spinner.buttonArrowColor", defaults.get( "ComboBox.buttonArrowColor" ) );
defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) );
// some themes specify colors for TextField.background, but forget to specify it for other components
// (probably because those components are not used in IntelliJ)
if( uiKeys.contains( "TextField.background" ) ) {
Object textFieldBackground = defaults.get( "TextField.background" );
if( !uiKeys.contains( "FormattedTextField.background" ) )
defaults.put( "FormattedTextField.background", textFieldBackground );
if( !uiKeys.contains( "PasswordField.background" ) )
defaults.put( "PasswordField.background", textFieldBackground );
if( !uiKeys.contains( "EditorPane.background" ) )
defaults.put( "EditorPane.background", textFieldBackground );
if( !uiKeys.contains( "TextArea.background" ) )
defaults.put( "TextArea.background", textFieldBackground );
if( !uiKeys.contains( "TextPane.background" ) )
defaults.put( "TextPane.background", textFieldBackground );
if( !uiKeys.contains( "Spinner.background" ) )
defaults.put( "Spinner.background", textFieldBackground );
}
}
/**
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#defining-named-colors
*/
private void loadNamedColors( UIDefaults defaults ) {
if( colors == null )
return;
namedColors = new HashMap<>();
for( Map.Entry<String, String> e : colors.entrySet() ) {
String value = e.getValue();
ColorUIResource color = UIDefaultsLoader.parseColor( value );
if( color != null ) {
String key = e.getKey();
namedColors.put( key, color );
defaults.put( "ColorPalette." + e.getKey(), color );
}
}
}
/**
* http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html#custom-ui-control-colors
*/
@SuppressWarnings( "unchecked" )
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
if( value instanceof Map ) {
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
} else {
uiKeys.add( key );
// map keys
key = uiKeyMapping.getOrDefault( key, key );
if( key.isEmpty() )
return; // ignore key
String valueStr = value.toString();
// map named colors
Object uiValue = namedColors.get( valueStr );
// parse value
if( uiValue == null ) {
// fix errors (missing '#' for colors)
if( !valueStr.startsWith( "#" ) && (key.endsWith( "ground" ) || key.endsWith( "Color" )) )
valueStr = "#" + valueStr;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) ) {
List<String> parts = StringUtils.split( valueStr, ',' );
if( parts.size() == 5 && !parts.get( 4 ).startsWith( "#" ) ) {
parts.set( 4, "#" + parts.get( 4 ) );
valueStr = String.join( ",", parts );
}
}
// parse value
try {
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
} catch( RuntimeException ex ) {
UIDefaultsLoader.logParseError( key, valueStr, ex );
return; // ignore invalid value
}
}
if( key.startsWith( "*." ) ) {
// wildcard
String tail = key.substring( 1 );
// because we can not iterate over the UI defaults keys while
// modifying UI defaults in the same loop, we have to copy the keys
if( defaultsKeysCache.size() != defaults.size() ) {
defaultsKeysCache.clear();
Enumeration<Object> e = defaults.keys();
while( e.hasMoreElements() )
defaultsKeysCache.add( e.nextElement() );
}
// replace all values in UI defaults that match the wildcard key
for( Object k : defaultsKeysCache ) {
if( k instanceof String ) {
// support replacing of mapped keys
// (e.g. set ComboBox.buttonEditableBackground to *.background
// because it is mapped from ComboBox.ArrowButton.background)
String km = uiKeyInverseMapping.getOrDefault( k, (String) k );
if( km.endsWith( tail ) && !noWildcardReplace.contains( k ) && !((String)k).startsWith( "CheckBox.icon." ) )
defaults.put( k, uiValue );
}
}
} else
defaults.put( key, uiValue );
}
}
private void applyColorPalette( UIDefaults defaults ) {
if( icons == null )
return;
Object palette = icons.get( "ColorPalette" );
if( !(palette instanceof Map) )
return;
@SuppressWarnings( "unchecked" )
Map<String, Object> colorPalette = (Map<String, Object>) palette;
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
String key = e.getKey();
Object value = e.getValue();
if( key.startsWith( "Checkbox." ) || !(value instanceof String) )
continue;
if( dark )
key = StringUtils.removeTrailing( key, ".Dark" );
ColorUIResource color = toColor( (String) value );
if( color != null )
defaults.put( key, color );
}
}
private ColorUIResource toColor( String value ) {
// map named colors
ColorUIResource color = namedColors.get( value );
// parse color
return (color != null) ? color : UIDefaultsLoader.parseColor( value );
}
/**
* Because IDEA uses SVGs for check boxes and radio buttons the colors for
* this two components are specified in "icons > ColorPalette".
* FlatLaf uses vector icons and expects colors for the two components in UI defaults.
*/
private void applyCheckBoxColors( UIDefaults defaults ) {
if( icons == null )
return;
Object palette = icons.get( "ColorPalette" );
if( !(palette instanceof Map) )
return;
boolean checkboxModified = false;
@SuppressWarnings( "unchecked" )
Map<String, Object> colorPalette = (Map<String, Object>) palette;
for( Map.Entry<String, Object> e : colorPalette.entrySet() ) {
String key = e.getKey();
Object value = e.getValue();
if( !key.startsWith( "Checkbox." ) || !(value instanceof String) )
continue;
if( key.equals( "Checkbox.Background.Default" ) ||
key.equals( "Checkbox.Foreground.Selected" ) )
{
// This two keys do not work correctly in IDEA because they
// map SVG color "#ffffff" to another color, but checkBox.svg and
// radio.svg (in package com.intellij.ide.ui.laf.icons.intellij)
// use "#fff". So use white to get same appearance as in IDEA.
value = "#ffffff";
}
if( dark )
key = StringUtils.removeTrailing( key, ".Dark" );
String newKey = checkboxKeyMapping.get( key );
if( newKey != null ) {
ColorUIResource color = toColor( (String) value );
if( color != null )
defaults.put( newKey, color );
checkboxModified = true;
}
}
// When IDEA replaces colors in SVGs it uses color values and not the keys
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
// have same color value:
// - Checkbox.Background.Default.Dark has same color as Checkbox.Background.Selected.Dark
// - Checkbox.Border.Default.Dark has same color as Checkbox.Border.Selected.Dark
// - Checkbox.Focus.Thin.Default.Dark has same color as Checkbox.Focus.Thin.Selected.Dark
//
// So if only e.g. Checkbox.Background.Default.Dark is specified in .theme.json,
// then this color is also used for Checkbox.Background.Selected.Dark.
// Occurs e.g. in "Dark purple" theme.
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Focus.Thin.Default.Dark", "Checkbox.Focus.Thin.Selected.Dark" );
// remove hover and pressed colors
if( checkboxModified ) {
defaults.remove( "CheckBox.icon.hoverBorderColor" );
defaults.remove( "CheckBox.icon.focusedBackground" );
defaults.remove( "CheckBox.icon.hoverBackground" );
defaults.remove( "CheckBox.icon.pressedBackground" );
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
}
}
private void fixCheckBoxColor( UIDefaults defaults, Map<String, Object> colorPalette, String key1, String key2 ) {
if( colorPalette.containsKey( key1 ) == colorPalette.containsKey( key2 ) )
return;
String newKey1 = checkboxKeyMapping.get( StringUtils.removeTrailing( key1, ".Dark" ) );
String newKey2 = checkboxKeyMapping.get( StringUtils.removeTrailing( key2, ".Dark" ) );
if( colorPalette.containsKey( key1 ) )
defaults.put( newKey2, defaults.get( newKey1 ) );
else
defaults.put( newKey1, defaults.get( newKey2 ) );
}
private static Map<String, String> uiKeyMapping = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
private static Set<String> noWildcardReplace = new HashSet<>();
static {
// Button
// IDEA buttons support gradient for background and border, but FlatLaf does not
uiKeyMapping.put( "Button.startBackground", "Button.background" );
uiKeyMapping.put( "Button.startBorderColor", "Button.borderColor" );
uiKeyMapping.put( "Button.default.startBackground", "Button.default.background" );
uiKeyMapping.put( "Button.default.startBorderColor", "Button.default.borderColor" );
uiKeyMapping.put( "Button.endBackground", "" ); // ignore
uiKeyMapping.put( "Button.endBorderColor", "" ); // ignore
uiKeyMapping.put( "Button.default.endBackground", "" ); // ignore
uiKeyMapping.put( "Button.default.endBorderColor", "" ); // ignore
// ComboBox
uiKeyMapping.put( "ComboBox.background", "" ); // ignore
uiKeyMapping.put( "ComboBox.nonEditableBackground", "ComboBox.background" );
uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" );
uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" );
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
// ProgressBar
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
uiKeyMapping.put( "ProgressBar.trackColor", "ProgressBar.background" );
uiKeyMapping.put( "ProgressBar.progressColor", "ProgressBar.foreground" );
// ScrollBar
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
uiKeyMapping.put( "ScrollBar.thumbColor", "ScrollBar.thumb" );
// Slider
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
checkboxKeyMapping.put( "Checkbox.Border.Disabled", "CheckBox.icon.disabledBorderColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Default", "CheckBox.icon.focusedBorderColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Wide", "CheckBox.icon.focusedColor" );
checkboxKeyMapping.put( "Checkbox.Foreground.Disabled", "CheckBox.icon.disabledCheckmarkColor" );
checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" );
checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" );
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
// because FlatLaf uses Button.background and Button.borderColor,
// but IDEA uses Button.startBackground and Button.startBorderColor,
// our default button background and border colors may be replaced by
// wildcard *.background and *.borderColor colors
noWildcardReplace.add( "Button.background" );
noWildcardReplace.add( "Button.borderColor" );
noWildcardReplace.add( "Button.default.background" );
noWildcardReplace.add( "Button.default.borderColor" );
noWildcardReplace.add( "ToggleButton.background" );
}
//---- class ThemeLaf -----------------------------------------------------
public static class ThemeLaf
extends FlatLaf
{
private final IntelliJTheme theme;
public ThemeLaf( IntelliJTheme theme ) {
this.theme = theme;
}
@Override
public String getName() {
return theme.name;
}
@Override
public String getDescription() {
return theme.name;
}
@Override
public boolean isDark() {
return theme.dark;
}
public IntelliJTheme getTheme() {
return theme;
}
@Override
public UIDefaults getDefaults() {
UIDefaults defaults = super.getDefaults();
theme.applyProperties( defaults );
return defaults;
}
@Override
ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
ArrayList<Class<?>> lafClasses = new ArrayList<>();
lafClasses.add( FlatLaf.class );
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
lafClasses.add( theme.dark ? FlatDarculaLaf.class : FlatIntelliJLaf.class );
lafClasses.add( ThemeLaf.class );
return lafClasses;
}
}
}

View File

@@ -28,6 +28,7 @@ import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -151,7 +152,7 @@ class LinuxFontPolicy
int size = 10;
if( generalFont != null ) {
List<String> strs = FlatLaf.split( generalFont, ',' );
List<String> strs = StringUtils.split( generalFont, ',' );
try {
family = strs.get( 0 );
size = Integer.parseInt( strs.get( 1 ) );

View File

@@ -0,0 +1,478 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Function;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.InsetsUIResource;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.ScaledNumber;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Load UI defaults from properties files associated to Flat LaF classes and add to UI defaults.
*
* Each class that extend the LaF class may have its own .properties file
* in the same package as the class. Properties from superclasses are loaded
* first to give subclasses a chance to override defaults.
* E.g. if running FlatDarkLaf, then the FlatLaf.properties is loaded first
* and FlatDarkLaf.properties loaded second.
*
* @author Karl Tauber
*/
class UIDefaultsLoader
{
private static final String TYPE_PREFIX = "{";
private static final String TYPE_PREFIX_END = "}";
private static final String VARIABLE_PREFIX = "@";
private static final String REF_PREFIX = VARIABLE_PREFIX + "@";
private static final String OPTIONAL_PREFIX = "?";
private static final String GLOBAL_PREFIX = "*.";
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, UIDefaults defaults ) {
// determine classes in class hierarchy in reverse order
ArrayList<Class<?>> lafClasses = new ArrayList<>();
for( Class<?> lafClass = lookAndFeelClass;
FlatLaf.class.isAssignableFrom( lafClass );
lafClass = lafClass.getSuperclass() )
{
lafClasses.add( 0, lafClass );
}
loadDefaultsFromProperties( lafClasses, defaults );
}
static void loadDefaultsFromProperties( List<Class<?>> lafClasses, UIDefaults defaults ) {
try {
// load properties files
Properties properties = new Properties();
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
for( Class<?> lafClass : lafClasses ) {
// load core properties
String propertiesName = "/" + lafClass.getName().replace( '.', '/' ) + ".properties";
try( InputStream in = lafClass.getResourceAsStream( propertiesName ) ) {
if( in != null )
properties.load( in );
}
// load properties from addons
for( FlatDefaultsAddon addon : addonLoader ) {
try( InputStream in = addon.getDefaults( lafClass ) ) {
if( in != null )
properties.load( in );
}
}
}
// collect all platform specific keys (but do not modify properties)
ArrayList<String> platformSpecificKeys = new ArrayList<>();
for( Object key : properties.keySet() ) {
if( ((String)key).startsWith( "[" ) )
platformSpecificKeys.add( (String) key );
}
// remove platform specific properties and re-add only properties
// for current platform, but with platform prefix removed
if( !platformSpecificKeys.isEmpty() ) {
String platformPrefix =
SystemInfo.IS_WINDOWS ? "[win]" :
SystemInfo.IS_MAC ? "[mac]" :
SystemInfo.IS_LINUX ? "[linux]" : "[unknown]";
for( String key : platformSpecificKeys ) {
Object value = properties.remove( key );
if( key.startsWith( platformPrefix ) )
properties.put( key.substring( platformPrefix.length() ), value );
}
}
Function<String, String> resolver = value -> {
return resolveValue( properties, value );
};
// get globals, which override all other defaults that end with same suffix
HashMap<String, Object> globals = new HashMap<>();
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( !key.startsWith( GLOBAL_PREFIX ) )
continue;
String value = resolveValue( properties, (String) e.getValue() );
try {
globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex );
}
}
// override UI defaults with globals
for( Object key : defaults.keySet() ) {
if( key instanceof String && ((String)key).contains( "." ) ) {
String skey = (String) key;
String globalKey = skey.substring( skey.lastIndexOf( '.' ) + 1 );
Object globalValue = globals.get( globalKey );
if( globalValue != null )
defaults.put( key, globalValue );
}
}
// add non-global properties to UI defaults
for( Map.Entry<Object, Object> e : properties.entrySet() ) {
String key = (String) e.getKey();
if( key.startsWith( VARIABLE_PREFIX ) || key.startsWith( GLOBAL_PREFIX ) )
continue;
String value = resolveValue( properties, (String) e.getValue() );
try {
defaults.put( key, parseValue( key, value, resolver ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex );
}
}
} catch( IOException ex ) {
ex.printStackTrace();
}
}
static void logParseError( String key, String value, RuntimeException ex ) {
System.err.println( "Failed to parse: '" + key + '=' + value + '\'' );
System.err.println( " " + ex.getMessage() );
}
private static String resolveValue( Properties properties, String value ) {
if( !value.startsWith( VARIABLE_PREFIX ) )
return value;
if( value.startsWith( REF_PREFIX ) )
value = value.substring( REF_PREFIX.length() );
boolean optional = false;
if( value.startsWith( OPTIONAL_PREFIX ) ) {
value = value.substring( OPTIONAL_PREFIX.length() );
optional = true;
}
String newValue = properties.getProperty( value );
if( newValue == null ) {
if( optional )
return "null";
throw new IllegalArgumentException( "variable or reference '" + value + "' not found" );
}
return resolveValue( properties, newValue );
}
private enum ValueType { UNKNOWN, STRING, INTEGER, BORDER, ICON, INSETS, SIZE, COLOR, SCALEDNUMBER }
static Object parseValue( String key, String value ) {
return parseValue( key, value, v -> v );
}
private static Object parseValue( String key, String value, Function<String, String> resolver ) {
value = value.trim();
// null, false, true
switch( value ) {
case "null": return null;
case "false": return false;
case "true": return true;
}
ValueType valueType = ValueType.UNKNOWN;
// check whether value type is specified in the value
if( value.startsWith( "#" ) )
valueType = ValueType.COLOR;
else if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
String typeStr = value.substring( TYPE_PREFIX.length(), end );
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
// remove type from value
value = value.substring( end + TYPE_PREFIX_END.length() );
} catch( IllegalArgumentException ex ) {
// ignore
}
}
}
// determine value type from key
if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( "ground" ) || key.endsWith( "Color" ) )
valueType = ValueType.COLOR;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER;
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
valueType = ValueType.ICON;
else if( key.endsWith( ".margin" ) || key.endsWith( ".padding" ) ||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
valueType = ValueType.INSETS;
else if( key.endsWith( "Size" ) )
valueType = ValueType.SIZE;
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
valueType = ValueType.INTEGER;
else if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
}
// parse value
switch( valueType ) {
case STRING: return value;
case INTEGER: return parseInteger( value, true );
case BORDER: return parseBorder( value, resolver );
case ICON: return parseInstance( value );
case INSETS: return parseInsets( value );
case SIZE: return parseSize( value );
case COLOR: return parseColorOrFunction( value, true );
case SCALEDNUMBER: return parseScaledNumber( value );
case UNKNOWN:
default:
// colors
ColorUIResource color = parseColorOrFunction( value, false );
if( color != null )
return color;
// integer
Integer integer = parseInteger( value, false );
if( integer != null )
return integer;
// string
return value;
}
}
private static Object parseBorder( String value, Function<String, String> resolver ) {
if( value.indexOf( ',' ) >= 0 ) {
// top,left,bottom,right[,lineColor]
List<String> parts = StringUtils.split( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() == 5)
? parseColorOrFunction( resolver.apply( parts.get( 4 ) ), true )
: null;
return (LazyValue) t -> {
return (lineColor != null)
? new FlatLineBorder( insets, lineColor )
: new FlatEmptyBorder( insets );
};
} else
return parseInstance( value );
}
private static Object parseInstance( String value ) {
return (LazyValue) t -> {
try {
return Class.forName( value ).newInstance();
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
ex.printStackTrace();
return null;
}
};
}
private static Insets parseInsets( String value ) {
List<String> numbers = StringUtils.split( value, ',' );
try {
return new InsetsUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ),
Integer.parseInt( numbers.get( 2 ) ),
Integer.parseInt( numbers.get( 3 ) ) );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException( "invalid insets '" + value + "'" );
}
}
private static Dimension parseSize( String value ) {
List<String> numbers = StringUtils.split( value, ',' );
try {
return new DimensionUIResource(
Integer.parseInt( numbers.get( 0 ) ),
Integer.parseInt( numbers.get( 1 ) ) );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException( "invalid size '" + value + "'" );
}
}
private static ColorUIResource parseColorOrFunction( String value, boolean reportError ) {
if( value.endsWith( ")" ) )
return parseColorFunctions( value, reportError );
return parseColor( value, reportError );
}
static ColorUIResource parseColor( String value ) {
return parseColor( value, false );
}
private static ColorUIResource parseColor( String value, boolean reportError ) {
try {
int rgba = parseColorRGBA( value );
return ((rgba & 0xff000000) == 0xff000000)
? new ColorUIResource( rgba )
: new ColorUIResource( new Color( rgba, true ) );
} catch( IllegalArgumentException ex ) {
if( reportError )
throw new IllegalArgumentException( "invalid color '" + value + "'" );
// not a color --> ignore
}
return null;
}
/**
* Parses a hex color in {@code #RGB}, {@code #RGBA}, {@code #RRGGBB} or {@code #RRGGBBAA}
* format and returns it as {@code rgba} integer suitable for {@link java.awt.Color},
* which includes alpha component in bits 24-31.
*
* @throws IllegalArgumentException
*/
static int parseColorRGBA( String value ) {
int len = value.length();
if( (len != 4 && len != 5 && len != 7 && len != 9) || value.charAt( 0 ) != '#' )
throw new IllegalArgumentException();
// parse hex
int n = 0;
for( int i = 1; i < len; i++ ) {
char ch = value.charAt( i );
int digit;
if( ch >= '0' && ch <= '9' )
digit = ch - '0';
else if( ch >= 'a' && ch <= 'f' )
digit = ch - 'a' + 10;
else if( ch >= 'A' && ch <= 'F' )
digit = ch - 'A' + 10;
else
throw new IllegalArgumentException();
n = (n << 4) | digit;
}
if( len <= 5 ) {
// double nibbles
int n1 = n & 0xf000;
int n2 = n & 0xf00;
int n3 = n & 0xf0;
int n4 = n & 0xf;
n = (n1 << 16) | (n1 << 12) | (n2 << 12) | (n2 << 8) | (n3 << 8) | (n3 << 4) | (n4 << 4) | n4;
}
return (len == 4 || len == 7)
? (0xff000000 | n) // set alpha to 255
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
}
private static ColorUIResource parseColorFunctions( String value, boolean reportError ) {
int paramsStart = value.indexOf( '(' );
if( paramsStart < 0 ) {
if( reportError )
throw new IllegalArgumentException( "missing opening parenthesis in function '" + value + "'" );
return null;
}
String function = value.substring( 0, paramsStart ).trim();
List<String> params = StringUtils.split( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
if( params.isEmpty() )
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
switch( function ) {
case "lighten": return parseColorLightenOrDarken( true, params, reportError );
case "darken": return parseColorLightenOrDarken( false, params, reportError );
}
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
}
/**
* Syntax: lighten(amount[,options]) or darken(amount[,options])
* - amount: percentage 0-100%
* - options: [relative] [autoInverse]
*/
private static ColorUIResource parseColorLightenOrDarken( boolean lighten, List<String> params, boolean reportError ) {
int amount = parsePercentage( params.get( 0 ) );
boolean relative = false;
boolean autoInverse = false;
if( params.size() >= 2 ) {
String options = params.get( 1 );
relative = options.contains( "relative" );
autoInverse = options.contains( "autoInverse" );
}
return new DerivedColor( lighten
? new ColorFunctions.Lighten( amount, relative, autoInverse )
: new ColorFunctions.Darken( amount, relative, autoInverse ) );
}
private static int parsePercentage( String value ) {
if( !value.endsWith( "%" ) )
throw new NumberFormatException( "invalid percentage '" + value + "'" );
int val;
try {
val = Integer.parseInt( value.substring( 0, value.length() - 1 ) );
} catch( NumberFormatException ex ) {
throw new NumberFormatException( "invalid percentage '" + value + "'" );
}
if( val < 0 || val > 100 )
throw new IllegalArgumentException( "percentage out of range (0-100%) '" + value + "'" );
return val;
}
private static Integer parseInteger( String value, boolean reportError ) {
try {
return Integer.parseInt( value );
} catch( NumberFormatException ex ) {
if( reportError )
throw new NumberFormatException( "invalid integer '" + value + "'" );
}
return null;
}
private static ScaledNumber parseScaledNumber( String value ) {
try {
return new ScaledNumber( Integer.parseInt( value ) );
} catch( NumberFormatException ex ) {
throw new NumberFormatException( "invalid integer '" + value + "'" );
}
}
}

View File

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

View File

@@ -16,14 +16,18 @@
package com.formdev.flatlaf.icons;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* Icon for {@link javax.swing.JCheckBox}.
@@ -34,6 +38,7 @@ import com.formdev.flatlaf.ui.FlatButtonUI;
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.focusColor Color
* @uiDefault CheckBox.icon.focusedColor Color optional; defaults to Component.focusColor
* @uiDefault CheckBox.icon.borderColor Color
* @uiDefault CheckBox.icon.disabledBorderColor Color
* @uiDefault CheckBox.icon.selectedBorderColor Color
@@ -50,6 +55,7 @@ import com.formdev.flatlaf.ui.FlatButtonUI;
* @uiDefault CheckBox.icon.selectedPressedBackground Color optional
* @uiDefault CheckBox.icon.checkmarkColor Color
* @uiDefault CheckBox.icon.disabledCheckmarkColor Color
* @uiDefault CheckBox.arc int
*
* @author Karl Tauber
*/
@@ -57,7 +63,9 @@ public class FlatCheckBoxIcon
extends FlatAbstractIcon
{
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusedColor",
UIManager.getColor( "Component.focusColor" ) );
protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 );
protected final Color borderColor = UIManager.getColor( "CheckBox.icon.borderColor" );
protected final Color disabledBorderColor = UIManager.getColor( "CheckBox.icon.disabledBorderColor" );
@@ -84,7 +92,8 @@ public class FlatCheckBoxIcon
@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
boolean selected = (c instanceof AbstractButton) ? ((AbstractButton)c).isSelected() : false;
boolean indeterminate = c instanceof JComponent && clientPropertyEquals( (JComponent) c, SELECTED_STATE, SELECTED_STATE_INDETERMINATE );
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
// paint focused border
if( c.hasFocus() && focusWidth > 0 ) {
@@ -102,33 +111,40 @@ public class FlatCheckBoxIcon
paintBorder( g2 );
// paint background
g2.setColor( FlatButtonUI.buttonStateColor( c,
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
selected ? selectedBackground : background,
disabledBackground,
focusedBackground,
selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground,
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ) );
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ),
background );
paintBackground( g2 );
// paint checkmark
if( selected ) {
if( selected || indeterminate ) {
g2.setColor( c.isEnabled() ? checkmarkColor : disabledCheckmarkColor );
paintCheckmark( g2 );
if( indeterminate )
paintIndeterminate( g2 );
else
paintCheckmark( g2 );
}
}
protected void paintFocusBorder( Graphics2D g2 ) {
// the outline focus border is painted outside of the icon
int wh = ICON_SIZE - 1 + (focusWidth * 2);
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, 8, 8 );
int arcwh = (arc + focusWidth) * 2;
g2.fillRoundRect( -focusWidth + 1, -focusWidth, wh, wh, arcwh, arcwh );
}
protected void paintBorder( Graphics2D g2 ) {
g2.fillRoundRect( 1, 0, 14, 14, 4, 4 );
int arcwh = arc * 2;
g2.fillRoundRect( 1, 0, 14, 14, arcwh, arcwh );
}
protected void paintBackground( Graphics2D g2 ) {
g2.fillRoundRect( 2, 1, 12, 12, 3, 3 );
int arcwh = (arc * 2) - 1;
g2.fillRoundRect( 2, 1, 12, 12, arcwh, arcwh );
}
protected void paintCheckmark( Graphics2D g2 ) {
@@ -140,4 +156,8 @@ public class FlatCheckBoxIcon
g2.setStroke( new BasicStroke( 1.9f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) );
g2.draw( path );
}
protected void paintIndeterminate( Graphics2D g2 ) {
g2.fill( new RoundRectangle2D.Float( 3.75f, 5.75f, 8.5f, 2.5f, 2f, 2f ) );
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,6 +24,7 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import javax.swing.UIManager;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatUIUtils;
/**
* Help button icon for {@link javax.swing.JButton}.
@@ -99,12 +100,12 @@ public class FlatHelpButtonIcon
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
// paint background
g2.setColor( FlatButtonUI.buttonStateColor( c,
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
background,
disabledBackground,
focusedBackground,
hoverBackground,
pressedBackground ) );
pressedBackground ), background );
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
// paint question mark

View File

@@ -60,7 +60,7 @@ public class FlatMenuArrowIcon
g.draw( path );
} else {
// triangle arrow
g.fill( FlatUIUtils.createPath( 0,0.5, 0,9.5, 5,5 ) );
g.fill( FlatUIUtils.createPath( 0,0.5, 5,5, 0,9.5 ) );
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,6 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Paint;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
@@ -72,7 +71,7 @@ public class FlatBorder
try {
FlatUIUtils.setRenderingHints( g2 );
boolean isCellEditor = FlatUIUtils.isTableCellEditor( c );
boolean isCellEditor = isTableCellEditor( c );
float focusWidth = isCellEditor ? 0 : getFocusWidth();
float borderWidth = getBorderWidth( c );
float arc = isCellEditor ? 0 : getArc();
@@ -83,7 +82,7 @@ public class FlatBorder
getLineWidth() + scale( (float) innerFocusWidth ), arc );
}
g2.setPaint( getBorderColor( c ) );
g2.setColor( getBorderColor( c ) );
FlatUIUtils.drawRoundRectangle( g2, x, y, width, height, focusWidth, borderWidth, arc );
} finally {
g2.dispose();
@@ -94,7 +93,7 @@ public class FlatBorder
return focusColor;
}
protected Paint getBorderColor( Component c ) {
protected Color getBorderColor( Component c ) {
boolean enabled = c.isEnabled() && (!(c instanceof JTextComponent) || ((JTextComponent)c).isEditable());
return enabled
? (isFocused( c ) ? focusedBorderColor : borderColor)
@@ -133,9 +132,13 @@ public class FlatBorder
return c.hasFocus();
}
protected boolean isTableCellEditor( Component c ) {
return FlatUIUtils.isTableCellEditor( c );
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
boolean isCellEditor = FlatUIUtils.isTableCellEditor( c );
boolean isCellEditor = isTableCellEditor( c );
float ow = (isCellEditor ? 0 : getFocusWidth()) + getLineWidth();
insets = super.getBorderInsets( c, insets );

View File

@@ -21,7 +21,6 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Paint;
import javax.swing.JButton;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
@@ -68,7 +67,7 @@ public class FlatButtonBorder
}
@Override
protected Paint getBorderColor( Component c ) {
protected Color getBorderColor( Component c ) {
boolean def = FlatButtonUI.isDefaultButton( c );
return FlatButtonUI.buttonStateColor( c,
def ? defaultBorderColor : borderColor,

View File

@@ -43,7 +43,16 @@ import com.formdev.flatlaf.FlatLaf;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JButton}.
*
* TODO document used UI defaults of superclass
* <!-- BasicButtonUI -->
*
* @uiDefault Button.font Font
* @uiDefault Button.background Color
* @uiDefault Button.foreground Color
* @uiDefault Button.border Border
* @uiDefault Button.margin Insets
* @uiDefault Button.rollover boolean
*
* <!-- FlatButtonUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Button.arc int
@@ -131,6 +140,7 @@ public class FlatButtonUI
defaults_initialized = true;
}
LookAndFeel.installProperty( b, "opaque", false );
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
MigLayoutVisualPadding.install( b, focusWidth );
@@ -153,10 +163,13 @@ public class FlatButtonUI
}
static boolean isIconOnlyButton( Component c ) {
String text;
return c instanceof JButton &&
((JButton)c).getIcon() != null &&
((text = ((JButton)c).getText()) == null || text.isEmpty());
if( !(c instanceof JButton) )
return false;
Icon icon = ((JButton)c).getIcon();
String text = ((JButton)c).getText();
return (icon != null && (text == null || text.isEmpty())) ||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
}
static boolean isHelpButton( Component c ) {
@@ -169,15 +182,16 @@ public class FlatButtonUI
@Override
public void update( Graphics g, JComponent c ) {
if( isHelpButton( c ) ) {
// fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() )
FlatUIUtils.paintParentBackground( g, c );
if( isHelpButton( c ) ) {
helpButtonIcon.paintIcon( c, g, 0, 0 );
return;
}
if( c.isOpaque() && isContentAreaFilled( c ) ) {
FlatUIUtils.paintParentBackground( g, c );
if( isContentAreaFilled( c ) ) {
Color background = getBackground( c );
if( background != null ) {
Graphics2D g2 = (Graphics2D) g.create();
@@ -188,7 +202,7 @@ public class FlatButtonUI
float focusWidth = (border instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (border instanceof FlatButtonBorder || isToolBarButton( c )) ? scale( (float) this.arc ) : 0;
g2.setColor( background );
FlatUIUtils.setColor( g2, background, isDefaultButton(c) ? defaultBackground : c.getBackground() );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
} finally {
g2.dispose();
@@ -284,8 +298,11 @@ public class FlatButtonUI
Dimension prefSize = super.getPreferredSize( c );
// apply minimum width, if not in toolbar and not a icon-only button
if( !isToolBarButton( c ) && !isIconOnlyButton( c ) )
// make button square if it is a icon-only button
// or apply minimum width, if not in toolbar and not a icon-only button
if( isIconOnlyButton( c ) )
prefSize.width = Math.max( prefSize.width, prefSize.height );
else if( !isToolBarButton( c ) )
prefSize.width = Math.max( prefSize.width, scale( minimumWidth + (focusWidth * 2) ) );
return prefSize;

View File

@@ -25,6 +25,26 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JCheckBoxMenuItem}.
*
* <!-- BasicCheckBoxMenuItemUI -->
*
* @uiDefault CheckBoxMenuItem.font Font
* @uiDefault CheckBoxMenuItem.background Color
* @uiDefault CheckBoxMenuItem.foreground Color
* @uiDefault CheckBoxMenuItem.disabledForeground Color
* @uiDefault CheckBoxMenuItem.selectionBackground Color
* @uiDefault CheckBoxMenuItem.selectionForeground Color
* @uiDefault CheckBoxMenuItem.acceleratorForeground Color
* @uiDefault CheckBoxMenuItem.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault CheckBoxMenuItem.border Border
* @uiDefault CheckBoxMenuItem.borderPainted boolean
* @uiDefault CheckBoxMenuItem.margin Insets
* @uiDefault CheckBoxMenuItem.arrowIcon Icon
* @uiDefault CheckBoxMenuItem.checkIcon Icon
* @uiDefault CheckBoxMenuItem.opaque boolean
* @uiDefault CheckBoxMenuItem.evenHeight boolean
*
* @author Karl Tauber
*/
public class FlatCheckBoxMenuItemUI

View File

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

View File

@@ -26,6 +26,11 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JColorChooser}.
*
* <!-- BasicColorChooserUI -->
*
* @uiDefault ColorChooser.font Font
* @uiDefault ColorChooser.background Color
* @uiDefault ColorChooser.foreground Color
* @uiDefault ColorChooser.showPreviewPanelText boolean
* @uiDefault ColorChooser.swatchesSwatchSize Dimension
* @uiDefault ColorChooser.swatchesRecentSwatchSize Dimension

View File

@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -33,6 +34,7 @@ import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
@@ -40,6 +42,7 @@ import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
@@ -54,13 +57,24 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JComboBox}.
*
* TODO document used UI defaults of superclass
* <!-- BasicComboBoxUI -->
*
* @uiDefault ComboBox.font Font
* @uiDefault ComboBox.background Color
* @uiDefault ComboBox.foreground Color
* @uiDefault ComboBox.border Border
* @uiDefault ComboBox.padding Insets
* @uiDefault ComboBox.squareButton boolean default is true
*
* <!-- FlatComboBoxUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.arc int
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color
* @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background
* @uiDefault ComboBox.disabledBackground Color
* @uiDefault ComboBox.disabledForeground Color
* @uiDefault ComboBox.buttonBackground Color
@@ -77,9 +91,11 @@ public class FlatComboBoxUI
protected int focusWidth;
protected int arc;
protected String arrowType;
protected boolean isIntelliJTheme;
protected Color borderColor;
protected Color disabledBorderColor;
protected Color editableBackground;
protected Color disabledBackground;
protected Color disabledForeground;
@@ -92,6 +108,8 @@ public class FlatComboBoxUI
private MouseListener hoverListener;
private boolean hover;
private WeakReference<Component> lastRendererComponent;
public static ComponentUI createUI( JComponent c ) {
return new FlatComboBoxUI();
}
@@ -122,12 +140,16 @@ public class FlatComboBoxUI
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty( comboBox, "opaque", false );
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( "Component.arc" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
borderColor = UIManager.getColor( "Component.borderColor" );
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" );
@@ -150,6 +172,7 @@ public class FlatComboBoxUI
borderColor = null;
disabledBorderColor = null;
editableBackground = null;
disabledBackground = null;
disabledForeground = null;
@@ -208,11 +231,14 @@ public class FlatComboBoxUI
String propertyName = e.getPropertyName();
if( editor != null &&
((source == comboBox && (propertyName == "background" || propertyName == "foreground")) ||
((source == comboBox && propertyName == "foreground") ||
(source == editor && propertyName == "enabled")) )
{
// fix editor component colors
updateEditorColors();
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
editor.applyComponentOrientation( o );
}
}
};
@@ -235,6 +261,12 @@ public class FlatComboBoxUI
if( editor instanceof JTextComponent )
((JTextComponent)editor).setBorder( BorderFactory.createEmptyBorder() );
// explicitly make non-opaque
if( editor instanceof JComponent )
((JComponent)editor).setOpaque( false );
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
updateEditorColors();
}
@@ -243,7 +275,6 @@ public class FlatComboBoxUI
// is used, then the editor is updated after the combobox and the
// colors are again replaced with default colors
boolean enabled = editor.isEnabled();
editor.setBackground( FlatUIUtils.nonUIResource( enabled ? comboBox.getBackground() : disabledBackground ) );
editor.setForeground( FlatUIUtils.nonUIResource( (enabled || editor instanceof JTextComponent)
? comboBox.getForeground()
: disabledForeground ) );
@@ -265,44 +296,46 @@ public class FlatComboBoxUI
@Override
public void update( Graphics g, JComponent c ) {
if( c.isOpaque() ) {
// fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() && (focusWidth > 0 || arc != 0) )
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
int arrowX = arrowButton.getX();
int arrowWidth = arrowButton.getWidth();
boolean enabled = comboBox.isEnabled();
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
int arrowX = arrowButton.getX();
int arrowWidth = arrowButton.getWidth();
boolean enabled = comboBox.isEnabled();
boolean isLeftToRight = comboBox.getComponentOrientation().isLeftToRight();
// paint background
g2.setColor( enabled ? c.getBackground() : disabledBackground );
// paint background
g2.setColor( enabled
? (editableBackground != null && comboBox.isEditable() ? editableBackground : c.getBackground())
: getDisabledBackground( comboBox ) );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc );
// paint arrow button background
if( enabled ) {
g2.setColor( comboBox.isEditable() ? buttonEditableBackground : buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
// paint arrow button background
if( enabled ) {
g2.setColor( comboBox.isEditable() ? buttonEditableBackground : buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc );
g2.setClip( oldClip );
}
if( comboBox.isEditable() ) {
// paint vertical line between value and arrow button
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
}
// paint vertical line between value and arrow button
if( comboBox.isEditable() ) {
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
}
paint( g, c );
@@ -312,14 +345,15 @@ public class FlatComboBoxUI
@SuppressWarnings( "unchecked" )
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
ListCellRenderer<Object> renderer = comboBox.getRenderer();
CellPaddingBorder.uninstall( renderer );
uninstallCellPaddingBorder( renderer );
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
c.setFont( comboBox.getFont() );
CellPaddingBorder.uninstall( c );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
uninstallCellPaddingBorder( c );
boolean enabled = comboBox.isEnabled();
c.setForeground( enabled ? comboBox.getForeground() : disabledForeground );
c.setBackground( enabled ? comboBox.getBackground() : disabledBackground );
c.setBackground( enabled ? comboBox.getBackground() : getDisabledBackground( comboBox ) );
boolean shouldValidate = (c instanceof JPanel);
if( padding != null )
@@ -336,10 +370,38 @@ public class FlatComboBoxUI
@Override
public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
g.setColor( comboBox.isEnabled() ? comboBox.getBackground() : disabledBackground );
g.setColor( comboBox.isEnabled() ? comboBox.getBackground() : getDisabledBackground( comboBox ) );
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height );
}
private Color getDisabledBackground( JComponent c ) {
return isIntelliJTheme ? FlatUIUtils.getParentBackground( c ) : disabledBackground;
}
@Override
protected Dimension getDefaultSize() {
@SuppressWarnings( "unchecked" )
ListCellRenderer<Object> renderer = comboBox.getRenderer();
uninstallCellPaddingBorder( renderer );
Dimension size = super.getDefaultSize();
uninstallCellPaddingBorder( renderer );
return size;
}
@Override
protected Dimension getDisplaySize() {
@SuppressWarnings( "unchecked" )
ListCellRenderer<Object> renderer = comboBox.getRenderer();
uninstallCellPaddingBorder( renderer );
Dimension displaySize = super.getDisplaySize();
uninstallCellPaddingBorder( renderer );
return displaySize;
}
@Override
protected Dimension getSizeForComponent( Component comp ) {
Dimension size = super.getSizeForComponent( comp );
@@ -363,6 +425,14 @@ public class FlatComboBoxUI
return null;
}
private void uninstallCellPaddingBorder( Object o ) {
CellPaddingBorder.uninstall( o );
if( lastRendererComponent != null ) {
CellPaddingBorder.uninstall( lastRendererComponent );
lastRendererComponent = null;
}
}
//---- class FlatComboPopup -----------------------------------------------
@SuppressWarnings( { "rawtypes", "unchecked" } )
@@ -370,10 +440,18 @@ public class FlatComboBoxUI
extends BasicComboPopup
{
private CellPaddingBorder paddingBorder;
private final ListCellRenderer renderer = new PopupListCellRenderer();
FlatComboPopup( JComboBox combo ) {
super( combo );
// BasicComboPopup listens to JComboBox.componentOrientation and updates
// the component orientation of the list, scroller and popup, but when
// switching the LaF and a new combo popup is created, the component
// orientation is not applied.
ComponentOrientation o = comboBox.getComponentOrientation();
list.setComponentOrientation( o );
scroller.setComponentOrientation( o );
setComponentOrientation( o );
}
@Override
@@ -387,7 +465,13 @@ public class FlatComboBoxUI
comboBox.setPrototypeDisplayValue( prototype );
// make popup wider if necessary
pw = Math.max( pw, displaySize.width );
if( displaySize.width > pw ) {
int diff = displaySize.width - pw;
pw = displaySize.width;
if( !comboBox.getComponentOrientation().isLeftToRight() )
px -= diff;
}
return super.computePopupBounds( px, py, pw, ph );
}
@@ -405,7 +489,7 @@ public class FlatComboBoxUI
protected void configureList() {
super.configureList();
list.setCellRenderer( renderer );
list.setCellRenderer( new PopupListCellRenderer() );
}
@Override
@@ -416,7 +500,7 @@ public class FlatComboBoxUI
super.propertyChange( e );
if( e.getPropertyName() == "renderer" )
list.setCellRenderer( renderer );
list.setCellRenderer( new PopupListCellRenderer() );
}
};
}
@@ -432,8 +516,10 @@ public class FlatComboBoxUI
{
ListCellRenderer renderer = comboBox.getRenderer();
CellPaddingBorder.uninstall( renderer );
CellPaddingBorder.uninstall( lastRendererComponent );
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
if( c instanceof JComponent ) {
if( paddingBorder == null )
@@ -441,6 +527,8 @@ public class FlatComboBoxUI
paddingBorder.install( (JComponent) c );
}
lastRendererComponent = (c != renderer) ? new WeakReference<>( c ) : null;
return c;
}
}
@@ -475,6 +563,9 @@ public class FlatComboBoxUI
}
static void uninstall( Object o ) {
if( o instanceof WeakReference )
o = ((WeakReference<?>)o).get();
if( !(o instanceof JComponent) )
return;

View File

@@ -18,18 +18,37 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.JTextComponent;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
*
* TODO document used UI defaults of superclass
* <!-- BasicEditorPaneUI -->
*
* @uiDefault EditorPane.font Font
* @uiDefault EditorPane.background Color also used if not editable
* @uiDefault EditorPane.foreground Color
* @uiDefault EditorPane.caretForeground Color
* @uiDefault EditorPane.selectionBackground Color
* @uiDefault EditorPane.selectionForeground Color
* @uiDefault EditorPane.disabledBackground Color used if not enabled
* @uiDefault EditorPane.inactiveBackground Color used if not editable
* @uiDefault EditorPane.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault EditorPane.border Border
* @uiDefault EditorPane.margin Insets
* @uiDefault EditorPane.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatEditorPaneUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
*
* @author Karl Tauber
*/
@@ -37,6 +56,7 @@ public class FlatEditorPaneUI
extends BasicEditorPaneUI
{
protected int minimumWidth;
protected boolean isIntelliJTheme;
private Object oldHonorDisplayProperties;
@@ -49,6 +69,7 @@ public class FlatEditorPaneUI
super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
// use component font and foreground for HTML text
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
@@ -80,4 +101,17 @@ public class FlatEditorPaneUI
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size;
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
// for compatibility with IntelliJ themes
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
FlatUIUtils.paintParentBackground( g, c );
return;
}
super.paintBackground( g );
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JFileChooser}.
*
* TODO document used UI defaults of superclass
*
* @author Karl Tauber
*/
public class FlatFileChooserUI
extends MetalFileChooserUI
{
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
}
public FlatFileChooserUI( JFileChooser filechooser ) {
super( filechooser );
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return UIScale.scale( super.getPreferredSize( c ) );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return UIScale.scale( super.getMinimumSize( c ) );
}
}

View File

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

View File

@@ -33,10 +33,15 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JLabel}.
*
* <!-- BasicLabelUI -->
*
* @uiDefault Label.font Font
* @uiDefault Label.background Color only used if opaque
* @uiDefault Label.foreground Color
*
* <!-- FlatLabelUI -->
*
* @uiDefault Label.disabledForeground Color
* @uiDefault Label.font Font
*
* @author Karl Tauber
*/

View File

@@ -27,7 +27,27 @@ import javax.swing.plaf.basic.BasicListUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JList}.
*
* TODO document used UI defaults of superclass
* <!-- BasicListUI -->
*
* @uiDefault List.font Font
* @uiDefault List.background Color
* @uiDefault List.foreground Color
* @uiDefault List.selectionBackground Color
* @uiDefault List.selectionForeground Color
* @uiDefault List.dropLineColor Color
* @uiDefault List.border Border
* @uiDefault List.cellRenderer ListCellRenderer
* @uiDefault FileChooser.listFont Font used if client property List.isFileList is true
*
* <!-- DefaultListCellRenderer -->
*
* @uiDefault List.cellNoFocusBorder Border
* @uiDefault List.focusCellHighlightBorder Border
* @uiDefault List.focusSelectedCellHighlightBorder Border
* @uiDefault List.dropCellBackground Color
* @uiDefault List.dropCellForeground Color
*
* <!-- FlatListUI -->
*
* @uiDefault List.selectionInactiveBackground Color
* @uiDefault List.selectionInactiveForeground Color

View File

@@ -23,6 +23,13 @@ import javax.swing.plaf.basic.BasicMenuBarUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
*
* <!-- BasicMenuBarUI -->
*
* @uiDefault MenuBar.font Font
* @uiDefault MenuBar.background Color
* @uiDefault MenuBar.foreground Color
* @uiDefault MenuBar.border Border
*
* @author Karl Tauber
*/
public class FlatMenuBarUI

View File

@@ -25,6 +25,26 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuItem}.
*
* <!-- BasicMenuItemUI -->
*
* @uiDefault MenuItem.font Font
* @uiDefault MenuItem.background Color
* @uiDefault MenuItem.foreground Color
* @uiDefault MenuItem.disabledForeground Color
* @uiDefault MenuItem.selectionBackground Color
* @uiDefault MenuItem.selectionForeground Color
* @uiDefault MenuItem.acceleratorForeground Color
* @uiDefault MenuItem.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault MenuItem.border Border
* @uiDefault MenuItem.borderPainted boolean
* @uiDefault MenuItem.margin Insets
* @uiDefault MenuItem.arrowIcon Icon
* @uiDefault MenuItem.checkIcon Icon
* @uiDefault MenuItem.opaque boolean
* @uiDefault MenuItem.evenHeight boolean
*
* @author Karl Tauber
*/
public class FlatMenuItemUI

View File

@@ -25,6 +25,29 @@ import javax.swing.plaf.basic.BasicMenuUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenu}.
*
* <!-- BasicMenuUI -->
*
* @uiDefault Menu.font Font
* @uiDefault Menu.background Color
* @uiDefault Menu.foreground Color
* @uiDefault Menu.disabledForeground Color
* @uiDefault Menu.selectionBackground Color
* @uiDefault Menu.selectionForeground Color
* @uiDefault Menu.acceleratorForeground Color
* @uiDefault Menu.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault Menu.border Border
* @uiDefault Menu.borderPainted boolean
* @uiDefault Menu.margin Insets
* @uiDefault Menu.arrowIcon Icon
* @uiDefault Menu.checkIcon Icon
* @uiDefault Menu.opaque boolean
* @uiDefault Menu.evenHeight boolean
* @uiDefault Menu.crossMenuMnemonic boolean default is false
* @uiDefault Menu.useMenuBarBackgroundForTopLevel boolean default is false
* @uiDefault MenuBar.background Color used if Menu.useMenuBarBackgroundForTopLevel is true
*
* @author Karl Tauber
*/
public class FlatMenuUI

View File

@@ -19,10 +19,15 @@ package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.util.UIScale;
@@ -30,6 +35,9 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JOptionPane}.
*
* @uiDefault OptionPane.font Font unused
* @uiDefault OptionPane.background Color
* @uiDefault OptionPane.foreground Color unused
* @uiDefault OptionPane.border Border
* @uiDefault OptionPane.messageAreaBorder Border
* @uiDefault OptionPane.buttonAreaBorder Border
@@ -77,6 +85,13 @@ public class FlatOptionPaneUI
focusWidth = UIManager.getInt( "Component.focusWidth" );
}
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override
public Dimension getMinimumOptionPaneSize() {
return UIScale.scale( super.getMinimumOptionPaneSize() );
@@ -130,6 +145,26 @@ public class FlatOptionPaneUI
super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
}
private void updateChildPanels( Container c ) {
for( Component child : c.getComponents() ) {
if( child instanceof JPanel ) {
JPanel panel = (JPanel)child;
// make sub-panel non-opaque for OptionPane.background
panel.setOpaque( false );
// use non-UIResource borders to avoid that they are replaced when switching LaF
Border border = panel.getBorder();
if( border instanceof UIResource )
panel.setBorder( new NonUIResourceBorder( border ) );
}
if( child instanceof Container ) {
updateChildPanels( (Container) child );
}
}
}
private Component findByName( Container c, String name ) {
for( Component child : c.getComponents() ) {
if( name.equals( child.getName() ) )
@@ -143,4 +178,31 @@ public class FlatOptionPaneUI
}
return null;
}
//---- class NonUIResourceBorder ------------------------------------------
private static class NonUIResourceBorder
implements Border
{
private final Border delegate;
NonUIResourceBorder( Border delegate ) {
this.delegate = delegate;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
delegate.paintBorder( c, g, x, y, width, height );
}
@Override
public Insets getBorderInsets( Component c ) {
return delegate.getBorderInsets( c );
}
@Override
public boolean isBorderOpaque() {
return delegate.isBorderOpaque();
}
}
}

View File

@@ -19,24 +19,38 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPasswordFieldUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
*
* TODO document used UI defaults of superclass
* <!-- BasicPasswordFieldUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault PasswordField.font Font
* @uiDefault PasswordField.background Color
* @uiDefault PasswordField.foreground Color also used if not editable
* @uiDefault PasswordField.caretForeground Color
* @uiDefault PasswordField.selectionBackground Color
* @uiDefault PasswordField.selectionForeground Color
* @uiDefault PasswordField.disabledBackground Color used if not enabled
* @uiDefault PasswordField.inactiveBackground Color used if not editable
* @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault PasswordField.border Border
* @uiDefault PasswordField.margin Insets
* @uiDefault PasswordField.echoChar character
* @uiDefault PasswordField.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatPasswordFieldUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
*
* @author Karl Tauber
*/
@@ -45,8 +59,9 @@ public class FlatPasswordFieldUI
{
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
private Handler handler;
private FocusListener focusListener;
public static ComponentUI createUI( JComponent c ) {
return new FlatPasswordFieldUI();
@@ -62,6 +77,9 @@ public class FlatPasswordFieldUI
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
MigLayoutVisualPadding.install( getComponent(), focusWidth );
}
@@ -77,41 +95,27 @@ public class FlatPasswordFieldUI
protected void installListeners() {
super.installListeners();
getComponent().addFocusListener( getHandler() );
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
getComponent().addFocusListener( focusListener );
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
getComponent().removeFocusListener( getHandler() );
handler = null;
getComponent().removeFocusListener( focusListener );
focusListener = null;
}
public Handler getHandler() {
if( handler == null )
handler = new Handler();
return handler;
@Override
protected void paintSafely( Graphics g ) {
FlatTextFieldUI.paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
super.paintSafely( g );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
g2.setColor( c.getBackground() );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, 0 );
} finally {
g2.dispose();
}
// background is painted elsewhere
}
@Override
@@ -129,20 +133,4 @@ public class FlatPasswordFieldUI
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
return size;
}
//---- class Handler ------------------------------------------------------
private class Handler
implements FocusListener
{
@Override
public void focusGained( FocusEvent e ) {
getComponent().repaint();
}
@Override
public void focusLost( FocusEvent e ) {
getComponent().repaint();
}
}
}

View File

@@ -22,6 +22,17 @@ import javax.swing.plaf.ComponentUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu.Separator}.
*
* <!-- BasicSeparatorUI -->
*
* @uiDefault PopupMenuSeparator.background Color unused
* @uiDefault PopupMenuSeparator.foreground Color
*
* <!-- FlatSeparatorUI -->
*
* @uiDefault PopupMenuSeparator.height int height (or width) of the component; may be larger than stripe
* @uiDefault PopupMenuSeparator.stripeWidth int width of the stripe
* @uiDefault PopupMenuSeparator.stripeIndent int indent of stripe from top (or left); allows positioning of stripe within component
*
* @author Karl Tauber
*/
public class FlatPopupMenuSeparatorUI

View File

@@ -23,6 +23,13 @@ import javax.swing.plaf.basic.BasicPopupMenuUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JPopupMenu}.
*
* <!-- BasicPopupMenuUI -->
*
* @uiDefault PopupMenu.font Font
* @uiDefault PopupMenu.background Color
* @uiDefault PopupMenu.foreground Color
* @uiDefault PopupMenu.border Border
*
* @author Karl Tauber
*/
public class FlatPopupMenuUI

View File

@@ -23,6 +23,7 @@ import java.awt.Insets;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JComponent;
import javax.swing.JProgressBar;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicProgressBarUI;
import com.formdev.flatlaf.util.UIScale;
@@ -30,6 +31,19 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JProgressBar}.
*
* <!-- BasicProgressBarUI -->
*
* @uiDefault ProgressBar.font Font
* @uiDefault ProgressBar.background Color
* @uiDefault ProgressBar.foreground Color
* @uiDefault ProgressBar.selectionBackground Color
* @uiDefault ProgressBar.selectionForeground Color
* @uiDefault ProgressBar.border Border
* @uiDefault ProgressBar.horizontalSize Dimension default is 146,12
* @uiDefault ProgressBar.verticalSize Dimension default is 12,146
* @uiDefault ProgressBar.repaintInterval int default is 50 milliseconds
* @uiDefault ProgressBar.cycleTime int default is 3000 milliseconds
*
* @author Karl Tauber
*/
public class FlatProgressBarUI
@@ -39,6 +53,13 @@ public class FlatProgressBarUI
return new FlatProgressBarUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty( progressBar, "opaque", false );
}
@Override
protected Dimension getPreferredInnerHorizontal() {
return UIScale.scale( super.getPreferredInnerHorizontal() );

View File

@@ -25,6 +25,26 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButtonMenuItem}.
*
* <!-- BasicRadioButtonMenuItemUI -->
*
* @uiDefault RadioButtonMenuItem.font Font
* @uiDefault RadioButtonMenuItem.background Color
* @uiDefault RadioButtonMenuItem.foreground Color
* @uiDefault RadioButtonMenuItem.disabledForeground Color
* @uiDefault RadioButtonMenuItem.selectionBackground Color
* @uiDefault RadioButtonMenuItem.selectionForeground Color
* @uiDefault RadioButtonMenuItem.acceleratorForeground Color
* @uiDefault RadioButtonMenuItem.acceleratorSelectionForeground Color
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
* @uiDefault MenuItem.acceleratorDelimiter String
* @uiDefault RadioButtonMenuItem.border Border
* @uiDefault RadioButtonMenuItem.borderPainted boolean
* @uiDefault RadioButtonMenuItem.margin Insets
* @uiDefault RadioButtonMenuItem.arrowIcon Icon
* @uiDefault RadioButtonMenuItem.checkIcon Icon
* @uiDefault RadioButtonMenuItem.opaque boolean
* @uiDefault RadioButtonMenuItem.evenHeight boolean
*
* @author Karl Tauber
*/
public class FlatRadioButtonMenuItemUI

View File

@@ -21,19 +21,31 @@ import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.AbstractButton;
import javax.swing.CellRendererPane;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicRadioButtonUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JRadioButton}.
*
* TODO document used UI defaults of superclass
* <!-- BasicRadioButtonUI -->
*
* @uiDefault Button.iconTextGap int
* @uiDefault Button.disabledText Color
* @uiDefault RadioButton.font Font
* @uiDefault RadioButton.background Color
* @uiDefault RadioButton.foreground Color
* @uiDefault RadioButton.border Border
* @uiDefault RadioButton.margin Insets
* @uiDefault RadioButton.rollover boolean
* @uiDefault RadioButton.icon Icon
*
* <!-- FlatRadioButtonUI -->
*
* @uiDefault RadioButton.iconTextGap int
* @uiDefault RadioButton.disabledText Color
*
* @author Karl Tauber
*/
@@ -66,6 +78,7 @@ public class FlatRadioButtonUI
defaults_initialized = true;
}
LookAndFeel.installProperty( b, "opaque", false );
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
MigLayoutVisualPadding.install( b, null );
@@ -79,6 +92,21 @@ public class FlatRadioButtonUI
defaults_initialized = false;
}
@Override
public void paint( Graphics g, JComponent c ) {
// fill background even if opaque if
// - used as cell renderer (because of selection background)
// - if background was explicitly set to a non-UIResource color
if( !c.isOpaque() &&
(c.getParent() instanceof CellRendererPane || !(c.getBackground() instanceof UIResource)) )
{
g.setColor( c.getBackground() );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
super.paint( g, c );
}
@Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );

View File

@@ -32,6 +32,8 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollBar}.
*
* <!-- BasicScrollBarUI -->
*
* @uiDefault ScrollBar.background Color
* @uiDefault ScrollBar.foreground Color
* @uiDefault ScrollBar.track Color
@@ -39,8 +41,8 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ScrollBar.hoverTrackColor Color
* @uiDefault ScrollBar.hoverThumbColor Color
* @uiDefault ScrollBar.width int
* @uiDefault ScrollBar.minimumThumbSize Insets
* @uiDefault ScrollBar.maximumThumbSize Insets
* @uiDefault ScrollBar.minimumThumbSize Dimension
* @uiDefault ScrollBar.maximumThumbSize Dimension
* @uiDefault ScrollBar.allowsAbsolutePositioning boolean
*
* @author Karl Tauber

View File

@@ -17,10 +17,8 @@
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent;
@@ -29,15 +27,22 @@ import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneLayout;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicScrollPaneUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
*
* <!-- BasicScrollPaneUI -->
*
* @uiDefault ScrollPane.font Font unused
* @uiDefault ScrollPane.background Color
* @uiDefault ScrollPane.foreground Color unused
* @uiDefault ScrollPane.border Border
* @uiDefault ScrollPane.viewportBorder Border
*
* @author Karl Tauber
*/
public class FlatScrollPaneUI
@@ -53,17 +58,14 @@ public class FlatScrollPaneUI
public void installUI( JComponent c ) {
super.installUI( c );
if( scrollpane.getLayout() instanceof UIResource )
scrollpane.setLayout( new FlatScrollPaneLayout() );
int focusWidth = UIManager.getInt( "Component.focusWidth" );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
MigLayoutVisualPadding.install( scrollpane, UIManager.getInt( "Component.focusWidth" ) );
MigLayoutVisualPadding.install( scrollpane, focusWidth );
}
@Override
public void uninstallUI( JComponent c ) {
if( scrollpane.getLayout() instanceof FlatScrollPaneLayout )
scrollpane.setLayout( new ScrollPaneLayout.UIResource() );
MigLayoutVisualPadding.uninstall( scrollpane );
super.uninstallUI( c );
@@ -169,27 +171,4 @@ public class FlatScrollPaneUI
scrollpane.repaint();
}
}
//---- class FlatScrollPaneLayout -----------------------------------------
private static class FlatScrollPaneLayout
extends ScrollPaneLayout
{
@Override
public void layoutContainer( Container parent ) {
super.layoutContainer( parent );
// increase height of vertical scroll bar so that it also fills the upper right corner
if( colHead != null && vsb != null && colHead.isVisible() && vsb.isVisible() ) {
Rectangle colHeadBounds = colHead.getBounds();
Rectangle vsbBounds = vsb.getBounds();
if( vsbBounds.y > colHeadBounds.y ) {
vsbBounds.height += (vsbBounds.y - colHeadBounds.y);
vsbBounds.y = colHeadBounds.y;
vsb.setBounds( vsbBounds );
}
}
}
}
}

View File

@@ -30,8 +30,13 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSeparator}.
*
* <!-- BasicSeparatorUI -->
*
* @uiDefault Separator.background Color unused
* @uiDefault Separator.foreground Color
*
* <!-- FlatSeparatorUI -->
*
* @uiDefault Separator.height int height (or width) of the component; may be larger than stripe
* @uiDefault Separator.stripeWidth int width of the stripe
* @uiDefault Separator.stripeIndent int indent of stripe from top (or left); allows positioning of stripe within component

View File

@@ -25,6 +25,7 @@ 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.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSliderUI;
@@ -33,21 +34,27 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSlider}.
*
* <!-- BasicSliderUI -->
*
* @uiDefault Slider.font Font
* @uiDefault Slider.background Color
* @uiDefault Slider.foreground Color unused
* @uiDefault Slider.disabledForeground Color used for track and thumb if disabled
* @uiDefault Slider.trackColor Color
* @uiDefault Slider.thumbColor Color
* @uiDefault Slider.tickColor Color
* @uiDefault Slider.focusedColor Color
* @uiDefault Slider.hoverColor Color optional; defaults to Slider.focusedColor
* @uiDefault Slider.trackWidth int
* @uiDefault Slider.thumbWidth int
* @uiDefault Slider.horizontalSize Dimension preferred horizontal size; height is ignored; computed slider height is used
* @uiDefault Slider.verticalSize Dimension preferred vertical size; width is ignored; computed slider width is used
* @uiDefault Slider.minimumHorizontalSize Dimension height is ignored; computed slider height is used
* @uiDefault Slider.minimumVerticalSize Dimension width is ignored; computed slider width is used
* @uiDefault Slider.border Border
*
* <!-- FlatSliderUI -->
*
* @uiDefault Slider.trackWidth int
* @uiDefault Slider.thumbWidth int
* @uiDefault Slider.trackColor Color
* @uiDefault Slider.thumbColor Color
* @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
*
* @author Karl Tauber
*/
@@ -96,12 +103,14 @@ public class FlatSliderUI
protected void installDefaults( JSlider slider ) {
super.installDefaults( slider );
LookAndFeel.installProperty( slider, "opaque", false );
trackWidth = UIManager.getInt( "Slider.trackWidth" );
thumbWidth = UIManager.getInt( "Slider.thumbWidth" );
trackColor = UIManager.getColor( "Slider.trackColor" );
thumbColor = UIManager.getColor( "Slider.thumbColor" );
focusColor = UIManager.getColor( "Slider.focusedColor" );
focusColor = FlatUIUtils.getUIColor( "Slider.focusedColor", "Component.focusColor" );
hoverColor = FlatUIUtils.getUIColor( "Slider.hoverColor", focusColor );
disabledForeground = UIManager.getColor( "Slider.disabledForeground" );
}
@@ -170,9 +179,15 @@ public class FlatSliderUI
if( slider.getOrientation() == JSlider.HORIZONTAL ) {
float y = trackRect.y + (trackRect.height - tw) / 2f;
if( enabled && isRoundThumb() ) {
int cw = thumbRect.x + (thumbRect.width / 2) - trackRect.x;
coloredTrack = new RoundRectangle2D.Float( trackRect.x, y, cw, tw, arc, arc );
track = new RoundRectangle2D.Float( trackRect.x + cw, y, trackRect.width - cw, tw, arc, arc );
if( slider.getComponentOrientation().isLeftToRight() ) {
int cw = thumbRect.x + (thumbRect.width / 2) - trackRect.x;
coloredTrack = new RoundRectangle2D.Float( trackRect.x, y, cw, tw, arc, arc );
track = new RoundRectangle2D.Float( trackRect.x + cw, y, trackRect.width - cw, tw, arc, arc );
} else {
int cw = trackRect.x + trackRect.width - thumbRect.x - (thumbRect.width / 2);
coloredTrack = new RoundRectangle2D.Float( trackRect.x + trackRect.width - cw, y, cw, tw, arc, arc );
track = new RoundRectangle2D.Float( trackRect.x, y, trackRect.width - cw, tw, arc, arc );
}
} else
track = new RoundRectangle2D.Float( trackRect.x, y, trackRect.width, tw, arc, arc );
} else {
@@ -186,7 +201,7 @@ public class FlatSliderUI
}
if( coloredTrack != null ) {
g.setColor( slider.hasFocus() ? focusColor : (hover ? hoverColor : thumbColor) );
FlatUIUtils.setColor( g, slider.hasFocus() ? focusColor : (hover ? hoverColor : thumbColor), thumbColor );
((Graphics2D)g).fill( coloredTrack );
}
@@ -196,9 +211,10 @@ public class FlatSliderUI
@Override
public void paintThumb( Graphics g ) {
g.setColor( slider.isEnabled()
FlatUIUtils.setColor( g, slider.isEnabled()
? (slider.hasFocus() ? focusColor : (hover ? hoverColor : thumbColor))
: disabledForeground );
: disabledForeground,
thumbColor );
if( isRoundThumb() )
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );

View File

@@ -35,6 +35,7 @@ import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
@@ -43,12 +44,23 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
*
* TODO document used UI defaults of superclass
* <!-- BasicSpinnerUI -->
*
* @uiDefault Spinner.font Font
* @uiDefault Spinner.background Color
* @uiDefault Spinner.foreground Color
* @uiDefault Spinner.border Border
* @uiDefault Spinner.disableOnBoundaryValues boolean default is false
* @uiDefault Spinner.editorAlignment int 0=center, 2=left, 4=right, 10=leading, 11=trailing
* @uiDefault Spinner.editorBorderPainted boolean paint inner editor border; defaults to false
*
* <!-- FlatSpinnerUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.arc int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault Component.borderColor Color
* @uiDefault Component.disabledBorderColor Color
* @uiDefault Spinner.disabledBackground Color
@@ -70,6 +82,7 @@ public class FlatSpinnerUI
protected int arc;
protected int minimumWidth;
protected String arrowType;
protected boolean isIntelliJTheme;
protected Color borderColor;
protected Color disabledBorderColor;
protected Color disabledBackground;
@@ -88,10 +101,13 @@ public class FlatSpinnerUI
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty( spinner, "opaque", false );
focusWidth = UIManager.getInt( "Component.focusWidth" );
arc = UIManager.getInt( "Component.arc" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
arrowType = UIManager.getString( "Component.arrowType" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
borderColor = UIManager.getColor( "Component.borderColor" );
disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
disabledBackground = UIManager.getColor( "Spinner.disabledBackground" );
@@ -152,6 +168,13 @@ public class FlatSpinnerUI
@Override
protected JComponent createEditor() {
JComponent editor = super.createEditor();
// explicitly make non-opaque
editor.setOpaque( false );
JTextField textField = getEditorTextField( editor );
if( textField != null )
textField.setOpaque( false );
updateEditorColors();
return editor;
}
@@ -183,8 +206,6 @@ public class FlatSpinnerUI
// use non-UIResource colors because when SwingUtilities.updateComponentTreeUI()
// is used, then the text field is updated after the spinner and the
// colors are again replaced with default colors
textField.setBackground( FlatUIUtils.nonUIResource( spinner.isEnabled()
? spinner.getBackground() : disabledBackground ) );
textField.setForeground( FlatUIUtils.nonUIResource( spinner.getForeground() ) );
textField.setDisabledTextColor( FlatUIUtils.nonUIResource( disabledForeground ) );
}
@@ -225,45 +246,47 @@ public class FlatSpinnerUI
@Override
public void update( Graphics g, JComponent c ) {
if( c.isOpaque() ) {
// fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() && (focusWidth > 0 || arc != 0) )
FlatUIUtils.paintParentBackground( g, c );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
Graphics2D g2 = (Graphics2D) g;
FlatUIUtils.setRenderingHints( g2 );
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
Component nextButton = getHandler().nextButton;
int arrowX = nextButton.getX();
int arrowWidth = nextButton.getWidth();
boolean enabled = spinner.isEnabled();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
int width = c.getWidth();
int height = c.getHeight();
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
Component nextButton = getHandler().nextButton;
int arrowX = nextButton.getX();
int arrowWidth = nextButton.getWidth();
boolean enabled = spinner.isEnabled();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
// paint background
g2.setColor( enabled ? c.getBackground() : disabledBackground );
// paint background
g2.setColor( enabled
? c.getBackground()
: (isIntelliJTheme ? FlatUIUtils.getParentBackground( c ) : disabledBackground) );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc );
// 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.fillRoundRectangle( g2, 0, 0, width, height, focusWidth, arc );
// 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.fillRoundRectangle( 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;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
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;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
paint( g, c );
}
@@ -370,7 +393,6 @@ public class FlatSpinnerUI
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case "background":
case "foreground":
case "enabled":
updateEditorColors();

View File

@@ -31,14 +31,19 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JSplitPane}.
*
* @uiDefault Component.arrowType String triangle (default) or chevron
* <!-- BasicSplitPaneUI -->
*
* @uiDefault SplitPane.background Color
* @uiDefault SplitPane.foreground Color unused
* @uiDefault SplitPane.dividerSize int
* @uiDefault SplitPane.continuousLayout boolean
* @uiDefault SplitPane.border Border
* @uiDefault SplitPaneDivider.border Border
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
*
* <!-- FlatSplitPaneUI -->
*
* @uiDefault Component.arrowType String triangle (default) or chevron
* @uiDefault SplitPane.continuousLayout boolean
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
*

View File

@@ -17,12 +17,19 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
@@ -108,7 +115,6 @@ public class FlatTabbedPaneUI
tabAreaInsets = scale( tabAreaInsets );
tabHeight = scale( tabHeight );
tabSelectionHeight = scale( tabSelectionHeight );
contentSeparatorHeight = scale( contentSeparatorHeight );
MigLayoutVisualPadding.install( tabPane, null );
}
@@ -135,7 +141,7 @@ public class FlatTabbedPaneUI
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
if( "JTabbedPane.hasFullBorder".equals( e.getPropertyName() ) ) {
if( TABBED_PANE_HAS_FULL_BORDER.equals( e.getPropertyName() ) ) {
tabPane.revalidate();
tabPane.repaint();
}
@@ -176,70 +182,45 @@ public class FlatTabbedPaneUI
@Override
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 // was added by superclass
+ (!isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
}
@Override
protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) {
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ )
+ (isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
}
/**
* The content border insets are used to create a separator between tabs and content.
* Except in scroll tab policy, where the separator is painted in paintTabArea().
* If client property JTabbedPane.hasFullBorder is true, then the content border insets
* are also used for the border.
*/
@Override
protected Insets getContentBorderInsets( int tabPlacement ) {
boolean hasFullBorder = this.hasFullBorder || (tabPane.getClientProperty( "JTabbedPane.hasFullBorder" ) == Boolean.TRUE);
int sh = contentSeparatorHeight;
boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = scale( contentSeparatorHeight );
Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 );
if( isScrollTabLayout() )
insets.top = 0;
Insets contentBorderInsets = new Insets( 0, 0, 0, 0 );
rotateInsets( insets, contentBorderInsets, tabPlacement );
return contentBorderInsets;
}
@Override
protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && !isTopOrBottom( tabPlacement ) ) {
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == LEFT ? -shift : shift );
} else
return 0;
return 0;
}
@Override
protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && isTopOrBottom( tabPlacement ) ) {
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == TOP ? -shift : shift );
} else
return 0;
return 0;
}
@Override
protected void paintTabArea( Graphics g, int tabPlacement, int selectedIndex ) {
if( isScrollTabLayout() ) {
// paint separator between tabs and content
Rectangle bounds = g.getClipBounds();
g.setColor( contentAreaColor );
public void update( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
int y = (tabPlacement == TOP) ? bounds.y + bounds.height - contentSeparatorHeight : bounds.y;
g.fillRect( bounds.x, y, bounds.x + bounds.width, contentSeparatorHeight );
} else {
int x = (tabPlacement == LEFT) ? bounds.x + bounds.width - contentSeparatorHeight : bounds.x;
g.fillRect( x, bounds.y, contentSeparatorHeight, bounds.y + bounds.height );
}
}
super.paintTabArea( g, tabPlacement, selectedIndex );
super.update( g, c );
}
@Override
@@ -275,19 +256,6 @@ public class FlatTabbedPaneUI
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected )
{
if( isScrollTabLayout() ) {
// make tab bounds smaller for separator between tabs and content
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
if( tabPlacement == BOTTOM )
y += contentSeparatorHeight;
h -= contentSeparatorHeight;
} else {
if( tabPlacement == RIGHT )
x += contentSeparatorHeight;
w -= contentSeparatorHeight;
}
}
// paint tab background
boolean enabled = tabPane.isEnabled();
g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
@@ -302,8 +270,38 @@ public class FlatTabbedPaneUI
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected )
{
if( !isSelected )
return;
if( isSelected )
paintTabSelection( g, tabPlacement, x, y, w, h );
}
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
// increase clip bounds in scroll-tab-layout to paint over the separator line
Rectangle clipBounds = isScrollTabLayout() ? g.getClipBounds() : null;
if( clipBounds != null ) {
Rectangle newClipBounds = new Rectangle( clipBounds );
int contentSeparatorHeight = scale( this.contentSeparatorHeight );
switch( tabPlacement ) {
case TOP:
default:
newClipBounds.height += contentSeparatorHeight;
break;
case BOTTOM:
newClipBounds.y -= contentSeparatorHeight;
newClipBounds.height += contentSeparatorHeight;
break;
case LEFT:
newClipBounds.width += contentSeparatorHeight;
break;
case RIGHT:
newClipBounds.x -= contentSeparatorHeight;
newClipBounds.width += contentSeparatorHeight;
break;
}
g.setClip( newClipBounds );
}
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
@@ -330,13 +328,16 @@ public class FlatTabbedPaneUI
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
break;
}
if( clipBounds != null )
g.setClip( clipBounds );
}
/**
* Actually does the nearly the same as super.paintContentBorder() but
* - content pane is always opaque
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
* - not invoking paintContentBorder*Edge() methods
* - repaint selection
*/
@Override
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
@@ -377,9 +378,34 @@ public class FlatTabbedPaneUI
h -= (y - insets.top);
}
// compute insets for separator or full border
boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
Insets ci = new Insets( 0, 0, 0, 0 );
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
// paint content area
g.setColor( contentAreaColor );
g.fillRect( x, y, w, h );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Rectangle2D.Float( x, y, w, h ), false );
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
((Graphics2D)g).fill( path );
// repaint selection in scroll-tab-layout because it may be painted before
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
if( isScrollTabLayout() && selectedIndex >= 0 ) {
Component scrollableTabViewport = findComponentByClassName( tabPane,
BasicTabbedPaneUI.class.getName() + "$ScrollableTabViewport" );
if( scrollableTabViewport != null ) {
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
Shape oldClip = g.getClip();
g.setClip( scrollableTabViewport.getBounds() );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip );
}
}
}
@Override
@@ -392,7 +418,17 @@ public class FlatTabbedPaneUI
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
}
private boolean isTopOrBottom( int tabPlacement ) {
return tabPlacement == TOP || tabPlacement == BOTTOM;
private Component findComponentByClassName( Container c, String className ) {
for( Component child : c.getComponents() ) {
if( className.equals( child.getClass().getName() ) )
return child;
if( child instanceof Container ) {
Component c2 = findComponentByClassName( (Container) child, className );
if( c2 != null )
return c2;
}
}
return null;
}
}

View File

@@ -36,7 +36,13 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.table.JTableHeader}.
*
* TODO document used UI defaults of superclass
* <!-- BasicTableHeaderUI -->
*
* @uiDefault TableHeader.font Font
* @uiDefault TableHeader.background Color
* @uiDefault TableHeader.foreground Color
*
* <!-- FlatTableHeaderUI -->
*
* @uiDefault TableHeader.separatorColor Color
* @uiDefault TableHeader.bottomSeparatorColor Color
@@ -75,8 +81,10 @@ public class FlatTableHeaderUI
@Override
public void paint( Graphics g, JComponent c ) {
// do not paint borders if JTableHeader.setDefaultRenderer() was used
boolean paintBorders = header.getDefaultRenderer().getClass().getName().equals(
"sun.swing.table.DefaultTableCellHeaderRenderer" );
String rendererClassName = header.getDefaultRenderer().getClass().getName();
boolean paintBorders =
rendererClassName.equals( "sun.swing.table.DefaultTableCellHeaderRenderer" ) ||
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
if( paintBorders )
paintColumnBorders( g, c );

View File

@@ -29,7 +29,30 @@ import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTable}.
*
* TODO document used UI defaults of superclass
* <!-- BasicTableUI -->
*
* @uiDefault Table.font Font
* @uiDefault Table.background Color
* @uiDefault Table.foreground Color
* @uiDefault Table.selectionBackground Color
* @uiDefault Table.selectionForeground Color
* @uiDefault Table.gridColor Color
* @uiDefault Table.scrollPaneBorder Border
* @uiDefault Table.dropLineColor Color
* @uiDefault Table.dropLineShortColor Color
*
* <!-- DefaultTableCellRenderer -->
*
* @uiDefault Table.cellNoFocusBorder Border
* @uiDefault Table.focusCellHighlightBorder Border
* @uiDefault Table.focusSelectedCellHighlightBorder Border
* @uiDefault Table.dropCellBackground Color
* @uiDefault Table.dropCellForeground Color
* @uiDefault Table.alternateRowColor Color
* @uiDefault Table.focusCellBackground Color
* @uiDefault Table.focusCellForeground Color
*
* <!-- FlatTableUI -->
*
* @uiDefault Table.rowHeight int
* @uiDefault Table.selectionInactiveBackground Color

View File

@@ -21,6 +21,7 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
@@ -30,11 +31,25 @@ import javax.swing.text.JTextComponent;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextArea}.
*
* TODO document used UI defaults of superclass
* <!-- BasicTextAreaUI -->
*
* @uiDefault TextArea.font Font
* @uiDefault TextArea.background Color
* @uiDefault TextArea.foreground Color also used if not editable
* @uiDefault TextArea.caretForeground Color
* @uiDefault TextArea.selectionBackground Color
* @uiDefault TextArea.selectionForeground Color
* @uiDefault TextArea.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault TextArea.border Border
* @uiDefault TextArea.margin Insets
* @uiDefault TextArea.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatTextAreaUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault TextArea.disabledBackground Color
* @uiDefault TextArea.inactiveBackground Color
* @uiDefault Component.isIntelliJTheme boolean
* @uiDefault TextArea.disabledBackground Color used if not enabled
* @uiDefault TextArea.inactiveBackground Color used if not editable
*
* @author Karl Tauber
*/
@@ -42,6 +57,7 @@ public class FlatTextAreaUI
extends BasicTextAreaUI
{
protected int minimumWidth;
protected boolean isIntelliJTheme;
protected Color disabledBackground;
protected Color inactiveBackground;
@@ -54,6 +70,7 @@ public class FlatTextAreaUI
super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
disabledBackground = UIManager.getColor( "TextArea.disabledBackground" );
inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" );
}
@@ -73,23 +90,29 @@ public class FlatTextAreaUI
Color background = c.getBackground();
g.setColor( !(background instanceof UIResource)
? background
: (!c.isEnabled()
? disabledBackground
: (!c.isEditable() ? inactiveBackground : background)) );
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
? FlatUIUtils.getParentBackground( c )
: (!c.isEnabled()
? disabledBackground
: (!c.isEditable() ? inactiveBackground : background))) );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) );
return applyMinimumWidth( super.getPreferredSize( c ), c );
}
@Override
public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ) );
return applyMinimumWidth( super.getMinimumSize( c ), c );
}
private Dimension applyMinimumWidth( Dimension size ) {
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
// do not apply minimum width if JTextArea.columns is set
if( c instanceof JTextArea && ((JTextArea)c).getColumns() > 0 )
return size;
// Assume that text area is in a scroll pane (that displays the border)
// and subtract 1px border line width.
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding

View File

@@ -17,27 +17,46 @@
package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.JTextComponent;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
*
* TODO document used UI defaults of superclass
* <!-- BasicTextFieldUI -->
*
* @uiDefault TextField.font Font
* @uiDefault TextField.background Color
* @uiDefault TextField.foreground Color also used if not editable
* @uiDefault TextField.caretForeground Color
* @uiDefault TextField.selectionBackground Color
* @uiDefault TextField.selectionForeground Color
* @uiDefault TextField.disabledBackground Color used if not enabled
* @uiDefault TextField.inactiveBackground Color used if not editable
* @uiDefault TextField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault TextField.border Border
* @uiDefault TextField.margin Insets
* @uiDefault TextField.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatTextFieldUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
*
* @author Karl Tauber
*/
@@ -46,8 +65,9 @@ public class FlatTextFieldUI
{
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
private Handler handler;
private FocusListener focusListener;
public static ComponentUI createUI( JComponent c ) {
return new FlatTextFieldUI();
@@ -59,6 +79,9 @@ public class FlatTextFieldUI
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
MigLayoutVisualPadding.install( getComponent(), focusWidth );
}
@@ -74,38 +97,56 @@ public class FlatTextFieldUI
protected void installListeners() {
super.installListeners();
getComponent().addFocusListener( getHandler() );
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
getComponent().addFocusListener( focusListener );
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
getComponent().removeFocusListener( getHandler() );
handler = null;
getComponent().removeFocusListener( focusListener );
focusListener = null;
}
public Handler getHandler() {
if( handler == null )
handler = new Handler();
return handler;
@Override
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
super.paintSafely( g );
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
// background is painted elsewhere
}
FlatUIUtils.paintParentBackground( g, c );
static void paintBackground( Graphics g, JTextComponent c, int focusWidth, boolean isIntelliJTheme ) {
// do not paint background if:
// - not opaque and
// - border is not a flat border and
// - opaque was explicitly set (to false)
// (same behaviour as in AquaTextFieldUI)
if( !c.isOpaque() && !(c.getBorder() instanceof FlatBorder) && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
return;
// fill background if opaque to avoid garbage if user sets opaque to true
if( c.isOpaque() && focusWidth > 0 )
FlatUIUtils.paintParentBackground( g, c );
// paint background
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
float fFocusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) focusWidth ) : 0;
g2.setColor( c.getBackground() );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, 0 );
Color background = c.getBackground();
g2.setColor( !(background instanceof UIResource)
? background
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
? FlatUIUtils.getParentBackground( c )
: background) );
FlatUIUtils.fillRoundRectangle( g2, 0, 0, c.getWidth(), c.getHeight(), fFocusWidth, 0 );
} finally {
g2.dispose();
}
@@ -122,6 +163,10 @@ public class FlatTextFieldUI
}
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
// do not apply minimum width if JTextField.columns is set
if( c instanceof JTextField && ((JTextField)c).getColumns() > 0 )
return size;
Container parent = c.getParent();
if( parent instanceof JComboBox ||
parent instanceof JSpinner ||
@@ -132,20 +177,4 @@ public class FlatTextFieldUI
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
return size;
}
//---- class Handler ------------------------------------------------------
private class Handler
implements FocusListener
{
@Override
public void focusGained( FocusEvent e ) {
getComponent().repaint();
}
@Override
public void focusLost( FocusEvent e ) {
getComponent().repaint();
}
}
}

View File

@@ -18,18 +18,37 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextPaneUI;
import javax.swing.text.JTextComponent;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextPane}.
*
* TODO document used UI defaults of superclass
* <!-- BasicTextPaneUI -->
*
* @uiDefault TextPane.font Font
* @uiDefault TextPane.background Color
* @uiDefault TextPane.foreground Color also used if not editable
* @uiDefault TextPane.caretForeground Color
* @uiDefault TextPane.selectionBackground Color
* @uiDefault TextPane.selectionForeground Color
* @uiDefault TextPane.disabledBackground Color used if not enabled
* @uiDefault TextPane.inactiveBackground Color used if not editable
* @uiDefault TextPane.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground)
* @uiDefault TextPane.border Border
* @uiDefault TextPane.margin Insets
* @uiDefault TextPane.caretBlinkRate int default is 500 milliseconds
*
* <!-- FlatTextPaneUI -->
*
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
*
* @author Karl Tauber
*/
@@ -37,6 +56,7 @@ public class FlatTextPaneUI
extends BasicTextPaneUI
{
protected int minimumWidth;
protected boolean isIntelliJTheme;
private Object oldHonorDisplayProperties;
@@ -49,6 +69,7 @@ public class FlatTextPaneUI
super.installDefaults();
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
// use component font and foreground for HTML text
oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES );
@@ -80,4 +101,17 @@ public class FlatTextPaneUI
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
return size;
}
@Override
protected void paintBackground( Graphics g ) {
JTextComponent c = getComponent();
// for compatibility with IntelliJ themes
if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) {
FlatUIUtils.paintParentBackground( g, c );
return;
}
super.paintBackground( g );
}
}

View File

@@ -26,17 +26,31 @@ import javax.swing.plaf.ComponentUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToggleButton}.
*
* TODO document used UI defaults of superclass
* <!-- BasicButtonUI -->
*
* @uiDefault ToggleButton.font Font
* @uiDefault ToggleButton.background Color
* @uiDefault ToggleButton.foreground Color
* @uiDefault ToggleButton.border Border
* @uiDefault ToggleButton.margin Insets
* @uiDefault ToggleButton.rollover boolean
*
* <!-- FlatButtonUI -->
*
* @uiDefault Component.focusWidth int
* @uiDefault ToggleButton.arc int
* @uiDefault ToggleButton.minimumWidth int
* @uiDefault ToggleButton.iconTextGap int
* @uiDefault ToggleButton.pressedBackground Color
* @uiDefault ToggleButton.disabledText Color
* @uiDefault ToggleButton.toolbar.hoverBackground Color
* @uiDefault ToggleButton.toolbar.pressedBackground Color
*
* <!-- FlatToggleButtonUI -->
*
* @uiDefault ToggleButton.selectedBackground Color
* @uiDefault ToggleButton.selectedForeground Color
* @uiDefault ToggleButton.disabledSelectedBackground Color
* @uiDefault ToggleButton.toolbar.hoverBackground Color
* @uiDefault ToggleButton.toolbar.pressedBackground Color
* @uiDefault ToggleButton.toolbar.selectedBackground Color
*
*

View File

@@ -33,6 +33,8 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToolBar.Separator}.
*
* <!-- FlatToolBarSeparatorUI -->
*
* @uiDefault ToolBar.separatorWidth int
* @uiDefault ToolBar.separatorColor Color
*

View File

@@ -33,7 +33,19 @@ import javax.swing.plaf.basic.BasicToolBarUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToolBar}.
*
* TODO document used UI defaults of superclass
* <!-- BasicToolBarUI -->
*
* @uiDefault ToolBar.font Font
* @uiDefault ToolBar.background Color
* @uiDefault ToolBar.foreground Color
* @uiDefault ToolBar.border Border
* @uiDefault ToolBar.dockingBackground Color
* @uiDefault ToolBar.dockingForeground Color
* @uiDefault ToolBar.floatingBackground Color
* @uiDefault ToolBar.floatingForeground Color
* @uiDefault ToolBar.isRollover boolean
*
* <!-- FlatToolBarUI -->
*
* @uiDefault ToolBar.buttonMargins Insets
*

View File

@@ -27,11 +27,21 @@ import javax.swing.JToolTip;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicToolTipUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.StringUtils;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JToolTip}.
*
* <!-- BasicToolTipUI -->
*
* @uiDefault ToolTip.font Font
* @uiDefault ToolTip.background Color
* @uiDefault ToolTip.foreground Color
* @uiDefault ToolTip.backgroundInactive Color
* @uiDefault ToolTip.foregroundInactive Color
* @uiDefault ToolTip.border Border
* @uiDefault ToolTip.borderInactive Border
*
* @author Karl Tauber
*/
public class FlatToolTipUI
@@ -51,7 +61,7 @@ public class FlatToolTipUI
FontMetrics fm = c.getFontMetrics( c.getFont() );
Insets insets = c.getInsets();
List<String> lines = FlatLaf.split( ((JToolTip)c).getTipText(), '\n' );
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
int width = 0;
int height = fm.getHeight() * Math.max( lines.size(), 1 );
for( String line : lines )
@@ -71,14 +81,17 @@ public class FlatToolTipUI
FlatUIUtils.setRenderingHints( (Graphics2D) g );
g.setColor( c.getForeground() );
List<String> lines = FlatLaf.split( ((JToolTip)c).getTipText(), '\n' );
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
int x = insets.left;
int x2 = c.getWidth() - insets.right;
int y = insets.top - fm.getDescent();
int lineHeight = fm.getHeight();
JComponent comp = ((JToolTip)c).getComponent();
boolean leftToRight = (comp != null ? comp : c).getComponentOrientation().isLeftToRight();
for( String line : lines ) {
y += lineHeight;
g.drawString( line, x, y );
FlatUIUtils.drawString( c, g, line, leftToRight ? x : x2 - SwingUtilities.computeStringWidth( fm, line ), y );
}
} else
super.paint( g, c );

View File

@@ -26,6 +26,8 @@ import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
@@ -33,8 +35,10 @@ import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.function.Consumer;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.UIScale;
@@ -69,6 +73,14 @@ public class FlatUIUtils
dim.height + insets.top + insets.bottom );
}
public static Insets addInsets( Insets insets1, Insets insets2 ) {
return new Insets(
insets1.top + insets2.top,
insets1.left + insets2.left,
insets1.bottom + insets2.bottom,
insets1.right + insets2.right );
}
public static Color getUIColor( String key, int defaultColorRGB ) {
Color color = UIManager.getColor( key );
return (color != null) ? color : new Color( defaultColorRGB );
@@ -79,6 +91,11 @@ public class FlatUIUtils
return (color != null) ? color : defaultColor;
}
public static Color getUIColor( String key, String defaultKey ) {
Color color = UIManager.getColor( key );
return (color != null) ? color : UIManager.getColor( defaultKey );
}
public static int getUIInt( String key, int defaultValue ) {
Object value = UIManager.get( key );
return (value instanceof Integer) ? (Integer) value : defaultValue;
@@ -101,6 +118,12 @@ public class FlatUIUtils
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
}
public static void setColor( Graphics g, Color color, Color baseColor ) {
if( color instanceof DerivedColor )
color = ((DerivedColor)color).derive( baseColor );
g.setColor( color );
}
/**
* Draws a round rectangle.
*/
@@ -145,6 +168,16 @@ public class FlatUIUtils
}
}
/**
* Gets the background color of the first opaque parent.
*/
public static Color getParentBackground( JComponent c ) {
Container parent = findOpaqueParent( c );
return (parent != null)
? parent.getBackground()
: UIManager.getColor( "Panel.background" ); // fallback, probably never used
}
/**
* Find the first parent that is opaque.
*/
@@ -238,6 +271,22 @@ public class FlatUIUtils
}
/**
* Draws the given string at the specified location using text properties
* and anti-aliasing hints from the provided component.
*
* Use this method instead of Graphics.drawString() for correct anti-aliasing.
*
* Replacement for SwingUtilities2.drawString()
*/
public static void drawString( JComponent c, Graphics g, String text, int x, int y ) {
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, -1, x, y );
}
/**
* Draws the given string at the specified location underlining the specified
* character. The provided component is used to query text properties and
* anti-aliasing hints.
*
* Replacement for SwingUtilities2.drawStringUnderlineCharAt()
*/
public static void drawStringUnderlineCharAt( JComponent c, Graphics g,
@@ -246,15 +295,23 @@ public class FlatUIUtils
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
}
public static boolean hasOpaqueBeenExplicitlySet( JComponent c ) {
boolean oldOpaque = c.isOpaque();
LookAndFeel.installProperty( c, "opaque", !oldOpaque );
boolean explicitlySet = c.isOpaque() == oldOpaque;
LookAndFeel.installProperty( c, "opaque", oldOpaque );
return explicitlySet;
}
//---- class HoverListener ------------------------------------------------
public static class HoverListener
extends MouseAdapter
{
private final JComponent repaintComponent;
private final Component repaintComponent;
private final Consumer<Boolean> hoverChanged;
public HoverListener( JComponent repaintComponent, Consumer<Boolean> hoverChanged ) {
public HoverListener( Component repaintComponent, Consumer<Boolean> hoverChanged ) {
this.repaintComponent = repaintComponent;
this.hoverChanged = hoverChanged;
}
@@ -276,4 +333,26 @@ public class FlatUIUtils
repaintComponent.repaint();
}
}
//---- class RepaintFocusListener -----------------------------------------
public static class RepaintFocusListener
implements FocusListener
{
private final Component repaintComponent;
public RepaintFocusListener( Component repaintComponent ) {
this.repaintComponent = repaintComponent;
}
@Override
public void focusGained( FocusEvent e ) {
repaintComponent.repaint();
}
@Override
public void focusLost( FocusEvent e ) {
repaintComponent.repaint();
}
}
}

View File

@@ -27,6 +27,12 @@ import javax.swing.plaf.basic.BasicViewportUI;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JViewport}.
*
* <!-- BasicViewportUI -->
*
* @uiDefault Viewport.font Font unused
* @uiDefault Viewport.background Color
* @uiDefault Viewport.foreground Color unused
*
* @author Karl Tauber
*/
public class FlatViewportUI

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Color;
/**
* Functions that modify colors.
*
* @author Karl Tauber
*/
public class ColorFunctions
{
public static Color applyFunctions( Color color, ColorFunction[] functions ) {
float[] hsl = HSLColor.fromRGB( color );
float alpha = color.getAlpha() / 255f;
for( ColorFunction function : functions )
function.apply( hsl );
return HSLColor.toRGB( hsl, alpha );
}
private static float clamp( float value ) {
return (value < 0)
? 0
: ((value > 100)
? 100
: value);
}
//---- interface ColorFunction --------------------------------------------
public interface ColorFunction {
void apply( float[] hsl );
}
//---- class Lighten ------------------------------------------------------
/**
* Increase the lightness of a color in the HSL color space by an absolute
* or relative amount.
*/
public static class Lighten
implements ColorFunction
{
private final float amount;
private final boolean relative;
private final boolean autoInverse;
public Lighten( float amount, boolean relative, boolean autoInverse ) {
this.amount = amount;
this.relative = relative;
this.autoInverse = autoInverse;
}
@Override
public void apply( float[] hsl ) {
float amount2 = autoInverse && shouldInverse( hsl ) ? -amount : amount;
hsl[2] = clamp( relative
? (hsl[2] * ((100 + amount2) / 100))
: (hsl[2] + amount2) );
}
protected boolean shouldInverse( float[] hsl ) {
return hsl[2] >= 50;
}
}
//---- class Darken -------------------------------------------------------
/**
* Decrease the lightness of a color in the HSL color space by an absolute
* or relative amount.
*/
public static class Darken
extends Lighten
{
public Darken( float amount, boolean relative, boolean autoInverse ) {
super( -amount, relative, autoInverse );
}
@Override
protected boolean shouldInverse( float[] hsl ) {
return hsl[2] < 50;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Color;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
/**
* A (red) color that acts as a placeholder in UI defaults.
* The actual color is derived from another color,
* which is modified by the given color functions.
*
* @author Karl Tauber
*/
public class DerivedColor
extends ColorUIResource
{
private final ColorFunction[] functions;
public DerivedColor( ColorFunction... functions ) {
super( Color.red );
this.functions = functions;
}
public Color derive( Color baseColor ) {
return ColorFunctions.applyFunctions( baseColor, functions );
}
}

View File

@@ -0,0 +1,432 @@
/*
* From http://tips4java.wordpress.com/2009/07/05/hsl-color/
*
* License note on http://tips4java.wordpress.com/about/
* "You are free to use and/or modify any or all code posted on the
* Java Tips Weblog without restriction. A credit in the code comments
* would be nice, but not in any way mandatory."
*/
package com.formdev.flatlaf.util;
import java.awt.Color;
/**
* The HSLColor class provides methods to manipulate HSL (Hue, Saturation
* Luminance) values to create a corresponding Color object using the RGB
* ColorSpace.
*
* The HUE is the color, the Saturation is the purity of the color (with
* respect to grey) and Luminance is the brightness of the color (with respect
* to black and white)
*
* The Hue is specified as an angel between 0 - 360 degrees where red is 0,
* green is 120 and blue is 240. In between you have the colors of the rainbow.
* Saturation is specified as a percentage between 0 - 100 where 100 is fully
* saturated and 0 approaches gray. Luminance is specified as a percentage
* between 0 - 100 where 0 is black and 100 is white.
*
* In particular the HSL color space makes it easier change the Tone or Shade
* of a color by adjusting the luminance value.
*/
public class HSLColor
{
private final Color rgb;
private final float[] hsl;
private final float alpha;
/**
* Create a HSLColor object using an RGB Color object.
*
* @param rgb the RGB Color object
*/
public HSLColor(Color rgb)
{
this.rgb = rgb;
hsl = fromRGB( rgb );
alpha = rgb.getAlpha() / 255.0f;
}
/**
* Create a HSLColor object using individual HSL values and a default
* alpha value of 1.0.
*
* @param h is the Hue value in degrees between 0 - 360
* @param s is the Saturation percentage between 0 - 100
* @param l is the Lumanance percentage between 0 - 100
*/
public HSLColor(float h, float s, float l)
{
this(h, s, l, 1.0f);
}
/**
* Create a HSLColor object using individual HSL values.
*
* @param h the Hue value in degrees between 0 - 360
* @param s the Saturation percentage between 0 - 100
* @param l the Lumanance percentage between 0 - 100
* @param alpha the alpha value between 0 - 1
*/
public HSLColor(float h, float s, float l, float alpha)
{
hsl = new float[] {h, s, l};
this.alpha = alpha;
rgb = toRGB(hsl, alpha);
}
/**
* Create a HSLColor object using an an array containing the
* individual HSL values and with a default alpha value of 1.
*
* @param hsl array containing HSL values
*/
public HSLColor(float[] hsl)
{
this(hsl, 1.0f);
}
/**
* Create a HSLColor object using an an array containing the
* individual HSL values.
*
* @param hsl array containing HSL values
* @param alpha the alpha value between 0 - 1
*/
public HSLColor(float[] hsl, float alpha)
{
this.hsl = hsl;
this.alpha = alpha;
rgb = toRGB(hsl, alpha);
}
/**
* Create a RGB Color object based on this HSLColor with a different
* Hue value. The degrees specified is an absolute value.
*
* @param degrees - the Hue value between 0 - 360
* @return the RGB Color object
*/
public Color adjustHue(float degrees)
{
return toRGB(degrees, hsl[1], hsl[2], alpha);
}
/**
* Create a RGB Color object based on this HSLColor with a different
* Luminance value. The percent specified is an absolute value.
*
* @param percent - the Luminance value between 0 - 100
* @return the RGB Color object
*/
public Color adjustLuminance(float percent)
{
return toRGB(hsl[0], hsl[1], percent, alpha);
}
/**
* Create a RGB Color object based on this HSLColor with a different
* Saturation value. The percent specified is an absolute value.
*
* @param percent - the Saturation value between 0 - 100
* @return the RGB Color object
*/
public Color adjustSaturation(float percent)
{
return toRGB(hsl[0], percent, hsl[2], alpha);
}
/**
* Create a RGB Color object based on this HSLColor with a different
* Shade. Changing the shade will return a darker color. The percent
* specified is a relative value.
*
* @param percent - the value between 0 - 100
* @return the RGB Color object
*/
public Color adjustShade(float percent)
{
float multiplier = (100.0f - percent) / 100.0f;
float l = Math.max(0.0f, hsl[2] * multiplier);
return toRGB(hsl[0], hsl[1], l, alpha);
}
/**
* Create a RGB Color object based on this HSLColor with a different
* Tone. Changing the tone will return a lighter color. The percent
* specified is a relative value.
*
* @param percent - the value between 0 - 100
* @return the RGB Color object
*/
public Color adjustTone(float percent)
{
float multiplier = (100.0f + percent) / 100.0f;
float l = Math.min(100.0f, hsl[2] * multiplier);
return toRGB(hsl[0], hsl[1], l, alpha);
}
/**
* Get the Alpha value.
*
* @return the Alpha value.
*/
public float getAlpha()
{
return alpha;
}
/**
* Create a RGB Color object that is the complementary color of this
* HSLColor. This is a convenience method. The complementary color is
* determined by adding 180 degrees to the Hue value.
* @return the RGB Color object
*/
public Color getComplementary()
{
float hue = (hsl[0] + 180.0f) % 360.0f;
return toRGB(hue, hsl[1], hsl[2]);
}
/**
* Get the Hue value.
*
* @return the Hue value.
*/
public float getHue()
{
return hsl[0];
}
/**
* Get the HSL values.
*
* @return the HSL values.
*/
public float[] getHSL()
{
return hsl;
}
/**
* Get the Luminance value.
*
* @return the Luminance value.
*/
public float getLuminance()
{
return hsl[2];
}
/**
* Get the RGB Color object represented by this HDLColor.
*
* @return the RGB Color object.
*/
public Color getRGB()
{
return rgb;
}
/**
* Get the Saturation value.
*
* @return the Saturation value.
*/
public float getSaturation()
{
return hsl[1];
}
@Override
public String toString()
{
String toString =
"HSLColor[h=" + hsl[0] +
",s=" + hsl[1] +
",l=" + hsl[2] +
",alpha=" + alpha + "]";
return toString;
}
/**
* Convert a RGB Color to it corresponding HSL values.
*
* @return an array containing the 3 HSL values.
*/
public static float[] fromRGB(Color color)
{
// Get RGB values in the range 0 - 1
float[] rgb = color.getRGBColorComponents( null );
float r = rgb[0];
float g = rgb[1];
float b = rgb[2];
// Minimum and Maximum RGB values are used in the HSL calculations
float min = Math.min(r, Math.min(g, b));
float max = Math.max(r, Math.max(g, b));
// Calculate the Hue
float h = 0;
if (max == min)
h = 0;
else if (max == r)
h = ((60 * (g - b) / (max - min)) + 360) % 360;
else if (max == g)
h = (60 * (b - r) / (max - min)) + 120;
else if (max == b)
h = (60 * (r - g) / (max - min)) + 240;
// Calculate the Luminance
float l = (max + min) / 2;
// System.out.println(max + " : " + min + " : " + l);
// Calculate the Saturation
float s = 0;
if (max == min)
s = 0;
else if (l <= .5f)
s = (max - min) / (max + min);
else
s = (max - min) / (2 - max - min);
// System.out.println(new HSLColor( new float[] {h, s * 100, l * 100} ));
return new float[] {h, s * 100, l * 100};
}
/**
* Convert HSL values to a RGB Color with a default alpha value of 1.
* H (Hue) is specified as degrees in the range 0 - 360.
* S (Saturation) is specified as a percentage in the range 1 - 100.
* L (Lumanance) is specified as a percentage in the range 1 - 100.
*
* @param hsl an array containing the 3 HSL values
*
* @return the RGB Color object
*/
public static Color toRGB(float[] hsl)
{
return toRGB(hsl, 1.0f);
}
/**
* Convert HSL values to a RGB Color.
* H (Hue) is specified as degrees in the range 0 - 360.
* S (Saturation) is specified as a percentage in the range 1 - 100.
* L (Lumanance) is specified as a percentage in the range 1 - 100.
*
* @param hsl an array containing the 3 HSL values
* @param alpha the alpha value between 0 - 1
*
* @return the RGB Color object
*/
public static Color toRGB(float[] hsl, float alpha)
{
return toRGB(hsl[0], hsl[1], hsl[2], alpha);
}
/**
* Convert HSL values to a RGB Color with a default alpha value of 1.
*
* @param h Hue is specified as degrees in the range 0 - 360.
* @param s Saturation is specified as a percentage in the range 1 - 100.
* @param l Lumanance is specified as a percentage in the range 1 - 100.
*
* @return the RGB Color object
*/
public static Color toRGB(float h, float s, float l)
{
return toRGB(h, s, l, 1.0f);
}
/**
* Convert HSL values to a RGB Color.
*
* @param h Hue is specified as degrees in the range 0 - 360.
* @param s Saturation is specified as a percentage in the range 1 - 100.
* @param l Lumanance is specified as a percentage in the range 1 - 100.
* @param alpha the alpha value between 0 - 1
*
* @return the RGB Color object
*/
public static Color toRGB(float h, float s, float l, float alpha)
{
if (s <0.0f || s > 100.0f)
{
String message = "Color parameter outside of expected range - Saturation";
throw new IllegalArgumentException( message );
}
if (l <0.0f || l > 100.0f)
{
String message = "Color parameter outside of expected range - Luminance";
throw new IllegalArgumentException( message );
}
if (alpha <0.0f || alpha > 1.0f)
{
String message = "Color parameter outside of expected range - Alpha";
throw new IllegalArgumentException( message );
}
// Formula needs all values between 0 - 1.
h = h % 360.0f;
h /= 360f;
s /= 100f;
l /= 100f;
float q = 0;
if (l < 0.5)
q = l * (1 + s);
else
q = (l + s) - (s * l);
float p = 2 * l - q;
float r = Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f)));
float g = Math.max(0, HueToRGB(p, q, h));
float b = Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f)));
r = Math.min(r, 1.0f);
g = Math.min(g, 1.0f);
b = Math.min(b, 1.0f);
return new Color(r, g, b, alpha);
}
private static float HueToRGB(float p, float q, float h)
{
if (h < 0) h += 1;
if (h > 1 ) h -= 1;
if (6 * h < 1)
{
return p + ((q - p) * 6 * h);
}
if (2 * h < 1 )
{
return q;
}
if (3 * h < 2)
{
return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) );
}
return p;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.util.ArrayList;
import java.util.List;
/**
* Utility methods for strings.
*
* @author Karl Tauber
*/
public class StringUtils
{
public static String removeLeading( String string, String leading ) {
return string.startsWith( leading )
? string.substring( leading.length() )
: string;
}
public static String removeTrailing( String string, String trailing ) {
return string.endsWith( trailing )
? string.substring( 0, string.length() - trailing.length() )
: string;
}
public static List<String> split( String str, char delim ) {
ArrayList<String> strs = new ArrayList<>();
int delimIndex = str.indexOf( delim );
int index = 0;
while( delimIndex >= 0 ) {
strs.add( str.substring( index, delimIndex ) );
index = delimIndex + 1;
delimIndex = str.indexOf( delim, index );
}
strs.add( str.substring( index ) );
return strs;
}
}

View File

@@ -24,4 +24,6 @@ module com.formdev.flatlaf {
exports com.formdev.flatlaf.icons;
exports com.formdev.flatlaf.ui;
exports com.formdev.flatlaf.util;
uses com.formdev.flatlaf.FlatDefaultsAddon;
}

View File

@@ -20,16 +20,20 @@
#---- variables ----
@background=3c3f41
@foreground=bbbbbb
@selectionBackground=4B6EAF
@background=#3c3f41
@foreground=#bbbbbb
@selectionBackground=#4B6EAF
@selectionForeground=@foreground
@selectionInactiveBackground=0D293E
@selectionInactiveBackground=#0D293E
@selectionInactiveForeground=@foreground
@disabledText=777777
@textComponentBackground=45494A
@cellFocusColor=000000
@icon=adadad
@disabledText=#777777
@textComponentBackground=#45494A
@cellFocusColor=#000000
@icon=#adadad
# Button
@buttonHoverBackground=lighten(3%,autoInverse)
@buttonPressedBackground=lighten(6%,autoInverse)
#---- globals ----
@@ -46,7 +50,7 @@
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=bbbbbb
*.acceleratorForeground=#bbbbbb
*.acceleratorSelectionForeground=@selectionForeground
@@ -62,62 +66,63 @@ window=@background
#---- Button ----
Button.background=4c5052
Button.hoverBackground=525658
Button.pressedBackground=5c6164
Button.background=#4c5052
Button.hoverBackground=@buttonHoverBackground
Button.pressedBackground=@buttonPressedBackground
Button.borderColor=5e6060
Button.disabledBorderColor=5e6060
Button.focusedBorderColor=466d94
Button.borderColor=#5e6060
Button.disabledBorderColor=#5e6060
Button.focusedBorderColor=#466d94
Button.hoverBorderColor=@@Button.focusedBorderColor
Button.default.background=365880
Button.default.foreground=bbbbbb
Button.default.hoverBackground=3d6185
Button.default.pressedBackground=43688c
Button.default.borderColor=4c708c
Button.default.hoverBorderColor=537699
Button.default.focusedBorderColor=537699
Button.default.focusColor=43688c
Button.default.background=#365880
Button.default.foreground=#bbbbbb
Button.default.hoverBackground=@buttonHoverBackground
Button.default.pressedBackground=@buttonPressedBackground
Button.default.borderColor=#4c708c
Button.default.hoverBorderColor=#537699
Button.default.focusedBorderColor=#537699
Button.default.focusColor=#43688c
Button.default.boldText=true
Button.toolbar.hoverBackground=4c5052
Button.toolbar.pressedBackground=555a5d
Button.toolbar.hoverBackground=#4c5052
Button.toolbar.pressedBackground=#555a5d
#---- CheckBox ----
CheckBox.icon.borderColor=6B6B6B
CheckBox.icon.disabledBorderColor=545556
CheckBox.icon.selectedBorderColor=6B6B6B
CheckBox.icon.focusedBorderColor=466D94
CheckBox.icon.borderColor=#6B6B6B
CheckBox.icon.disabledBorderColor=#545556
CheckBox.icon.selectedBorderColor=#6B6B6B
CheckBox.icon.focusedBorderColor=#466D94
CheckBox.icon.hoverBorderColor=@@CheckBox.icon.focusedBorderColor
CheckBox.icon.selectedFocusedBorderColor=466D94
CheckBox.icon.background=43494A
CheckBox.icon.selectedFocusedBorderColor=#466D94
CheckBox.icon.background=#43494A
CheckBox.icon.disabledBackground=@background
CheckBox.icon.hoverBackground=4c5052
CheckBox.icon.pressedBackground=@@Button.pressedBackground
CheckBox.icon.selectedBackground=43494A
CheckBox.icon.checkmarkColor=A7A7A7
CheckBox.icon.disabledCheckmarkColor=606060
CheckBox.icon.hoverBackground=@buttonHoverBackground
CheckBox.icon.pressedBackground=@buttonPressedBackground
CheckBox.icon.selectedBackground=#43494A
CheckBox.icon.checkmarkColor=#A7A7A7
CheckBox.icon.disabledCheckmarkColor=#606060
#---- ComboBox ----
ComboBox.background=@textComponentBackground
ComboBox.buttonBackground=@textComponentBackground
ComboBox.buttonEditableBackground=404445
ComboBox.buttonArrowColor=9A9DA1
ComboBox.buttonDisabledArrowColor=585858
ComboBox.buttonHoverArrowColor=bbbbbb
ComboBox.buttonEditableBackground=#404445
ComboBox.buttonArrowColor=#9A9DA1
ComboBox.buttonDisabledArrowColor=#585858
ComboBox.buttonHoverArrowColor=#bbbbbb
#---- Component ----
Component.borderColor=646464
Component.disabledBorderColor=646464
Component.focusedBorderColor=466d94
Component.focusColor=3d6185
Component.borderColor=#646464
Component.disabledBorderColor=#646464
Component.focusedBorderColor=#466d94
Component.focusColor=#3d6185
Component.linkColor=#589df6
#---- List ----
@@ -127,110 +132,101 @@ List.background=@textComponentBackground
#---- Menu ----
Menu.icon.arrowColor=A7A7A7
Menu.icon.disabledArrowColor=606060
Menu.icon.arrowColor=#A7A7A7
Menu.icon.disabledArrowColor=#606060
#---- MenuBar ----
MenuBar.borderColor=515151
MenuBar.borderColor=#515151
#---- MenuItemCheckBox ----
MenuItemCheckBox.icon.checkmarkColor=A7A7A7
MenuItemCheckBox.icon.disabledCheckmarkColor=606060
#---- OptionPane ----
OptionPane.icon.errorColor=C75450
OptionPane.icon.informationColor=3592C4
OptionPane.icon.questionColor=3592C4
OptionPane.icon.warningColor=F0A732
OptionPane.icon.foreground=null
MenuItemCheckBox.icon.checkmarkColor=#A7A7A7
MenuItemCheckBox.icon.disabledCheckmarkColor=#606060
#---- PopupMenu ----
PopupMenu.borderColor=515151
PopupMenu.borderColor=#515151
#---- ProgressBar ----
ProgressBar.background=555555
ProgressBar.foreground=a0a0a0
ProgressBar.background=#555555
ProgressBar.foreground=#a0a0a0
ProgressBar.selectionForeground=@background
ProgressBar.selectionBackground=@foreground
#---- ScrollBar ----
ScrollBar.track=3F4244
ScrollBar.thumb=5B5E5F
ScrollBar.hoverTrackColor=434647
ScrollBar.hoverThumbColor=666868
ScrollBar.track=#3F4244
ScrollBar.thumb=#5B5E5F
ScrollBar.hoverTrackColor=#434647
ScrollBar.hoverThumbColor=#666868
#---- Separator ----
Separator.foreground=515151
Separator.foreground=#515151
#---- Slider ----
Slider.trackColor=646464
Slider.thumbColor=A6A6A6
Slider.tickColor=888888
Slider.focusedColor=@@Component.focusColor
Slider.hoverColor=888888
Slider.disabledForeground=4c5052
Slider.trackColor=#646464
Slider.thumbColor=#A6A6A6
Slider.tickColor=#888888
Slider.hoverColor=darken(15%,autoInverse)
Slider.disabledForeground=#4c5052
#---- SplitPane ----
SplitPaneDivider.draggingColor=646464
SplitPaneDivider.oneTouchHoverArrowColor=7A7D81
SplitPaneDivider.draggingColor=#646464
SplitPaneDivider.oneTouchHoverArrowColor=#7A7D81
#---- TabbedPane ----
TabbedPane.disabledForeground=777777
TabbedPane.underlineColor=4A88C7
TabbedPane.disabledUnderlineColor=7a7a7a
TabbedPane.hoverColor=2e3133
TabbedPane.focusColor=3d4b5c
TabbedPane.contentAreaColor=323232
TabbedPane.disabledForeground=#777777
TabbedPane.underlineColor=#4A88C7
TabbedPane.disabledUnderlineColor=#7a7a7a
TabbedPane.hoverColor=#2e3133
TabbedPane.focusColor=#3d4b5c
TabbedPane.contentAreaColor=#323232
#---- Table ----
Table.background=@textComponentBackground
Table.gridColor=4F5152
Table.gridColor=#4F5152
#---- TableHeader ----
TableHeader.background=45494A
TableHeader.separatorColor=585858
TableHeader.bottomSeparatorColor=585858
TableHeader.background=#45494A
TableHeader.separatorColor=#585858
TableHeader.bottomSeparatorColor=#585858
#---- ToggleButton ----
ToggleButton.selectedBackground=64696C
ToggleButton.selectedBackground=#64696C
ToggleButton.selectedForeground=@foreground
ToggleButton.disabledSelectedBackground=525658
ToggleButton.disabledSelectedBackground=#525658
ToggleButton.toolbar.selectedBackground=5c6164
ToggleButton.toolbar.selectedBackground=#5c6164
#---- ToolTip ----
ToolTip.background=4b4d4d
ToolTip.border=4,6,4,6
ToolTip.background=#1e2123
#---- Tree ----
Tree.background=@textComponentBackground
Tree.hash=505355
Tree.hash=#505355

View File

@@ -22,27 +22,27 @@
Button.focusedBackground=null
Button.default.background=4A86C7
Button.default.foreground=f0f0f0
Button.default.background=#4A86C7
Button.default.foreground=#f0f0f0
Button.default.focusedBackground=null
Button.default.hoverBackground=5B91CC
Button.default.pressedBackground=6E9ED2
Button.default.borderColor=3167ad
Button.default.hoverBorderColor=a8cef6
Button.default.focusedBorderColor=a8cef6
Button.default.focusColor=97c3f3
Button.default.hoverBackground=#5B91CC
Button.default.pressedBackground=#6E9ED2
Button.default.borderColor=#3167ad
Button.default.hoverBorderColor=#a8cef6
Button.default.focusedBorderColor=#a8cef6
Button.default.focusColor=#97c3f3
Button.default.boldText=true
#---- CheckBox ----
CheckBox.icon.selectedBorderColor=4982CC
CheckBox.icon.selectedFocusedBorderColor=ACCFF7
CheckBox.icon.selectedBackground=4D89C9
CheckBox.icon.checkmarkColor=FFFFFF
CheckBox.icon.selectedBorderColor=#4982CC
CheckBox.icon.selectedFocusedBorderColor=#ACCFF7
CheckBox.icon.selectedBackground=#4D89C9
CheckBox.icon.checkmarkColor=#FFFFFF
CheckBox.icon.selectedHoverBackground=5E94CE
CheckBox.icon.selectedPressedBackground=72A1D4
CheckBox.icon.selectedHoverBackground=#5E94CE
CheckBox.icon.selectedPressedBackground=#72A1D4
#---- Component ----

View File

@@ -22,6 +22,7 @@ CheckBoxMenuItemUI=com.formdev.flatlaf.ui.FlatCheckBoxMenuItemUI
ColorChooserUI=com.formdev.flatlaf.ui.FlatColorChooserUI
ComboBoxUI=com.formdev.flatlaf.ui.FlatComboBoxUI
EditorPaneUI=com.formdev.flatlaf.ui.FlatEditorPaneUI
FileChooserUI=com.formdev.flatlaf.ui.FlatFileChooserUI
FormattedTextFieldUI=com.formdev.flatlaf.ui.FlatFormattedTextFieldUI
LabelUI=com.formdev.flatlaf.ui.FlatLabelUI
ListUI=com.formdev.flatlaf.ui.FlatListUI
@@ -77,6 +78,7 @@ Button.default.borderWidth=1
CheckBox.border=com.formdev.flatlaf.ui.FlatMarginBorder
CheckBox.icon=com.formdev.flatlaf.icons.FlatCheckBoxIcon
CheckBox.arc=2
CheckBox.margin=2,2,2,2
CheckBox.iconTextGap=4
CheckBox.rollover=true
@@ -127,12 +129,6 @@ FileChooser.homeFolderIcon=com.formdev.flatlaf.icons.FlatFileChooserHomeFolderIc
FileChooser.detailsViewIcon=com.formdev.flatlaf.icons.FlatFileChooserDetailsViewIcon
FileChooser.listViewIcon=com.formdev.flatlaf.icons.FlatFileChooserListViewIcon
FileChooser.icon.newFolderColor=@icon
FileChooser.icon.upFolderColor=@icon
FileChooser.icon.homeFolderColor=@icon
FileChooser.icon.detailsViewColor=@icon
FileChooser.icon.listViewColor=@icon
#---- FileView ----
@@ -142,12 +138,6 @@ FileView.computerIcon=com.formdev.flatlaf.icons.FlatFileViewComputerIcon
FileView.hardDriveIcon=com.formdev.flatlaf.icons.FlatFileViewHardDriveIcon
FileView.floppyDriveIcon=com.formdev.flatlaf.icons.FlatFileViewFloppyDriveIcon
FileView.icon.directoryColor=@icon
FileView.icon.fileColor=@icon
FileView.icon.computerColor=@icon
FileView.icon.hardDriveColor=@icon
FileView.icon.floppyDriveColor=@icon
#---- FormattedTextField ----
@@ -216,6 +206,7 @@ OptionPane.buttonMinimumWidth={scaledNumber}72
OptionPane.sameSizeButtons=true
OptionPane.setButtonMargin=false
OptionPane.buttonOrientation=4
[mac]OptionPane.isYesLast=true
OptionPane.errorIcon=com.formdev.flatlaf.icons.FlatOptionPaneErrorIcon
OptionPane.informationIcon=com.formdev.flatlaf.icons.FlatOptionPaneInformationIcon
@@ -277,6 +268,7 @@ ScrollBar.width=10
ScrollPane.border=com.formdev.flatlaf.ui.FlatBorder
ScrollPane.background=@@ScrollBar.track
ScrollPane.fillUpperCorner=true
#---- Separator ----
@@ -348,7 +340,7 @@ Table.selectionInactiveForeground=@selectionInactiveForeground
#---- TableHeader ----
TableHeader.height=25
TableHeader.cellBorder=2,2,2,2
TableHeader.cellBorder=2,3,2,3
#---- TextArea ----
@@ -409,7 +401,10 @@ ToolBar.separatorColor=@@Separator.foreground
#---- ToolTip ----
ToolTip.border=2,6,2,6,@@Component.borderColor
ToolTip.border=4,6,4,6,@@Component.borderColor
ToolTip.borderInactive=null
ToolTip.backgroundInactive=@@ToolTip.background
ToolTip.foregroundInactive=@disabledText
#---- Tree ----
@@ -417,7 +412,7 @@ ToolTip.border=2,6,2,6,@@Component.borderColor
Tree.border=1,1,1,1
Tree.selectionInactiveBackground=@selectionInactiveBackground
Tree.selectionInactiveForeground=@selectionInactiveForeground
Tree.textBackground=@@Tree.background
Tree.textBackground=null
Tree.selectionBorderColor=@cellFocusColor
Tree.rendererMargins=1,2,1,2
Tree.paintLines=false

View File

@@ -20,39 +20,43 @@
#---- variables ----
@background=f2f2f2
@foreground=000000
@selectionBackground=4A6EB7
@selectionForeground=ffffff
@selectionInactiveBackground=d4d4d4
@background=#f2f2f2
@foreground=#000000
@selectionBackground=#4A6EB7
@selectionForeground=#ffffff
@selectionInactiveBackground=#d4d4d4
@selectionInactiveForeground=@foreground
@disabledText=999999
@textComponentBackground=ffffff
@cellFocusColor=000000
@icon=afafaf
@disabledText=#999999
@textComponentBackground=#ffffff
@cellFocusColor=#000000
@icon=#afafaf
# Button
@buttonHoverBackground=darken(3%,autoInverse)
@buttonPressedBackground=darken(10%,autoInverse)
#---- globals ----
*.background=@background
*.foreground=@foreground
*.textBackground=cccccc
*.textBackground=#cccccc
*.textForeground=@foreground
*.caretForeground=@foreground
*.inactiveBackground=@background
*.inactiveForeground=777777
*.inactiveForeground=#777777
*.selectionBackground=@selectionBackground
*.selectionForeground=@selectionForeground
*.disabledBackground=@background
*.disabledForeground=@disabledText
*.disabledText=@disabledText
*.acceleratorForeground=505050
*.acceleratorForeground=#505050
*.acceleratorSelectionForeground=@selectionForeground
#---- system ----
control=e0e0e0
control=#e0e0e0
controlText=@foreground
infoText=@foreground
text=@foreground
@@ -62,69 +66,70 @@ window=@background
#---- Button ----
Button.background=ffffff
Button.focusedBackground=e3f1fa
Button.hoverBackground=f8f8f8
Button.pressedBackground=dfdfdf
Button.background=#ffffff
Button.focusedBackground=#e3f1fa
Button.hoverBackground=@buttonHoverBackground
Button.pressedBackground=@buttonPressedBackground
Button.borderColor=bfbfbf
Button.disabledBorderColor=cfcfcf
Button.focusedBorderColor=87afda
Button.borderColor=#bfbfbf
Button.disabledBorderColor=#cfcfcf
Button.focusedBorderColor=#87afda
Button.hoverBorderColor=@@Button.focusedBorderColor
Button.default.background=@@Button.background
Button.default.foreground=@foreground
Button.default.focusedBackground=@@Button.focusedBackground
Button.default.hoverBackground=@@Button.hoverBackground
Button.default.pressedBackground=@@Button.pressedBackground
Button.default.borderColor=4D89C9
Button.default.hoverBackground=@buttonHoverBackground
Button.default.pressedBackground=@buttonPressedBackground
Button.default.borderColor=#4D89C9
Button.default.hoverBorderColor=@@Button.hoverBorderColor
Button.default.focusedBorderColor=@@Button.focusedBorderColor
Button.default.focusColor=@@Component.focusColor
Button.default.borderWidth=2
Button.toolbar.hoverBackground=dfdfdf
Button.toolbar.pressedBackground=d8d8d8
Button.toolbar.hoverBackground=#dfdfdf
Button.toolbar.pressedBackground=#d8d8d8
#---- CheckBox ----
CheckBox.icon.borderColor=878787
CheckBox.icon.disabledBorderColor=BDBDBD
CheckBox.icon.selectedBorderColor=878787
CheckBox.icon.focusedBorderColor=7B9FC7
CheckBox.icon.borderColor=#878787
CheckBox.icon.disabledBorderColor=#BDBDBD
CheckBox.icon.selectedBorderColor=#878787
CheckBox.icon.focusedBorderColor=#7B9FC7
CheckBox.icon.hoverBorderColor=@@CheckBox.icon.focusedBorderColor
CheckBox.icon.background=FFFFFF
CheckBox.icon.background=#FFFFFF
CheckBox.icon.disabledBackground=@background
CheckBox.icon.focusedBackground=@@Button.focusedBackground
CheckBox.icon.hoverBackground=@@Button.hoverBackground
CheckBox.icon.pressedBackground=@@Button.pressedBackground
CheckBox.icon.selectedBackground=FFFFFF
CheckBox.icon.checkmarkColor=4D89C9
CheckBox.icon.disabledCheckmarkColor=ABABAB
CheckBox.icon.hoverBackground=@buttonHoverBackground
CheckBox.icon.pressedBackground=@buttonPressedBackground
CheckBox.icon.selectedBackground=#FFFFFF
CheckBox.icon.checkmarkColor=#4D89C9
CheckBox.icon.disabledCheckmarkColor=#ABABAB
#---- ComboBox ----
ComboBox.background=@textComponentBackground
ComboBox.buttonBackground=@textComponentBackground
ComboBox.buttonEditableBackground=fafafa
ComboBox.buttonArrowColor=666666
ComboBox.buttonDisabledArrowColor=ABABAB
ComboBox.buttonHoverArrowColor=999999
ComboBox.buttonEditableBackground=#fafafa
ComboBox.buttonArrowColor=#666666
ComboBox.buttonDisabledArrowColor=#ABABAB
ComboBox.buttonHoverArrowColor=#999999
#---- Component ----
Component.borderColor=c4c4c4
Component.disabledBorderColor=cfcfcf
Component.focusedBorderColor=87afda
Component.focusColor=97c3f3
Component.borderColor=#c4c4c4
Component.disabledBorderColor=#cfcfcf
Component.focusedBorderColor=#87afda
Component.focusColor=#97c3f3
Component.linkColor=#4a78c2
#---- HelpButton ----
HelpButton.questionMarkColor=4D89C9
HelpButton.questionMarkColor=#4D89C9
#---- List ----
@@ -134,110 +139,100 @@ List.background=@textComponentBackground
#---- Menu ----
Menu.icon.arrowColor=666666
Menu.icon.disabledArrowColor=ABABAB
Menu.icon.arrowColor=#666666
Menu.icon.disabledArrowColor=#ABABAB
#---- MenuBar ----
MenuBar.borderColor=cdcdcd
MenuBar.borderColor=#cdcdcd
#---- MenuItemCheckBox ----
MenuItemCheckBox.icon.checkmarkColor=4D89C9
MenuItemCheckBox.icon.disabledCheckmarkColor=ABABAB
#---- OptionPane ----
OptionPane.icon.errorColor=DB5860
OptionPane.icon.informationColor=389FD6
OptionPane.icon.questionColor=389FD6
OptionPane.icon.warningColor=EDA200
OptionPane.icon.foreground=null
MenuItemCheckBox.icon.checkmarkColor=#4D89C9
MenuItemCheckBox.icon.disabledCheckmarkColor=#ABABAB
#---- PopupMenu ----
PopupMenu.borderColor=cdcdcd
PopupMenu.borderColor=#cdcdcd
#---- ProgressBar ----
ProgressBar.background=c4c4c4
ProgressBar.foreground=808080
ProgressBar.background=#c4c4c4
ProgressBar.foreground=#808080
ProgressBar.selectionForeground=@textComponentBackground
ProgressBar.selectionBackground=@foreground
#---- ScrollBar ----
ScrollBar.track=F5F5F5
ScrollBar.thumb=DBDBDB
ScrollBar.hoverTrackColor=e6e6e6
ScrollBar.hoverThumbColor=c6c6c6
ScrollBar.track=#F5F5F5
ScrollBar.thumb=#DBDBDB
ScrollBar.hoverTrackColor=#e6e6e6
ScrollBar.hoverThumbColor=#c6c6c6
#---- Separator ----
Separator.foreground=cdcdcd
Separator.foreground=#cdcdcd
#---- Slider ----
Slider.trackColor=c4c4c4
Slider.thumbColor=6e6e6e
Slider.tickColor=888888
Slider.focusedColor=@@Component.focusColor
Slider.hoverColor=999999
Slider.disabledForeground=c0c0c0
Slider.trackColor=#c4c4c4
Slider.thumbColor=#6e6e6e
Slider.tickColor=#888888
Slider.hoverColor=lighten(15%,autoInverse)
Slider.disabledForeground=#c0c0c0
#---- SplitPane ----
SplitPaneDivider.draggingColor=c4c4c4
SplitPaneDivider.oneTouchHoverArrowColor=333333
SplitPaneDivider.draggingColor=#c4c4c4
SplitPaneDivider.oneTouchHoverArrowColor=#333333
#---- TabbedPane ----
TabbedPane.disabledForeground=999999
TabbedPane.underlineColor=4083C9
TabbedPane.disabledUnderlineColor=ababab
TabbedPane.hoverColor=d9d9d9
TabbedPane.focusColor=dae4ed
TabbedPane.contentAreaColor=bfbfbf
TabbedPane.disabledForeground=#999999
TabbedPane.underlineColor=#4083C9
TabbedPane.disabledUnderlineColor=#ababab
TabbedPane.hoverColor=#d9d9d9
TabbedPane.focusColor=#dae4ed
TabbedPane.contentAreaColor=#bfbfbf
#---- Table ----
Table.background=@textComponentBackground
Table.gridColor=F7F7F7
Table.gridColor=#F7F7F7
#---- TableHeader ----
TableHeader.background=ffffff
TableHeader.separatorColor=e5e5e5
TableHeader.bottomSeparatorColor=e5e5e5
TableHeader.background=#ffffff
TableHeader.separatorColor=#e5e5e5
TableHeader.bottomSeparatorColor=#e5e5e5
#---- ToggleButton ----
ToggleButton.selectedBackground=cfcfcf
ToggleButton.selectedBackground=#cfcfcf
ToggleButton.selectedForeground=@foreground
ToggleButton.disabledSelectedBackground=dfdfdf
ToggleButton.disabledSelectedBackground=#dfdfdf
ToggleButton.toolbar.selectedBackground=cfcfcf
ToggleButton.toolbar.selectedBackground=#cfcfcf
#---- ToolTip ----
ToolTip.background=f7f7f7
ToolTip.background=#fafafa
#---- Tree ----
Tree.background=@textComponentBackground
Tree.hash=E6E6E6
Tree.hash=#E6E6E6

View File

@@ -0,0 +1,23 @@
#
# Copyright 2019 FormDev Software GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#---- Button ----
Button.hoverBorderColor=null
Button.default.hoverBorderColor=null
Button.default.hoverBackground=@buttonHoverBackground
Button.default.pressedBackground=@buttonPressedBackground

View File

@@ -1,264 +0,0 @@
#
# Copyright 2019 FormDev Software GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#---- variables ----
@background=ccffcc
@selectionBackground=00aa00
@selectionInactiveBackground=888888
@selectionInactiveForeground=ffffff
@textComponentBackground=ffffff
@cellFocusColor=ff0000
@icon=afafaf
#---- globals ----
*.background=@background
*.foreground=ff0000
*.textBackground=ccffcc
*.textForeground=ff0000
*.caretForeground=0000ff
*.inactiveBackground=f0f0f0
*.inactiveForeground=000088
*.selectionBackground=@selectionBackground
*.selectionForeground=ffff00
*.disabledBackground=e0e0e0
*.disabledForeground=000088
*.disabledText=000088
*.acceleratorForeground=ff8888
*.acceleratorSelectionForeground=ffffff
#---- Button ----
Button.background=ffffff
Button.focusedBackground=00ffff
Button.hoverBackground=ffff00
Button.pressedBackground=FFC800
Button.borderColor=0000ff
Button.disabledBorderColor=000088
Button.focusedBorderColor=466d94
Button.hoverBorderColor=ff0000
Button.default.background=dddddd
Button.default.foreground=880000
Button.default.focusedBackground=00ffff
Button.default.hoverBackground=ffff00
Button.default.pressedBackground=FFC800
Button.default.borderColor=ff0000
Button.default.hoverBorderColor=ff0000
Button.default.focusedBorderColor=537699
Button.default.focusColor=ff0000
Button.toolbar.hoverBackground=ffffff
Button.toolbar.pressedBackground=eeeeee
#---- CheckBox ----
CheckBox.icon.borderColor=878787
CheckBox.icon.disabledBorderColor=BDBDBD
CheckBox.icon.selectedBorderColor=4982CC
CheckBox.icon.focusedBorderColor=7B9FC7
CheckBox.icon.hoverBorderColor=ff0000
CheckBox.icon.selectedFocusedBorderColor=ACCFF7
CheckBox.icon.background=FFFFFF
CheckBox.icon.disabledBackground=F2F2F2
CheckBox.icon.focusedBackground=00ffff
CheckBox.icon.hoverBackground=ffff00
CheckBox.icon.pressedBackground=FFC800
CheckBox.icon.selectedBackground=4D89C9
CheckBox.icon.checkmarkColor=FFFFFF
CheckBox.icon.disabledCheckmarkColor=ABABAB
#---- ComboBox ----
ComboBox.background=ffffff
ComboBox.buttonBackground=f0f0f0
ComboBox.buttonEditableBackground=cccccc
ComboBox.buttonArrowColor=666666
ComboBox.buttonDisabledArrowColor=ABABAB
ComboBox.buttonHoverArrowColor=ff0000
#---- Component ----
Component.borderColor=ff0000
Component.disabledBorderColor=000088
Component.focusedBorderColor=466d94
Component.focusColor=97c3f3
#Component.focusWidth=5
#Component.arc=8
#---- HelpButton ----
HelpButton.questionMarkColor=0000ff
#---- Label ----
Label.foreground=008800
Label.disabledForeground=000088
#---- List ----
List.background=f0ffff
List.cellNoFocusBorder=1,6,1,6
List.focusSelectedCellHighlightBorder=1,6,1,6,880000
List.focusCellHighlightBorder=1,6,1,6,880000
#---- Menu ----
Menu.icon.arrowColor=4D89C9
Menu.icon.disabledArrowColor=ABABAB
#---- MenuBar ----
MenuBar.borderColor=4444ff
#---- MenuItemCheckBox ----
MenuItemCheckBox.icon.checkmarkColor=4D89C9
MenuItemCheckBox.icon.disabledCheckmarkColor=ABABAB
#---- OptionPane ----
OptionPane.icon.errorColor=ff0000
OptionPane.icon.informationColor=00ff00
OptionPane.icon.questionColor=0000ff
OptionPane.icon.warningColor=ffcc00
OptionPane.icon.foreground=ffffff
#---- PopupMenu ----
PopupMenu.borderColor=0000ff
#---- PopupMenuSeparator ----
PopupMenuSeparator.height=30
PopupMenuSeparator.stripeWidth=3
PopupMenuSeparator.stripeIndent=1
#---- ProgressBar ----
ProgressBar.background=88ff88
ProgressBar.foreground=33737373
ProgressBar.selectionForeground=ff0000
ProgressBar.selectionBackground=000088
ProgressBar.cycleTime=10000
#---- ScrollBar ----
ScrollBar.track=88ff88
ScrollBar.thumb=33737373
ScrollBar.hoverTrackColor=00ff00
ScrollBar.hoverThumbColor=ff0000
#---- Separator ----
Separator.foreground=00bb00
Separator.height=20
Separator.stripeWidth=10
Separator.stripeIndent=5
#---- Slider ----
Slider.trackColor=00bb00
Slider.thumbColor=880000
Slider.tickColor=ff0000
Slider.focusedColor=@@Component.focusColor
Slider.hoverColor=0000ff
Slider.disabledForeground=000088
#---- SplitPane ----
SplitPaneDivider.draggingColor=880000
SplitPaneDivider.oneTouchArrowColor=00ff00
SplitPaneDivider.oneTouchHoverArrowColor=ff0000
#---- TabbedPane ----
TabbedPane.disabledForeground=777777
TabbedPane.underlineColor=4A88C7
TabbedPane.disabledUnderlineColor=7a7a7a
TabbedPane.hoverColor=eeeeee
TabbedPane.focusColor=dddddd
TabbedPane.contentAreaColor=bbbbbb
#---- Table ----
Table.rowHeight=25
Table.background=fffff0
Table.sortIconColor=ffff00
Table.gridColor=00ffff
#---- TableHeader ----
TableHeader.background=4444ff
TableHeader.foreground=ffffff
TableHeader.separatorColor=00ff00
TableHeader.bottomSeparatorColor=00ff00
#---- TitledBorder ----
TitledBorder.titleColor=ff00ff
TitledBorder.border=1,1,1,1,ff00ff
#---- ToggleButton ----
ToggleButton.background=ddddff
ToggleButton.selectedBackground=44ff44
ToggleButton.selectedForeground=000000
ToggleButton.disabledSelectedBackground=44dd44
ToggleButton.focusedBackground=00ffff
ToggleButton.hoverBackground=ffff00
ToggleButton.pressedBackground=FFC800
ToggleButton.toolbar.selectedBackground=dddddd
#---- ToolTip ----
ToolTip.background=eeeeff
#---- Tree ----
Tree.background=fff0ff
Tree.paintLines=true
Tree.hash=ff0000

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

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

View File

@@ -23,19 +23,20 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( project( ":flatlaf-extras" ) )
implementation( "com.miglayout:miglayout-swing:5.2" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
implementation( "com.formdev:svgSalamander:1.1.2.1" )
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks {
jar {
dependsOn( ":flatlaf-core:jar" )
dependsOn( ":flatlaf-extras:jar" )
manifest {
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )

View File

@@ -40,6 +40,9 @@ class BasicComponentsPanel
JButton button3 = new JButton();
JButton button4 = new JButton();
JButton button13 = new JButton();
JButton button14 = new JButton();
JButton button15 = new JButton();
JButton button16 = new JButton();
JLabel checkBoxLabel = new JLabel();
JCheckBox checkBox1 = new JCheckBox();
JCheckBox checkBox2 = new JCheckBox();
@@ -174,6 +177,18 @@ class BasicComponentsPanel
button13.setIcon(UIManager.getIcon("Tree.closedIcon"));
add(button13, "cell 5 1");
//---- button14 ----
button14.setText("...");
add(button14, "cell 5 1");
//---- button15 ----
button15.setText("\u2026");
add(button15, "cell 5 1");
//---- button16 ----
button16.setText("#");
add(button16, "cell 5 1");
//---- checkBoxLabel ----
checkBoxLabel.setText("JCheckBox");
add(checkBoxLabel, "cell 0 2");

View File

@@ -75,6 +75,24 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button14"
"text": "..."
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button15"
"text": "…"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button16"
"text": "#"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "checkBoxLabel"
"text": "JCheckBox"

View File

@@ -23,6 +23,7 @@ import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import com.formdev.flatlaf.*;
@@ -43,11 +44,11 @@ class ControlBar
initComponents();
// initialize look and feels combo box
DefaultComboBoxModel<LafInfo> lafModel = new DefaultComboBoxModel<>();
lafModel.addElement( new LafInfo( "Flat Light (F1)", FlatLightLaf.class.getName() ) );
lafModel.addElement( new LafInfo( "Flat Dark (F2)", FlatDarkLaf.class.getName() ) );
lafModel.addElement( new LafInfo( "Flat IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
lafModel.addElement( new LafInfo( "Flat Darcula (F4)", FlatDarculaLaf.class.getName() ) );
DefaultComboBoxModel<LookAndFeelInfo> lafModel = new DefaultComboBoxModel<>();
lafModel.addElement( new LookAndFeelInfo( "Flat Light (F1)", FlatLightLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat Dark (F2)", FlatDarkLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "Flat Darcula (F4)", FlatDarculaLaf.class.getName() ) );
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
@@ -66,18 +67,9 @@ class ControlBar
else if( className.equals( NimbusLookAndFeel.class.getName() ) )
name += " (F11)";
lafModel.addElement( new LafInfo( name, className ) );
lafModel.addElement( new LookAndFeelInfo( name, className ) );
}
LookAndFeel activeLaf = UIManager.getLookAndFeel();
String activeLafClassName = activeLaf.getClass().getName();
int sel = lafModel.getIndexOf( new LafInfo( null, activeLafClassName ) );
if( sel < 0 ) {
lafModel.addElement( new LafInfo( activeLaf.getName(), activeLafClassName ) );
sel = lafModel.getSize() - 1;
}
lafModel.setSelectedItem( lafModel.getElementAt( sel ) );
lookAndFeelComboBox.setModel( lafModel );
UIManager.addPropertyChangeListener( e -> {
@@ -161,26 +153,23 @@ class ControlBar
}
private void selectLookAndFeel( String lafClassName ) {
DefaultComboBoxModel<LafInfo> lafModel = (DefaultComboBoxModel<LafInfo>) lookAndFeelComboBox.getModel();
int sel = lafModel.getIndexOf( new LafInfo( null, lafClassName ) );
if( sel >= 0 )
lookAndFeelComboBox.setSelectedIndex( sel );
lookAndFeelComboBox.setSelectedLookAndFeel( lafClassName );
}
private void lookAndFeelChanged() {
LafInfo newLaf = (LafInfo) lookAndFeelComboBox.getSelectedItem();
if( newLaf == null )
String lafClassName = lookAndFeelComboBox.getSelectedLookAndFeel();
if( lafClassName == null )
return;
if( newLaf.className.equals( UIManager.getLookAndFeel().getClass().getName() ) )
if( lafClassName.equals( UIManager.getLookAndFeel().getClass().getName() ) )
return;
FlatLafDemo.prefs.put( FlatLafDemo.KEY_LAF, newLaf.className );
FlatLafDemo.prefs.put( FlatLafDemo.KEY_LAF, lafClassName );
EventQueue.invokeLater( () -> {
try {
// change look and feel
UIManager.setLookAndFeel( newLaf.className );
UIManager.setLookAndFeel( lafClassName );
// update all components
FlatLaf.updateUI();
@@ -250,7 +239,7 @@ class ControlBar
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
separator1 = new JSeparator();
lookAndFeelComboBox = new JComboBox<>();
lookAndFeelComboBox = new LookAndFeelsComboBox();
rightToLeftCheckBox = new JCheckBox();
enabledCheckBox = new JCheckBox();
infoLabel = new JLabel();
@@ -300,33 +289,10 @@ class ControlBar
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JSeparator separator1;
private JComboBox<LafInfo> lookAndFeelComboBox;
private LookAndFeelsComboBox lookAndFeelComboBox;
private JCheckBox rightToLeftCheckBox;
private JCheckBox enabledCheckBox;
private JLabel infoLabel;
private JButton closeButton;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class LafInfo ------------------------------------------------------
static class LafInfo
{
final String name;
final String className;
LafInfo( String name, String className ) {
this.name = name;
this.className = className;
}
@Override
public boolean equals( Object obj ) {
return obj instanceof LafInfo && className.equals( ((LafInfo)obj).className );
}
@Override
public String toString() {
return name;
}
}
}

View File

@@ -15,11 +15,8 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0 5 1"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
add( new FormComponent( "com.formdev.flatlaf.demo.LookAndFeelsComboBox" ) {
name: "lookAndFeelComboBox"
auxiliary() {
"JavaCodeGenerator.typeParameters": "LafInfo"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "lookAndFeelChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf.demo;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import net.miginfocom.swing.*;
/**
@@ -87,6 +89,7 @@ class DemoFrame
TabsPanel tabsPanel = new TabsPanel();
OptionPanePanel optionPanePanel = new OptionPanePanel();
controlBar = new ControlBar();
IJThemesPanel themesPanel = new IJThemesPanel();
//======== this ========
setTitle("FlatLaf Demo");
@@ -205,18 +208,37 @@ class DemoFrame
//======== toolBar1 ========
{
toolBar1.setMargin(new Insets(3, 3, 3, 3));
//---- backButton ----
backButton.setToolTipText("Back");
toolBar1.add(backButton);
//---- forwardButton ----
forwardButton.setToolTipText("Forward");
toolBar1.add(forwardButton);
toolBar1.addSeparator();
//---- cutButton ----
cutButton.setToolTipText("Cut");
toolBar1.add(cutButton);
//---- copyButton ----
copyButton.setToolTipText("Copy");
toolBar1.add(copyButton);
//---- pasteButton ----
pasteButton.setToolTipText("Paste");
toolBar1.add(pasteButton);
toolBar1.addSeparator();
//---- refreshButton ----
refreshButton.setToolTipText("Refresh");
toolBar1.add(refreshButton);
toolBar1.addSeparator();
//---- showToggleButton ----
showToggleButton.setSelected(true);
showToggleButton.setToolTipText("Show Details");
toolBar1.add(showToggleButton);
}
contentPane.add(toolBar1, BorderLayout.NORTH);
@@ -243,6 +265,7 @@ class DemoFrame
}
contentPane.add(contentPanel, BorderLayout.CENTER);
contentPane.add(controlBar, BorderLayout.SOUTH);
contentPane.add(themesPanel, BorderLayout.EAST);
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
@@ -251,20 +274,20 @@ class DemoFrame
buttonGroup1.add(radioButtonMenuItem3);
// JFormDesigner - End of component initialization //GEN-END:initComponents
undoMenuItem.setIcon( new ScaledSVGIcon( "icons/undo.svg" ) );
redoMenuItem.setIcon( new ScaledSVGIcon( "icons/redo.svg" ) );
undoMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/undo.svg" ) );
redoMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/redo.svg" ) );
cutMenuItem.setIcon( new ScaledSVGIcon( "icons/menu-cut.svg" ) );
copyMenuItem.setIcon( new ScaledSVGIcon( "icons/copy.svg" ) );
pasteMenuItem.setIcon( new ScaledSVGIcon( "icons/menu-paste.svg" ) );
cutMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-cut.svg" ) );
copyMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/copy.svg" ) );
pasteMenuItem.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-paste.svg" ) );
backButton.setIcon( new ScaledSVGIcon( "icons/back.svg" ) );
forwardButton.setIcon( new ScaledSVGIcon( "icons/forward.svg" ) );
cutButton.setIcon( new ScaledSVGIcon( "icons/menu-cut.svg" ) );
copyButton.setIcon( new ScaledSVGIcon( "icons/copy.svg" ) );
pasteButton.setIcon( new ScaledSVGIcon( "icons/menu-paste.svg" ) );
refreshButton.setIcon( new ScaledSVGIcon( "icons/refresh.svg" ) );
showToggleButton.setIcon( new ScaledSVGIcon( "icons/show.svg" ) );
backButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/back.svg" ) );
forwardButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/forward.svg" ) );
cutButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-cut.svg" ) );
copyButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/copy.svg" ) );
pasteButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/menu-paste.svg" ) );
refreshButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/refresh.svg" ) );
showToggleButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/show.svg" ) );
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables

View File

@@ -17,27 +17,33 @@ new FormModel {
"margin": new java.awt.Insets( 3, 3, 3, 3 )
add( new FormComponent( "javax.swing.JButton" ) {
name: "backButton"
"toolTipText": "Back"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "forwardButton"
"toolTipText": "Forward"
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator5"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "cutButton"
"toolTipText": "Cut"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "copyButton"
"toolTipText": "Copy"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "pasteButton"
"toolTipText": "Paste"
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator6"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "refreshButton"
"toolTipText": "Refresh"
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
name: "separator7"
@@ -45,6 +51,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "showToggleButton"
"selected": true
"toolTipText": "Show Details"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "North"
@@ -100,6 +107,11 @@ new FormModel {
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "South"
} )
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
name: "themesPanel"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "East"
} )
menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "menuBar1"
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
@@ -213,7 +225,7 @@ new FormModel {
}
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 800, 710 )
"size": new java.awt.Dimension( 935, 710 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonGroup1"

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.demo;
import java.util.prefs.Preferences;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLightLaf;
@@ -32,29 +33,31 @@ public class FlatLafDemo
static Preferences prefs;
public static void main( String[] args ) {
prefs = Preferences.userRoot().node( PREFS_ROOT_PATH );
SwingUtilities.invokeLater( () -> {
prefs = Preferences.userRoot().node( PREFS_ROOT_PATH );
// set look and feel
try {
if( args.length > 0 )
UIManager.setLookAndFeel( args[0] );
else {
String lafClassName = prefs.get( KEY_LAF, FlatLightLaf.class.getName() );
UIManager.setLookAndFeel( lafClassName );
// set look and feel
try {
if( args.length > 0 )
UIManager.setLookAndFeel( args[0] );
else {
String lafClassName = prefs.get( KEY_LAF, FlatLightLaf.class.getName() );
UIManager.setLookAndFeel( lafClassName );
}
} catch( Exception ex ) {
ex.printStackTrace();
// fallback
FlatLightLaf.install();
}
} catch( Exception ex ) {
ex.printStackTrace();
// fallback
FlatLightLaf.install();
}
// create frame
DemoFrame frame = new DemoFrame();
// create frame
DemoFrame frame = new DemoFrame();
// show frame
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
// show frame
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
} );
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.demo;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.MutableComboBoxModel;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
/**
* @author Karl Tauber
*/
public class LookAndFeelsComboBox
extends JComboBox<UIManager.LookAndFeelInfo>
{
private final PropertyChangeListener lafListener = this::lafChanged;
@SuppressWarnings( "unchecked" )
public LookAndFeelsComboBox() {
setRenderer( new BasicComboBoxRenderer() {
@Override
@SuppressWarnings( "rawtypes" )
public Component getListCellRendererComponent( JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus )
{
value = (value != null)
? ((LookAndFeelInfo)value).getName()
: UIManager.getLookAndFeel().getName();
return super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
}
} );
}
public void addLookAndFeel( String name, String className ) {
getMutableModel().addElement( new LookAndFeelInfo( name, className ) );
}
public String getSelectedLookAndFeel() {
Object sel = getSelectedItem();
return (sel instanceof LookAndFeelInfo) ? ((LookAndFeelInfo)sel).getClassName() : null;
}
public void setSelectedLookAndFeel( String className ) {
setSelectedIndex( getIndexOfLookAndFeel( className ) );
}
public void selectedCurrentLookAndFeel() {
setSelectedLookAndFeel( UIManager.getLookAndFeel().getClass().getName() );
}
public void removeLookAndFeel( String className ) {
int index = getIndexOfLookAndFeel( className );
if( index >= 0 )
getMutableModel().removeElementAt( index );
}
public int getIndexOfLookAndFeel( String className ) {
ComboBoxModel<LookAndFeelInfo> model = getModel();
int size = model.getSize();
for( int i = 0; i < size; i++ ) {
if( className.equals( model.getElementAt( i ).getClassName() ) )
return i;
}
return -1;
}
private MutableComboBoxModel<LookAndFeelInfo> getMutableModel() {
return (MutableComboBoxModel<LookAndFeelInfo>) getModel();
}
@Override
public void addNotify() {
super.addNotify();
selectedCurrentLookAndFeel();
UIManager.addPropertyChangeListener( lafListener );
}
@Override
public void removeNotify() {
super.removeNotify();
UIManager.removePropertyChangeListener( lafListener );
}
void lafChanged( PropertyChangeEvent e ) {
if( "lookAndFeel".equals( e.getPropertyName() ) )
selectedCurrentLookAndFeel();
}
}

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