Compare commits

...

86 Commits

Author SHA1 Message Date
Karl Tauber
4d7a6ff331 AnimatedPainter:
- renamed `getValues()` to `getAnimatableValues()`
- renamed `getClientPropertyKey()` to `getAnimationClientPropertyKey()`
- AnimatedPainterSupport improvements
2025-03-13 11:46:14 +01:00
Karl Tauber
23fc3674c9 LineChartPanel:
- support "synchron" charts
- reworked to make code easier to understand/maintain
- added some JavaBean properties to make it configurable in JFormDesigner
- fixes some bugs

removed `FlatAnimatorTest.LineChartPanel` and replaced it with `LineChartPanel`
2025-03-12 18:35:56 +01:00
Karl Tauber
34a7214dd8 LineChartPanel copied from branch smooth-scrolling, commit 44a04cca2c 2025-03-11 19:33:36 +01:00
Karl Tauber
26d7008c04 AnimatedPainter: support individual animation duration, resolution and interpolator depending on value(s) 2021-11-22 22:07:45 +01:00
Karl Tauber
3b489e8e1a AnimatedPainter: support independent animation of multiple values 2021-11-22 22:07:20 +01:00
Karl Tauber
ccbf577f46 AnimatedBorder: demo for labeled material border 2021-11-21 23:20:47 +01:00
Karl Tauber
b903f18130 FlatAnimatorTest:
- support synchronized line chart
- LineChartPanel: added slider to change horizontal scaling
- FlatAnimatedIconTest: added line chart panel
- FlatAnimatedBorderTest: added line chart panel
2021-11-21 12:27:31 +01:00
Karl Tauber
9523c89c51 FlatAnimatorTest: added standard-easing and line chart
(line chart copied from `FlatSmoothScrollingTest.LineChartPanel` in branch `smooth-scrolling` commit 331ab06b0087d3a70f7c131368b88e5e7c92102f)
2021-11-20 15:14:03 +01:00
Karl Tauber
aa6fb2fcce AnimatedPainter added (refactored from AnimatedBorder and AnimatedIcon)
(checked API compatibility of AnimatedIcon with gradle task sigtestCheck)
2021-11-20 11:13:06 +01:00
Karl Tauber
e4fa2e28ea AnimatedBorder:
- support repainting only necessary region while animating
- use AbstractBorder in test app and fixed insets
2021-11-19 18:02:54 +01:00
Karl Tauber
b2245e2246 AnimatedBorder added (for future animations) (issue #66) 2021-11-19 18:02:54 +01:00
Karl Tauber
13a6b92e47 Merge PR #429: Window title bar improvements (Windows 10/11 only) 2021-11-19 18:01:34 +01:00
Karl Tauber
9ba008002b Merge PR #396: Typography 2021-11-19 14:57:36 +01:00
Karl Tauber
8914cf78a1 Typography: Theme Editor: added h1.regular, h2.regular and h3.regular to preview 2021-11-19 11:37:46 +01:00
Karl Tauber
d360375b4f Typography:
- use semibold for `h1`, `h2` and `h3`
- added `h1.regular`, `h2.regular` and `h3.regular`
2021-11-19 11:13:32 +01:00
Karl Tauber
1caab194af TabbedPane:
- added styling support for properties added in PR #343
- updated change log
2021-11-18 18:01:07 +01:00
Karl Tauber
31754eba5d Merge PR #343: New tabbed pane active tab border painting style 2021-11-18 17:27:23 +01:00
Karl Tauber
3cfa16b8b7 TabbedPane: completed review of PR #343 (active tab border painting style)
- replaced `activeTabBorder` with `tabType`
- added `TabbedPane.cardTabSelectionHeight`
2021-11-18 16:47:21 +01:00
Karl Tauber
f80d2bacf4 Typography: use light and semibold in FlatTypographyTest 2021-11-17 19:39:51 +01:00
Karl Tauber
5df3717d94 Typography: added "Material Design 3" to FlatTypographyTest 2021-11-17 00:25:38 +01:00
Karl Tauber
68897f04a2 Typography: removed thin font/style because
- there is no thin font available on Windows
- previously used "Segoe UI Light" for `thin.font` and "Segoe UI Semilight" for `light.font`, but "Segoe UI Semilight" is too close to regular font so that it is better to use "Segoe UI Light" for `light.font` and drop `thin.font`
- the usefulness of having thin font in addition to light font is low

on macOS use "HelveticaNeue-Thin" for `light.font` (instead of "HelveticaNeue-Light")
2021-11-17 00:23:54 +01:00
Karl Tauber
4cb6aeae36 OptionPane: hide window icon by default; can be shown via UI default OptionPane.showIcon = true (issue #416) 2021-11-16 21:20:01 +01:00
Karl Tauber
0a765a35bf Merge remote-tracking branch 'origin/release-1.6.4' into main
# Conflicts:
#	CHANGELOG.md
2021-11-16 15:52:15 +01:00
Karl Tauber
6c0b122fbc release 1.6.4 2021-11-16 15:37:19 +01:00
Karl Tauber
4da2bd90cb ComboBox: fixed regression in FlatLaf 1.6.3 that makes selected item invisible in popup list if DefaultListCellRenderer is used as renderer (issue #426) 2021-11-16 15:36:14 +01:00
Karl Tauber
f0275192c6 Demo: added "Options > Show window title bar icon" to menu 2021-11-16 11:08:04 +01:00
Karl Tauber
df905a1d73 Demo: made hint popups look nicer (balloon with arrow and shadow) 2021-11-16 10:49:53 +01:00
Karl Tauber
ad8ad06f44 Window decorations: FlatTitlePane: fixed size of hit test spot on right side of menu bar if it contains a glue and a stretching component (e.g. progress bar)
removed HIT_TEST_SPOT_GROW because it is no longer necessary since commit 54e6cefa67
2021-11-15 20:43:25 +01:00
Karl Tauber
d6b9e2df62 RootPane: give the root pane useful background, foreground and font
(fixes wrong background in title bar and menu bar when switching from Nimbus to FlatLaf)
2021-11-15 16:58:44 +01:00
Karl Tauber
5c9b36556f Window decorations: FlatMenuBarBorder: use same conditions for bottom separator painting as for menu bar background painting (fixes/improves previous commit) 2021-11-15 01:00:10 +01:00
Karl Tauber
80a8348a99 Window decorations:
- enabled `TitlePane.unifiedBackground` by default (because seems to be standard on Windows 11)
- no longer paint a bottom separator for the menu bar (if unified background is enabled)
2021-11-15 01:00:10 +01:00
Karl Tauber
005c9f471e Window decorations:
- option to hide window icon (via client property or UI default)
- no longer show the Java "duke/cup" icon if no window icon image is set (issue #416)
2021-11-15 01:00:10 +01:00
Karl Tauber
b40532a830 UI defaults inspector: fixed color value renderer for some 3rd party Lafs 2021-11-15 00:59:13 +01:00
Karl Tauber
fc7a4408e9 FlatWindowDecorationsTest: update decoration style radio buttons from window 2021-11-15 00:35:19 +01:00
Karl Tauber
93b5f0081d Window decorations: reduced number of FlatTitlePane.updateNativeTitleBarHeightAndHitTestSpots() invokations 2021-11-15 00:24:56 +01:00
Karl Tauber
ce049ea3ee Window decorations: fixed exception in SwingUtilities.convertPoint() when doing new JDialog( (Window) null )
regression since commits 54e6cefa67 and
fb37be5734
2021-11-15 00:20:04 +01:00
Karl Tauber
fcc39b2db5 Merge branch 'release-1.6.3' into main
# Conflicts:
#	CHANGELOG.md
#	flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
2021-11-14 23:23:29 +01:00
Karl Tauber
cb70fb4e82 GitHub Actions: run Gradle wrapper validation only once
(to reduce risk of connection timeout)
2021-11-14 23:06:23 +01:00
Karl Tauber
2593a43d72 release 1.6.3 2021-11-14 22:42:10 +01:00
Karl Tauber
e44ff5b72a Tree: Fixed editing cell issue with custom cell renderer and cell editor that use same component for rendering and editing (fixes #385)
(cherry picked from commit 161ee090a8)
2021-11-14 22:34:28 +01:00
Karl Tauber
22cb1b50a6 ComboBox (not editable): fixed regression that may display text in non-editable combo boxes in bold (issue #423)
fixes commits 02f3239669
and 0b6df8be1c
2021-11-14 22:11:19 +01:00
Karl Tauber
a42c413705 Testing: added modular app 2021-11-12 17:28:04 +01:00
Karl Tauber
d59d38dc7c added method FlatLaf.registerCustomDefaultsSource(URL packageUrl) for JPMS (issue #325) 2021-11-12 17:00:26 +01:00
Karl Tauber
77582be7fd Demo: fixed exception if hint is shown and switching to a non-FlatLaf L&F 2021-11-12 11:39:33 +01:00
Karl Tauber
0cb50355b7 added hint to FlatLaf.registerCustomDefaultsSource() javadoc that the package must be opened in module-info.java (issue #325) 2021-11-12 11:16:53 +01:00
Karl Tauber
a2d66e91ff Extras: FlatSVGIcon and FlatSVGUtils: use soft cache for SVG diagrams to allow freeing memory if no longer used 2021-11-12 10:49:02 +01:00
Karl Tauber
ccdb981917 refactored private class UIDefaultsLoader.Cache to public class SoftCache and implement the Map interface 2021-11-12 10:12:34 +01:00
Karl Tauber
d80b581ace Extras: FlatSVGUtils: support loading SVG from URL (for JPMS) (issue #325) 2021-11-12 09:28:24 +01:00
Karl Tauber
53efb6711d Extras: FlatSVGIcon: support loading SVG from URL (for JPMS), URI, File or InputStream (issues #419 and #325)
also improved error handling and javadoc
2021-11-12 09:27:04 +01:00
Karl Tauber
1de6e875f9 Merge branch 'release-1.6.2' into main
# Conflicts:
#	CHANGELOG.md
2021-11-11 13:05:33 +01:00
Karl Tauber
95a15c3cf8 release 1.6.2 2021-11-11 12:45:50 +01:00
Karl Tauber
ab320684f5 Native window decorations: fixed layout loop (issue #420)
(cherry picked from commit d3355eda65)
2021-11-11 12:35:51 +01:00
Karl Tauber
a284b69a1e FileChooser: workaround for crash on Windows with Java 17 32bit (issue #403)
(cherry picked from commits 44d8545c09 and 33b25c1129)
2021-11-11 12:35:16 +01:00
Karl Tauber
b590f41254 Linux: fixed NPE when using java.awt.TrayIcon (issue #405)
(cherry picked from commit 16a769ea61)
2021-11-11 12:31:14 +01:00
Karl Tauber
a97076ead5 ComboBox: fix NPE in CellPaddingBorder.install() (issue #408)
(cherry picked from commit d48b98f582)
2021-11-11 12:30:10 +01:00
Karl Tauber
0b6df8be1c ComboBox (not editable): fixed background painted outside of border if round edges are enabled (similar to issue #382; regression since fixing #330 in FlatLaf 1.4)
(cherry picked from commit 02f3239669)
2021-11-11 12:26:09 +01:00
Karl Tauber
150bab0b57 Table: do not select text in cell editor when it gets focus (when JTable.surrendersFocusOnKeystroke is true) and TextComponent.selectAllOnFocusPolicy is once (the default) or always (issue #395)
(cherry picked from commit f8b9f4c1fa)
2021-11-11 12:19:58 +01:00
Karl Tauber
d3355eda65 Native window decorations: fixed layout loop (issue #420) 2021-11-11 11:49:39 +01:00
Karl Tauber
fbf10e553d Native window decorations: updated DLLs (issues #397 and #407)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/1440605430
2021-11-09 18:35:55 +01:00
Karl Tauber
fb37be5734 Native window decorations: show (system) tooltips for minimize/maximize/close buttons on Windows 10 and for minimize/close buttons on Windows 11 (issues #397 and #407) 2021-11-09 18:21:17 +01:00
Karl Tauber
54e6cefa67 Native window decorations: show Windows 11 snap layouts menu when hovering the mouse over the maximize button (issues #397 and #407) 2021-11-09 15:36:21 +01:00
Karl Tauber
33b25c1129 FileChooser: updated CHANGELOG.md for previous commit (issue #403) 2021-11-06 00:34:06 +01:00
Karl Tauber
44d8545c09 FileChooser: workaround for crash on Windows with Java 17 32bit (issue #403) 2021-11-06 00:03:17 +01:00
Karl Tauber
7a2808243c Typography: Demo: make typography/fonts visible in "screen shot" mode; hide password fields and labels 2021-11-04 14:54:01 +01:00
Karl Tauber
1be84de26b Typography: added thin/light/semibold fonts to demo 2021-11-04 12:48:46 +01:00
Karl Tauber
78e37f7ab4 Typography: since there is no thin font available on Windows, use "Segoe UI Light" (which looks thin IMHO) for thin.font and "Segoe UI Semilight" for light.font 2021-11-04 12:16:11 +01:00
Karl Tauber
f742f83834 Typography: added thin font/style 2021-10-26 13:16:52 +02:00
Karl Tauber
e6e4e53a73 Typography: added light and semibold font/style 2021-10-25 13:27:40 +02:00
Karl Tauber
7c594ba7a9 Typography: UIDefaultsDump: dumps updated on macOS Big Sur 2021-10-25 13:06:34 +02:00
Karl Tauber
d34619824c Typography: support deriving font from any other font (was always from defaultFont) (issue #384) 2021-10-17 16:54:05 +02:00
Karl Tauber
80297f113f UIDefaultsLoader: detect string values, that start and end with '"', after determining value type from key to allow font values like "Roboto Mono" 2021-10-17 14:59:18 +02:00
Karl Tauber
80f51bfe1e Theme Editor: fixed StackOverflowError when setting "defaultFont" to non-font value (e.g. defaultFont = #fff) 2021-10-17 14:43:12 +02:00
Karl Tauber
587f431ef4 Typography: added FlatFontsTest to quickly view all available fonts on the current system 2021-10-16 18:56:53 +02:00
Karl Tauber
e560f9cbd6 Typography: added typography/fonts preview to theme editor 2021-10-13 15:47:55 +02:00
Karl Tauber
80235d53f4 Typography: added FlatTypographyTest, which was used to compare various typography systems 2021-10-13 14:29:57 +02:00
Karl Tauber
892b9a732e Typography: added typography/fonts to demo 2021-10-13 14:17:40 +02:00
Karl Tauber
d8a0a015e4 Typography: Extras: add labelType property to FlatLabel 2021-10-13 13:48:03 +02:00
Karl Tauber
e60e3b9fae Typography: added monospaced font/style 2021-10-13 13:46:43 +02:00
Karl Tauber
6715f01b8c Typography: use typography styles in demo, theme editor, etc 2021-10-13 11:48:28 +02:00
Karl Tauber
465af9bc41 Typography: added fonts/styles for headings and various text sizes 2021-10-13 10:53:37 +02:00
Karl Tauber
435cf05f9f TabbedPane: reviewed PR #343 (active tab border painting style)
- moved focus listener to class `Handler`
- instead of repainting content border in `repaintContentBorder()`, increase size of repaint region in `repaintTab()` to include part of content border
- simplified `paintContentBorder()` by using `getTabBounds()` and `Rectangle2D.intersect()` to compute gap rectangle
- slightly make code smaller
- minor formatting, renaming, ...
2021-07-12 13:37:24 +02:00
DUDSS
943e211cf1 TabbedPane: ActiveTabBorder: Added handling for thick content separators and 0 height selection indicator. Fixed scaling issues (presumably). 2021-06-20 14:01:01 +02:00
DUDSS
ad0a13004e TabbedPane: Changed name in demo and added separator repaint on focus gained. 2021-06-20 02:56:19 +02:00
DUDSS
04bb6a5275 TabbedPane: Adjustements 2021-06-20 02:14:31 +02:00
DUDSS
d3c917eac1 TabbedPane: Added the new style option to the flatlaf demo. 2021-06-20 02:14:27 +02:00
DUDSS
4c13271a5b TabbedPane: Added activeTabBorder option to tabbed panes. Changes the active tab design to more closely resemble classic tabbed pane designs. 2021-06-20 02:13:54 +02:00
96 changed files with 8626 additions and 809 deletions

View File

@@ -34,6 +34,7 @@ jobs:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
if: matrix.java == '1.8'
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v1

View File

@@ -12,6 +12,22 @@ FlatLaf Change Log
- Style classes allow defining style rules at a single place (in UI defaults)
and use them in any component. (PR #388)\
E.g.: `mySlider.putClientProperty( "FlatLaf.styleClass", "myclass" );`
- Typography defines several font styles for headers and various text sizes,
which makes it easy to use consistent font styles across the application. (PR
#396)
- Native window decorations (Windows 10/11 only):
- Unified backgrounds for window title bar is now enabled by default (window
title bar has now same background color as window content). Bottom separator
for menu bars is no longer painted (if unified background is enabled).
- Show Windows 11 snap layouts menu when hovering the mouse over the maximize
button. (issues #397 and #407)
- Possibility to hide window title bar icon (for single window set client
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
value `TitlePane.showIcon` to `false`).
- OptionPane: Hide window title bar icon by default. Can be be made visibly by
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
- No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416)
- TextField, FormattedTextField and PasswordField: Support leading and trailing
icons (set client property `JTextField.leadingIcon` or
`JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368)
@@ -44,25 +60,58 @@ FlatLaf Change Log
incompatible changes in FlatLaf properties files.
- Slider: Support specifying width of thumb border (see UI value
`Slider.thumbBorderWidth`).
- TabbedPane: Optionally paint selected tab as card. (PR #343)
- Added more color functions to class `ColorFunctions` for easy use in
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
`tint()`, `shade()` and `luma()`.
- Support defining fonts in FlatLaf properties files. (issue #384)
- Extras: Added class `FlatDesktop` for easy integration into macOS screen menu
(About, Preferences and Quit) when using Java 8.
- Added method `FlatLaf.registerCustomDefaultsSource(URL packageUrl)` for JPMS.
(issue #325)
- Extras:
- Added class `FlatDesktop` for easy integration into macOS screen menu
(About, Preferences and Quit) when using Java 8.
- `FlatSVGIcon`: Support loading SVG from `URL` (for JPMS), `URI`, `File` or
`InputStream`. (issues #419 and #325)
- `FlatSVGUtils`: Support loading SVG from `URL` (for JPMS). (issue #325)
## 1.6.4
#### Fixed bugs
- ComboBox: Fixed regression in FlatLaf 1.6.3 that makes selected item invisible
in popup list if `DefaultListCellRenderer` is used as renderer. If using
default renderer, it works. (issue #426)
## 1.6.3
#### Fixed bugs
- ComboBox (not editable): Fixed regression in FlatLaf 1.6.2 that may display
text in non-editable combo boxes in bold. (issue #423)
- Tree: Fixed editing cell issue with custom cell renderer and cell editor that
use same component for rendering and editing. (issue #385)
## 1.6.2
#### Fixed bugs
- ComboBox (not editable): Fixed background painted outside of border if round
edges are enabled (client property `JComponent.roundRect` is `true`). (similar
to issue #382; regression since fixing #330 in FlatLaf 1.4)
- Tree: Fixed editing cell issue with custom cell renderer and cell editor that
use same component for rendering and editing. (issue #385)
- ComboBox: Fixed `NullPointerException`, which may occur under special
circumstances. (issue #408)
- Table: Do not select text in cell editor when it gets focus (when
`JTable.surrendersFocusOnKeystroke` is `true`) and
`TextComponent.selectAllOnFocusPolicy` is `once` (the default) or `always`.
(issue #395)
- Linux: Fixed NPE when using `java.awt.TrayIcon`. (issue #405)
- FileChooser: Workaround for crash on Windows with Java 17 32-bit (disabled
Windows icons). Java 17 64-bit is not affected. (issue #403)
- Native window decorations: Fixed layout loop, which may occur under special
circumstances and slows down the application. (issue #420)
## 1.6.1

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
val releaseVersion = "1.6.1"
val releaseVersion = "1.6.4"
val developmentVersion = "2.0-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion

View File

@@ -331,6 +331,24 @@ public interface FlatClientProperties
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Specifies whether the window icon should be shown in the window title bar
* (requires enabled window decorations).
* <p>
* Setting this shows/hides the windows icon
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* This client property has higher priority than UI default {@code TitlePane.showIcon}.
* <p>
* (requires Window 10)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 2
*/
String TITLE_BAR_SHOW_ICON = "JRootPane.titleBarShowIcon";
/**
* Background color of window title bar (requires enabled window decorations).
* <p>
@@ -375,6 +393,35 @@ public interface FlatClientProperties
//---- JTabbedPane --------------------------------------------------------
/**
* Specifies type of the selected tab.
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #TABBED_PANE_TAB_TYPE_UNDERLINED} or
* {@link #TABBED_PANE_TAB_TYPE_CARD}
*
* @since 2
*/
String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType";
/**
* Paint the selected tab underlined.
*
* @see #TABBED_PANE_TAB_TYPE
* @since 2
*/
String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined";
/**
* Paint the selected tab as card.
*
* @see #TABBED_PANE_TAB_TYPE
* @since 2
*/
String TABBED_PANE_TAB_TYPE_CARD = "card";
/**
* Specifies whether separators are shown between tabs.
* <p>

View File

@@ -31,6 +31,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@@ -392,6 +393,7 @@ public abstract class FlatLaf
"EditorPane.inactiveBackground",
"FormattedTextField.disabledBackground",
"PasswordField.disabledBackground",
"RootPane.background",
"Spinner.disabledBackground",
"TextArea.disabledBackground",
"TextArea.inactiveBackground",
@@ -410,7 +412,8 @@ public abstract class FlatLaf
"Spinner.disabledForeground",
"ToggleButton.disabledText" );
putDefaults( defaults, defaults.getColor( "textText" ),
"DesktopIcon.foreground" );
"DesktopIcon.foreground",
"RootPane.foreground" );
initFonts( defaults );
initIconColors( defaults, isDark() );
@@ -420,7 +423,7 @@ public abstract class FlatLaf
// (using defaults.remove() to avoid that lazy value is resolved and icon loaded here)
Object icon = defaults.remove( "InternalFrame.icon" );
defaults.put( "InternalFrame.icon", icon );
defaults.put( "TitlePane.icon", icon );
defaults.put( "TitlePane.icon", icon ); // no longer used, but keep for compatibility
// get addons and sort them by priority
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
@@ -533,13 +536,16 @@ public abstract class FlatLaf
// use active value for all fonts to allow changing fonts in all components with:
// UIManager.put( "defaultFont", myFont );
// (this is similar as in Nimbus L&F)
Object activeFont = new ActiveFont( null, -1, 0, 0, 0, 0 );
Object activeFont = new ActiveFont( null, null, -1, 0, 0, 0, 0 );
// override fonts
for( Object key : defaults.keySet() ) {
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
defaults.put( key, activeFont );
}
// add fonts that are not set in BasicLookAndFeel
defaults.put( "RootPane.font", activeFont );
}
private void initDefaultFont( UIDefaults defaults ) {
@@ -617,7 +623,7 @@ public abstract class FlatLaf
/** @since 1.1 */
public static ActiveValue createActiveFontValue( float scaleFactor ) {
return new ActiveFont( null, -1, 0, 0, 0, scaleFactor );
return new ActiveFont( null, null, -1, 0, 0, 0, scaleFactor );
}
/**
@@ -751,6 +757,9 @@ public abstract class FlatLaf
* and can therefore override all UI defaults.
* <p>
* Invoke this method before setting the look and feel.
* <p>
* If using Java modules, the package must be opened in {@code module-info.java}.
* Otherwise use {@link #registerCustomDefaultsSource(URL)}.
*
* @param packageName a package name (e.g. "com.myapp.resources")
*/
@@ -792,6 +801,32 @@ public abstract class FlatLaf
}
}
/**
* Registers a package where FlatLaf searches for properties files with custom UI defaults.
* <p>
* See {@link #registerCustomDefaultsSource(String)} for details.
* <p>
* This method is useful if using Java modules and the package containing the properties files
* is not opened in {@code module-info.java}.
* E.g. {@code FlatLaf.registerCustomDefaultsSource( MyApp.class.getResource( "/com/myapp/themes/" ) )}.
*
* @param packageUrl a package URL
* @since 2
*/
public static void registerCustomDefaultsSource( URL packageUrl ) {
if( customDefaultsSources == null )
customDefaultsSources = new ArrayList<>();
customDefaultsSources.add( packageUrl );
}
/** @since 2 */
public static void unregisterCustomDefaultsSource( URL packageUrl ) {
if( customDefaultsSources == null )
return;
customDefaultsSources.remove( packageUrl );
}
/**
* Registers a folder where FlatLaf searches for properties files with custom UI defaults.
* <p>
@@ -1191,6 +1226,7 @@ public abstract class FlatLaf
static class ActiveFont
implements ActiveValue
{
private final String baseFontKey;
private final List<String> families;
private final int style;
private final int styleChange;
@@ -1200,7 +1236,9 @@ public abstract class FlatLaf
// cache (scaled/derived) font
private FontUIResource font;
private Font lastDefaultFont;
private Font lastBaseFont;
private boolean inCreateValue;
/**
* @param families list of font families, or {@code null}
@@ -1211,9 +1249,10 @@ public abstract class FlatLaf
* @param relativeSize added to size of base font, or {@code 0}
* @param scaleSize multiply size of base font, or {@code 0}
*/
ActiveFont( List<String> families, int style, int styleChange,
ActiveFont( String baseFontKey, List<String> families, int style, int styleChange,
int absoluteSize, int relativeSize, float scaleSize )
{
this.baseFontKey = baseFontKey;
this.families = families;
this.style = style;
this.styleChange = styleChange;
@@ -1224,16 +1263,30 @@ public abstract class FlatLaf
@Override
public Object createValue( UIDefaults table ) {
Font defaultFont = UIManager.getFont( "defaultFont" );
if( inCreateValue )
throw new IllegalStateException( "FlatLaf: endless recursion in font" );
// fallback (to avoid NPE in case that this is used in another Laf)
if( defaultFont == null )
defaultFont = UIManager.getFont( "Label.font" );
Font baseFont = null;
if( lastDefaultFont != defaultFont ) {
lastDefaultFont = defaultFont;
inCreateValue = true;
try {
if( baseFontKey != null )
baseFont = (Font) UIDefaultsLoader.lazyUIManagerGet( baseFontKey );
font = derive( defaultFont, fontSize -> UIScale.scale( fontSize ) );
if( baseFont == null )
baseFont = UIManager.getFont( "defaultFont" );
// fallback (to avoid NPE in case that this is used in another Laf)
if( baseFont == null )
baseFont = UIManager.getFont( "Label.font" );
} finally {
inCreateValue = false;
}
if( lastBaseFont != baseFont ) {
lastBaseFont = baseFont;
font = derive( baseFont, fontSize -> UIScale.scale( fontSize ) );
}
return font;

View File

@@ -22,13 +22,12 @@ import java.awt.Font;
import java.awt.Insets;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -56,6 +55,7 @@ import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.HSLColor;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SoftCache;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -84,7 +84,7 @@ class UIDefaultsLoader
private static int parseColorDepth;
private static final Cache<String, Object> fontCache = new Cache<>();
private static final SoftCache<String, Object> fontCache = new SoftCache<>();
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
Properties additionalDefaults, boolean dark, UIDefaults defaults )
@@ -158,6 +158,18 @@ class UIDefaultsLoader
properties.load( in );
}
}
} else if( source instanceof URL ) {
// load from package URL
URL packageUrl = (URL) source;
for( Class<?> lafClass : lafClasses ) {
URL propertiesUrl = new URL( packageUrl + lafClass.getSimpleName() + ".properties" );
try( InputStream in = propertiesUrl.openStream() ) {
properties.load( in );
} catch( FileNotFoundException ex ) {
// ignore
}
}
} else if( source instanceof File ) {
// load from folder
File folder = (File) source;
@@ -429,10 +441,7 @@ class UIDefaultsLoader
// check whether value type is specified in the value
if( value.startsWith( "#" ) )
valueType = ValueType.COLOR;
else if( value.startsWith( "\"" ) && value.indexOf( '"', 1 ) == value.length() - 1 ) {
valueType = ValueType.STRING;
value = value.substring( 1, value.length() - 1 );
} else if( value.startsWith( TYPE_PREFIX ) ) {
else if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
@@ -524,6 +533,12 @@ class UIDefaultsLoader
case GRAYFILTER: return parseGrayFilter( value );
case UNKNOWN:
default:
// string
if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
resultValueType[0] = ValueType.STRING;
return value.substring( 1, value.length() - 1 );
}
// colors
Object color = parseColorOrFunction( value, resolver, false );
if( color != null ) {
@@ -1039,7 +1054,7 @@ class UIDefaultsLoader
}
/**
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]]
* Syntax: [normal] [bold|+bold|-bold] [italic|+italic|-italic] [<size>|+<incr>|-<decr>|<percent>%] [family[, family]] [$baseFontKey]
*/
private static Object parseFont( String value ) {
Object font = fontCache.get( value );
@@ -1052,6 +1067,7 @@ class UIDefaultsLoader
int relativeSize = 0;
float scaleSize = 0;
List<String> families = null;
String baseFontKey = null;
// use StreamTokenizer to split string because it supports quoted strings
StreamTokenizer st = new StreamTokenizer( new StringReader( value ) );
@@ -1101,6 +1117,12 @@ class UIDefaultsLoader
scaleSize = parseInteger( param.substring( 0, param.length() - 1 ), true ) / 100f;
else
absoluteSize = parseInteger( param, true );
} else if( firstChar == '$' ) {
// reference to base font
if( baseFontKey != null )
throw new IllegalArgumentException( "baseFontKey specified more than once in '" + value + "'" );
baseFontKey = param.substring( 1 );
} else {
// font family
if( families == null )
@@ -1127,7 +1149,7 @@ class UIDefaultsLoader
throw new IllegalArgumentException( "can not use '+italic' and '-italic' in '" + value + "'" );
}
font = new FlatLaf.ActiveFont( families, style, styleChange, absoluteSize, relativeSize, scaleSize );
font = new FlatLaf.ActiveFont( baseFontKey, families, style, styleChange, absoluteSize, relativeSize, scaleSize );
fontCache.put( value, font );
return font;
}
@@ -1280,7 +1302,7 @@ class UIDefaultsLoader
* For use in LazyValue to get value for given key from UIManager and report error
* if not found. If key is prefixed by '?', then no error is reported.
*/
private static Object lazyUIManagerGet( String uiKey ) {
static Object lazyUIManagerGet( String uiKey ) {
boolean optional = false;
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
@@ -1296,43 +1318,4 @@ class UIDefaultsLoader
private static void throwMissingParametersException( String value ) {
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
}
//---- class Cache --------------------------------------------------------
private static class Cache<K,V>
{
private final Map<K, CacheReference<K,V>> map = new HashMap<>();
private final ReferenceQueue<V> queue = new ReferenceQueue<>();
V get( K key ) {
expungeStaleEntries();
CacheReference<K,V> ref = map.get( key );
return (ref != null) ? ref.get() : null;
}
void put( K key, V value ) {
expungeStaleEntries();
map.put( key, new CacheReference<>( key, value, queue ) );
}
@SuppressWarnings( "unchecked" )
void expungeStaleEntries() {
Reference<? extends V> reference;
while( (reference = queue.poll()) != null )
map.remove( ((CacheReference<K,V>)reference).key );
}
//---- class CacheReference ----
private static class CacheReference<K,V>
extends SoftReference<V>
{
final K key;
public CacheReference( K key, V value, ReferenceQueue<? super V> queue ) {
super( value, queue );
this.key = key;
}
}
}
}

View File

@@ -69,7 +69,13 @@ public abstract class FlatAbstractIcon
}
}
protected abstract void paintIcon( Component c, Graphics2D g2 );
/**
* Paint the icon at {@code [0,0]} location.
* <p>
* The given graphics context is scaled.
* Use unscaled coordinates, width and height for painting.
*/
protected abstract void paintIcon( Component c, Graphics2D g );
@Override
public int getIconWidth() {

View File

@@ -21,6 +21,7 @@ import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import com.formdev.flatlaf.util.AnimatedIcon;
import com.formdev.flatlaf.util.AnimatedPainter;
/**
* Base class for animated icons that scales width and height, creates and initializes
@@ -30,7 +31,7 @@ import com.formdev.flatlaf.util.AnimatedIcon;
* <p>
* This class does not store any state information (needed for animation) in its instance.
* Instead a client property is set on the painted component.
* This makes it possible to use a share icon instance for multiple components.
* This makes it possible to use a shared icon instance for multiple components.
*
* @author Karl Tauber
*/
@@ -45,11 +46,34 @@ public abstract class FlatAnimatedIcon
@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
super.paintIcon( c, g, x, y );
AnimatedIcon.AnimationSupport.saveIconLocation( this, c, x, y );
AnimatedPainter.saveRepaintLocation( this, c, x, y );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
AnimatedIcon.AnimationSupport.paintIcon( this, c, g, 0, 0 );
paintWithAnimation( c, g, 0, 0, getIconWidth(), getIconHeight() );
}
/**
* Delegates painting to {@link #paintIconAnimated(Component, Graphics2D, float[])}.
* Ignores the given bounds because {@code [x,y]} are always {@code [0,0]} and
* {@code [width,height]} are scaled, but painting code should use unscaled width
* and height because given graphics context is scaled.
*
* @since 2
*/
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
paintIconAnimated( c, g, animatedValues );
}
/**
* Paint the icon at {@code 0,0} location.
* <p>
* The given graphics context is scaled.
* Use unscaled coordinates, width and height for painting.
*
* @since 2
*/
protected abstract void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues );
}

View File

@@ -575,22 +575,6 @@ public class FlatComboBoxUI
@Override
@SuppressWarnings( "unchecked" )
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
// apply clipping using rounded rectangle to avoid that renderer paints
// outside of border if combobox uses larger arc for edges
// (e.g. FlatClientProperties.COMPONENT_ROUND_RECT is true)
FlatBorder border = FlatUIUtils.getOutsideFlatBorder( comboBox );
if( border != null ) {
int clipArc = border.getArc( comboBox ) - (border.getLineWidth( comboBox ) * 2);
if( clipArc > 0 ) {
int x = bounds.x;
int width = bounds.width + bounds.height;
if( !comboBox.getComponentOrientation().isLeftToRight() )
x -= bounds.height;
((Graphics2D)g).clip( FlatUIUtils.createComponentRectangle(
x, bounds.y, width, bounds.height, scale( (float) clipArc ) ) );
}
}
paddingBorder.uninstall();
ListCellRenderer<Object> renderer = comboBox.getRenderer();
@@ -604,11 +588,20 @@ public class FlatComboBoxUI
c.setBackground( getBackground( enabled ) );
c.setForeground( getForeground( enabled ) );
// make renderer component temporary non-opaque to avoid that renderer paints
// background outside of border if combobox uses larger arc for edges
// (e.g. FlatClientProperties.COMPONENT_ROUND_RECT is true)
if( c instanceof JComponent )
((JComponent)c).setOpaque( false );
boolean shouldValidate = (c instanceof JPanel);
paddingBorder.install( c );
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
paddingBorder.uninstall();
if( c instanceof JComponent )
((JComponent)c).setOpaque( true );
}
@Override

View File

@@ -262,12 +262,20 @@ public class FlatFileChooserUI
@Override
public FileView getFileView( JFileChooser fc ) {
return fileView;
return doNotUseSystemIcons() ? super.getFileView( fc ) : fileView;
}
@Override
public void clearIconCache() {
fileView.clearIconCache();
if( doNotUseSystemIcons() )
super.clearIconCache();
else
fileView.clearIconCache();
}
private boolean doNotUseSystemIcons() {
// Java 17 32bit craches on Windows when using system icons
return SystemInfo.isWindows && SystemInfo.isJava_17_orLater && !SystemInfo.isX86_64;
}
//---- class FlatFileView -------------------------------------------------

View File

@@ -53,6 +53,9 @@ public class FlatMenuBarBorder
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( !showBottomSeparator( c ) )
return;
float lineHeight = scale( (float) 1 );
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
}
@@ -68,4 +71,9 @@ public class FlatMenuBarBorder
insets.right = scale( margin.right );
return insets;
}
/** @since 2 */
protected boolean showBottomSeparator( Component c ) {
return !FlatMenuBarUI.useUnifiedBackground( c );
}
}

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Window;
@@ -189,10 +190,8 @@ public class FlatMenuBarUI
return background;
// use parent background for unified title pane
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
if( UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() ) )
background = FlatUIUtils.getParentBackground( c );
if( useUnifiedBackground( c ) )
background = FlatUIUtils.getParentBackground( c );
// paint background in full screen mode
if( FlatUIUtils.isFullScreen( rootPane ) )
@@ -202,6 +201,22 @@ public class FlatMenuBarUI
return FlatRootPaneUI.isMenuBarEmbedded( rootPane ) ? null : background;
}
/**@since 2 */
static boolean useUnifiedBackground( Component c ) {
// check whether:
// - TitlePane.unifiedBackground is true and
// - menu bar is the "main" menu bar and
// - window has custom decorations enabled
JRootPane rootPane;
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
return UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
(rootPane = SwingUtilities.getRootPane( c )) != null &&
rootPane.getParent() instanceof Window &&
rootPane.getJMenuBar() == c &&
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() );
}
//---- class TakeFocus ----------------------------------------------------
/**

View File

@@ -235,7 +235,8 @@ public class FlatNativeWindowBorder
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds )
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
@@ -245,9 +246,8 @@ public class FlatNativeWindowBorder
if( !isSupported() )
return;
nativeProvider.setTitleBarHeight( window, titleBarHeight );
nativeProvider.setTitleBarHitTestSpots( window, hitTestSpots );
nativeProvider.setTitleBarAppIconBounds( window, appIconBounds );
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots,
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
}
static boolean showWindow( Window window, int cmd ) {
@@ -268,7 +268,7 @@ public class FlatNativeWindowBorder
try {
/*
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.windows.FlatWindowsNativeWindowBorder" );
Method m = cls.getMethod( "getInstance" );
java.lang.reflect.Method m = cls.getMethod( "getInstance" );
setNativeProvider( (Provider) m.invoke( null ) );
*/
setNativeProvider( FlatWindowsNativeWindowBorder.getInstance() );
@@ -292,9 +292,9 @@ public class FlatNativeWindowBorder
{
boolean hasCustomDecoration( Window window );
void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void setTitleBarHeight( Window window, int titleBarHeight );
void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots );
void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds );
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds );
// commands for showWindow(); values must match Win32 API
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow

View File

@@ -22,18 +22,22 @@ import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.beans.PropertyChangeListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
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.FlatClientProperties;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -79,6 +83,7 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatOptionPaneUI -->
*
* @uiDefault OptionPane.showIcon boolean
* @uiDefault OptionPane.iconMessageGap int
* @uiDefault OptionPane.messagePadding int
* @uiDefault OptionPane.maxCharactersPerLine int
@@ -88,6 +93,7 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatOptionPaneUI
extends BasicOptionPaneUI
{
/** @since 2 */ protected boolean showIcon;
protected int iconMessageGap;
protected int messagePadding;
protected int maxCharactersPerLine;
@@ -102,6 +108,7 @@ public class FlatOptionPaneUI
protected void installDefaults() {
super.installDefaults();
showIcon = UIManager.getBoolean( "OptionPane.showIcon" );
iconMessageGap = UIManager.getInt( "OptionPane.iconMessageGap" );
messagePadding = UIManager.getInt( "OptionPane.messagePadding" );
maxCharactersPerLine = UIManager.getInt( "OptionPane.maxCharactersPerLine" );
@@ -116,6 +123,24 @@ public class FlatOptionPaneUI
updateChildPanels( optionPane );
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener();
return e -> {
superListener.propertyChange( e );
// hide window title bar icon
// (only if showIcon is false, otherwise the default behavior is used)
if( !showIcon && "ancestor".equals( e.getPropertyName() ) && e.getNewValue() != null ) {
JRootPane rootPane = SwingUtilities.getRootPane( optionPane );
if( rootPane != null &&
rootPane.getContentPane().getComponentCount() > 0 &&
rootPane.getContentPane().getComponent( 0 ) == optionPane )
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, false );
}
};
}
@Override
public Dimension getMinimumOptionPaneSize() {
return UIScale.scale( super.getMinimumOptionPaneSize() );

View File

@@ -66,6 +66,9 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatWindowResizer -->
*
* @uiDefault RootPane.font Font unused
* @uiDefault RootPane.background Color
* @uiDefault RootPane.foreground Color unused
* @uiDefault RootPane.borderDragThickness int
* @uiDefault RootPane.cornerDragWidth int
* @uiDefault RootPane.honorFrameMinimumSizeOnResize boolean
@@ -126,8 +129,23 @@ public class FlatRootPaneUI
protected void installDefaults( JRootPane c ) {
super.installDefaults( c );
// Give the root pane useful background, foreground and font.
// Background is used for title bar and menu bar if native window decorations
// and unified background are enabled.
// Foreground and font are usually not used, but set for completeness.
// Not using LookAndFeel.installColorsAndFont() here because it will not work
// because the properties are null by default but inherit non-null values from parent.
if( !c.isBackgroundSet() || c.getBackground() instanceof UIResource )
c.setBackground( UIManager.getColor( "RootPane.background" ) );
if( !c.isForegroundSet() || c.getForeground() instanceof UIResource )
c.setForeground( UIManager.getColor( "RootPane.foreground" ) );
if( !c.isFontSet() || c.getFont() instanceof UIResource )
c.setFont( UIManager.getFont( "RootPane.font" ) );
// Update background color of JFrame or JDialog parent to avoid bad border
// on HiDPI screens when switching from light to dark Laf.
// Window background color is also used in native window decorations
// to fill background when window is initially shown or when resizing window.
// The background of JFrame is initialized in JFrame.frameInit() and
// the background of JDialog in JDialog.dialogInit(),
// but it was not updated when switching Laf.
@@ -313,6 +331,11 @@ public class FlatRootPaneUI
}
break;
case FlatClientProperties.TITLE_BAR_SHOW_ICON:
if( titlePane != null )
titlePane.updateIcon();
break;
case FlatClientProperties.TITLE_BAR_BACKGROUND:
case FlatClientProperties.TITLE_BAR_FOREGROUND:
if( titlePane != null )

View File

@@ -40,6 +40,8 @@ import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
@@ -134,12 +136,15 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.maximumTabWidth int optional
* @uiDefault TabbedPane.tabHeight int
* @uiDefault TabbedPane.tabSelectionHeight int
* @uiDefault TabbedPane.cardTabSelectionHeight int
* @uiDefault TabbedPane.contentSeparatorHeight int
* @uiDefault TabbedPane.showTabSeparators boolean
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
* @uiDefault TabbedPane.hasFullBorder boolean
* @uiDefault TabbedPane.activeTabBorder boolean
*
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
* @uiDefault TabbedPane.tabType String underlined (default) or card
* @uiDefault TabbedPane.tabsPopupPolicy String never or asNeeded (default)
* @uiDefault TabbedPane.scrollButtonsPolicy String never, asNeeded or asNeededSingle (default)
* @uiDefault TabbedPane.scrollButtonsPlacement String both (default) or trailing
@@ -165,6 +170,10 @@ public class FlatTabbedPaneUI
extends BasicTabbedPaneUI
implements StyleableUI
{
// tab type
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
/** @since 2 */ protected static final int TAB_TYPE_CARD = 1;
// tabs popup policy / scroll arrows policy
protected static final int NEVER = 0;
// protected static final int ALWAYS = 1;
@@ -200,12 +209,14 @@ public class FlatTabbedPaneUI
@Styleable protected int maximumTabWidth;
@Styleable protected int tabHeight;
@Styleable protected int tabSelectionHeight;
/** @since 2 */ @Styleable protected int cardTabSelectionHeight;
@Styleable protected int contentSeparatorHeight;
@Styleable protected boolean showTabSeparators;
@Styleable protected boolean tabSeparatorsFullHeight;
@Styleable protected boolean hasFullBorder;
@Styleable protected boolean tabsOpaque = true;
@Styleable(type=String.class) private int tabType;
@Styleable(type=String.class) private int tabsPopupPolicy;
@Styleable(type=String.class) private int scrollButtonsPolicy;
@Styleable(type=String.class) private int scrollButtonsPlacement;
@@ -318,12 +329,14 @@ public class FlatTabbedPaneUI
maximumTabWidth = UIManager.getInt( "TabbedPane.maximumTabWidth" );
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
cardTabSelectionHeight = UIManager.getInt( "TabbedPane.cardTabSelectionHeight" );
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) );
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
scrollButtonsPolicy = parseScrollButtonsPolicy( UIManager.getString( "TabbedPane.scrollButtonsPolicy" ) );
scrollButtonsPlacement = parseScrollButtonsPlacement( UIManager.getString( "TabbedPane.scrollButtonsPlacement" ) );
@@ -560,6 +573,13 @@ public class FlatTabbedPaneUI
return handler;
}
@Override
protected FocusListener createFocusListener() {
Handler handler = getHandler();
handler.focusDelegate = super.createFocusListener();
return handler;
}
@Override
protected LayoutManager createLayoutManager() {
if( tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT )
@@ -618,6 +638,7 @@ public class FlatTabbedPaneUI
if( value instanceof String ) {
switch( key ) {
case "tabType": value = parseTabType( (String) value ); break;
case "tabsPopupPolicy": value = parseTabsPopupPolicy( (String) value ); break;
case "scrollButtonsPolicy": value = parseScrollButtonsPolicy( (String) value ); break;
case "scrollButtonsPlacement": value = parseScrollButtonsPlacement( (String) value ); break;
@@ -707,8 +728,25 @@ public class FlatTabbedPaneUI
return;
Rectangle r = getTabBounds( tabPane, tabIndex );
if( r != null )
tabPane.repaint( r );
if( r == null )
return;
// increase size of repaint region to include part of content border
if( contentSeparatorHeight > 0 &&
getTabType() == TAB_TYPE_CARD &&
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
{
int sh = scale( contentSeparatorHeight );
switch( tabPane.getTabPlacement() ) {
default:
case TOP: r.height += sh; break;
case BOTTOM: r.height += sh; r.y -= sh; break;
case LEFT: r.width += sh; break;
case RIGHT: r.width += sh; r.x -= sh; break;
}
}
tabPane.repaint( r );
}
private boolean inCalculateEqual;
@@ -1045,16 +1083,21 @@ public class FlatTabbedPaneUI
int x, int y, int w, int h, boolean isSelected )
{
// paint tab background
Color background = getTabBackground( tabPlacement, tabIndex, isSelected );
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
g.fillRect( x, y, w, h );
}
/** @since 2 */
protected Color getTabBackground( int tabPlacement, int tabIndex, boolean isSelected ) {
boolean enabled = tabPane.isEnabled();
Color background = enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
return enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
? hoverColor
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
? focusColor
: (selectedBackground != null && enabled && isSelected
? selectedBackground
: tabPane.getBackgroundAt( tabIndex )));
g.setColor( FlatUIUtils.deriveColor( background, tabPane.getBackground() ) );
g.fillRect( x, y, w, h );
}
@Override
@@ -1064,7 +1107,62 @@ public class FlatTabbedPaneUI
// paint tab separators
if( clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ) &&
!isLastInRun( tabIndex ) )
paintTabSeparator( g, tabPlacement, x, y, w, h );
{
if( getTabType() == TAB_TYPE_CARD ) {
// some separators need to be omitted if selected tab is painted as card
int selectedIndex = tabPane.getSelectedIndex();
if( tabIndex != selectedIndex - 1 && tabIndex != selectedIndex )
paintTabSeparator( g, tabPlacement, x, y, w, h );
} else
paintTabSeparator( g, tabPlacement, x, y, w, h );
}
// paint active tab border
if( isSelected && getTabType() == TAB_TYPE_CARD )
paintCardTabBorder( g, tabPlacement, tabIndex, x, y, w, h );
}
/** @since 2 */
protected void paintCardTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
Graphics2D g2 = (Graphics2D) g;
float borderWidth = scale( (float) contentSeparatorHeight );
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
switch( tabPlacement ) {
default:
case TOP:
case BOTTOM:
// paint left and right tab border
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
break;
case LEFT:
case RIGHT:
// paint top and bottom tab border
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
break;
}
if( cardTabSelectionHeight <= 0 ) {
// if there is no tab selection indicator, paint a top border as well
switch( tabPlacement ) {
default:
case TOP:
g2.fill( new Rectangle2D.Float( x, y, w, borderWidth ) );
break;
case BOTTOM:
g2.fill( new Rectangle2D.Float( x, y + h - borderWidth, w, borderWidth ) );
break;
case LEFT:
g2.fill( new Rectangle2D.Float( x, y, borderWidth, h ) );
break;
case RIGHT:
g2.fill( new Rectangle2D.Float( x + w - borderWidth, y, borderWidth, h ) );
break;
}
}
}
protected void paintTabCloseButton( Graphics g, int tabIndex, int x, int y, int w, int h ) {
@@ -1110,26 +1208,30 @@ public class FlatTabbedPaneUI
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
// paint underline selection
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
Insets contentInsets = getContentBorderInsets( tabPlacement );
int tabSelectionHeight = scale( this.tabSelectionHeight );
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
int sx, sy;
switch( tabPlacement ) {
case TOP:
default:
int sy = y + h + contentInsets.top - tabSelectionHeight;
sy = atBottom ? (y + h + contentInsets.top - tabSelectionHeight) : y;
g.fillRect( x, sy, w, tabSelectionHeight );
break;
case BOTTOM:
g.fillRect( x, y - contentInsets.bottom, w, tabSelectionHeight );
sy = atBottom ? (y - contentInsets.bottom) : (y + h - tabSelectionHeight);
g.fillRect( x, sy, w, tabSelectionHeight );
break;
case LEFT:
int sx = x + w + contentInsets.left - tabSelectionHeight;
sx = atBottom ? (x + w + contentInsets.left - tabSelectionHeight) : x;
g.fillRect( sx, y, tabSelectionHeight, h );
break;
case RIGHT:
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
sx = atBottom ? (x - contentInsets.right) : (x + w - tabSelectionHeight);
g.fillRect( sx, y, tabSelectionHeight, h );
break;
}
}
@@ -1141,6 +1243,7 @@ public class FlatTabbedPaneUI
* - paint full border (if enabled)
* - not invoking paintContentBorder*Edge() methods
* - repaint selection
* - painting active tab border style
*/
@Override
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
@@ -1189,12 +1292,49 @@ public class FlatTabbedPaneUI
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 separator or full border
g.setColor( contentAreaColor );
// create path for content separator or full border
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 );
// add gap for selected tab to path
if( getTabType() == TAB_TYPE_CARD ) {
float csh = scale( (float) contentSeparatorHeight );
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
Rectangle2D.Float innerTabRect = new Rectangle2D.Float( tabRect.x + csh, tabRect.y + csh,
tabRect.width - (csh * 2), tabRect.height - (csh * 2) );
// Ensure that the separator outside the tabViewport is present (doesn't get cutoff by the active tab)
// If left unsolved the active tab is "visible" in the separator (the gap) even when outside the viewport
if( tabViewport != null )
Rectangle2D.intersect( tabViewport.getBounds(), innerTabRect, innerTabRect );
Rectangle2D.Float gap = null;
if( isHorizontalTabPlacement() ) {
if( innerTabRect.width > 0 ) {
float y2 = (tabPlacement == TOP) ? y : y + h - csh;
gap = new Rectangle2D.Float( innerTabRect.x, y2, innerTabRect.width, csh );
}
} else {
if( innerTabRect.height > 0 ) {
float x2 = (tabPlacement == LEFT) ? x : x + w - csh;
gap = new Rectangle2D.Float( x2, innerTabRect.y, csh, innerTabRect.height );
}
}
if( gap != null ) {
path.append( gap, false );
// fill gap in case that the tab is colored (e.g. focused or hover)
g.setColor( getTabBackground( tabPlacement, selectedIndex, true ) );
((Graphics2D)g).fill( gap );
}
}
// paint content separator or full border
g.setColor( contentAreaColor );
((Graphics2D)g).fill( path );
// repaint selection in scroll-tab-layout because it may be painted before
@@ -1399,6 +1539,15 @@ public class FlatTabbedPaneUI
clientPropertyBoolean( tabPane, TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB, hideTabAreaWithOneTab );
}
/** @since 2 */
protected int getTabType() {
Object value = tabPane.getClientProperty( TABBED_PANE_TAB_TYPE );
return (value instanceof String)
? parseTabType( (String) value )
: tabType;
}
protected int getTabsPopupPolicy() {
Object value = tabPane.getClientProperty( TABBED_PANE_TABS_POPUP_POLICY );
@@ -1451,6 +1600,18 @@ public class FlatTabbedPaneUI
: tabWidthMode;
}
/** @since 2 */
protected static int parseTabType( String str ) {
if( str == null )
return TAB_TYPE_UNDERLINED;
switch( str ) {
default:
case TABBED_PANE_TAB_TYPE_UNDERLINED: return TAB_TYPE_UNDERLINED;
case TABBED_PANE_TAB_TYPE_CARD: return TAB_TYPE_CARD;
}
}
protected static int parseTabsPopupPolicy( String str ) {
if( str == null )
return AS_NEEDED;
@@ -2264,11 +2425,12 @@ public class FlatTabbedPaneUI
private class Handler
implements MouseListener, MouseMotionListener, PropertyChangeListener,
ChangeListener, ComponentListener, ContainerListener
ChangeListener, ComponentListener, ContainerListener, FocusListener
{
MouseListener mouseDelegate;
PropertyChangeListener propertyChangeDelegate;
ChangeListener changeDelegate;
FocusListener focusDelegate;
private final PropertyChangeListener contentListener = this::contentPropertyChange;
@@ -2438,6 +2600,10 @@ public class FlatTabbedPaneUI
break;
case TABBED_PANE_SHOW_TAB_SEPARATORS:
case TABBED_PANE_TAB_TYPE:
tabPane.repaint();
break;
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
case TABBED_PANE_HAS_FULL_BORDER:
case TABBED_PANE_HIDE_TAB_AREA_WITH_ONE_TAB:
@@ -2536,6 +2702,20 @@ public class FlatTabbedPaneUI
if( !(c instanceof UIResource) )
c.removePropertyChangeListener( contentListener );
}
//---- interface FocusListener ----
@Override
public void focusGained( FocusEvent e ) {
focusDelegate.focusGained( e );
repaintTab( tabPane.getSelectedIndex() );
}
@Override
public void focusLost( FocusEvent e ) {
focusDelegate.focusLost( e );
repaintTab( tabPane.getSelectedIndex() );
}
}
//---- class FlatTabbedPaneLayout -----------------------------------------

View File

@@ -50,8 +50,6 @@ import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
@@ -66,7 +64,6 @@ import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -80,6 +77,8 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.embeddedForeground Color
* @uiDefault TitlePane.borderColor Color optional
* @uiDefault TitlePane.unifiedBackground boolean
* @uiDefault TitlePane.showIcon boolean
* @uiDefault TitlePane.noIconLeftGap int
* @uiDefault TitlePane.iconSize Dimension
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
@@ -88,7 +87,6 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.icon Icon
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon
@@ -106,6 +104,8 @@ public class FlatTitlePane
protected final Color embeddedForeground = UIManager.getColor( "TitlePane.embeddedForeground" );
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
@@ -224,6 +224,11 @@ public class FlatTitlePane
restoreButton = createButton( "TitlePane.restoreIcon", "Restore", e -> restore() );
closeButton = createButton( "TitlePane.closeIcon", "Close", e -> close() );
// initially hide buttons that are only supported in frames
iconifyButton.setVisible( false );
maximizeButton.setVisible( false );
restoreButton.setVisible( false );
buttonPanel = new JPanel() {
@Override
public Dimension getPreferredSize() {
@@ -243,11 +248,9 @@ public class FlatTitlePane
if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) {
// JRootPane.FRAME works only for frames (and not for dialogs)
// but at this time the owner window type is unknown (not yet added)
// so we add the iconify/maximize/restore buttons and they are hidden
// so we add the iconify/maximize/restore buttons and they are shown
// later in frameStateChanged(), which is invoked from addNotify()
restoreButton.setVisible( false );
buttonPanel.add( iconifyButton );
buttonPanel.add( maximizeButton );
buttonPanel.add( restoreButton );
@@ -337,36 +340,27 @@ public class FlatTitlePane
protected void updateIcon() {
// get window images
List<Image> images = window.getIconImages();
if( images.isEmpty() ) {
// search in owners
for( Window owner = window.getOwner(); owner != null; owner = owner.getOwner() ) {
images = owner.getIconImages();
if( !images.isEmpty() )
break;
List<Image> images = null;
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, showIcon ) ) {
images = window.getIconImages();
if( images.isEmpty() ) {
// search in owners
for( Window owner = window.getOwner(); owner != null; owner = owner.getOwner() ) {
images = owner.getIconImages();
if( !images.isEmpty() )
break;
}
}
}
boolean hasIcon = true;
boolean hasIcon = (images != null && !images.isEmpty());
// set icon
if( !images.isEmpty() )
iconLabel.setIcon( new FlatTitlePaneIcon( images, iconSize ) );
else {
// no icon set on window --> use default icon
Icon defaultIcon = UIManager.getIcon( "TitlePane.icon" );
if( defaultIcon != null && (defaultIcon.getIconWidth() == 0 || defaultIcon.getIconHeight() == 0) )
defaultIcon = null;
if( defaultIcon != null ) {
if( defaultIcon instanceof ImageIcon )
defaultIcon = new ScaledImageIcon( (ImageIcon) defaultIcon, iconSize.width, iconSize.height );
iconLabel.setIcon( defaultIcon );
} else
hasIcon = false;
}
iconLabel.setIcon( hasIcon ? new FlatTitlePaneIcon( images, iconSize ) : null );
// show/hide icon
iconLabel.setVisible( hasIcon );
leftPanel.setBorder( hasIcon ? null : new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) );
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@@ -508,7 +502,7 @@ public class FlatTitlePane
protected void menuBarLayouted() {
updateNativeTitleBarHeightAndHitTestSpotsLater();
revalidate();
doLayout();
}
/*debug
@@ -521,17 +515,23 @@ public class FlatTitlePane
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight );
}
if( debugHitTestSpots != null ) {
g.setColor( Color.red );
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
for( Rectangle r : debugHitTestSpots )
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
}
if( debugAppIconBounds != null ) {
g.setColor( Color.blue);
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
Rectangle r = debugAppIconBounds;
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
paintRect( g, Color.red, r );
}
paintRect( g, Color.cyan, debugCloseButtonBounds );
paintRect( g, Color.blue, debugAppIconBounds );
paintRect( g, Color.blue, debugMinimizeButtonBounds );
paintRect( g, Color.magenta, debugMaximizeButtonBounds );
paintRect( g, Color.cyan, debugCloseButtonBounds );
}
private void paintRect( Graphics g, Color color, Rectangle r ) {
if( r == null )
return;
g.setColor( color );
Point offset = SwingUtilities.convertPoint( this, 0, 0, window );
g.drawRect( r.x - offset.x, r.y - offset.y, r.width - 1, r.height - 1 );
}
debug*/
@@ -702,9 +702,15 @@ debug*/
return window != null && FlatNativeWindowBorder.hasCustomDecoration( window );
}
// used to invoke updateNativeTitleBarHeightAndHitTestSpots() only once from latest invokeLater()
private int laterCounter;
protected void updateNativeTitleBarHeightAndHitTestSpotsLater() {
laterCounter++;
EventQueue.invokeLater( () -> {
updateNativeTitleBarHeightAndHitTestSpots();
laterCounter--;
if( laterCounter == 0 )
updateNativeTitleBarHeightAndHitTestSpots();
} );
}
@@ -722,8 +728,9 @@ debug*/
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) {
// compute real icon size (without insets; 1px wider for easier hitting)
// compute real icon size (without insets; 1px larger for easier hitting)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets();
Rectangle iconBounds = new Rectangle(
@@ -759,7 +766,7 @@ debug*/
JMenuBar menuBar = rootPane.getJMenuBar();
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBarPlaceholder );
r = getNativeHitTestSpot( menuBar );
if( r != null ) {
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
@@ -768,18 +775,18 @@ debug*/
// the glue component area can used to move the window.
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
int x2 = glueLocation.x + horizontalGlue.getWidth();
Rectangle r2;
if( getComponentOrientation().isLeftToRight() ) {
int trailingWidth = (r.x + r.width - HIT_TEST_SPOT_GROW) - glueLocation.x;
r.width -= trailingWidth;
r2 = new Rectangle( glueLocation.x + horizontalGlue.getWidth(), r.y, trailingWidth, r.height );
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
r.width = glueLocation.x - r.x;
} else {
int leadingWidth = (glueLocation.x + horizontalGlue.getWidth()) - (r.x + HIT_TEST_SPOT_GROW);
r.x += leadingWidth;
r.width -= leadingWidth;
r2 = new Rectangle( glueLocation.x -leadingWidth, r.y, leadingWidth, r.height );
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
r.width = (r.x + r.width) - x2;
r.x = x2;
}
r2.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
hitTestSpots.add( r2 );
}
@@ -787,16 +794,30 @@ debug*/
}
}
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots, appIconBounds );
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
Rectangle closeButtonBounds = boundsInWindow( closeButton );
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
hitTestSpots, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
/*debug
debugTitleBarHeight = titleBarHeight;
debugHitTestSpots = hitTestSpots;
debugAppIconBounds = appIconBounds;
debugMinimizeButtonBounds = minimizeButtonBounds;
debugMaximizeButtonBounds = maximizeButtonBounds;
debugCloseButtonBounds = closeButtonBounds;
repaint();
debug*/
}
private Rectangle boundsInWindow( JComponent c ) {
return c.isShowing()
? SwingUtilities.convertRectangle( c.getParent(), c.getBounds(), window )
: null;
}
protected Rectangle getNativeHitTestSpot( JComponent c ) {
Dimension size = c.getSize();
if( size.width <= 0 || size.height <= 0 )
@@ -804,17 +825,16 @@ debug*/
Point location = SwingUtilities.convertPoint( c, 0, 0, window );
Rectangle r = new Rectangle( location, size );
// slightly increase rectangle so that component receives mouseExit events
r.grow( HIT_TEST_SPOT_GROW, HIT_TEST_SPOT_GROW );
return r;
}
private static final int HIT_TEST_SPOT_GROW = 2;
/*debug
private int debugTitleBarHeight;
private List<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds;
private Rectangle debugMinimizeButtonBounds;
private Rectangle debugMaximizeButtonBounds;
private Rectangle debugCloseButtonBounds;
debug*/
//---- class FlatTitlePaneBorder ------------------------------------------
@@ -846,7 +866,7 @@ debug*/
Border menuBarBorder = getMenuBarBorder();
if( menuBarBorder != null ) {
// if menu bar is embedded, paint menu bar border
menuBarBorder.paintBorder( c, g, x, y, width, height );
menuBarBorder.paintBorder( rootPane.getJMenuBar(), g, x, y, width, height );
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) {
// paint border between title pane and content if border color is specified
float lineHeight = UIScale.scale( (float) 1 );

View File

@@ -54,6 +54,10 @@ import com.formdev.flatlaf.util.SystemInfo;
// https://github.com/oberth/custom-chrome
// https://github.com/rossy/borderless-window
//
// Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
// https://github.com/dotnet/wpf/issues/4825#issuecomment-930442736
//
/**
* Native window border support for Windows 10 when using custom decorations.
@@ -177,30 +181,24 @@ class FlatWindowsNativeWindowBorder
}
@Override
public void setTitleBarHeight( Window window, int titleBarHeight ) {
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds )
{
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.titleBarHeight = titleBarHeight;
}
@Override
public void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
wndProc.closeButtonBounds = cloneRectange( closeButtonBounds );
}
@Override
public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
private static Rectangle cloneRectange( Rectangle rect ) {
return (rect != null) ? new Rectangle( rect ) : null;
}
@Override
@@ -303,14 +301,21 @@ class FlatWindowsNativeWindowBorder
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTTOP = 12;
HTMINBUTTON = 8,
HTMAXBUTTON = 9,
HTTOP = 12,
HTCLOSE = 20;
private Window window;
private final long hwnd;
// Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds;
private Rectangle closeButtonBounds;
WndProc( Window window ) {
this.window = window;
@@ -355,7 +360,7 @@ class FlatWindowsNativeWindowBorder
// invoked from native code
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
// scale-down mouse x/y
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
@@ -363,9 +368,26 @@ class FlatWindowsNativeWindowBorder
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
if( contains( appIconBounds, sx, sy ) )
return HTSYSMENU;
// return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) )
return HTMINBUTTON;
// return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) )
return HTMAXBUTTON;
// return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) )
return HTCLOSE;
boolean isOnTitleBar = (sy < titleBarHeight);
if( isOnTitleBar ) {
@@ -382,6 +404,10 @@ class FlatWindowsNativeWindowBorder
return isOnResizeBorder ? HTTOP : HTCLIENT;
}
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
}
/**
* Scales down in the same way as AWT.
* See AwtWin32GraphicsDevice::ScaleDownX() and ::ScaleDownY()

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import javax.swing.border.Border;
/**
* Border that automatically animates painting on component value changes.
* <p>
* {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component.
* If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])}
* is invoked multiple times with animated value(s) (from old value(s) to new value(s)).
* If {@link #getAnimatableValues(Component)} returns multiple values, then each value
* gets its own independent animation, which may start/end at different points in time,
* may have different duration, resolution and interpolator.
* <p>
* Example for an animated border:
* <pre>
* private class MyAnimatedBorder
* implements AnimatedBorder
* {
* &#64;Override
* public float[] getAnimatableValues( Component c ) {
* return new float[] { c.isFocusOwner() ? 1 : 0 };
* }
*
* &#64;Override
* public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
* int lh = UIScale.scale( 2 );
*
* g.setColor( Color.blue );
* g.fillRect( x, y + height - lh, Math.round( width * animatedValues[0] ), lh );
* }
*
* &#64;Override
* public Insets getBorderInsets( Component c ) {
* return UIScale.scale( new Insets( 4, 4, 4, 4 ) );
* }
*
* &#64;Override public boolean isBorderOpaque() { return false; }
* }
*
* // sample usage
* JTextField textField = new JTextField();
* textField.setBorder( new MyAnimatedBorder() );
* </pre>
*
* Animation works only if the component passed to {@link #paintBorder(Component, Graphics, int, int, int, int)}
* is a instance of {@link JComponent}.
* A client property is set on the component to store the animation state.
*
* @author Karl Tauber
* @since 2
*/
public interface AnimatedBorder
extends Border, AnimatedPainter
{
/**
* Invokes {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
*/
@Override
default void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
paintWithAnimation( c, g, x, y, width, height );
}
}

View File

@@ -18,62 +18,87 @@ package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.Icon;
import javax.swing.JComponent;
import com.formdev.flatlaf.util.Animator.Interpolator;
/**
* Icon that automatically animates painting on component value changes.
* <p>
* {@link #getValue(Component)} returns the value of the component.
* If the value changes, then {@link #paintIconAnimated(Component, Graphics, int, int, float)}
* is invoked multiple times with animated value (from old value to new value).
* {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component.
* If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])}
* is invoked multiple times with animated value(s) (from old value(s) to new value(s)).
* If {@link #getAnimatableValues(Component)} returns multiple values, then each value
* gets its own independent animation, which may start/end at different points in time,
* may have different duration, resolution and interpolator.
* <p>
* Example for an animated icon:
* <pre>
* private class AnimatedMinimalTestIcon
* private class MyAnimatedIcon
* implements AnimatedIcon
* {
* &#64;Override
* public float[] getAnimatableValues( Component c ) {
* return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
* }
*
* &#64;Override public int getIconWidth() { return 100; }
* &#64;Override public int getIconHeight() { return 20; }
*
* &#64;Override
* public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
* int w = getIconWidth();
* int h = getIconHeight();
*
* public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
* g.setColor( Color.red );
* g.drawRect( x, y, w - 1, h - 1 );
* g.fillRect( x, y, Math.round( w * animatedValue ), h );
* }
*
* &#64;Override
* public float getValue( Component c ) {
* return ((AbstractButton)c).isSelected() ? 1 : 0;
* g.drawRect( x, y, width - 1, height - 1 );
* g.fillRect( x, y, Math.round( width * animatedValues[0] ), height );
* }
* }
*
* // sample usage
* JCheckBox checkBox = new JCheckBox( "test" );
* checkBox.setIcon( new AnimatedMinimalTestIcon() );
* checkBox.setIcon( new MyAnimatedIcon() );
* </pre>
*
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
* is a instance of {@link JComponent}.
* is an instance of {@link JComponent}.
* A client property is set on the component to store the animation state.
*
* @author Karl Tauber
*/
public interface AnimatedIcon
extends Icon
extends Icon, AnimatedPainter
{
/**
* {@inheritDoc}
*
* @since 2
*/
@Override
public default void paintIcon( Component c, Graphics g, int x, int y ) {
AnimationSupport.paintIcon( this, c, g, x, y );
default float[] getAnimatableValues( Component c ) {
// for compatibility
return new float[] { getValue( c ) };
}
/**
* Paints the icon for the given animated value.
* Invokes {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
*/
@Override
default void paintIcon( Component c, Graphics g, int x, int y ) {
paintWithAnimation( c, g, x, y, getIconWidth(), getIconHeight() );
}
/**
* {@inheritDoc}
*
* @since 2
*/
@Override
default void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
// for compatibility
paintIconAnimated( c, g, x, y, animatedValues[0] );
}
/**
* Paints the icon for the given (animated) value.
*
* @param c the component that this icon belongs to
* @param g the graphics context
@@ -82,52 +107,45 @@ public interface AnimatedIcon
* @param animatedValue the animated value, which is either equal to what {@link #getValue(Component)}
* returned, or somewhere between the previous value and the latest value
* that {@link #getValue(Component)} returned
*
* @deprecated override {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])} instead
*/
void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue );
@Deprecated
default void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
}
/**
* Gets the value of the component.
* Gets the animatable value of the component.
* <p>
* This can be any value and depends on the component.
* If the value changes, then this class animates from the old value to the new one.
* <p>
* For a toggle button this could be {@code 0} for off and {@code 1} for on.
*
* @deprecated override {@link #getAnimatableValues(Component)} instead
*/
float getValue( Component c );
/**
* Returns whether animation is enabled for this icon (default is {@code true}).
*/
default boolean isAnimationEnabled() {
return true;
@Deprecated
default float getValue( Component c ) {
return 0;
}
/**
* Returns the duration of the animation in milliseconds (default is 150).
* {@inheritDoc}
*
* @since TODO
*/
default int getAnimationDuration() {
return 150;
}
/**
* Returns the resolution of the animation in milliseconds (default is 10).
* Resolution is the amount of time between timing events.
*/
default int getAnimationResolution() {
return 10;
}
/**
* Returns the interpolator for the animation.
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
*/
default Interpolator getAnimationInterpolator() {
return CubicBezierEasing.STANDARD_EASING;
@Override
default Object getAnimationClientPropertyKey() {
// for compatibility
return getClientPropertyKey();
}
/**
* Returns the client property key used to store the animation support.
*
* @deprecated override {@link #getAnimationClientPropertyKey()} instead
*/
@Deprecated
default Object getClientPropertyKey() {
return getClass();
}
@@ -135,115 +153,25 @@ public interface AnimatedIcon
//---- class AnimationSupport ---------------------------------------------
/**
* Animation support class that stores the animation state and implements the animation.
* Animation support.
*/
@Deprecated
class AnimationSupport
{
private float startValue;
private float targetValue;
private float animatedValue;
private float fraction;
private Animator animator;
// last x,y coordinates of the icon needed to repaint while animating
private int x;
private int y;
/**
* @deprecated use {@link AnimatedPainter#paintWithAnimation(Component, Graphics, int, int, int, int)} instead
*/
@Deprecated
public static void paintIcon( AnimatedIcon icon, Component c, Graphics g, int x, int y ) {
if( !isAnimationEnabled( icon, c ) ) {
// paint without animation if animation is disabled or
// component is not a JComponent and therefore does not support
// client properties, which are required to keep animation state
paintIconImpl( icon, c, g, x, y, null );
return;
}
JComponent jc = (JComponent) c;
Object key = icon.getClientPropertyKey();
AnimationSupport as = (AnimationSupport) jc.getClientProperty( key );
if( as == null ) {
// painted first time --> do not animate, but remember current component value
as = new AnimationSupport();
as.startValue = as.targetValue = as.animatedValue = icon.getValue( c );
as.x = x;
as.y = y;
jc.putClientProperty( key, as );
} else {
// get component value
float value = icon.getValue( c );
if( value != as.targetValue ) {
// value changed --> (re)start animation
if( as.animator == null ) {
// create animator
AnimationSupport as2 = as;
as.animator = new Animator( icon.getAnimationDuration(), fraction -> {
// check whether component was removed while animation is running
if( !c.isDisplayable() ) {
as2.animator.stop();
return;
}
// compute animated value
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
as2.fraction = fraction;
// repaint icon
c.repaint( as2.x, as2.y, icon.getIconWidth(), icon.getIconHeight() );
}, () -> {
as2.startValue = as2.animatedValue = as2.targetValue;
as2.animator = null;
} );
}
if( as.animator.isRunning() ) {
// if animation is still running, restart it from the current
// animated value to the new target value with reduced duration
as.animator.cancel();
int duration2 = (int) (icon.getAnimationDuration() * as.fraction);
if( duration2 > 0 )
as.animator.setDuration( duration2 );
as.startValue = as.animatedValue;
} else {
// new animation
as.animator.setDuration( icon.getAnimationDuration() );
as.animator.setResolution( icon.getAnimationResolution() );
as.animator.setInterpolator( icon.getAnimationInterpolator() );
as.animatedValue = as.startValue;
}
as.targetValue = value;
as.animator.start();
}
as.x = x;
as.y = y;
}
paintIconImpl( icon, c, g, x, y, as );
}
private static void paintIconImpl( AnimatedIcon icon, Component c, Graphics g, int x, int y, AnimationSupport as ) {
float value = (as != null) ? as.animatedValue : icon.getValue( c );
icon.paintIconAnimated( c, g, x, y, value );
}
private static boolean isAnimationEnabled( AnimatedIcon icon, Component c ) {
return Animator.useAnimation() && icon.isAnimationEnabled() && c instanceof JComponent;
AnimatedPainterSupport.paint( icon, c, (Graphics2D) g, x, y, icon.getIconWidth(), icon.getIconHeight() );
}
/**
* @deprecated use {@link AnimatedPainter#saveRepaintLocation(AnimatedPainter, Component, int, int)} instead
*/
@Deprecated
public static void saveIconLocation( AnimatedIcon icon, Component c, int x, int y ) {
if( !isAnimationEnabled( icon, c ) )
return;
AnimationSupport as = (AnimationSupport) ((JComponent)c).getClientProperty( icon.getClientPropertyKey() );
if( as != null ) {
as.x = x;
as.y = y;
}
AnimatedPainterSupport.saveRepaintLocation( icon, c, x, y );
}
}
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import com.formdev.flatlaf.util.Animator.Interpolator;
/**
* Painter that automatically animates painting on component value(s) changes.
* <p>
* {@link #getAnimatableValues(Component)} returns the animatable value(s) of the component.
* If the value(s) have changed, then {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])}
* is invoked multiple times with animated value(s) (from old value(s) to new value(s)).
* If {@link #getAnimatableValues(Component)} returns multiple values, then each value
* gets its own independent animation, which may start/end at different points in time,
* may have different duration, resolution and interpolator.
* <p>
* See {@link AnimatedBorder} or {@link AnimatedIcon} for examples.
* <p>
* Animation works only if the component passed to {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}
* is an instance of {@link JComponent}.
* A client property is set on the component to store the animation state.
*
* @author Karl Tauber
* @since 2
*/
public interface AnimatedPainter
{
/**
* Gets the animatable value(s) of the component.
* <p>
* This can be any value(s) and depends on the component.
* If the value(s) changes, then this class animates from the old value(s) to the new ones.
* If multiple values are returned, then each value gets its own independent animation.
* <p>
* For a toggle button this could be {@code 0} for off and {@code 1} for on.
* A complex check box could return values for selected, hover, pressed and focused states.
* The painter then can show independent animations for those states.
*/
float[] getAnimatableValues( Component c );
/**
* Starts painting.
* Either invokes {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])}
* once to paint current value(s) (see {@link #getAnimatableValues(Component)}. Or if value(s) has
* changed, compared to last painting, then it starts an animation and invokes
* {@link #paintAnimated(Component, Graphics2D, int, int, int, int, float[])}
* multiple times with animated value(s) (from old value(s) to new value(s)).
*
* @param c the component that this painter belongs to
* @param g the graphics context
* @param x the x coordinate of the paint area
* @param y the y coordinate of the paint area
* @param width the width of the paint area
* @param height the height of the paint area
*/
default void paintWithAnimation( Component c, Graphics g, int x, int y, int width, int height ) {
AnimatedPainterSupport.paint( this, c, (Graphics2D) g, x, y, width, height );
}
/**
* Paints the given (animated) value(s).
* <p>
* Invoked from {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}.
*
* @param c the component that this painter belongs to
* @param g the graphics context
* @param x the x coordinate of the paint area
* @param y the y coordinate of the paint area
* @param width the width of the paint area
* @param height the height of the paint area
* @param animatedValues the animated values, which are either equal to what {@link #getAnimatableValues(Component)}
* returned, or somewhere between the previous values and the latest values
* that {@link #getAnimatableValues(Component)} returned
*/
void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues );
/**
* Invoked from animator to repaint an area.
* <p>
* Useful to limit the repaint region. E.g. if only the bottom border is animated.
* If more than one border side is animated (e.g. bottom and right side), then it
* makes no sense to do separate repaints because the Swing repaint manager unions
* the regions and the whole component is repainted.
* <p>
* The default implementation repaints the whole given area.
*/
default void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
c.repaint( x, y, width, height );
}
/**
* Returns whether animation is enabled for this painter (default is {@code true}).
*/
default boolean isAnimationEnabled() {
return true;
}
/**
* Returns the duration of the animation in milliseconds (default is 150).
*/
default int getAnimationDuration() {
return 150;
}
/**
* Returns the resolution of the animation in milliseconds (default is 10).
* Resolution is the amount of time between timing events.
*/
default int getAnimationResolution() {
return 10;
}
/**
* Returns the interpolator for the animation.
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
*/
default Interpolator getAnimationInterpolator() {
return CubicBezierEasing.STANDARD_EASING;
}
/**
* Returns the duration of the animation in milliseconds (default is 150)
* for the given value index and value.
*/
default int getAnimationDuration( int valueIndex, float value ) {
return getAnimationDuration();
}
/**
* Returns the resolution of the animation in milliseconds (default is 10)
* for the given value index and value.
* Resolution is the amount of time between timing events.
*/
default int getAnimationResolution( int valueIndex, float value ) {
return getAnimationResolution();
}
/**
* Returns the interpolator for the animation
* for the given value index and value.
* Default is {@link CubicBezierEasing#STANDARD_EASING}.
*/
default Interpolator getAnimationInterpolator( int valueIndex, float value ) {
return getAnimationInterpolator();
}
/**
* Returns the client property key used to store the animation support.
*/
default Object getAnimationClientPropertyKey() {
return getClass();
}
/**
* Saves location for repainting animated area with
* {@link AnimatedPainter#repaintDuringAnimation(Component, int, int, int, int)}.
* Only needed when graphics context passed to {@link #paintWithAnimation(Component, Graphics, int, int, int, int)}
* uses transformed location.
*/
static void saveRepaintLocation( AnimatedPainter painter, Component c, int x, int y ) {
AnimatedPainterSupport.saveRepaintLocation( painter, c, x, y );
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Graphics2D;
import javax.swing.JComponent;
/**
* Animation support class that stores the animation state and implements the animation.
*
* @author Karl Tauber
* @since 2
*/
class AnimatedPainterSupport
{
private final int valueIndex;
private float startValue;
private float targetValue;
private float animatedValue;
private float fraction;
private Animator animator;
// last bounds of the paint area needed to repaint while animating
private int x;
private int y;
private int width;
private int height;
private AnimatedPainterSupport( int valueIndex ) {
this.valueIndex = valueIndex;
}
static void paint( AnimatedPainter painter, Component c, Graphics2D g,
int x, int y, int width, int height )
{
// get animatable component values
float[] values = painter.getAnimatableValues( c );
if( !isAnimationEnabled( painter, c ) ) {
// paint without animation if animation is disabled or
// component is not a JComponent and therefore does not support
// client properties, which are required to keep animation state
painter.paintAnimated( c, g, x, y, width, height, values );
return;
}
JComponent jc = (JComponent) c;
Object key = painter.getAnimationClientPropertyKey();
AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) jc.getClientProperty( key );
// check whether length of values array has changed
if( ass != null && ass.length != values.length ) {
// cancel all running animations
for( int i = 0; i < ass.length; i++ ) {
AnimatedPainterSupport as = ass[i];
if( as.animator != null )
as.animator.cancel();
}
ass = null;
}
if( ass == null ) {
ass = new AnimatedPainterSupport[values.length];
jc.putClientProperty( key, ass );
}
for( int i = 0; i < ass.length; i++ ) {
AnimatedPainterSupport as = ass[i];
float value = values[i];
if( as == null ) {
// painted first time --> do not animate, but remember current component value
as = new AnimatedPainterSupport( i );
as.startValue = as.targetValue = as.animatedValue = value;
ass[i] = as;
} else if( value != as.targetValue ) {
// value changed --> (re)start animation
int animationDuration = painter.getAnimationDuration( as.valueIndex, value );
// do not animate if animation duration (for current value) is zero
if( animationDuration <= 0 ) {
if( as.animator != null ) {
as.animator.cancel();
as.animator = null;
}
as.startValue = as.targetValue = as.animatedValue = value;
as.fraction = 0;
continue;
}
if( as.animator == null ) {
// create animator
AnimatedPainterSupport as2 = as;
as.animator = new Animator( 1, fraction -> {
// check whether component was removed while animation is running
if( !c.isDisplayable() ) {
as2.animator.stop();
return;
}
// compute animated value
as2.animatedValue = as2.startValue + ((as2.targetValue - as2.startValue) * fraction);
as2.fraction = fraction;
// repaint
painter.repaintDuringAnimation( c, as2.x, as2.y, as2.width, as2.height );
}, () -> {
as2.startValue = as2.animatedValue = as2.targetValue;
as2.animator = null;
} );
}
if( as.animator.isRunning() ) {
// if animation is still running, restart it from the current
// animated value to the new target value with reduced duration
as.animator.cancel();
int duration2 = (int) (animationDuration * as.fraction);
if( duration2 > 0 )
as.animator.setDuration( duration2 );
as.startValue = as.animatedValue;
} else {
// new animation
as.animator.setDuration( animationDuration );
as.animatedValue = as.startValue;
}
// update animator for new value
as.animator.setResolution( painter.getAnimationResolution( as.valueIndex, value ) );
as.animator.setInterpolator( painter.getAnimationInterpolator( as.valueIndex, value ) );
// start animation
as.targetValue = value;
as.animator.start();
}
as.x = x;
as.y = y;
as.width = width;
as.height = height;
}
float[] animatedValues = new float[ass.length];
for( int i = 0; i < ass.length; i++ )
animatedValues[i] = ass[i].animatedValue;
painter.paintAnimated( c, g, x, y, width, height, animatedValues );
}
private static boolean isAnimationEnabled( AnimatedPainter painter, Component c ) {
return Animator.useAnimation() && painter.isAnimationEnabled() && c instanceof JComponent;
}
static void saveRepaintLocation( AnimatedPainter painter, Component c, int x, int y ) {
if( !isAnimationEnabled( painter, c ) )
return;
AnimatedPainterSupport[] ass = (AnimatedPainterSupport[]) ((JComponent)c).getClientProperty( painter.getAnimationClientPropertyKey() );
if( ass != null ) {
for( int i = 0; i < ass.length; i++ ) {
AnimatedPainterSupport as = ass[i];
as.x = x;
as.y = y;
}
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
/**
* A simple cache (map) that uses soft references for the values.
*
* @author Karl Tauber
* @since 2
*/
public class SoftCache<K,V>
implements Map<K, V>
{
private final Map<K, CacheReference<K,V>> map;
private final ReferenceQueue<V> queue = new ReferenceQueue<>();
public SoftCache() {
map = new HashMap<>();
}
public SoftCache( int initialCapacity ) {
map = new HashMap<>( initialCapacity );
}
@Override
public int size() {
expungeStaleEntries();
return map.size();
}
@Override
public boolean isEmpty() {
expungeStaleEntries();
return map.isEmpty();
}
@Override
public boolean containsKey( Object key ) {
expungeStaleEntries();
return map.containsKey( key );
}
/**
* Not supported. Throws {@link UnsupportedOperationException}.
*/
@Override
public boolean containsValue( Object value ) {
throw new UnsupportedOperationException();
}
@Override
public V get( Object key ) {
expungeStaleEntries();
return getRef( map.get( key ) );
}
@Override
public V put( K key, V value ) {
expungeStaleEntries();
return getRef( map.put( key, new CacheReference<>( key, value, queue ) ) );
}
@Override
public V remove( Object key ) {
expungeStaleEntries();
return getRef( map.remove( key ) );
}
private V getRef( CacheReference<K,V> ref ) {
return (ref != null) ? ref.get() : null;
}
@Override
public void putAll( Map<? extends K, ? extends V> m ) {
expungeStaleEntries();
for( Entry<? extends K, ? extends V> e : m.entrySet() )
put( e.getKey(), e.getValue() );
}
@Override
public void clear() {
map.clear();
expungeStaleEntries();
}
@Override
public Set<K> keySet() {
expungeStaleEntries();
return map.keySet();
}
/**
* Not supported. Throws {@link UnsupportedOperationException}.
*/
@Override
public Collection<V> values() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Throws {@link UnsupportedOperationException}.
*/
@Override
public Set<Entry<K, V>> entrySet() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Throws {@link UnsupportedOperationException}.
*/
@Override
public void forEach( BiConsumer<? super K, ? super V> action ) {
throw new UnsupportedOperationException();
}
/**
* Not supported. Throws {@link UnsupportedOperationException}.
*/
@Override
public void replaceAll( BiFunction<? super K, ? super V, ? extends V> function ) {
throw new UnsupportedOperationException();
}
@SuppressWarnings( "unchecked" )
private void expungeStaleEntries() {
Reference<? extends V> reference;
while( (reference = queue.poll()) != null )
map.remove( ((CacheReference<K,V>)reference).key );
}
//---- class CacheReference ----
private static class CacheReference<K,V>
extends SoftReference<V>
{
// needed to remove reference from map in expungeStaleEntries()
final K key;
CacheReference( K key, V value, ReferenceQueue<? super V> queue ) {
super( value, queue );
this.key = key;
}
}
}

View File

@@ -46,6 +46,7 @@ public class SystemInfo
public static final boolean isJava_9_orLater;
public static final boolean isJava_11_orLater;
public static final boolean isJava_15_orLater;
/** @since 2 */ public static final boolean isJava_17_orLater;
// Java VMs
public static final boolean isJetBrainsJVM;
@@ -82,6 +83,7 @@ public class SystemInfo
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 ));
isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 ));
isJava_17_orLater = (javaVersion >= toVersion( 17, 0, 0, 0 ));
// Java VMs
isJetBrainsJVM = System.getProperty( "java.vm.vendor", "Unknown" )

View File

@@ -26,6 +26,68 @@
# Instead copy and modify only those properties that you need to alter.
#
#---- typography / fonts ----
# headings
h00.font = +24
h0.font = +18
h1.font = +12 $semibold.font
h2.font = +6 $semibold.font
h3.font = +3 $semibold.font
h4.font = bold
h1.regular.font = +12
h2.regular.font = +6
h3.regular.font = +3
# text
large.font = +2
medium.font = -1
small.font = -2
mini.font = -3
# default font
#defaultFont = ...
# font weights
# Windows
[win]light.font = "Segoe UI Light"
[win]semibold.font = "Segoe UI Semibold"
# macOS
[mac]light.font = "HelveticaNeue-Thin"
[mac]semibold.font = "HelveticaNeue-Medium"
# Linux
[linux]light.font = "Lato Light", "Ubuntu Light", "Cantarell Light"
[linux]semibold.font = "Lato Semibold", "Ubuntu Medium"
# fallback for unknown platform
light.font = +0
semibold.font = +0
# monospaced
[win]monospaced.font = Consolas, "Courier New", Monospaced
[mac]monospaced.font = Menlo, Monospaced
[linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced
monospaced.font = Monospaced
# styles
[style].h00 = font: $h00.font
[style].h0 = font: $h0.font
[style].h1 = font: $h1.font
[style].h2 = font: $h2.font
[style].h3 = font: $h3.font
[style].h4 = font: $h4.font
[style].h1.regular = font: $h1.regular.font
[style].h2.regular = font: $h2.regular.font
[style].h3.regular = font: $h3.regular.font
[style].large = font: $large.font
[style].medium = font: $medium.font
[style].small = font: $small.font
[style].mini = font: $mini.font
[style].light = font: $light.font
[style].semibold = font: $semibold.font
[style].monospaced = font: $monospaced.font
#---- UI delegates ----
ButtonUI = com.formdev.flatlaf.ui.FlatButtonUI
@@ -394,6 +456,7 @@ OptionPane.messageAreaBorder = 0,0,0,0
OptionPane.buttonAreaBorder = 12,0,0,0
OptionPane.messageForeground = null
OptionPane.showIcon = false
OptionPane.maxCharactersPerLine = 80
OptionPane.iconMessageGap = 16
OptionPane.messagePadding = 3
@@ -587,6 +650,7 @@ SplitPaneDivider.gripGap = 2
TabbedPane.tabHeight = 32
TabbedPane.tabSelectionHeight = 3
TabbedPane.cardTabSelectionHeight = 2
TabbedPane.contentSeparatorHeight = 1
TabbedPane.showTabSeparators = false
TabbedPane.tabSeparatorsFullHeight = false
@@ -608,6 +672,9 @@ TabbedPane.tabAlignment = center
# allowed values: preferred, equal or compact
TabbedPane.tabWidthMode = preferred
# allowed values: underlined or card
TabbedPane.tabType = underlined
# allowed values: chevron or triangle
TabbedPane.arrowType = chevron
TabbedPane.buttonInsets = 2,1,2,1
@@ -709,7 +776,9 @@ TitledBorder.border = 1,1,1,1,$Separator.foreground
TitlePane.useWindowDecorations = true
TitlePane.menuBarEmbedded = true
TitlePane.unifiedBackground = false
TitlePane.unifiedBackground = true
TitlePane.showIcon = true
TitlePane.noIconLeftGap = 8
TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0

View File

@@ -685,12 +685,14 @@ public class TestFlatStyleableInfo
"maximumTabWidth", int.class,
"tabHeight", int.class,
"tabSelectionHeight", int.class,
"cardTabSelectionHeight", int.class,
"contentSeparatorHeight", int.class,
"showTabSeparators", boolean.class,
"tabSeparatorsFullHeight", boolean.class,
"hasFullBorder", boolean.class,
"tabsOpaque", boolean.class,
"tabType", String.class,
"tabsPopupPolicy", String.class,
"scrollButtonsPolicy", String.class,
"scrollButtonsPlacement", String.class,

View File

@@ -849,12 +849,14 @@ public class TestFlatStyling
ui.applyStyle( "maximumTabWidth: 100" );
ui.applyStyle( "tabHeight: 30" );
ui.applyStyle( "tabSelectionHeight: 3" );
ui.applyStyle( "cardTabSelectionHeight: 2" );
ui.applyStyle( "contentSeparatorHeight: 1" );
ui.applyStyle( "showTabSeparators: false" );
ui.applyStyle( "tabSeparatorsFullHeight: false" );
ui.applyStyle( "hasFullBorder: false" );
ui.applyStyle( "tabsOpaque: false" );
ui.applyStyle( "tabType: card" );
ui.applyStyle( "tabsPopupPolicy: asNeeded" );
ui.applyStyle( "scrollButtonsPolicy: asNeeded" );
ui.applyStyle( "scrollButtonsPlacement: both" );

View File

@@ -22,6 +22,10 @@ import javax.swing.text.DefaultEditorKit;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.icons.FlatSearchIcon;
import net.miginfocom.layout.AC;
import net.miginfocom.layout.BoundSize;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.DimConstraint;
import net.miginfocom.swing.*;
/**
@@ -130,6 +134,22 @@ class BasicComponentsPanel
JTextField leadingIconTextField = new JTextField();
JTextField trailingIconTextField = new JTextField();
JTextField iconsTextField = new JTextField();
JLabel fontsLabel = new JLabel();
JLabel h00Label = new JLabel();
JLabel h0Label = new JLabel();
JLabel h1Label = new JLabel();
JLabel h2Label = new JLabel();
JLabel h3Label = new JLabel();
JLabel h4Label = new JLabel();
JLabel lightLabel = new JLabel();
JLabel semiboldLabel = new JLabel();
JLabel fontZoomLabel = new JLabel();
JLabel largeLabel = new JLabel();
JLabel defaultLabel = new JLabel();
JLabel mediumLabel = new JLabel();
JLabel smallLabel = new JLabel();
JLabel miniLabel = new JLabel();
JLabel monospacedLabel = new JLabel();
JPopupMenu popupMenu1 = new JPopupMenu();
JMenuItem cutMenuItem = new JMenuItem();
JMenuItem copyMenuItem = new JMenuItem();
@@ -160,6 +180,8 @@ class BasicComponentsPanel
"[]para" +
"[]" +
"[]" +
"[]" +
"[]0" +
"[]"));
//---- labelLabel ----
@@ -667,6 +689,85 @@ class BasicComponentsPanel
iconsTextField.setText("text");
add(iconsTextField, "cell 3 14,growx");
//---- fontsLabel ----
fontsLabel.setText("Typography / Fonts:");
add(fontsLabel, "cell 0 15");
//---- h00Label ----
h00Label.setText("H00");
h00Label.putClientProperty("FlatLaf.styleClass", "h00");
add(h00Label, "cell 1 15 5 1");
//---- h0Label ----
h0Label.setText("H0");
h0Label.putClientProperty("FlatLaf.styleClass", "h0");
add(h0Label, "cell 1 15 5 1");
//---- h1Label ----
h1Label.setText("H1");
h1Label.putClientProperty("FlatLaf.styleClass", "h1");
add(h1Label, "cell 1 15 5 1");
//---- h2Label ----
h2Label.setText("H2");
h2Label.putClientProperty("FlatLaf.styleClass", "h2");
add(h2Label, "cell 1 15 5 1");
//---- h3Label ----
h3Label.setText("H3");
h3Label.putClientProperty("FlatLaf.styleClass", "h3");
add(h3Label, "cell 1 15 5 1");
//---- h4Label ----
h4Label.setText("H4");
h4Label.putClientProperty("FlatLaf.styleClass", "h4");
add(h4Label, "cell 1 15 5 1");
//---- lightLabel ----
lightLabel.setText("light");
lightLabel.putClientProperty("FlatLaf.style", "font: 200% $light.font");
add(lightLabel, "cell 1 15 5 1,gapx 30");
//---- semiboldLabel ----
semiboldLabel.setText("semibold");
semiboldLabel.putClientProperty("FlatLaf.style", "font: 200% $semibold.font");
add(semiboldLabel, "cell 1 15 5 1");
//---- fontZoomLabel ----
fontZoomLabel.setText("(200%)");
fontZoomLabel.putClientProperty("FlatLaf.styleClass", "small");
fontZoomLabel.setEnabled(false);
add(fontZoomLabel, "cell 1 15 5 1");
//---- largeLabel ----
largeLabel.setText("large");
largeLabel.putClientProperty("FlatLaf.styleClass", "large");
add(largeLabel, "cell 1 16 5 1");
//---- defaultLabel ----
defaultLabel.setText("default");
add(defaultLabel, "cell 1 16 5 1");
//---- mediumLabel ----
mediumLabel.setText("medium");
mediumLabel.putClientProperty("FlatLaf.styleClass", "medium");
add(mediumLabel, "cell 1 16 5 1");
//---- smallLabel ----
smallLabel.setText("small");
smallLabel.putClientProperty("FlatLaf.styleClass", "small");
add(smallLabel, "cell 1 16 5 1");
//---- miniLabel ----
miniLabel.setText("mini");
miniLabel.putClientProperty("FlatLaf.styleClass", "mini");
add(miniLabel, "cell 1 16 5 1");
//---- monospacedLabel ----
monospacedLabel.setText("monospaced");
monospacedLabel.putClientProperty("FlatLaf.styleClass", "monospaced");
add(monospacedLabel, "cell 1 16 5 1,gapx 30");
//======== popupMenu1 ========
{
@@ -705,37 +806,55 @@ class BasicComponentsPanel
if( FlatLafDemo.screenshotsMode ) {
// hide some components
Component[] hiddenComponents = {
labelLabel, label1, label2,
button13, button14, button15, button16, comboBox5, comboBox6,
textField6, passwordField5,
textFieldLabel, textField2, textField4, textField6,
formattedTextFieldLabel, formattedTextField1, formattedTextField2, formattedTextField3, formattedTextField4, formattedTextField5,
passwordFieldLabel, passwordField1, passwordField2, passwordField3, passwordField4, passwordField5,
textAreaLabel, scrollPane1, scrollPane2, scrollPane3, scrollPane4, textArea5,
editorPaneLabel, scrollPane5, scrollPane6, scrollPane7, scrollPane8, editorPane5,
textPaneLabel, scrollPane9, scrollPane10, scrollPane11, scrollPane12, textPane5,
errorHintsLabel, errorHintsTextField, errorHintsComboBox, errorHintsSpinner,
warningHintsLabel, warningHintsTextField, warningHintsComboBox, warningHintsSpinner,
fontZoomLabel,
};
for( Component c : hiddenComponents )
c.setVisible( false );
// move leading/trailing icon fields and password fields some rows up
Component[] formattedTextFields = { formattedTextFieldLabel, formattedTextField1, formattedTextField2, formattedTextField3, formattedTextField4 };
Component[] passwordFields = { passwordFieldLabel, passwordField1, passwordField2, passwordField3, passwordField4 };
Component[] iconsFields = { iconsLabel, leadingIconTextField, trailingIconTextField, iconsTextField };
// update layout (change row gaps to zero)
MigLayout layout = (MigLayout) getLayout();
for( int i = 0; i < iconsFields.length; i++ ) {
Object cons = layout.getComponentConstraints( passwordFields[i] );
layout.setComponentConstraints( iconsFields[i], cons );
}
for( int i = 0; i < passwordFields.length; i++ ) {
Object cons = layout.getComponentConstraints( formattedTextFields[i] );
layout.setComponentConstraints( passwordFields[i], cons );
}
Object rowCons = layout.getRowConstraints();
AC ac = (rowCons instanceof String)
? ConstraintParser.parseColumnConstraints( (String) rowCons )
: (AC) rowCons;
BoundSize zeroGap = ConstraintParser.parseBoundSize( "0", true, true );
DimConstraint[] rows = ac.getConstaints();
rows[6].setGapBefore( zeroGap );
rows[7].setGapBefore( zeroGap );
rows[8].setGapBefore( zeroGap );
rows[9].setGapBefore( zeroGap );
rows[10].setGapBefore( zeroGap );
rows[11].setGapBefore( zeroGap );
rows[11].setGapAfter( zeroGap );
rows[12].setGapBefore( zeroGap );
rows[13].setGapBefore( zeroGap );
rows[15].setGapBefore( zeroGap );
layout.setRowConstraints( ac );
// move two text field into same row as spinners
spinnerLabel.setText( "JSpinner / JTextField:" );
layout.setComponentConstraints( textField1, "cell 3 5,growx" );
layout.setComponentConstraints( textField3, "cell 4 5,growx" );
// make "Not editable disabled" combobox smaller
Object cons = layout.getComponentConstraints( comboBox4 );
layout.setComponentConstraints( comboBox4, cons + ",width 50:50" );
revalidate();
repaint();
}
}

View File

@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[][sizegroup 1][sizegroup 1][sizegroup 1][][]"
"$rowConstraints": "[][][][][][][][][][][][]para[][][]"
"$rowConstraints": "[][][][][][][][][][][][]para[][][][]0[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -671,9 +671,120 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 14,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontsLabel"
"text": "Typography / Fonts:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 15"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h00Label"
"text": "H00"
"$client.FlatLaf.styleClass": "h00"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h0Label"
"text": "H0"
"$client.FlatLaf.styleClass": "h0"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h1Label"
"text": "H1"
"$client.FlatLaf.styleClass": "h1"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h2Label"
"text": "H2"
"$client.FlatLaf.styleClass": "h2"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h3Label"
"text": "H3"
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h4Label"
"text": "H4"
"$client.FlatLaf.styleClass": "h4"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "lightLabel"
"text": "light"
"$client.FlatLaf.style": "font: 200% $light.font"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1,gapx 30"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "semiboldLabel"
"text": "semibold"
"$client.FlatLaf.style": "font: 200% $semibold.font"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontZoomLabel"
"text": "(200%)"
"$client.FlatLaf.styleClass": "small"
"enabled": false
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "largeLabel"
"text": "large"
"$client.FlatLaf.styleClass": "large"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "defaultLabel"
"text": "default"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "mediumLabel"
"text": "medium"
"$client.FlatLaf.styleClass": "medium"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "smallLabel"
"text": "small"
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "miniLabel"
"text": "mini"
"$client.FlatLaf.styleClass": "mini"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "monospacedLabel"
"text": "monospaced"
"$client.FlatLaf.styleClass": "monospaced"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 16 5 1,gapx 30"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 920, 480 )
"size": new java.awt.Dimension( 920, 550 )
} )
add( new FormContainer( "javax.swing.JPopupMenu", new FormLayoutManager( class javax.swing.JPopupMenu ) ) {
name: "popupMenu1"
@@ -693,7 +804,7 @@ new FormModel {
"mnemonic": 80
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 500 )
"location": new java.awt.Point( 0, 570 )
"size": new java.awt.Dimension( 91, 87 )
} )
}

View File

@@ -29,6 +29,7 @@ import java.util.prefs.Preferences;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.StyleContext;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatDarculaLaf;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
@@ -50,7 +51,6 @@ import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
import net.miginfocom.layout.UnitValue;
@@ -160,8 +160,7 @@ class DemoFrame
private void aboutActionPerformed() {
JLabel titleLabel = new JLabel( "FlatLaf Demo" );
Font titleFont = titleLabel.getFont();
titleLabel.setFont( titleFont.deriveFont( (float) titleFont.getSize() + UIScale.scale( 6 ) ) );
titleLabel.putClientProperty( FlatClientProperties.STYLE_CLASS, "h1" );
String link = "https://www.formdev.com/flatlaf/";
JLabel linkLabel = new JLabel( "<html><a href=\"#\">" + link + "</a></html>" );
@@ -216,6 +215,7 @@ class DemoFrame
menuBarEmbeddedCheckBoxMenuItem.setEnabled( windowDecorations );
unifiedTitleBarMenuItem.setEnabled( windowDecorations );
showTitleBarIconMenuItem.setEnabled( windowDecorations );
}
private void menuBarEmbeddedChanged() {
@@ -228,6 +228,16 @@ class DemoFrame
FlatLaf.repaintAllFramesAndDialogs();
}
private void showTitleBarIcon() {
boolean showIcon = showTitleBarIconMenuItem.isSelected();
// for main frame (because already created)
getRootPane().putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, showIcon );
// for other not yet created frames/dialogs
UIManager.put( "TitlePane.showIcon", showIcon );
}
private void underlineMenuSelection() {
UIManager.put( "MenuItem.selectionType", underlineMenuSelectionMenuItem.isSelected() ? "underline" : null );
}
@@ -467,6 +477,7 @@ class DemoFrame
windowDecorationsCheckBoxMenuItem = new JCheckBoxMenuItem();
menuBarEmbeddedCheckBoxMenuItem = new JCheckBoxMenuItem();
unifiedTitleBarMenuItem = new JCheckBoxMenuItem();
showTitleBarIconMenuItem = new JCheckBoxMenuItem();
underlineMenuSelectionMenuItem = new JCheckBoxMenuItem();
alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem();
animatedLafChangeMenuItem = new JCheckBoxMenuItem();
@@ -718,13 +729,11 @@ class DemoFrame
//---- windowDecorationsCheckBoxMenuItem ----
windowDecorationsCheckBoxMenuItem.setText("Window decorations");
windowDecorationsCheckBoxMenuItem.setSelected(true);
windowDecorationsCheckBoxMenuItem.addActionListener(e -> windowDecorationsChanged());
optionsMenu.add(windowDecorationsCheckBoxMenuItem);
//---- menuBarEmbeddedCheckBoxMenuItem ----
menuBarEmbeddedCheckBoxMenuItem.setText("Embedded menu bar");
menuBarEmbeddedCheckBoxMenuItem.setSelected(true);
menuBarEmbeddedCheckBoxMenuItem.addActionListener(e -> menuBarEmbeddedChanged());
optionsMenu.add(menuBarEmbeddedCheckBoxMenuItem);
@@ -733,6 +742,11 @@ class DemoFrame
unifiedTitleBarMenuItem.addActionListener(e -> unifiedTitleBar());
optionsMenu.add(unifiedTitleBarMenuItem);
//---- showTitleBarIconMenuItem ----
showTitleBarIconMenuItem.setText("Show window title bar icon");
showTitleBarIconMenuItem.addActionListener(e -> showTitleBarIcon());
optionsMenu.add(showTitleBarIconMenuItem);
//---- underlineMenuSelectionMenuItem ----
underlineMenuSelectionMenuItem.setText("Use underline menu selection");
underlineMenuSelectionMenuItem.addActionListener(e -> underlineMenuSelection());
@@ -876,6 +890,11 @@ class DemoFrame
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
if( FlatLaf.supportsNativeWindowDecorations() ) {
windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() );
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) );
if( JBRCustomDecorations.isSupported() ) {
// If the JetBrains Runtime is used, it forces the use of it's own custom
// window decoration, which can not disabled.
@@ -885,6 +904,7 @@ class DemoFrame
unsupported( windowDecorationsCheckBoxMenuItem );
unsupported( menuBarEmbeddedCheckBoxMenuItem );
unsupported( unifiedTitleBarMenuItem );
unsupported( showTitleBarIconMenuItem );
}
if( SystemInfo.isMacOS )
@@ -917,6 +937,7 @@ class DemoFrame
private JCheckBoxMenuItem windowDecorationsCheckBoxMenuItem;
private JCheckBoxMenuItem menuBarEmbeddedCheckBoxMenuItem;
private JCheckBoxMenuItem unifiedTitleBarMenuItem;
private JCheckBoxMenuItem showTitleBarIconMenuItem;
private JCheckBoxMenuItem underlineMenuSelectionMenuItem;
private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem;
private JCheckBoxMenuItem animatedLafChangeMenuItem;

View File

@@ -354,7 +354,6 @@ new FormModel {
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "windowDecorationsCheckBoxMenuItem"
"text": "Window decorations"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -363,7 +362,6 @@ new FormModel {
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "menuBarEmbeddedCheckBoxMenuItem"
"text": "Embedded menu bar"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -377,6 +375,14 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "unifiedTitleBar", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "showTitleBarIconMenuItem"
"text": "Show window title bar icon"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTitleBarIcon", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "underlineMenuSelectionMenuItem"
"text": "Use underline menu selection"

View File

@@ -18,11 +18,15 @@ package com.formdev.flatlaf.demo;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatDropShadowBorder;
import com.formdev.flatlaf.ui.FlatPopupMenuBorder;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
@@ -87,6 +91,9 @@ class HintManager
initComponents();
setOpaque( false );
updateBalloonBorder();
hintLabel.setText( "<html>" + hint.message + "</html>" );
// grab all mouse events to avoid that components overlapped
@@ -98,8 +105,28 @@ class HintManager
public void updateUI() {
super.updateUI();
setBackground( UIManager.getColor( "HintPanel.backgroundColor" ) );
setBorder( new FlatPopupMenuBorder() );
if( UIManager.getLookAndFeel() instanceof FlatLaf )
setBackground( UIManager.getColor( "HintPanel.backgroundColor" ) );
else {
// using nonUIResource() because otherwise Nimbus does not fill the background
setBackground( FlatUIUtils.nonUIResource( UIManager.getColor( "info" ) ) );
}
if( hint != null )
updateBalloonBorder();
}
private void updateBalloonBorder() {
int direction;
switch( hint.position ) {
case SwingConstants.LEFT: direction = SwingConstants.RIGHT; break;
case SwingConstants.TOP: direction = SwingConstants.BOTTOM; break;
case SwingConstants.RIGHT: direction = SwingConstants.LEFT; break;
case SwingConstants.BOTTOM: direction = SwingConstants.TOP; break;
default: throw new IllegalArgumentException();
}
setBorder( new BalloonBorder( direction, FlatUIUtils.getUIColor( "PopupMenu.borderColor", Color.gray ) ) );
}
void showHint() {
@@ -115,11 +142,6 @@ class HintManager
public void updateUI() {
super.updateUI();
setBorder( new FlatDropShadowBorder(
UIManager.getColor( "Popup.dropShadowColor" ),
UIManager.getInsets( "Popup.dropShadowInsets" ),
FlatUIUtils.getUIFloat( "Popup.dropShadowOpacity", 0.5f ) ) );
// use invokeLater because at this time the UI delegates
// of child components are not yet updated
EventQueue.invokeLater( () -> {
@@ -216,4 +238,127 @@ class HintManager
private JButton gotItButton;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
//---- class BalloonBorder ------------------------------------------------
private static class BalloonBorder
extends FlatEmptyBorder
{
private static int ARC = 8;
private static int ARROW_XY = 16;
private static int ARROW_SIZE = 8;
private static int SHADOW_SIZE = 6;
private static int SHADOW_TOP_SIZE = 3;
private static int SHADOW_SIZE2 = SHADOW_SIZE + 2;
private final int direction;
private final Color borderColor;
private final Border shadowBorder;
public BalloonBorder( int direction, Color borderColor ) {
super( 1 + SHADOW_TOP_SIZE, 1 + SHADOW_SIZE, 1 + SHADOW_SIZE, 1 + SHADOW_SIZE );
this.direction = direction;
this.borderColor = borderColor;
switch( direction ) {
case SwingConstants.LEFT: left += ARROW_SIZE; break;
case SwingConstants.TOP: top += ARROW_SIZE; break;
case SwingConstants.RIGHT: right += ARROW_SIZE; break;
case SwingConstants.BOTTOM: bottom += ARROW_SIZE; break;
}
shadowBorder = UIManager.getLookAndFeel() instanceof FlatLaf
? new FlatDropShadowBorder(
UIManager.getColor( "Popup.dropShadowColor" ),
new Insets( SHADOW_SIZE2, SHADOW_SIZE2, SHADOW_SIZE2, SHADOW_SIZE2 ),
FlatUIUtils.getUIFloat( "Popup.dropShadowOpacity", 0.5f ) )
: null;
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.translate( x, y );
// shadow coordinates
int sx = 0;
int sy = 0;
int sw = width;
int sh = height;
int arrowSize = UIScale.scale( ARROW_SIZE );
switch( direction ) {
case SwingConstants.LEFT: sx += arrowSize; sw -= arrowSize; break;
case SwingConstants.TOP: sy += arrowSize; sh -= arrowSize; break;
case SwingConstants.RIGHT: sw -= arrowSize; break;
case SwingConstants.BOTTOM: sh -= arrowSize; break;
}
// paint shadow
if( shadowBorder != null )
shadowBorder.paintBorder( c, g2, sx, sy, sw, sh );
// create balloon shape
int bx = UIScale.scale( SHADOW_SIZE );
int by = UIScale.scale( SHADOW_TOP_SIZE );
int bw = width - UIScale.scale( SHADOW_SIZE + SHADOW_SIZE );
int bh = height - UIScale.scale( SHADOW_TOP_SIZE + SHADOW_SIZE );
g2.translate( bx, by );
Shape shape = createBalloonShape( bw, bh );
// fill balloon background
g2.setColor( c.getBackground() );
g2.fill( shape );
// paint balloon border
g2.setColor( borderColor );
g2.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
g2.draw( shape );
} finally {
g2.dispose();
}
}
private Shape createBalloonShape( int width, int height ) {
int arc = UIScale.scale( ARC );
int xy = UIScale.scale( ARROW_XY );
int awh = UIScale.scale( ARROW_SIZE );
Shape rect;
Shape arrow;
switch( direction ) {
case SwingConstants.LEFT:
rect = new RoundRectangle2D.Float( awh, 0, width - 1 - awh, height - 1, arc, arc );
arrow = FlatUIUtils.createPath( awh,xy, 0,xy+awh, awh,xy+awh+awh );
break;
case SwingConstants.TOP:
rect = new RoundRectangle2D.Float( 0, awh, width - 1, height - 1 - awh, arc, arc );
arrow = FlatUIUtils.createPath( xy,awh, xy+awh,0, xy+awh+awh,awh );
break;
case SwingConstants.RIGHT:
rect = new RoundRectangle2D.Float( 0, 0, width - 1 - awh, height - 1, arc, arc );
int x = width - 1 - awh;
arrow = FlatUIUtils.createPath( x,xy, x+awh,xy+awh, x,xy+awh+awh );
break;
case SwingConstants.BOTTOM:
rect = new RoundRectangle2D.Float( 0, 0, width - 1, height - 1 - awh, arc, arc );
int y = height - 1 - awh;
arrow = FlatUIUtils.createPath( xy,y, xy+awh,y+awh, xy+awh+awh,y );
break;
default:
throw new RuntimeException();
}
Area area = new Area( rect );
area.add( new Area( arrow ) );
return area;
}
}
}

View File

@@ -21,6 +21,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.FlatLaf;
import net.miginfocom.swing.*;
/**
@@ -44,6 +45,29 @@ class OptionPanePanel
"OK",
"Cancel",
} );
if( FlatLaf.supportsNativeWindowDecorations() ) {
updateShowTitleBarIcon();
UIManager.getDefaults().addPropertyChangeListener( e -> {
switch( e.getPropertyName() ) {
case "TitlePane.showIcon":
case "TitlePane.useWindowDecorations":
updateShowTitleBarIcon();
break;
}
} );
} else
showTitleBarIconCheckBox.setEnabled( false );
}
private void updateShowTitleBarIcon() {
showTitleBarIconCheckBox.setEnabled( UIManager.getBoolean( "TitlePane.showIcon" ) &&
FlatLaf.isUseNativeWindowDecorations() );
}
private void showTitleBarIcon() {
UIManager.put( "OptionPane.showIcon", showTitleBarIconCheckBox.isSelected() );
}
private void initComponents() {
@@ -54,6 +78,7 @@ class OptionPanePanel
JPanel panel1 = new JPanel();
JOptionPane plainOptionPane = new JOptionPane();
plainShowDialogLabel = new OptionPanePanel.ShowDialogLinkLabel();
showTitleBarIconCheckBox = new JCheckBox();
JLabel errorLabel = new JLabel();
JPanel panel2 = new JPanel();
JOptionPane errorOptionPane = new JOptionPane();
@@ -93,7 +118,7 @@ class OptionPanePanel
//======== panel9 ========
{
panel9.setLayout(new MigLayout(
"flowy,insets dialog,hidemode 3",
"insets dialog,hidemode 3",
// columns
"[]" +
"[]" +
@@ -126,7 +151,12 @@ class OptionPanePanel
//---- plainShowDialogLabel ----
plainShowDialogLabel.setOptionPane(plainOptionPane);
plainShowDialogLabel.setTitleLabel(plainLabel);
panel9.add(plainShowDialogLabel, "cell 2 0");
panel9.add(plainShowDialogLabel, "cell 1 0");
//---- showTitleBarIconCheckBox ----
showTitleBarIconCheckBox.setText("Show window title bar icon");
showTitleBarIconCheckBox.addActionListener(e -> showTitleBarIcon());
panel9.add(showTitleBarIconCheckBox, "cell 2 0");
//---- errorLabel ----
errorLabel.setText("Error");
@@ -148,7 +178,7 @@ class OptionPanePanel
//---- errorShowDialogLabel ----
errorShowDialogLabel.setTitleLabel(errorLabel);
errorShowDialogLabel.setOptionPane(errorOptionPane);
panel9.add(errorShowDialogLabel, "cell 2 1");
panel9.add(errorShowDialogLabel, "cell 1 1");
//---- informationLabel ----
informationLabel.setText("Information");
@@ -170,7 +200,7 @@ class OptionPanePanel
//---- informationShowDialogLabel ----
informationShowDialogLabel.setOptionPane(informationOptionPane);
informationShowDialogLabel.setTitleLabel(informationLabel);
panel9.add(informationShowDialogLabel, "cell 2 2");
panel9.add(informationShowDialogLabel, "cell 1 2");
//---- questionLabel ----
questionLabel.setText("Question");
@@ -214,7 +244,7 @@ class OptionPanePanel
//---- warningShowDialogLabel ----
warningShowDialogLabel.setOptionPane(warningOptionPane);
warningShowDialogLabel.setTitleLabel(warningLabel);
panel9.add(warningShowDialogLabel, "cell 2 4");
panel9.add(warningShowDialogLabel, "cell 1 4");
//---- inputLabel ----
inputLabel.setText("Input");
@@ -236,7 +266,7 @@ class OptionPanePanel
//---- inputShowDialogLabel ----
inputShowDialogLabel.setOptionPane(inputOptionPane);
inputShowDialogLabel.setTitleLabel(inputLabel);
panel9.add(inputShowDialogLabel, "cell 2 5");
panel9.add(inputShowDialogLabel, "cell 1 5");
//---- inputIconLabel ----
inputIconLabel.setText("Input + icon");
@@ -259,7 +289,7 @@ class OptionPanePanel
//---- inputIconShowDialogLabel ----
inputIconShowDialogLabel.setTitleLabel(inputIconLabel);
inputIconShowDialogLabel.setOptionPane(inputIconOptionPane);
panel9.add(inputIconShowDialogLabel, "cell 2 6");
panel9.add(inputIconShowDialogLabel, "cell 1 6");
//---- customLabel ----
customLabel.setText("Custom");
@@ -279,7 +309,7 @@ class OptionPanePanel
//---- customShowDialogLabel ----
customShowDialogLabel.setOptionPane(customOptionPane);
customShowDialogLabel.setTitleLabel(customLabel);
panel9.add(customShowDialogLabel, "cell 2 7");
panel9.add(customShowDialogLabel, "cell 1 7");
}
scrollPane1.setViewportView(panel9);
}
@@ -289,6 +319,7 @@ class OptionPanePanel
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private OptionPanePanel.ShowDialogLinkLabel plainShowDialogLabel;
private JCheckBox showTitleBarIconCheckBox;
private OptionPanePanel.ShowDialogLinkLabel errorShowDialogLabel;
private OptionPanePanel.ShowDialogLinkLabel informationShowDialogLabel;
private JOptionPane customOptionPane;
@@ -304,6 +335,7 @@ class OptionPanePanel
ShowDialogLinkLabel() {
setText( "<html><a href=\"#\">Show dialog</a></html>" );
setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) );
addMouseListener( new MouseAdapter() {
@Override

View File

@@ -12,7 +12,7 @@ new FormModel {
name: "scrollPane1"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormContainer( "com.formdev.flatlaf.demo.ScrollablePanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "flowy,insets dialog,hidemode 3"
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[][][fill]"
"$rowConstraints": "[top][top][top][top][top][top][top][top]"
} ) {
@@ -42,6 +42,16 @@ new FormModel {
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTitleBarIconCheckBox"
"text": "Show window title bar icon"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTitleBarIcon", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
@@ -73,7 +83,7 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "informationLabel"
@@ -103,7 +113,7 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "questionLabel"
@@ -157,7 +167,7 @@ new FormModel {
"optionPane": new FormReference( "warningOptionPane" )
"titleLabel": new FormReference( "warningLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
"value": "cell 1 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputLabel"
@@ -184,7 +194,7 @@ new FormModel {
"optionPane": new FormReference( "inputOptionPane" )
"titleLabel": new FormReference( "inputLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputIconLabel"
@@ -212,7 +222,7 @@ new FormModel {
"titleLabel": new FormReference( "inputIconLabel" )
"optionPane": new FormReference( "inputIconOptionPane" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 6"
"value": "cell 1 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "customLabel"
@@ -240,7 +250,7 @@ new FormModel {
"optionPane": new FormReference( "customOptionPane" )
"titleLabel": new FormReference( "customLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7"
"value": "cell 1 7"
} )
} )
}, new FormLayoutConstraints( class java.lang.String ) {
@@ -248,7 +258,7 @@ new FormModel {
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 840, 900 )
"size": new java.awt.Dimension( 955, 900 )
} )
}
}

View File

@@ -303,6 +303,11 @@ class TabsPanel
putTabbedPanesClientProperty( TABBED_PANE_SCROLL_BUTTONS_PLACEMENT, scrollButtonsPlacement );
}
private void tabTypeChanged() {
String tabType = cardTabTypeButton.isSelected() ? TABBED_PANE_TAB_TYPE_CARD : null;
putTabbedPanesClientProperty( TABBED_PANE_TAB_TYPE, tabType );
}
private void showTabSeparatorsChanged() {
Boolean showTabSeparators = showTabSeparatorsCheckBox.isSelected() ? true : null;
putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators );
@@ -396,11 +401,15 @@ class TabsPanel
scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
tabsPopupPolicyLabel = new JLabel();
tabsPopupPolicyToolBar = new JToolBar();
popupAsNeededButton = new JToggleButton();
popupNeverButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
tabTypeLabel = new JLabel();
tabTypeToolBar = new JToolBar();
underlinedTabTypeButton = new JToggleButton();
cardTabTypeButton = new JToggleButton();
//======== this ========
setName("this");
@@ -435,7 +444,7 @@ class TabsPanel
//---- tabPlacementLabel ----
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.setFont(tabPlacementLabel.getFont().deriveFont(tabPlacementLabel.getFont().getSize() + 4f));
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabPlacementLabel.setName("tabPlacementLabel");
panel1.add(tabPlacementLabel, "cell 0 0");
@@ -448,28 +457,28 @@ class TabsPanel
//---- topPlacementButton ----
topPlacementButton.setText("top");
topPlacementButton.setSelected(true);
topPlacementButton.setFont(topPlacementButton.getFont().deriveFont(topPlacementButton.getFont().getSize() - 2f));
topPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
topPlacementButton.setName("topPlacementButton");
topPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(topPlacementButton);
//---- bottomPlacementButton ----
bottomPlacementButton.setText("bottom");
bottomPlacementButton.setFont(bottomPlacementButton.getFont().deriveFont(bottomPlacementButton.getFont().getSize() - 2f));
bottomPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
bottomPlacementButton.setName("bottomPlacementButton");
bottomPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(bottomPlacementButton);
//---- leftPlacementButton ----
leftPlacementButton.setText("left");
leftPlacementButton.setFont(leftPlacementButton.getFont().deriveFont(leftPlacementButton.getFont().getSize() - 2f));
leftPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
leftPlacementButton.setName("leftPlacementButton");
leftPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(leftPlacementButton);
//---- rightPlacementButton ----
rightPlacementButton.setText("right");
rightPlacementButton.setFont(rightPlacementButton.getFont().deriveFont(rightPlacementButton.getFont().getSize() - 2f));
rightPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
rightPlacementButton.setName("rightPlacementButton");
rightPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(rightPlacementButton);
@@ -477,14 +486,14 @@ class TabsPanel
//---- scrollButton ----
scrollButton.setText("scroll");
scrollButton.setFont(scrollButton.getFont().deriveFont(scrollButton.getFont().getSize() - 2f));
scrollButton.putClientProperty("FlatLaf.styleClass", "small");
scrollButton.setName("scrollButton");
scrollButton.addActionListener(e -> scrollChanged());
tabPlacementToolBar.add(scrollButton);
//---- borderButton ----
borderButton.setText("border");
borderButton.setFont(borderButton.getFont().deriveFont(borderButton.getFont().getSize() - 2f));
borderButton.putClientProperty("FlatLaf.styleClass", "small");
borderButton.setName("borderButton");
borderButton.addActionListener(e -> borderChanged());
tabPlacementToolBar.add(borderButton);
@@ -499,7 +508,7 @@ class TabsPanel
//---- tabLayoutLabel ----
tabLayoutLabel.setText("Tab layout");
tabLayoutLabel.setFont(tabLayoutLabel.getFont().deriveFont(tabLayoutLabel.getFont().getSize() + 4f));
tabLayoutLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabLayoutLabel.setName("tabLayoutLabel");
panel1.add(tabLayoutLabel, "cell 0 2");
@@ -511,15 +520,15 @@ class TabsPanel
//---- scrollTabLayoutButton ----
scrollTabLayoutButton.setText("scroll");
scrollTabLayoutButton.setFont(scrollTabLayoutButton.getFont().deriveFont(scrollTabLayoutButton.getFont().getSize() - 2f));
scrollTabLayoutButton.setSelected(true);
scrollTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTabLayoutButton.setName("scrollTabLayoutButton");
scrollTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(scrollTabLayoutButton);
//---- wrapTabLayoutButton ----
wrapTabLayoutButton.setText("wrap");
wrapTabLayoutButton.setFont(wrapTabLayoutButton.getFont().deriveFont(wrapTabLayoutButton.getFont().getSize() - 2f));
wrapTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
wrapTabLayoutButton.setName("wrapTabLayoutButton");
wrapTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(wrapTabLayoutButton);
@@ -529,14 +538,14 @@ class TabsPanel
//---- scrollLayoutNoteLabel ----
scrollLayoutNoteLabel.setText("(use mouse wheel to scroll; arrow button shows hidden tabs)");
scrollLayoutNoteLabel.setEnabled(false);
scrollLayoutNoteLabel.setFont(scrollLayoutNoteLabel.getFont().deriveFont(scrollLayoutNoteLabel.getFont().getSize() - 2f));
scrollLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
scrollLayoutNoteLabel.setName("scrollLayoutNoteLabel");
panel1.add(scrollLayoutNoteLabel, "cell 0 3");
//---- wrapLayoutNoteLabel ----
wrapLayoutNoteLabel.setText("(probably better to use scroll layout?)");
wrapLayoutNoteLabel.setEnabled(false);
wrapLayoutNoteLabel.setFont(wrapLayoutNoteLabel.getFont().deriveFont(wrapLayoutNoteLabel.getFont().getSize() - 2f));
wrapLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
wrapLayoutNoteLabel.setName("wrapLayoutNoteLabel");
panel1.add(wrapLayoutNoteLabel, "cell 0 3");
@@ -554,7 +563,7 @@ class TabsPanel
//---- closableTabsLabel ----
closableTabsLabel.setText("Closable tabs");
closableTabsLabel.setFont(closableTabsLabel.getFont().deriveFont(closableTabsLabel.getFont().getSize() + 4f));
closableTabsLabel.putClientProperty("FlatLaf.styleClass", "h3");
closableTabsLabel.setName("closableTabsLabel");
panel1.add(closableTabsLabel, "cell 0 5");
@@ -566,22 +575,22 @@ class TabsPanel
//---- squareCloseButton ----
squareCloseButton.setText("square");
squareCloseButton.setFont(squareCloseButton.getFont().deriveFont(squareCloseButton.getFont().getSize() - 2f));
squareCloseButton.setSelected(true);
squareCloseButton.putClientProperty("FlatLaf.styleClass", "small");
squareCloseButton.setName("squareCloseButton");
squareCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(squareCloseButton);
//---- circleCloseButton ----
circleCloseButton.setText("circle");
circleCloseButton.setFont(circleCloseButton.getFont().deriveFont(circleCloseButton.getFont().getSize() - 2f));
circleCloseButton.putClientProperty("FlatLaf.styleClass", "small");
circleCloseButton.setName("circleCloseButton");
circleCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(circleCloseButton);
//---- redCrossCloseButton ----
redCrossCloseButton.setText("red cross");
redCrossCloseButton.setFont(redCrossCloseButton.getFont().deriveFont(redCrossCloseButton.getFont().getSize() - 2f));
redCrossCloseButton.putClientProperty("FlatLaf.styleClass", "small");
redCrossCloseButton.setName("redCrossCloseButton");
redCrossCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(redCrossCloseButton);
@@ -596,7 +605,7 @@ class TabsPanel
//---- tabAreaComponentsLabel ----
tabAreaComponentsLabel.setText("Custom tab area components");
tabAreaComponentsLabel.setFont(tabAreaComponentsLabel.getFont().deriveFont(tabAreaComponentsLabel.getFont().getSize() + 4f));
tabAreaComponentsLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabAreaComponentsLabel.setName("tabAreaComponentsLabel");
panel1.add(tabAreaComponentsLabel, "cell 0 7");
@@ -608,16 +617,16 @@ class TabsPanel
//---- leadingComponentButton ----
leadingComponentButton.setText("leading");
leadingComponentButton.setFont(leadingComponentButton.getFont().deriveFont(leadingComponentButton.getFont().getSize() - 2f));
leadingComponentButton.setSelected(true);
leadingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
leadingComponentButton.setName("leadingComponentButton");
leadingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(leadingComponentButton);
//---- trailingComponentButton ----
trailingComponentButton.setText("trailing");
trailingComponentButton.setFont(trailingComponentButton.getFont().deriveFont(trailingComponentButton.getFont().getSize() - 2f));
trailingComponentButton.setSelected(true);
trailingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
trailingComponentButton.setName("trailingComponentButton");
trailingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(trailingComponentButton);
@@ -655,14 +664,14 @@ class TabsPanel
//---- tabIconPlacementLabel ----
tabIconPlacementLabel.setText("Tab icon placement");
tabIconPlacementLabel.setFont(tabIconPlacementLabel.getFont().deriveFont(tabIconPlacementLabel.getFont().getSize() + 4f));
tabIconPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabIconPlacementLabel.setName("tabIconPlacementLabel");
panel2.add(tabIconPlacementLabel, "cell 0 0");
//---- tabIconPlacementNodeLabel ----
tabIconPlacementNodeLabel.setText("(top/bottom/leading/trailing)");
tabIconPlacementNodeLabel.setEnabled(false);
tabIconPlacementNodeLabel.setFont(tabIconPlacementNodeLabel.getFont().deriveFont(tabIconPlacementNodeLabel.getFont().getSize() - 2f));
tabIconPlacementNodeLabel.putClientProperty("FlatLaf.styleClass", "small");
tabIconPlacementNodeLabel.setName("tabIconPlacementNodeLabel");
panel2.add(tabIconPlacementNodeLabel, "cell 0 1");
@@ -692,14 +701,14 @@ class TabsPanel
//---- tabAreaAlignmentLabel ----
tabAreaAlignmentLabel.setText("Tab area alignment");
tabAreaAlignmentLabel.setFont(tabAreaAlignmentLabel.getFont().deriveFont(tabAreaAlignmentLabel.getFont().getSize() + 4f));
tabAreaAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabAreaAlignmentLabel.setName("tabAreaAlignmentLabel");
panel2.add(tabAreaAlignmentLabel, "cell 0 6");
//---- tabAreaAlignmentNoteLabel ----
tabAreaAlignmentNoteLabel.setText("(leading/center/trailing/fill)");
tabAreaAlignmentNoteLabel.setEnabled(false);
tabAreaAlignmentNoteLabel.setFont(tabAreaAlignmentNoteLabel.getFont().deriveFont(tabAreaAlignmentNoteLabel.getFont().getSize() - 2f));
tabAreaAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
tabAreaAlignmentNoteLabel.setName("tabAreaAlignmentNoteLabel");
panel2.add(tabAreaAlignmentNoteLabel, "cell 0 7");
@@ -750,14 +759,14 @@ class TabsPanel
//---- tabWidthModeLabel ----
tabWidthModeLabel.setText("Tab width mode");
tabWidthModeLabel.setFont(tabWidthModeLabel.getFont().deriveFont(tabWidthModeLabel.getFont().getSize() + 4f));
tabWidthModeLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabWidthModeLabel.setName("tabWidthModeLabel");
panel3.add(tabWidthModeLabel, "cell 0 0");
//---- tabWidthModeNoteLabel ----
tabWidthModeNoteLabel.setText("(preferred/equal/compact)");
tabWidthModeNoteLabel.setFont(tabWidthModeNoteLabel.getFont().deriveFont(tabWidthModeNoteLabel.getFont().getSize() - 2f));
tabWidthModeNoteLabel.setEnabled(false);
tabWidthModeNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
tabWidthModeNoteLabel.setName("tabWidthModeNoteLabel");
panel3.add(tabWidthModeNoteLabel, "cell 0 1");
@@ -781,7 +790,7 @@ class TabsPanel
//---- minMaxTabWidthLabel ----
minMaxTabWidthLabel.setText("Minimum/maximum tab width");
minMaxTabWidthLabel.setFont(minMaxTabWidthLabel.getFont().deriveFont(minMaxTabWidthLabel.getFont().getSize() + 4f));
minMaxTabWidthLabel.putClientProperty("FlatLaf.styleClass", "h3");
minMaxTabWidthLabel.setName("minMaxTabWidthLabel");
panel3.add(minMaxTabWidthLabel, "cell 0 5");
@@ -799,7 +808,7 @@ class TabsPanel
//---- tabAlignmentLabel ----
tabAlignmentLabel.setText("Tab title alignment");
tabAlignmentLabel.setFont(tabAlignmentLabel.getFont().deriveFont(tabAlignmentLabel.getFont().getSize() + 4f));
tabAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
tabAlignmentLabel.setName("tabAlignmentLabel");
panel3.add(tabAlignmentLabel, "cell 0 8");
@@ -820,14 +829,14 @@ class TabsPanel
//---- tabAlignmentNoteLabel ----
tabAlignmentNoteLabel.setText("(leading/center/trailing)");
tabAlignmentNoteLabel.setEnabled(false);
tabAlignmentNoteLabel.setFont(tabAlignmentNoteLabel.getFont().deriveFont(tabAlignmentNoteLabel.getFont().getSize() - 2f));
tabAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
tabAlignmentNoteLabel.setName("tabAlignmentNoteLabel");
panel5.add(tabAlignmentNoteLabel, "cell 0 0");
//---- tabAlignmentNoteLabel2 ----
tabAlignmentNoteLabel2.setText("(trailing)");
tabAlignmentNoteLabel2.setEnabled(false);
tabAlignmentNoteLabel2.setFont(tabAlignmentNoteLabel2.getFont().deriveFont(tabAlignmentNoteLabel2.getFont().getSize() - 2f));
tabAlignmentNoteLabel2.putClientProperty("FlatLaf.styleClass", "small");
tabAlignmentNoteLabel2.setName("tabAlignmentNoteLabel2");
panel5.add(tabAlignmentNoteLabel2, "cell 1 0,alignx right,growx 0");
@@ -873,7 +882,8 @@ class TabsPanel
"[]" +
"[fill]para" +
"[fill]" +
"[fill]para",
"[fill]para" +
"[fill]",
// rows
"[]" +
"[center]"));
@@ -891,22 +901,22 @@ class TabsPanel
//---- scrollAsNeededSingleButton ----
scrollAsNeededSingleButton.setText("asNeededSingle");
scrollAsNeededSingleButton.setFont(scrollAsNeededSingleButton.getFont().deriveFont(scrollAsNeededSingleButton.getFont().getSize() - 2f));
scrollAsNeededSingleButton.setSelected(true);
scrollAsNeededSingleButton.putClientProperty("FlatLaf.styleClass", "small");
scrollAsNeededSingleButton.setName("scrollAsNeededSingleButton");
scrollAsNeededSingleButton.addActionListener(e -> scrollButtonsPolicyChanged());
scrollButtonsPolicyToolBar.add(scrollAsNeededSingleButton);
//---- scrollAsNeededButton ----
scrollAsNeededButton.setText("asNeeded");
scrollAsNeededButton.setFont(scrollAsNeededButton.getFont().deriveFont(scrollAsNeededButton.getFont().getSize() - 2f));
scrollAsNeededButton.putClientProperty("FlatLaf.styleClass", "small");
scrollAsNeededButton.setName("scrollAsNeededButton");
scrollAsNeededButton.addActionListener(e -> scrollButtonsPolicyChanged());
scrollButtonsPolicyToolBar.add(scrollAsNeededButton);
//---- scrollNeverButton ----
scrollNeverButton.setText("never");
scrollNeverButton.setFont(scrollNeverButton.getFont().deriveFont(scrollNeverButton.getFont().getSize() - 2f));
scrollNeverButton.putClientProperty("FlatLaf.styleClass", "small");
scrollNeverButton.setName("scrollNeverButton");
scrollNeverButton.addActionListener(e -> scrollButtonsPolicyChanged());
scrollButtonsPolicyToolBar.add(scrollNeverButton);
@@ -926,21 +936,27 @@ class TabsPanel
//---- scrollBothButton ----
scrollBothButton.setText("both");
scrollBothButton.setFont(scrollBothButton.getFont().deriveFont(scrollBothButton.getFont().getSize() - 2f));
scrollBothButton.setSelected(true);
scrollBothButton.putClientProperty("FlatLaf.styleClass", "small");
scrollBothButton.setName("scrollBothButton");
scrollBothButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollBothButton);
//---- scrollTrailingButton ----
scrollTrailingButton.setText("trailing");
scrollTrailingButton.setFont(scrollTrailingButton.getFont().deriveFont(scrollTrailingButton.getFont().getSize() - 2f));
scrollTrailingButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTrailingButton.setName("scrollTrailingButton");
scrollTrailingButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollTrailingButton);
}
panel4.add(scrollButtonsPlacementToolBar, "cell 3 0");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.setName("showTabSeparatorsCheckBox");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 4 0");
//---- tabsPopupPolicyLabel ----
tabsPopupPolicyLabel.setText("Tabs popup policy:");
tabsPopupPolicyLabel.setName("tabsPopupPolicyLabel");
@@ -954,26 +970,47 @@ class TabsPanel
//---- popupAsNeededButton ----
popupAsNeededButton.setText("asNeeded");
popupAsNeededButton.setFont(popupAsNeededButton.getFont().deriveFont(popupAsNeededButton.getFont().getSize() - 2f));
popupAsNeededButton.setSelected(true);
popupAsNeededButton.putClientProperty("FlatLaf.styleClass", "small");
popupAsNeededButton.setName("popupAsNeededButton");
popupAsNeededButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupAsNeededButton);
//---- popupNeverButton ----
popupNeverButton.setText("never");
popupNeverButton.setFont(popupNeverButton.getFont().deriveFont(popupNeverButton.getFont().getSize() - 2f));
popupNeverButton.putClientProperty("FlatLaf.styleClass", "small");
popupNeverButton.setName("popupNeverButton");
popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupNeverButton);
}
panel4.add(tabsPopupPolicyToolBar, "cell 1 1");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.setName("showTabSeparatorsCheckBox");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 2 1 2 1");
//---- tabTypeLabel ----
tabTypeLabel.setText("Tab type:");
tabTypeLabel.setName("tabTypeLabel");
panel4.add(tabTypeLabel, "cell 2 1");
//======== tabTypeToolBar ========
{
tabTypeToolBar.setFloatable(false);
tabTypeToolBar.setName("tabTypeToolBar");
//---- underlinedTabTypeButton ----
underlinedTabTypeButton.setText("underlined");
underlinedTabTypeButton.setSelected(true);
underlinedTabTypeButton.putClientProperty("FlatLaf.styleClass", "small");
underlinedTabTypeButton.setName("underlinedTabTypeButton");
underlinedTabTypeButton.addActionListener(e -> tabTypeChanged());
tabTypeToolBar.add(underlinedTabTypeButton);
//---- cardTabTypeButton ----
cardTabTypeButton.setText("card");
cardTabTypeButton.putClientProperty("FlatLaf.styleClass", "small");
cardTabTypeButton.setName("cardTabTypeButton");
cardTabTypeButton.addActionListener(e -> tabTypeChanged());
tabTypeToolBar.add(cardTabTypeButton);
}
panel4.add(tabTypeToolBar, "cell 3 1");
}
add(panel4, "cell 0 2 3 1");
@@ -1010,6 +1047,11 @@ class TabsPanel
ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup();
tabsPopupPolicyButtonGroup.add(popupAsNeededButton);
tabsPopupPolicyButtonGroup.add(popupNeverButton);
//---- tabTypeButtonGroup ----
ButtonGroup tabTypeButtonGroup = new ButtonGroup();
tabTypeButtonGroup.add(underlinedTabTypeButton);
tabTypeButtonGroup.add(cardTabTypeButton);
// JFormDesigner - End of component initialization //GEN-END:initComponents
if( FlatLafDemo.screenshotsMode ) {
@@ -1089,10 +1131,14 @@ class TabsPanel
private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel tabsPopupPolicyLabel;
private JToolBar tabsPopupPolicyToolBar;
private JToggleButton popupAsNeededButton;
private JToggleButton popupNeverButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel tabTypeLabel;
private JToolBar tabTypeToolBar;
private JToggleButton underlinedTabTypeButton;
private JToggleButton cardTabTypeButton;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -22,7 +22,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabPlacementLabel"
"text": "Tab placement"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -38,28 +38,28 @@ new FormModel {
"text": "top"
"selected": true
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": &SwingDerivedFont0 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "bottomPlacementButton"
"text": "bottom"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "leftPlacementButton"
"text": "left"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "rightPlacementButton"
"text": "right"
"$buttonGroup": new FormReference( "tabPlacementButtonGroup" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToolBar$Separator" ) {
@@ -68,13 +68,13 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollButton"
"text": "scroll"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "borderButton"
"text": "border"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "borderChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
@@ -88,7 +88,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabLayoutLabel"
"text": "Tab layout"
"font": &SwingDerivedFont1 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -103,15 +103,15 @@ new FormModel {
name: "scrollTabLayoutButton"
"text": "scroll"
"$buttonGroup": new FormReference( "tabLayoutButtonGroup" )
"font": &SwingDerivedFont2 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"selected": true
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabLayoutChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "wrapTabLayoutButton"
"text": "wrap"
"$buttonGroup": new FormReference( "tabLayoutButtonGroup" )
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabLayoutChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
@@ -121,7 +121,7 @@ new FormModel {
name: "scrollLayoutNoteLabel"
"text": "(use mouse wheel to scroll; arrow button shows hidden tabs)"
"enabled": false
"font": &SwingDerivedFont3 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
@@ -129,7 +129,7 @@ new FormModel {
name: "wrapLayoutNoteLabel"
"text": "(probably better to use scroll layout?)"
"enabled": false
"font": #SwingDerivedFont3
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
@@ -146,7 +146,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "closableTabsLabel"
"text": "Closable tabs"
"font": #SwingDerivedFont1
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -160,23 +160,23 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "squareCloseButton"
"text": "square"
"font": &SwingDerivedFont4 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
"selected": true
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "circleCloseButton"
"text": "circle"
"font": #SwingDerivedFont4
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "redCrossCloseButton"
"text": "red cross"
"font": #SwingDerivedFont4
"$buttonGroup": new FormReference( "closableTabsButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "closeButtonStyleChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
@@ -190,7 +190,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAreaComponentsLabel"
"text": "Custom tab area components"
"font": #SwingDerivedFont1
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -204,15 +204,15 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "leadingComponentButton"
"text": "leading"
"font": &SwingDerivedFont5 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"selected": true
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customComponentsChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "trailingComponentButton"
"text": "trailing"
"font": #SwingDerivedFont5
"selected": true
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customComponentsChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
@@ -238,7 +238,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabIconPlacementLabel"
"text": "Tab icon placement"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -249,7 +249,7 @@ new FormModel {
name: "tabIconPlacementNodeLabel"
"text": "(top/bottom/leading/trailing)"
"enabled": false
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -279,7 +279,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAreaAlignmentLabel"
"text": "Tab area alignment"
"font": &SwingDerivedFont6 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -290,7 +290,7 @@ new FormModel {
name: "tabAreaAlignmentNoteLabel"
"text": "(leading/center/trailing/fill)"
"enabled": false
"font": &SwingDerivedFont7 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -332,7 +332,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabWidthModeLabel"
"text": "Tab width mode"
"font": #SwingDerivedFont6
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -342,8 +342,8 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabWidthModeNoteLabel"
"text": "(preferred/equal/compact)"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"enabled": false
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -368,7 +368,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "minMaxTabWidthLabel"
"text": "Minimum/maximum tab width"
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -388,7 +388,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabAlignmentLabel"
"text": "Tab title alignment"
"font": #SwingDerivedFont6
"$client.FlatLaf.styleClass": "h3"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -405,7 +405,7 @@ new FormModel {
name: "tabAlignmentNoteLabel"
"text": "(leading/center/trailing)"
"enabled": false
"font": #SwingDerivedFont7
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -416,7 +416,7 @@ new FormModel {
name: "tabAlignmentNoteLabel2"
"text": "(trailing)"
"enabled": false
"font": #SwingDerivedFont7
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
@@ -457,7 +457,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[][fill]para[fill][fill]para"
"$columnConstraints": "[][fill]para[fill][fill]para[fill]"
"$rowConstraints": "[][center]"
} ) {
name: "panel4"
@@ -477,23 +477,23 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollAsNeededSingleButton"
"text": "asNeededSingle"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollAsNeededButton"
"text": "asNeeded"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollNeverButton"
"text": "never"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPolicyButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPolicyChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
@@ -512,21 +512,31 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollBothButton"
"text": "both"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "scrollButtonsPlacementButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPlacementChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "scrollTrailingButton"
"text": "trailing"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "scrollButtonsPlacementButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "scrollButtonsPlacementChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox"
"text": "Show tab separators"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabsPopupPolicyLabel"
"text": "Tabs popup policy:"
@@ -540,37 +550,54 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "popupAsNeededButton"
"text": "asNeeded"
"font": #SwingDerivedFont2
"selected": true
"$buttonGroup": new FormReference( "tabsPopupPolicyButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabsPopupPolicyChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "popupNeverButton"
"text": "never"
"font": #SwingDerivedFont2
"$buttonGroup": new FormReference( "tabsPopupPolicyButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabsPopupPolicyChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox"
"text": "Show tab separators"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabTypeLabel"
"text": "Tab type:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1 2 1"
"value": "cell 2 1"
} )
add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) {
name: "tabTypeToolBar"
"floatable": false
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "underlinedTabTypeButton"
"text": "underlined"
"selected": true
"$buttonGroup": new FormReference( "tabTypeButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabTypeChanged", false ) )
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "cardTabTypeButton"
"text": "card"
"$buttonGroup": new FormReference( "tabTypeButtonGroup" )
"$client.FlatLaf.styleClass": "small"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabTypeChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 3 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 1075, 895 )
"size": new java.awt.Dimension( 1145, 895 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "tabPlacementButtonGroup"
@@ -602,5 +629,10 @@ new FormModel {
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 200, 1020 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "tabTypeButtonGroup"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 1072 )
} )
}
}

View File

@@ -474,9 +474,9 @@ public class FlatInspector
if( c instanceof JComponent )
appendRow( buf, "Border", toString( ((JComponent)c).getBorder(), classHierarchy ) );
appendRow( buf, "Background", toString( c.getBackground() ) );
appendRow( buf, "Foreground", toString( c.getForeground() ) );
appendRow( buf, "Font", toString( c.getFont() ) );
appendRow( buf, "Background", toString( c.getBackground() ) + (c.isBackgroundSet() ? "" : " NOT SET") );
appendRow( buf, "Foreground", toString( c.getForeground() ) + (c.isBackgroundSet() ? "" : " NOT SET") );
appendRow( buf, "Font", toString( c.getFont() ) + (c.isFontSet() ? "" : " NOT SET") );
if( c instanceof JComponent ) {
try {

View File

@@ -27,6 +27,10 @@ import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
@@ -44,6 +48,7 @@ import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SoftCache;
import com.formdev.flatlaf.util.UIScale;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
@@ -58,8 +63,12 @@ public class FlatSVGIcon
extends ImageIcon
implements DisabledIconProvider
{
// cache that uses soft references for values, which allows freeing SVG diagrams if no longer used
private static final SoftCache<URI, SVGDiagram> svgCache = new SoftCache<>();
// use own SVG universe so that it can not be cleared from anywhere
private static final SVGUniverse svgUniverse = new SVGUniverse();
private static int streamNumber;
private final String name;
private final int width;
@@ -67,23 +76,30 @@ public class FlatSVGIcon
private final float scale;
private final boolean disabled;
private final ClassLoader classLoader;
private final URI uri;
private ColorFilter colorFilter;
private SVGDiagram diagram;
private boolean dark;
private boolean loadFailed;
/**
* Creates an SVG icon from the given resource name.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name ) {
this( name, -1, -1, 1, false, null );
this( name, -1, -1, 1, false, null, null );
}
/**
@@ -92,27 +108,37 @@ public class FlatSVGIcon
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, ClassLoader classLoader ) {
this( name, -1, -1, 1, false, classLoader );
this( name, -1, -1, 1, false, classLoader, null );
}
/**
* Creates an SVG icon from the given resource name with the given width and height.
* <p>
* The icon is scaled if the given size is different to the size specified in the SVG file.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @param width the width of the icon
* @param height the height of the icon
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, int width, int height ) {
this( name, width, height, 1, false, null );
this( name, width, height, 1, false, null, null );
}
/**
@@ -120,15 +146,20 @@ public class FlatSVGIcon
* The SVG file is loaded from the given class loader.
* <p>
* The icon is scaled if the given size is different to the size specified in the SVG file.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @param width the width of the icon
* @param height the height of the icon
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, int width, int height, ClassLoader classLoader ) {
this( name, width, height, 1, false, classLoader );
this( name, width, height, 1, false, classLoader, null );
}
/**
@@ -137,13 +168,18 @@ public class FlatSVGIcon
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as base icon size, which is multiplied
* by the given scale factor.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @param scale the amount by which the icon size is scaled
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, float scale ) {
this( name, -1, -1, scale, false, null );
this( name, -1, -1, scale, false, null, null );
}
/**
@@ -153,23 +189,107 @@ public class FlatSVGIcon
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as base icon size, which is multiplied
* by the given scale factor.
* <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise use {@link #FlatSVGIcon(URL)}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param name the name of the SVG resource (a '/'-separated path)
* @param name the name of the SVG resource (a '/'-separated path; e.g. {@code "com/myapp/myicon.svg"})
* @param scale the amount by which the icon size is scaled
* @param classLoader the class loader used to load the SVG resource
* @see ClassLoader#getResource(String)
*/
public FlatSVGIcon( String name, float scale, ClassLoader classLoader ) {
this( name, -1, -1, scale, false, classLoader );
this( name, -1, -1, scale, false, classLoader, null );
}
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader ) {
/**
* Creates an SVG icon from the given URL.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* This method is useful if using Java modules and the package containing the icon
* is not opened in {@code module-info.java}.
* E.g. {@code new FlatSVGIcon( getClass().getResource( "/com/myapp/myicon.svg" ) )}.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param url the URL of the SVG resource
* @see ClassLoader#getResource(String)
* @since 2
*/
public FlatSVGIcon( URL url ) {
this( null, -1, -1, 1, false, null, url2uri( url ) );
}
/**
* Creates an SVG icon from the given URI.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param uri the URI of the SVG resource
* @see ClassLoader#getResource(String)
* @since 2
*/
public FlatSVGIcon( URI uri ) {
this( null, -1, -1, 1, false, null, uri );
}
/**
* Creates an SVG icon from the given file.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* This is cheap operation because the icon is only loaded when used.
*
* @param file the SVG file
* @since 2
*/
public FlatSVGIcon( File file ) {
this( null, -1, -1, 1, false, null, file.toURI() );
}
/**
* Creates an SVG icon from the given input stream.
* <p>
* The SVG attributes {@code width} and {@code height} (or {@code viewBox})
* in the tag {@code <svg>} are used as icon size.
* <p>
* The input stream is loaded, parsed and closed immediately.
*
* @param in the input stream for reading a SVG resource
* @throws IOException if an I/O exception occurs
* @since 2
*/
public FlatSVGIcon( InputStream in ) throws IOException {
this( null, -1, -1, 1, false, null, loadFromStream( in ) );
// since the input stream is already loaded and parsed,
// get diagram here and remove it from cache
update();
svgCache.remove( uri );
}
private static URI loadFromStream( InputStream in ) throws IOException {
try( InputStream in2 = in ) {
return svgUniverse.loadSVG( in2, "/flatlaf-stream-" + (streamNumber++) );
}
}
protected FlatSVGIcon( String name, int width, int height, float scale, boolean disabled, ClassLoader classLoader, URI uri ) {
this.name = name;
this.width = width;
this.height = height;
this.scale = scale;
this.disabled = disabled;
this.classLoader = classLoader;
this.uri = uri;
}
/**
@@ -248,7 +368,7 @@ public class FlatSVGIcon
if( width == this.width && height == this.height )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.dark = dark;
@@ -267,7 +387,7 @@ public class FlatSVGIcon
if( scale == this.scale )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, disabled, classLoader, uri );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.dark = dark;
@@ -286,7 +406,7 @@ public class FlatSVGIcon
if( disabled )
return this;
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader );
FlatSVGIcon icon = new FlatSVGIcon( name, width, height, scale, true, classLoader, uri );
icon.colorFilter = colorFilter;
icon.diagram = diagram;
icon.dark = dark;
@@ -324,20 +444,56 @@ public class FlatSVGIcon
}
private void update() {
if( loadFailed )
return;
if( dark == isDarkLaf() && diagram != null )
return;
dark = isDarkLaf();
URL url = getIconURL( name, dark );
if( url == null & dark )
url = getIconURL( name, false );
// load/get image
try {
diagram = svgUniverse.getDiagram( url.toURI() );
} catch( URISyntaxException ex ) {
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to load SVG icon '" + url + "'.", ex );
// SVGs already loaded via url or input stream can not have light/dark variants
if( uri != null && diagram != null )
return;
URI uri = this.uri;
if( uri == null ) {
URL url = getIconURL( name, dark );
if( url == null & dark )
url = getIconURL( name, false );
if( url == null ) {
loadFailed = true;
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: resource '" + name + "' not found (if using Java modules, check whether icon package is opened in module-info.java)", null );
return;
}
uri = url2uri( url );
}
diagram = loadSVG( uri );
loadFailed = (diagram == null);
}
static SVGDiagram loadSVG( URI uri ) {
// get from our cache
SVGDiagram diagram = svgCache.get( uri );
if( diagram != null )
return diagram;
// load/get SVG diagram
diagram = svgUniverse.getDiagram( uri );
if( diagram == null ) {
LoggingFacade.INSTANCE.logSevere( "FlatSVGIcon: failed to load '" + uri + "'", null );
return null;
}
// add to our (soft) cache and remove from SVGUniverse (hard) cache
svgCache.put( uri, diagram );
svgUniverse.removeDocument( uri );
return diagram;
}
private URL getIconURL( String name, boolean dark ) {
@@ -487,6 +643,14 @@ public class FlatSVGIcon
return MultiResolutionImageSupport.create( 0, dimensions, producer );
}
static URI url2uri( URL url ) {
try {
return url.toURI();
} catch( URISyntaxException ex ) {
throw new IllegalArgumentException( ex );
}
}
private static Boolean darkLaf;
/**

View File

@@ -21,7 +21,6 @@ import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
@@ -29,7 +28,6 @@ import java.util.List;
import javax.swing.JWindow;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo;
import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
@@ -50,6 +48,9 @@ public class FlatSVGUtils
* for requested sizes from SVG.
* This has the advantage that only images for used sizes are created.
* Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #createWindowIconImages(URL)}.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
@@ -57,7 +58,32 @@ public class FlatSVGUtils
* @see JWindow#setIconImages(List)
*/
public static List<Image> createWindowIconImages( String svgName ) {
SVGDiagram diagram = loadSVG( svgName );
return createWindowIconImages( getResource( svgName ) );
}
/**
* Creates from the given SVG a list of icon images with different sizes that
* can be used for windows headers. The SVG should have a size of 16x16,
* otherwise it is scaled.
* <p>
* If running on Windows in Java 9 or later and multi-resolution image support is available,
* then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG.
* This has the advantage that only images for used sizes are created.
* Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p>
* This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}.
* E.g. {@code createWindowIconImages( getClass().getResource( "/com/myapp/myicon.svg" ) )}.
*
* @param svgUrl the URL of the SVG resource
* @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
* @throws RuntimeException if failed to load or render SVG file
* @see JWindow#setIconImages(List)
* @since 2
*/
public static List<Image> createWindowIconImages( URL svgUrl ) {
SVGDiagram diagram = loadSVG( svgUrl );
if( SystemInfo.isWindows && MultiResolutionImageSupport.isAvailable() ) {
// use a multi-resolution image that creates images on demand for requested sizes
@@ -93,6 +119,9 @@ public class FlatSVGUtils
/**
* Creates a buffered image and renders the given SVG into it.
* <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #svg2image(URL, int, int)}.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @param width the width of the image
@@ -101,11 +130,32 @@ public class FlatSVGUtils
* @throws RuntimeException if failed to load or render SVG file
*/
public static BufferedImage svg2image( String svgName, int width, int height ) {
return svg2image( loadSVG( svgName ), width, height );
return svg2image( getResource( svgName ), width, height );
}
/**
* Creates a buffered image and renders the given SVG into it.
* <p>
* This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}.
* E.g. {@code svg2image( getClass().getResource( "/com/myapp/myicon.svg" ), 24, 24 )}.
*
* @param svgUrl the URL of the SVG resource
* @param width the width of the image
* @param height the height of the image
* @return the image
* @throws RuntimeException if failed to load or render SVG file
* @since 2
*/
public static BufferedImage svg2image( URL svgUrl, int width, int height ) {
return svg2image( loadSVG( svgUrl ), width, height );
}
/**
* Creates a buffered image and renders the given SVG into it.
* <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise use {@link #svg2image(URL, float)}.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @param scaleFactor the amount by which the SVG size is scaled
@@ -113,7 +163,24 @@ public class FlatSVGUtils
* @throws RuntimeException if failed to load or render SVG file
*/
public static BufferedImage svg2image( String svgName, float scaleFactor ) {
SVGDiagram diagram = loadSVG( svgName );
return svg2image( getResource( svgName ), scaleFactor );
}
/**
* Creates a buffered image and renders the given SVG into it.
* <p>
* This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}.
* E.g. {@code svg2image( getClass().getResource( "/com/myapp/myicon.svg" ), 1.5f )}.
*
* @param svgUrl the URL of the SVG resource
* @param scaleFactor the amount by which the SVG size is scaled
* @return the image
* @throws RuntimeException if failed to load or render SVG file
* @since 2
*/
public static BufferedImage svg2image( URL svgUrl, float scaleFactor ) {
SVGDiagram diagram = loadSVG( svgUrl );
int width = (int) (diagram.getWidth() * scaleFactor);
int height = (int) (diagram.getHeight() * scaleFactor);
return svg2image( diagram, width, height );
@@ -155,19 +222,11 @@ public class FlatSVGUtils
}
}
/**
* Loads a SVG file.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
* @return the SVG diagram
* @throws RuntimeException if failed to load SVG file
*/
private static SVGDiagram loadSVG( String svgName ) {
try {
URL url = FlatSVGUtils.class.getResource( svgName );
return SVGCache.getSVGUniverse().getDiagram( url.toURI() );
} catch( URISyntaxException ex ) {
throw new RuntimeException( ex );
}
private static URL getResource( String svgName ) {
return FlatSVGUtils.class.getResource( svgName );
}
private static SVGDiagram loadSVG( URL url ) {
return FlatSVGIcon.loadSVG( FlatSVGIcon.url2uri( url ) );
}
}

View File

@@ -1000,6 +1000,9 @@ public class FlatUIDefaultsInspector
{
private Item item;
// used instead of getBackground() because this did not work in some 3rd party Lafs
private Color valueColor;
@Override
public Component getTableCellRendererComponent( JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column )
@@ -1023,7 +1026,7 @@ public class FlatUIDefaultsInspector
if( item.value instanceof Color ) {
Color color = (item.info instanceof Color[]) ? ((Color[])item.info)[0] : (Color) item.value;
boolean isDark = new HSLColor( color ).getLuminance() < 70 && color.getAlpha() >= 128;
setBackground( color );
valueColor = color;
setForeground( isDark ? Color.white : Color.black );
} else if( item.value instanceof Icon ) {
Icon icon = (Icon) item.value;
@@ -1048,7 +1051,7 @@ public class FlatUIDefaultsInspector
if( item.value instanceof Color ) {
int width = getWidth();
int height = getHeight();
Color background = getBackground();
Color background = valueColor;
// paint color
fillRect( g, background, 0, 0, width, height );

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.extras.components;
import static com.formdev.flatlaf.FlatClientProperties.STYLE_CLASS;
import javax.swing.JLabel;
/**
@@ -26,6 +27,24 @@ import javax.swing.JLabel;
*/
public class FlatLabel
extends JLabel
implements FlatStyleableComponent
implements FlatComponentExtension, FlatStyleableComponent
{
// NOTE: enum names must be equal to typography/font styles
public enum LabelType { h00, h0, h1, h2, h3, h4, large, regular, medium, small, mini, monospaced }
/**
* Returns type of the label.
*/
public LabelType getLabelType() {
return getClientPropertyEnumString( STYLE_CLASS, LabelType.class, null, LabelType.regular );
}
/**
* Specifies type of the label.
*/
public void setLabelType( LabelType labelType ) {
if( labelType == LabelType.regular )
labelType = null;
putClientPropertyEnumString( STYLE_CLASS, labelType );
}
}

View File

@@ -363,6 +363,29 @@ public class FlatTabbedPane
}
// NOTE: enum names must be equal to allowed strings
/** @since 2 */ public enum TabType { underlined, card };
/**
* Returns type of selected tab.
*
* @since 2
*/
public TabType getTabType() {
return getClientPropertyEnumString( TABBED_PANE_TAB_TYPE, TabType.class,
"TabbedPane.tabType", TabType.underlined );
}
/**
* Specifies type of selected tab.
*
* @since 2
*/
public void setTabType( TabType tabType ) {
putClientPropertyEnumString( TABBED_PANE_TAB_TYPE, tabType );
}
// NOTE: enum names must be equal to allowed strings
public enum TabsPopupPolicy { never, asNeeded };

View File

@@ -20,6 +20,6 @@ plugins {
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "net.java.dev.jna:jna:5.7.0" )
implementation( "net.java.dev.jna:jna-platform:5.7.0" )
implementation( "net.java.dev.jna:jna:5.10.0" )
implementation( "net.java.dev.jna:jna-platform:5.10.0" )
}

View File

@@ -47,7 +47,7 @@ import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.BaseTSD.LONG_PTR;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.GDI32;
import com.sun.jna.platform.win32.Shell32;
@@ -77,6 +77,10 @@ import com.sun.jna.win32.W32APIOptions;
// https://github.com/oberth/custom-chrome
// https://github.com/rossy/borderless-window
//
// Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
// https://github.com/dotnet/wpf/issues/4825#issuecomment-930442736
//
/**
* Native window border support for Windows 10 when using custom decorations.
@@ -160,30 +164,24 @@ public class FlatWindowsNativeWindowBorder
}
@Override
public void setTitleBarHeight( Window window, int titleBarHeight ) {
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds )
{
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.titleBarHeight = titleBarHeight;
}
@Override
public void setTitleBarHitTestSpots( Window window, List<Rectangle> hitTestSpots ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] );
wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
wndProc.closeButtonBounds = cloneRectange( closeButtonBounds );
}
@Override
public void setTitleBarAppIconBounds( Window window, Rectangle appIconBounds ) {
WndProc wndProc = windowsMap.get( window );
if( wndProc == null )
return;
wndProc.appIconBounds = (appIconBounds != null) ? new Rectangle( appIconBounds ) : null;
private static Rectangle cloneRectange( Rectangle rect ) {
return (rect != null) ? new Rectangle( rect ) : null;
}
@Override
@@ -293,7 +291,16 @@ public class FlatWindowsNativeWindowBorder
WM_ERASEBKGND = 0x0014,
WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084,
WM_NCMOUSEMOVE = 0x00A0,
WM_NCLBUTTONDOWN = 0x00A1,
WM_NCLBUTTONUP = 0x00A2,
WM_NCRBUTTONUP = 0x00A5,
WM_MOUSEMOVE= 0x0200,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
// WM_SIZE wParam
@@ -306,7 +313,10 @@ public class FlatWindowsNativeWindowBorder
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTTOP = 12;
HTMINBUTTON = 8,
HTMAXBUTTON = 9,
HTTOP = 12,
HTCLOSE = 20;
private static final int ABS_AUTOHIDE = 0x0000001;
private static final int ABM_GETAUTOHIDEBAREX = 0x0000000b;
@@ -328,13 +338,17 @@ public class FlatWindowsNativeWindowBorder
private Window window;
private final HWND hwnd;
private final BaseTSD.LONG_PTR defaultWndProc;
private final LONG_PTR defaultWndProc;
private int wmSizeWParam = -1;
private HBRUSH background;
// Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight;
private Rectangle[] hitTestSpots;
private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds;
private Rectangle closeButtonBounds;
WndProc( Window window ) {
this.window = window;
@@ -420,6 +434,7 @@ public class FlatWindowsNativeWindowBorder
*/
@Override
public LRESULT callback( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam ) {
long wparam = wParam.longValue();
switch( uMsg ) {
case WM_NCCALCSIZE:
return WmNcCalcSize( hwnd, uMsg, wParam, lParam );
@@ -427,8 +442,29 @@ public class FlatWindowsNativeWindowBorder
case WM_NCHITTEST:
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
case WM_NCMOUSEMOVE:
// if mouse is moved over some non-client areas,
// send it also to the client area to allow Swing to process it
// (required for Windows 11 maximize button)
if( wparam == HTMINBUTTON || wparam == HTMAXBUTTON || wparam == HTCLOSE ||
wparam == HTCAPTION || wparam == HTSYSMENU )
sendMessageToClientArea( hwnd, WM_MOUSEMOVE, lParam );
break;
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
// if left mouse was pressed/released over minimize/maximize/close button,
// send it also to the client area to allow Swing to process it
// (required for Windows 11 maximize button)
if( wparam == HTMINBUTTON || wparam == HTMAXBUTTON || wparam == HTCLOSE ) {
int uClientMsg = (uMsg == WM_NCLBUTTONDOWN) ? WM_LBUTTONDOWN : WM_LBUTTONUP;
sendMessageToClientArea( hwnd, uClientMsg, lParam );
return new LRESULT( 0 );
}
break;
case WM_NCRBUTTONUP:
if( wParam.longValue() == HTCAPTION || wParam.longValue() == HTSYSMENU )
if( wparam == HTCAPTION || wparam == HTSYSMENU )
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
break;
@@ -522,7 +558,7 @@ public class FlatWindowsNativeWindowBorder
boolean isMaximized = User32Ex.INSTANCE.IsZoomed( hwnd );
if( isMaximized && !isFullscreen() ) {
// When a window is maximized, its size is actually a little bit more
// When a window is maximized, its size is actually a little bit larger
// than the monitor's work area. The window is positioned and sized in
// such a way that the resize handles are outside of the monitor and
// then the window is clipped to the monitor so that the resize handle
@@ -574,15 +610,12 @@ public class FlatWindowsNativeWindowBorder
if( lResult.longValue() != HTCLIENT )
return lResult;
// get window rectangle needed to convert mouse x/y from screen to window coordinates
RECT rcWindow = new RECT();
User32.INSTANCE.GetWindowRect( hwnd, rcWindow );
// get mouse x/y in window coordinates
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
int x = GET_X_LPARAM( xy );
int y = GET_Y_LPARAM( xy );
// scale-down mouse x/y
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
@@ -590,9 +623,26 @@ public class FlatWindowsNativeWindowBorder
// return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE
if( appIconBounds != null && appIconBounds.contains( sx, sy ) )
if( contains( appIconBounds, sx, sy ) )
return new LRESULT( HTSYSMENU );
// return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) )
return new LRESULT( HTMINBUTTON );
// return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) )
return new LRESULT( HTMAXBUTTON );
// return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) )
return new LRESULT( HTCLOSE );
int resizeBorderHeight = getResizeHandleHeight();
boolean isOnResizeBorder = (y < resizeBorderHeight) &&
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
@@ -612,6 +662,25 @@ public class FlatWindowsNativeWindowBorder
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT );
}
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
}
/**
* Converts screen coordinates to window coordinates.
*/
private LRESULT screen2windowCoordinates( HWND hwnd, LPARAM lParam ) {
// get window rectangle needed to convert mouse x/y from screen to window coordinates
RECT rcWindow = new RECT();
User32.INSTANCE.GetWindowRect( hwnd, rcWindow );
// get mouse x/y in window coordinates
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
return new LRESULT( MAKELONG( x, y ) );
}
/**
* Returns the height of the little space at the top of the window used to
* resize the window.
@@ -678,7 +747,7 @@ public class FlatWindowsNativeWindowBorder
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
*/
private int GET_X_LPARAM( LPARAM lParam ) {
private int GET_X_LPARAM( LONG_PTR lParam ) {
return (short) (lParam.longValue() & 0xffff);
}
@@ -688,10 +757,17 @@ public class FlatWindowsNativeWindowBorder
*
* https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest#remarks
*/
private int GET_Y_LPARAM( LPARAM lParam ) {
private int GET_Y_LPARAM( LONG_PTR lParam ) {
return (short) ((lParam.longValue() >> 16) & 0xffff);
}
/**
* Same implementation as MAKELONG(wLow, wHigh) macro in windef.h.
*/
private long MAKELONG( int low, int high ) {
return (low & 0xffff) | ((high & 0xffff) << 16);
}
/**
* Same implementation as RGB(r,g,b) macro in wingdi.h.
*/
@@ -699,6 +775,14 @@ public class FlatWindowsNativeWindowBorder
return new DWORD( (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) );
}
private void sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam ) {
// get mouse x/y in window coordinates
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
// send message
User32.INSTANCE.SendMessage( hwnd, uMsg, new WPARAM(), new LPARAM( xy.longValue() ) );
}
/**
* Opens the window's system menu.
* The system menu is the menu that opens when the user presses Alt+Space or

View File

@@ -215,6 +215,27 @@ LRESULT CALLBACK FlatWndProc::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, L
case WM_NCHITTEST:
return WmNcHitTest( hwnd, uMsg, wParam, lParam );
case WM_NCMOUSEMOVE:
// if mouse is moved over some non-client areas,
// send it also to the client area to allow Swing to process it
// (required for Windows 11 maximize button)
if( wParam == HTMINBUTTON || wParam == HTMAXBUTTON || wParam == HTCLOSE ||
wParam == HTCAPTION || wParam == HTSYSMENU )
sendMessageToClientArea( hwnd, WM_MOUSEMOVE, lParam );
break;
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
// if left mouse was pressed/released over minimize/maximize/close button,
// send it also to the client area to allow Swing to process it
// (required for Windows 11 maximize button)
if( wParam == HTMINBUTTON || wParam == HTMAXBUTTON || wParam == HTCLOSE ) {
int uClientMsg = (uMsg == WM_NCLBUTTONDOWN) ? WM_LBUTTONDOWN : WM_LBUTTONUP;
sendMessageToClientArea( hwnd, uClientMsg, lParam );
return 0;
}
break;
case WM_NCRBUTTONUP:
if( wParam == HTCAPTION || wParam == HTSYSMENU )
openSystemMenu( hwnd, GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) );
@@ -305,7 +326,7 @@ LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lP
bool isMaximized = ::IsZoomed( hwnd );
if( isMaximized && !isFullscreen() ) {
// When a window is maximized, its size is actually a little bit more
// When a window is maximized, its size is actually a little bit larger
// than the monitor's work area. The window is positioned and sized in
// such a way that the resize handles are outside of the monitor and
// then the window is clipped to the monitor so that the resize handle
@@ -354,6 +375,22 @@ LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPa
if( lResult != HTCLIENT )
return lResult;
// get mouse x/y in window coordinates
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
int x = GET_X_LPARAM( xy );
int y = GET_Y_LPARAM( xy );
int resizeBorderHeight = getResizeHandleHeight();
bool isOnResizeBorder = (y < resizeBorderHeight) &&
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
return onNcHitTest( x, y, isOnResizeBorder );
}
/**
* Converts screen coordinates to window coordinates.
*/
LRESULT FlatWndProc::screen2windowCoordinates( HWND hwnd, LPARAM lParam ) {
// get window rectangle needed to convert mouse x/y from screen to window coordinates
RECT rcWindow;
::GetWindowRect( hwnd, &rcWindow );
@@ -362,11 +399,7 @@ LRESULT FlatWndProc::WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lPa
int x = GET_X_LPARAM( lParam ) - rcWindow.left;
int y = GET_Y_LPARAM( lParam ) - rcWindow.top;
int resizeBorderHeight = getResizeHandleHeight();
bool isOnResizeBorder = (y < resizeBorderHeight) &&
(::GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
return onNcHitTest( x, y, isOnResizeBorder );
return MAKELONG( x, y );
}
/**
@@ -429,6 +462,14 @@ JNIEnv* FlatWndProc::getEnv() {
return env;
}
void FlatWndProc::sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam ) {
// get mouse x/y in window coordinates
LRESULT xy = screen2windowCoordinates( hwnd, lParam );
// send message
::SendMessage( hwnd, uMsg, 0, xy );
}
/**
* Opens the window's system menu.
* The system menu is the menu that opens when the user presses Alt+Space or

View File

@@ -54,6 +54,7 @@ private:
LRESULT WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WmNcHitTest( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam );
LRESULT screen2windowCoordinates( HWND hwnd, LPARAM lParam );
int getResizeHandleHeight();
bool hasAutohideTaskbar( UINT edge, RECT rcMonitor );
BOOL isFullscreen();
@@ -61,6 +62,7 @@ private:
void fireStateChangedLaterOnce();
JNIEnv* getEnv();
void sendMessageToClientArea( HWND hwnd, int uMsg, LPARAM lParam );
void openSystemMenu( HWND hwnd, int x, int y );
void setMenuItemState( HMENU systemMenu, int item, bool enabled );

View File

@@ -13,8 +13,14 @@ extern "C" {
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCAPTION 2L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTSYSMENU 3L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMINBUTTON
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMINBUTTON 8L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMAXBUTTON
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTMAXBUTTON 9L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTTOP 12L
#undef com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLOSE
#define com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc_HTCLOSE 20L
/*
* Class: com_formdev_flatlaf_ui_FlatWindowsNativeWindowBorder_WndProc
* Method: installImpl

View File

@@ -1,20 +1,46 @@
- Java 1.8.0_202
+ Java 1.8.0_292
- OS Windows 10
+ OS Mac OS X
#---- ComboBox ----
+ ComboBox.showPopupOnNavigation true
#---- FileChooser ----
- FileChooser.useSystemExtensionHiding true
+ FileChooser.useSystemExtensionHiding false
#---- Menu ----
- Menu.shortcutKeys length=1 [I
[0] 8
+ Menu.shortcutKeys length=1 [I
[0] 10
#---- MenuItem ----
- MenuItem.acceleratorDelimiter -
+ MenuItem.acceleratorDelimiter
#---- OptionPane ----
+ OptionPane.isYesLast true
#---- ProgressBar ----
- ProgressBar.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
+ ProgressBar.font [active] .SF NS Text plain 11 javax.swing.plaf.FontUIResource [UI]
+ ProgressBar.font [active] Helvetica Neue plain 11 javax.swing.plaf.FontUIResource [UI]
#---- ScrollBar ----
- ScrollBar.hoverThumbWithTrack false
+ ScrollBar.hoverThumbWithTrack true
@@ -27,7 +53,93 @@
- ScrollBar.thumbInsets 0,0,0,0 javax.swing.plaf.InsetsUIResource [UI]
+ ScrollBar.thumbInsets 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]
- defaultFont Segoe UI plain 12 javax.swing.plaf.FontUIResource [UI]
+ defaultFont .SF NS Text plain 13 javax.swing.plaf.FontUIResource [UI]
+ defaultFont Helvetica Neue plain 13 javax.swing.plaf.FontUIResource [UI]
#---- h0 ----
- h0.font [active] Segoe UI plain 30 javax.swing.plaf.FontUIResource [UI]
+ h0.font [active] Helvetica Neue plain 31 javax.swing.plaf.FontUIResource [UI]
#---- h00 ----
- h00.font [active] Segoe UI plain 36 javax.swing.plaf.FontUIResource [UI]
+ h00.font [active] Helvetica Neue plain 37 javax.swing.plaf.FontUIResource [UI]
#---- h1 ----
- h1.font [active] Segoe UI Semibold plain 24 javax.swing.plaf.FontUIResource [UI]
+ h1.font [active] HelveticaNeue-Medium plain 25 javax.swing.plaf.FontUIResource [UI]
- h1.regular.font [active] Segoe UI plain 24 javax.swing.plaf.FontUIResource [UI]
+ h1.regular.font [active] Helvetica Neue plain 25 javax.swing.plaf.FontUIResource [UI]
#---- h2 ----
- h2.font [active] Segoe UI Semibold plain 18 javax.swing.plaf.FontUIResource [UI]
+ h2.font [active] HelveticaNeue-Medium plain 19 javax.swing.plaf.FontUIResource [UI]
- h2.regular.font [active] Segoe UI plain 18 javax.swing.plaf.FontUIResource [UI]
+ h2.regular.font [active] Helvetica Neue plain 19 javax.swing.plaf.FontUIResource [UI]
#---- h3 ----
- h3.font [active] Segoe UI Semibold plain 15 javax.swing.plaf.FontUIResource [UI]
+ h3.font [active] HelveticaNeue-Medium plain 16 javax.swing.plaf.FontUIResource [UI]
- h3.regular.font [active] Segoe UI plain 15 javax.swing.plaf.FontUIResource [UI]
+ h3.regular.font [active] Helvetica Neue plain 16 javax.swing.plaf.FontUIResource [UI]
#---- h4 ----
- h4.font [active] Segoe UI bold 12 javax.swing.plaf.FontUIResource [UI]
+ h4.font [active] Helvetica Neue bold 13 javax.swing.plaf.FontUIResource [UI]
#---- large ----
- large.font [active] Segoe UI plain 14 javax.swing.plaf.FontUIResource [UI]
+ large.font [active] Helvetica Neue plain 15 javax.swing.plaf.FontUIResource [UI]
#---- light ----
- light.font [active] Segoe UI Light plain 12 javax.swing.plaf.FontUIResource [UI]
+ light.font [active] HelveticaNeue-Thin plain 13 javax.swing.plaf.FontUIResource [UI]
#---- medium ----
- medium.font [active] Segoe UI plain 11 javax.swing.plaf.FontUIResource [UI]
+ medium.font [active] Helvetica Neue plain 12 javax.swing.plaf.FontUIResource [UI]
#---- mini ----
- mini.font [active] Segoe UI plain 9 javax.swing.plaf.FontUIResource [UI]
+ mini.font [active] Helvetica Neue plain 10 javax.swing.plaf.FontUIResource [UI]
#---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]
#---- semibold ----
- semibold.font [active] Segoe UI Semibold plain 12 javax.swing.plaf.FontUIResource [UI]
+ semibold.font [active] HelveticaNeue-Medium plain 13 javax.swing.plaf.FontUIResource [UI]
#---- small ----
- small.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
+ small.font [active] Helvetica Neue plain 11 javax.swing.plaf.FontUIResource [UI]

View File

@@ -671,6 +671,7 @@ OptionPane.minimumSize 262,90 javax.swing.plaf.DimensionUIResource [U
OptionPane.questionIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneQuestionIcon [UI]
OptionPane.sameSizeButtons true
OptionPane.setButtonMargin false
OptionPane.showIcon false
OptionPane.warningIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneWarningIcon [UI]
OptionPane.windowBindings length=2 [Ljava.lang.Object;
[0] ESCAPE
@@ -818,6 +819,7 @@ Resizable.resizeBorder [lazy] 4,4,4,4 false com.formdev.flatlaf.ui.F
#---- RootPane ----
RootPane.activeBorderColor #4d5154 HSL 206 4 32 com.formdev.flatlaf.util.DerivedColor [UI] lighten(7% autoInverse)
RootPane.background #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI]
RootPane.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRootPaneUI$FlatWindowBorder [UI]
RootPane.borderDragThickness 5
RootPane.cornerDragWidth 16
@@ -830,6 +832,8 @@ RootPane.defaultButtonWindowKeyBindings length=8 [Ljava.lang.Object;
[5] press
[6] ctrl released ENTER
[7] release
RootPane.font [active] $defaultFont [UI]
RootPane.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
RootPane.honorDialogMinimumSizeOnResize true
RootPane.honorFrameMinimumSizeOnResize false
RootPane.inactiveBorderColor #484c4e HSL 200 4 29 com.formdev.flatlaf.util.DerivedColor [UI] lighten(5% autoInverse)
@@ -1001,6 +1005,7 @@ TabbedPane.buttonArc 6
TabbedPane.buttonHoverBackground #303234 HSL 210 4 20 com.formdev.flatlaf.util.DerivedColor [UI] darken(5%)
TabbedPane.buttonInsets 2,1,2,1 javax.swing.plaf.InsetsUIResource [UI]
TabbedPane.buttonPressedBackground #282a2c HSL 210 5 16 com.formdev.flatlaf.util.DerivedColor [UI] darken(8%)
TabbedPane.cardTabSelectionHeight 2
TabbedPane.closeArc 4
TabbedPane.closeCrossFilledSize 7.5
TabbedPane.closeCrossLineWidth 1
@@ -1043,6 +1048,7 @@ TabbedPane.tabInsets 4,12,4,12 javax.swing.plaf.InsetsUIResource [U
TabbedPane.tabRunOverlay 0
TabbedPane.tabSelectionHeight 3
TabbedPane.tabSeparatorsFullHeight false
TabbedPane.tabType underlined
TabbedPane.tabWidthMode preferred
TabbedPane.tabsOpaque true
TabbedPane.tabsOverlapBorder false
@@ -1213,9 +1219,11 @@ TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.Colo
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1384,6 +1392,26 @@ Viewport.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.Colo
ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style] ----
[style].h00 font: $h00.font
[style].h0 font: $h0.font
[style].h1.regular font: $h1.regular.font
[style].h1 font: $h1.font
[style].h2.regular font: $h2.regular.font
[style].h2 font: $h2.font
[style].h3.regular font: $h3.regular.font
[style].h3 font: $h3.font
[style].h4 font: $h4.font
[style].large font: $large.font
[style].light font: $light.font
[style].medium font: $medium.font
[style].mini font: $mini.font
[style].monospaced font: $monospaced.font
[style].semibold font: $semibold.font
[style].small font: $small.font
#---- ----
activeCaption #434e60 HSL 217 18 32 javax.swing.plaf.ColorUIResource [UI]
@@ -1399,6 +1427,39 @@ defaultFont Segoe UI plain 12 javax.swing.plaf.FontUIResou
desktop #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI]
#---- h0 ----
h0.font [active] Segoe UI plain 30 javax.swing.plaf.FontUIResource [UI]
#---- h00 ----
h00.font [active] Segoe UI plain 36 javax.swing.plaf.FontUIResource [UI]
#---- h1 ----
h1.font [active] Segoe UI Semibold plain 24 javax.swing.plaf.FontUIResource [UI]
h1.regular.font [active] Segoe UI plain 24 javax.swing.plaf.FontUIResource [UI]
#---- h2 ----
h2.font [active] Segoe UI Semibold plain 18 javax.swing.plaf.FontUIResource [UI]
h2.regular.font [active] Segoe UI plain 18 javax.swing.plaf.FontUIResource [UI]
#---- h3 ----
h3.font [active] Segoe UI Semibold plain 15 javax.swing.plaf.FontUIResource [UI]
h3.regular.font [active] Segoe UI plain 15 javax.swing.plaf.FontUIResource [UI]
#---- h4 ----
h4.font [active] Segoe UI bold 12 javax.swing.plaf.FontUIResource [UI]
#---- html ----
html.missingImage [lazy] 38,38 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
@@ -1420,13 +1481,52 @@ laf.dark true
laf.scaleFactor [active] 1.0
#---- large ----
large.font [active] Segoe UI plain 14 javax.swing.plaf.FontUIResource [UI]
#---- light ----
light.font [active] Segoe UI Light plain 12 javax.swing.plaf.FontUIResource [UI]
#---- medium ----
medium.font [active] Segoe UI plain 11 javax.swing.plaf.FontUIResource [UI]
#---- ----
menu #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI]
menuText #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
#---- mini ----
mini.font [active] Segoe UI plain 9 javax.swing.plaf.FontUIResource [UI]
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----
scrollbar #3e4244 HSL 200 5 25 com.formdev.flatlaf.util.DerivedColor [UI] lighten(1%)
#---- semibold ----
semibold.font [active] Segoe UI Semibold plain 12 javax.swing.plaf.FontUIResource [UI]
#---- small ----
small.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
#---- swingx/TaskPane ----
swingx/TaskPaneUI com.formdev.flatlaf.swingx.ui.FlatTaskPaneUI

View File

@@ -1,20 +1,46 @@
- Java 1.8.0_202
+ Java 1.8.0_292
- OS Windows 10
+ OS Mac OS X
#---- ComboBox ----
+ ComboBox.showPopupOnNavigation true
#---- FileChooser ----
- FileChooser.useSystemExtensionHiding true
+ FileChooser.useSystemExtensionHiding false
#---- Menu ----
- Menu.shortcutKeys length=1 [I
[0] 8
+ Menu.shortcutKeys length=1 [I
[0] 10
#---- MenuItem ----
- MenuItem.acceleratorDelimiter -
+ MenuItem.acceleratorDelimiter
#---- OptionPane ----
+ OptionPane.isYesLast true
#---- ProgressBar ----
- ProgressBar.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
+ ProgressBar.font [active] .SF NS Text plain 11 javax.swing.plaf.FontUIResource [UI]
+ ProgressBar.font [active] Helvetica Neue plain 11 javax.swing.plaf.FontUIResource [UI]
#---- ScrollBar ----
- ScrollBar.hoverThumbWithTrack false
+ ScrollBar.hoverThumbWithTrack true
@@ -27,7 +53,93 @@
- ScrollBar.thumbInsets 0,0,0,0 javax.swing.plaf.InsetsUIResource [UI]
+ ScrollBar.thumbInsets 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]
- defaultFont Segoe UI plain 12 javax.swing.plaf.FontUIResource [UI]
+ defaultFont .SF NS Text plain 13 javax.swing.plaf.FontUIResource [UI]
+ defaultFont Helvetica Neue plain 13 javax.swing.plaf.FontUIResource [UI]
#---- h0 ----
- h0.font [active] Segoe UI plain 30 javax.swing.plaf.FontUIResource [UI]
+ h0.font [active] Helvetica Neue plain 31 javax.swing.plaf.FontUIResource [UI]
#---- h00 ----
- h00.font [active] Segoe UI plain 36 javax.swing.plaf.FontUIResource [UI]
+ h00.font [active] Helvetica Neue plain 37 javax.swing.plaf.FontUIResource [UI]
#---- h1 ----
- h1.font [active] Segoe UI Semibold plain 24 javax.swing.plaf.FontUIResource [UI]
+ h1.font [active] HelveticaNeue-Medium plain 25 javax.swing.plaf.FontUIResource [UI]
- h1.regular.font [active] Segoe UI plain 24 javax.swing.plaf.FontUIResource [UI]
+ h1.regular.font [active] Helvetica Neue plain 25 javax.swing.plaf.FontUIResource [UI]
#---- h2 ----
- h2.font [active] Segoe UI Semibold plain 18 javax.swing.plaf.FontUIResource [UI]
+ h2.font [active] HelveticaNeue-Medium plain 19 javax.swing.plaf.FontUIResource [UI]
- h2.regular.font [active] Segoe UI plain 18 javax.swing.plaf.FontUIResource [UI]
+ h2.regular.font [active] Helvetica Neue plain 19 javax.swing.plaf.FontUIResource [UI]
#---- h3 ----
- h3.font [active] Segoe UI Semibold plain 15 javax.swing.plaf.FontUIResource [UI]
+ h3.font [active] HelveticaNeue-Medium plain 16 javax.swing.plaf.FontUIResource [UI]
- h3.regular.font [active] Segoe UI plain 15 javax.swing.plaf.FontUIResource [UI]
+ h3.regular.font [active] Helvetica Neue plain 16 javax.swing.plaf.FontUIResource [UI]
#---- h4 ----
- h4.font [active] Segoe UI bold 12 javax.swing.plaf.FontUIResource [UI]
+ h4.font [active] Helvetica Neue bold 13 javax.swing.plaf.FontUIResource [UI]
#---- large ----
- large.font [active] Segoe UI plain 14 javax.swing.plaf.FontUIResource [UI]
+ large.font [active] Helvetica Neue plain 15 javax.swing.plaf.FontUIResource [UI]
#---- light ----
- light.font [active] Segoe UI Light plain 12 javax.swing.plaf.FontUIResource [UI]
+ light.font [active] HelveticaNeue-Thin plain 13 javax.swing.plaf.FontUIResource [UI]
#---- medium ----
- medium.font [active] Segoe UI plain 11 javax.swing.plaf.FontUIResource [UI]
+ medium.font [active] Helvetica Neue plain 12 javax.swing.plaf.FontUIResource [UI]
#---- mini ----
- mini.font [active] Segoe UI plain 9 javax.swing.plaf.FontUIResource [UI]
+ mini.font [active] Helvetica Neue plain 10 javax.swing.plaf.FontUIResource [UI]
#---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]
#---- semibold ----
- semibold.font [active] Segoe UI Semibold plain 12 javax.swing.plaf.FontUIResource [UI]
+ semibold.font [active] HelveticaNeue-Medium plain 13 javax.swing.plaf.FontUIResource [UI]
#---- small ----
- small.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
+ small.font [active] Helvetica Neue plain 11 javax.swing.plaf.FontUIResource [UI]

View File

@@ -676,6 +676,7 @@ OptionPane.minimumSize 262,90 javax.swing.plaf.DimensionUIResource [U
OptionPane.questionIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneQuestionIcon [UI]
OptionPane.sameSizeButtons true
OptionPane.setButtonMargin false
OptionPane.showIcon false
OptionPane.warningIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneWarningIcon [UI]
OptionPane.windowBindings length=2 [Ljava.lang.Object;
[0] ESCAPE
@@ -823,6 +824,7 @@ Resizable.resizeBorder [lazy] 4,4,4,4 false com.formdev.flatlaf.ui.F
#---- RootPane ----
RootPane.activeBorderColor #737373 HSL 0 0 45 com.formdev.flatlaf.util.DerivedColor [UI] darken(50% autoInverse)
RootPane.background #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI]
RootPane.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRootPaneUI$FlatWindowBorder [UI]
RootPane.borderDragThickness 5
RootPane.cornerDragWidth 16
@@ -835,6 +837,8 @@ RootPane.defaultButtonWindowKeyBindings length=8 [Ljava.lang.Object;
[5] press
[6] ctrl released ENTER
[7] release
RootPane.font [active] $defaultFont [UI]
RootPane.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
RootPane.honorDialogMinimumSizeOnResize true
RootPane.honorFrameMinimumSizeOnResize false
RootPane.inactiveBorderColor #a6a6a6 HSL 0 0 65 com.formdev.flatlaf.util.DerivedColor [UI] darken(30% autoInverse)
@@ -1006,6 +1010,7 @@ TabbedPane.buttonArc 6
TabbedPane.buttonHoverBackground #e0e0e0 HSL 0 0 88 com.formdev.flatlaf.util.DerivedColor [UI] darken(7% autoInverse)
TabbedPane.buttonInsets 2,1,2,1 javax.swing.plaf.InsetsUIResource [UI]
TabbedPane.buttonPressedBackground #d9d9d9 HSL 0 0 85 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse)
TabbedPane.cardTabSelectionHeight 2
TabbedPane.closeArc 4
TabbedPane.closeCrossFilledSize 7.5
TabbedPane.closeCrossLineWidth 1
@@ -1048,6 +1053,7 @@ TabbedPane.tabInsets 4,12,4,12 javax.swing.plaf.InsetsUIResource [U
TabbedPane.tabRunOverlay 0
TabbedPane.tabSelectionHeight 3
TabbedPane.tabSeparatorsFullHeight false
TabbedPane.tabType underlined
TabbedPane.tabWidthMode preferred
TabbedPane.tabsOpaque true
TabbedPane.tabsOverlapBorder false
@@ -1218,9 +1224,11 @@ TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.Colo
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1389,6 +1397,26 @@ Viewport.foreground #000000 HSL 0 0 0 javax.swing.plaf.Colo
ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style] ----
[style].h00 font: $h00.font
[style].h0 font: $h0.font
[style].h1.regular font: $h1.regular.font
[style].h1 font: $h1.font
[style].h2.regular font: $h2.regular.font
[style].h2 font: $h2.font
[style].h3.regular font: $h3.regular.font
[style].h3 font: $h3.font
[style].h4 font: $h4.font
[style].large font: $large.font
[style].light font: $light.font
[style].medium font: $medium.font
[style].mini font: $mini.font
[style].monospaced font: $monospaced.font
[style].semibold font: $semibold.font
[style].small font: $small.font
#---- ----
activeCaption #99b4d1 HSL 211 38 71 javax.swing.plaf.ColorUIResource [UI]
@@ -1404,6 +1432,39 @@ defaultFont Segoe UI plain 12 javax.swing.plaf.FontUIResou
desktop #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
#---- h0 ----
h0.font [active] Segoe UI plain 30 javax.swing.plaf.FontUIResource [UI]
#---- h00 ----
h00.font [active] Segoe UI plain 36 javax.swing.plaf.FontUIResource [UI]
#---- h1 ----
h1.font [active] Segoe UI Semibold plain 24 javax.swing.plaf.FontUIResource [UI]
h1.regular.font [active] Segoe UI plain 24 javax.swing.plaf.FontUIResource [UI]
#---- h2 ----
h2.font [active] Segoe UI Semibold plain 18 javax.swing.plaf.FontUIResource [UI]
h2.regular.font [active] Segoe UI plain 18 javax.swing.plaf.FontUIResource [UI]
#---- h3 ----
h3.font [active] Segoe UI Semibold plain 15 javax.swing.plaf.FontUIResource [UI]
h3.regular.font [active] Segoe UI plain 15 javax.swing.plaf.FontUIResource [UI]
#---- h4 ----
h4.font [active] Segoe UI bold 12 javax.swing.plaf.FontUIResource [UI]
#---- html ----
html.missingImage [lazy] 38,38 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
@@ -1425,13 +1486,52 @@ laf.dark false
laf.scaleFactor [active] 1.0
#---- large ----
large.font [active] Segoe UI plain 14 javax.swing.plaf.FontUIResource [UI]
#---- light ----
light.font [active] Segoe UI Light plain 12 javax.swing.plaf.FontUIResource [UI]
#---- medium ----
medium.font [active] Segoe UI plain 11 javax.swing.plaf.FontUIResource [UI]
#---- ----
menu #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI]
menuText #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
#---- mini ----
mini.font [active] Segoe UI plain 9 javax.swing.plaf.FontUIResource [UI]
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----
scrollbar #f5f5f5 HSL 0 0 96 com.formdev.flatlaf.util.DerivedColor [UI] lighten(1%)
#---- semibold ----
semibold.font [active] Segoe UI Semibold plain 12 javax.swing.plaf.FontUIResource [UI]
#---- small ----
small.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
#---- swingx/TaskPane ----
swingx/TaskPaneUI com.formdev.flatlaf.swingx.ui.FlatTaskPaneUI

View File

@@ -687,6 +687,7 @@ OptionPane.minimumSize 262,90 javax.swing.plaf.DimensionUIResource [U
OptionPane.questionIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneQuestionIcon [UI]
OptionPane.sameSizeButtons true
OptionPane.setButtonMargin false
OptionPane.showIcon false
OptionPane.warningIcon [lazy] 32,32 com.formdev.flatlaf.icons.FlatOptionPaneWarningIcon [UI]
OptionPane.windowBindings length=2 [Ljava.lang.Object;
[0] ESCAPE
@@ -833,6 +834,7 @@ Resizable.resizeBorder [lazy] 4,4,4,4 false com.formdev.flatlaf.ui.F
#---- RootPane ----
RootPane.background #ccffcc HSL 120 100 90 javax.swing.plaf.ColorUIResource [UI]
RootPane.border [lazy] 1,1,1,1 false com.formdev.flatlaf.ui.FlatRootPaneUI$FlatWindowBorder [UI]
RootPane.borderDragThickness 5
RootPane.cornerDragWidth 16
@@ -845,6 +847,8 @@ RootPane.defaultButtonWindowKeyBindings length=8 [Ljava.lang.Object;
[5] press
[6] ctrl released ENTER
[7] release
RootPane.font [active] $defaultFont [UI]
RootPane.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
RootPane.honorDialogMinimumSizeOnResize true
RootPane.honorFrameMinimumSizeOnResize false
RootPaneUI com.formdev.flatlaf.ui.FlatRootPaneUI
@@ -1017,6 +1021,7 @@ TabbedPane.buttonArc 6
TabbedPane.buttonHoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.buttonInsets 2,1,2,1 javax.swing.plaf.InsetsUIResource [UI]
TabbedPane.buttonPressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.cardTabSelectionHeight 2
TabbedPane.closeArc 999
TabbedPane.closeCrossFilledSize 6.5
TabbedPane.closeCrossLineWidth 2
@@ -1062,6 +1067,7 @@ TabbedPane.tabRunOverlay 0
TabbedPane.tabSelectionHeight 3
TabbedPane.tabSeparatorColor #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
TabbedPane.tabSeparatorsFullHeight false
TabbedPane.tabType underlined
TabbedPane.tabWidthMode preferred
TabbedPane.tabsOpaque true
TabbedPane.tabsOverlapBorder false
@@ -1233,9 +1239,11 @@ TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.Colo
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.unifiedBackground false
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1403,6 +1411,26 @@ Viewport.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.Colo
ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style] ----
[style].h00 font: $h00.font
[style].h0 font: $h0.font
[style].h1.regular font: $h1.regular.font
[style].h1 font: $h1.font
[style].h2.regular font: $h2.regular.font
[style].h2 font: $h2.font
[style].h3.regular font: $h3.regular.font
[style].h3 font: $h3.font
[style].h4 font: $h4.font
[style].large font: $large.font
[style].light font: $light.font
[style].medium font: $medium.font
[style].mini font: $mini.font
[style].monospaced font: $monospaced.font
[style].semibold font: $semibold.font
[style].small font: $small.font
#---- ----
activeCaption #99b4d1 HSL 211 38 71 javax.swing.plaf.ColorUIResource [UI]
@@ -1418,6 +1446,39 @@ defaultFont Segoe UI plain 12 javax.swing.plaf.FontUIResou
desktop #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
#---- h0 ----
h0.font [active] Segoe UI plain 30 javax.swing.plaf.FontUIResource [UI]
#---- h00 ----
h00.font [active] Segoe UI plain 36 javax.swing.plaf.FontUIResource [UI]
#---- h1 ----
h1.font [active] Segoe UI Semibold plain 24 javax.swing.plaf.FontUIResource [UI]
h1.regular.font [active] Segoe UI plain 24 javax.swing.plaf.FontUIResource [UI]
#---- h2 ----
h2.font [active] Segoe UI Semibold plain 18 javax.swing.plaf.FontUIResource [UI]
h2.regular.font [active] Segoe UI plain 18 javax.swing.plaf.FontUIResource [UI]
#---- h3 ----
h3.font [active] Segoe UI Semibold plain 15 javax.swing.plaf.FontUIResource [UI]
h3.regular.font [active] Segoe UI plain 15 javax.swing.plaf.FontUIResource [UI]
#---- h4 ----
h4.font [active] Segoe UI bold 12 javax.swing.plaf.FontUIResource [UI]
#---- html ----
html.missingImage [lazy] 38,38 sun.swing.ImageIconUIResource [UI] (sun.awt.image.ToolkitImage)
@@ -1439,13 +1500,52 @@ laf.dark false
laf.scaleFactor [active] 1.0
#---- large ----
large.font [active] Segoe UI plain 14 javax.swing.plaf.FontUIResource [UI]
#---- light ----
light.font [active] Segoe UI Light plain 12 javax.swing.plaf.FontUIResource [UI]
#---- medium ----
medium.font [active] Segoe UI plain 11 javax.swing.plaf.FontUIResource [UI]
#---- ----
menu #ccffcc HSL 120 100 90 javax.swing.plaf.ColorUIResource [UI]
menuText #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
#---- mini ----
mini.font [active] Segoe UI plain 9 javax.swing.plaf.FontUIResource [UI]
#---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ----
scrollbar #88ff88 HSL 120 100 77 javax.swing.plaf.ColorUIResource [UI]
#---- semibold ----
semibold.font [active] Segoe UI Semibold plain 12 javax.swing.plaf.FontUIResource [UI]
#---- small ----
small.font [active] Segoe UI plain 10 javax.swing.plaf.FontUIResource [UI]
#---- swingx/TaskPane ----
swingx/TaskPaneUI com.formdev.flatlaf.swingx.ui.FlatTaskPaneUI

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/flatlaf-testing-modular-app"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk-17/"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="-Ddummy=dummy"/>
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk-17/&quot; path=&quot;4&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/flatlaf-testing-modular-app/build/libs/flatlaf-testing-modular-app-2.0-SNAPSHOT.jar&quot; path=&quot;4&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/flatlaf-core/build/libs/flatlaf-2.0-SNAPSHOT.jar&quot; path=&quot;4&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/flatlaf-extras/build/libs/flatlaf-extras-2.0-SNAPSHOT.jar&quot; path=&quot;4&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;C:/Users/charly/.gradle/caches/modules-2/files-2.1/com.formdev/svgSalamander/1.1.2.4/828c2ce815bf62031764f5219597948bd2587aa5/svgSalamander-1.1.2.4.jar&quot; path=&quot;4&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="flatlaf-testing-modular-app"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-m com.formdev.flatlaf.testing.modular.app/com.formdev.flatlaf.testing.modular.app.FlatModularAppTest"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="flatlaf-testing-modular-app"/>
</launchConfiguration>

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
`java-library`
`flatlaf-module-info`
}
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( project( ":flatlaf-extras" ) )
}
flatlafModuleInfo {
dependsOn( ":flatlaf-core:jar" )
dependsOn( ":flatlaf-extras:jar" )
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing.modular.app;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatSVGUtils;
/**
* @author Karl Tauber
*/
public class FlatModularAppTest
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatLaf.registerCustomDefaultsSource(
FlatModularAppTest.class.getResource( "/com/formdev/flatlaf/testing/modular/app/themes/" ) );
FlatLightLaf.setup();
JButton button1 = new JButton( "Hello" );
JButton button2 = new JButton( "World" );
button1.setIcon( new FlatSVGIcon(
FlatModularAppTest.class.getResource( "/com/formdev/flatlaf/testing/modular/app/icons/copy.svg" ) ) );
JPanel panel = new JPanel();
panel.add( new JLabel( "Hello World" ) );
panel.add( button1 );
panel.add( button2 );
JFrame frame = new JFrame( "FlatModularAppTest" );
frame.setIconImages( FlatSVGUtils.createWindowIconImages(
FlatModularAppTest.class.getResource( "/com/formdev/flatlaf/testing/modular/app/icons/copy.svg" ) ) );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add( panel );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
} );
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing.modular.app.plaf;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatButtonUI;
/**
* @author Karl Tauber
*/
public class MyButtonUI
extends FlatButtonUI
{
public static ComponentUI createUI( JComponent c ) {
return new MyButtonUI();
}
protected MyButtonUI() {
super( false );
System.out.println( "MyButtonUI" );
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Karl Tauber
*/
module com.formdev.flatlaf.testing.modular.app {
requires java.desktop;
requires com.formdev.flatlaf;
requires com.formdev.flatlaf.extras;
requires com.kitfox.svg;
exports com.formdev.flatlaf.testing.modular.app.plaf;
}

View File

@@ -0,0 +1,7 @@
<!-- Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g fill="none" fill-rule="evenodd">
<path fill="#6E6E6E" d="M11,3 L4,3 L4,11 L2,11 L2,1 L11,1 L11,3 Z"/>
<path fill="#6E6E6E" d="M5,4 L14,4 L14,14 L5,14 L5,4 Z M7,6 L7,7 L12,7 L12,6 L7,6 Z M7,10 L7,11 L12,11 L12,10 L7,10 Z M7,8 L7,9 L12,9 L12,8 L7,8 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 510 B

View File

@@ -0,0 +1,20 @@
#
# Copyright 2020 FormDev Software GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
@background = #fff
@foreground = #f00
ButtonUI = com.formdev.flatlaf.testing.modular.app.plaf.MyButtonUI

View File

@@ -0,0 +1,438 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
import javax.swing.border.AbstractBorder;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.AnimatedBorder;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatAnimatedBorderTest
extends FlatTestPanel
{
private static final Color CHART_FADE_1 = Color.blue;
private static final Color CHART_FADE_2 = Color.red;
private static final Color CHART_MATERIAL_1 = Color.green;
private static final Color CHART_MATERIAL_2 = Color.magenta;
private static final Color CHART_MATERIAL_3 = Color.pink;
private static final Color CHART_MATERIAL_4 = Color.cyan;
private static final Color CHART_MINIMAL = Color.orange;
private static final String CHART_COLOR_KEY = "chartColor";
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatAnimatedBorderTest" );
frame.showFrame( FlatAnimatedBorderTest::new );
} );
}
FlatAnimatedBorderTest() {
initComponents();
fade1TextField.setBorder( new AnimatedFocusFadeBorder() );
fade2TextField.setBorder( new AnimatedFocusFadeBorder() );
material1TextField.setBorder( new AnimatedMaterialBorder() );
material2TextField.setBorder( new AnimatedMaterialBorder() );
material3TextField.setBorder( new AnimatedMaterialLabeledBorder() );
material4TextField.setBorder( new AnimatedMaterialLabeledBorder() );
minimalTextField.setBorder( new AnimatedMinimalTestBorder() );
fade1TextField.putClientProperty( CHART_COLOR_KEY, CHART_FADE_1 );
fade2TextField.putClientProperty( CHART_COLOR_KEY, CHART_FADE_2 );
material1TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_1 );
material2TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_2 );
material3TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_3 );
material4TextField.putClientProperty( CHART_COLOR_KEY, CHART_MATERIAL_4 );
minimalTextField.putClientProperty( CHART_COLOR_KEY, CHART_MINIMAL );
fade1ChartColor.setForeground( CHART_FADE_1 );
fade2ChartColor.setForeground( CHART_FADE_2 );
material1ChartColor.setForeground( CHART_MATERIAL_1 );
material2ChartColor.setForeground( CHART_MATERIAL_2 );
material3ChartColor.setForeground( CHART_MATERIAL_3 );
material4ChartColor.setForeground( CHART_MATERIAL_4 );
minimalChartColor.setForeground( CHART_MINIMAL );
material3TextField.putClientProperty( AnimatedMaterialLabeledBorder.LABEL_TEXT_KEY, "Label" );
material4TextField.putClientProperty( AnimatedMaterialLabeledBorder.LABEL_TEXT_KEY, "Label" );
material4TextField.setText( "Text" );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
label3 = new JLabel();
lineChartPanel = new LineChartPanel();
fade1TextField = new JTextField();
fade1ChartColor = new FlatAnimatorTest.JChartColor();
fade2TextField = new JTextField();
fade2ChartColor = new FlatAnimatorTest.JChartColor();
label2 = new JLabel();
material1TextField = new JTextField();
material1ChartColor = new FlatAnimatorTest.JChartColor();
material2TextField = new JTextField();
material2ChartColor = new FlatAnimatorTest.JChartColor();
material3TextField = new JTextField();
material3ChartColor = new FlatAnimatorTest.JChartColor();
material4TextField = new JTextField();
material4ChartColor = new FlatAnimatorTest.JChartColor();
label1 = new JLabel();
minimalTextField = new JTextField();
minimalChartColor = new FlatAnimatorTest.JChartColor();
durationLabel = new JLabel();
durationField = new JSpinner();
//======== this ========
setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[fill]" +
"[fill]para" +
"[grow,fill]",
// rows
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[grow]" +
"[]"));
//---- label3 ----
label3.setText("Fade:");
add(label3, "cell 0 0");
add(lineChartPanel, "cell 2 0 1 12,growy");
add(fade1TextField, "cell 0 1");
add(fade1ChartColor, "cell 1 1");
add(fade2TextField, "cell 0 2");
add(fade2ChartColor, "cell 1 2");
//---- label2 ----
label2.setText("Material:");
add(label2, "cell 0 3");
add(material1TextField, "cell 0 4");
add(material1ChartColor, "cell 1 4");
add(material2TextField, "cell 0 5");
add(material2ChartColor, "cell 1 5");
//---- material3TextField ----
material3TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
add(material3TextField, "cell 0 6");
add(material3ChartColor, "cell 1 6");
//---- material4TextField ----
material4TextField.putClientProperty(FlatClientProperties.STYLE_CLASS, "large");
add(material4TextField, "cell 0 7");
add(material4ChartColor, "cell 1 7");
//---- label1 ----
label1.setText("Minimal:");
add(label1, "cell 0 8");
add(minimalTextField, "cell 0 9");
add(minimalChartColor, "cell 1 9");
//---- durationLabel ----
durationLabel.setText("Duration:");
add(durationLabel, "cell 0 11");
//---- durationField ----
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
add(durationField, "cell 0 11");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel label3;
private LineChartPanel lineChartPanel;
private JTextField fade1TextField;
private FlatAnimatorTest.JChartColor fade1ChartColor;
private JTextField fade2TextField;
private FlatAnimatorTest.JChartColor fade2ChartColor;
private JLabel label2;
private JTextField material1TextField;
private FlatAnimatorTest.JChartColor material1ChartColor;
private JTextField material2TextField;
private FlatAnimatorTest.JChartColor material2ChartColor;
private JTextField material3TextField;
private FlatAnimatorTest.JChartColor material3ChartColor;
private JTextField material4TextField;
private FlatAnimatorTest.JChartColor material4ChartColor;
private JLabel label1;
private JTextField minimalTextField;
private FlatAnimatorTest.JChartColor minimalChartColor;
private JLabel durationLabel;
private JSpinner durationField;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class AnimatedMaterialBorder ---------------------------------------
/**
* Experimental text field border that:
* - animates focus indicator color and border width
*/
private class AnimatedFocusFadeBorder
extends AbstractBorder
implements AnimatedBorder
{
// needed because otherwise the empty paint method in superclass
// javax.swing.border.AbstractBorder would be used
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
paintWithAnimation( c, g, x, y, width, height );
}
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
float animatedValue = animatedValues[0];
FlatUIUtils.setRenderingHints( g );
// border width is 1 if not focused and 2 if focused
float lw = UIScale.scale( 1 + animatedValue );
// paint border
Color color = ColorFunctions.mix( Color.red, Color.lightGray, animatedValue );
FlatUIUtils.paintOutlinedComponent( g, x, y, width, height, 0, 0, 0, lw, 0,
null, color, null );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "fade" );
}
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets.top = insets.bottom = UIScale.scale( 3 );
insets.left = insets.right = UIScale.scale( 7 );
return insets;
}
@Override
public float[] getAnimatableValues( Component c ) {
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
}
@Override
public int getAnimationDuration() {
return (Integer) durationField.getValue();
}
}
//---- class AnimatedMaterialBorder ---------------------------------------
/**
* Experimental text field border that:
* - paint border only at bottom
* - animates focus indicator at bottom
*/
private class AnimatedMaterialBorder
extends AbstractBorder
implements AnimatedBorder
{
// needed because otherwise the empty paint method in superclass
// javax.swing.border.AbstractBorder would be used
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
paintWithAnimation( c, g, x, y, width, height );
}
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
float animatedValue = animatedValues[0];
FlatUIUtils.setRenderingHints( g );
// use paintAtScale1x() for consistent line thickness when scaled
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
float lh = (float) (UIScale.scale( 1f ) * scaleFactor);
g2d.setColor( Color.gray );
g2d.fill( new Rectangle2D.Float( x2, y2 + height2 - lh, width2, lh ) );
if( animatedValue > 0 ) {
lh = (float) (UIScale.scale( 2f ) * scaleFactor);
int lw = Math.round( width2 * animatedValue );
g2d.setColor( Color.red );
g2d.fill( new Rectangle2D.Float( x2 + (width2 - lw) / 2, y2 + height2 - lh, lw, lh ) );
}
} );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "material" );
}
}
@Override
public void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
// limit repaint to bottom border
int lh = UIScale.scale( 2 );
c.repaint( x, y + height - lh, width, lh );
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets.top = insets.bottom = UIScale.scale( 3 );
insets.left = insets.right = UIScale.scale( 7 );
return insets;
}
@Override
public float[] getAnimatableValues( Component c ) {
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
}
@Override
public int getAnimationDuration() {
return (Integer) durationField.getValue();
}
}
//---- class AnimatedMaterialLabeledBorder --------------------------------
/**
* Experimental text field border that:
* - paints a label above the text, or in center if text field is empty
* - paint border only at bottom
* - animates focus indicator at bottom
*/
private class AnimatedMaterialLabeledBorder
extends AnimatedMaterialBorder
{
static final String LABEL_TEXT_KEY = "JTextField.labelText";
private static final float LABEL_FONT_SCALE = 0.75f;
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
super.paintAnimated( c, g, x, y, width, height, animatedValues );
float animatedValue = animatedValues[0];
JComponent jc = (JComponent) c;
String label = (String) jc.getClientProperty( LABEL_TEXT_KEY );
if( label == null )
return;
FontMetrics fm = c.getFontMetrics( c.getFont() );
int labelFontHeight = Math.round( fm.getHeight() * LABEL_FONT_SCALE );
int tx = UIScale.scale( 7 );
int ty = y + labelFontHeight - UIScale.scale( 2 );
float sf = LABEL_FONT_SCALE;
if( ((JTextField)c).getDocument().getLength() == 0 ) {
// paint label in center of text field if it is empty
int ty2 = ((height - fm.getHeight()) / 2) + labelFontHeight;
ty += (ty2 - ty) * (1 - animatedValue);
sf += (1 - LABEL_FONT_SCALE) * (1 - animatedValue);
}
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.translate( tx, ty );
g2.scale( sf, sf );
g2.setColor( ColorFunctions.mix( Color.red, Color.gray, animatedValue ) );
FlatUIUtils.drawString( jc, g2, label, 0, 0 );
} finally {
g2.dispose();
}
}
@Override
public void repaintDuringAnimation( Component c, int x, int y, int width, int height ) {
c.repaint( x, y, width, height );
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
super.getBorderInsets( c, insets );
FontMetrics fm = c.getFontMetrics( c.getFont() );
int labelFontHeight = Math.round( fm.getHeight() * LABEL_FONT_SCALE );
insets.top = labelFontHeight;
insets.bottom = UIScale.scale( 5 );
return insets;
}
}
//---- class AnimatedMinimalTestBorder ------------------------------------
/**
* Minimal example for an animated border.
*/
private class AnimatedMinimalTestBorder
implements AnimatedBorder
{
@Override
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
float animatedValue = animatedValues[0];
int lh = UIScale.scale( 2 );
g.setColor( Color.blue );
g.fillRect( x, y + height - lh, Math.round( width * animatedValue ), lh );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
}
}
@Override
public float[] getAnimatableValues( Component c ) {
return new float[] { FlatUIUtils.isPermanentFocusOwner( c ) ? 1 : 0 };
}
@Override
public int getAnimationDuration() {
return (Integer) durationField.getValue();
}
@Override
public Insets getBorderInsets( Component c ) {
return UIScale.scale( new Insets( 3, 7, 3, 7 ) );
}
@Override
public boolean isBorderOpaque() {
return false;
}
}
}

View File

@@ -0,0 +1,128 @@
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[fill][fill]para[grow,fill]"
"$rowConstraints": "[][][]para[][][][][]para[][][grow][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "Fade:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
name: "lineChartPanel"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0 1 12,growy"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "fade1TextField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "fade1ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "fade2TextField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "fade2ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "Material:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "material1TextField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "material1ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "material2TextField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "material2ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "material3TextField"
"$client.FlatLaf.styleClass": "large"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "material3ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "material4TextField"
"$client.FlatLaf.styleClass": "large"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "material4ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "Minimal:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "minimalTextField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "minimalChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "durationLabel"
"text": "Duration:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "durationField"
"model": new javax.swing.SpinnerNumberModel {
minimum: 0
stepSize: 50
value: 200
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 725, 465 )
} )
}
}

View File

@@ -18,13 +18,14 @@ package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import javax.swing.*;
import com.formdev.flatlaf.icons.FlatAnimatedIcon;
import com.formdev.flatlaf.util.AnimatedIcon;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
@@ -33,6 +34,15 @@ import net.miginfocom.swing.*;
public class FlatAnimatedIconTest
extends FlatTestPanel
{
private static final Color CHART_RADIO_BUTTON_1 = Color.blue;
private static final Color CHART_RADIO_BUTTON_2 = Color.red;
private static final Color CHART_RADIO_BUTTON_3 = Color.green;
private static final Color CHART_CHECK_BOX_1 = Color.magenta;
private static final Color CHART_CHECK_BOX_2 = Color.orange;
private static final Color[] CHART_SWITCH_EX = { Color.red, Color.green, Color.blue };
private static final String CHART_COLOR_KEY = "chartColor";
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatAnimatedIconTest" );
@@ -49,16 +59,36 @@ public class FlatAnimatedIconTest
radioButton3.setIcon( radioIcon );
checkBox1.setIcon( new AnimatedSwitchIcon() );
checkBox3.setIcon( new AnimatedSwitchIconEx() );
checkBox2.setIcon( new AnimatedMinimalTestIcon() );
radioButton1.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_1 );
radioButton2.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_2 );
radioButton3.putClientProperty( CHART_COLOR_KEY, CHART_RADIO_BUTTON_3 );
checkBox1.putClientProperty( CHART_COLOR_KEY, CHART_CHECK_BOX_1 );
checkBox2.putClientProperty( CHART_COLOR_KEY, CHART_CHECK_BOX_2 );
radioButton1ChartColor.setForeground( CHART_RADIO_BUTTON_1 );
radioButton2ChartColor.setForeground( CHART_RADIO_BUTTON_2 );
radioButton3ChartColor.setForeground( CHART_RADIO_BUTTON_3 );
checkBox1ChartColor.setForeground( CHART_CHECK_BOX_1 );
checkBox2ChartColor.setForeground( CHART_CHECK_BOX_2 );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
radioButton1 = new JRadioButton();
radioButton1ChartColor = new FlatAnimatorTest.JChartColor();
lineChartPanel = new LineChartPanel();
radioButton2 = new JRadioButton();
radioButton2ChartColor = new FlatAnimatorTest.JChartColor();
radioButton3 = new JRadioButton();
radioButton3ChartColor = new FlatAnimatorTest.JChartColor();
checkBox1 = new JCheckBox();
checkBox1ChartColor = new FlatAnimatorTest.JChartColor();
checkBox3 = new JCheckBox();
checkBox2 = new JCheckBox();
checkBox2ChartColor = new FlatAnimatorTest.JChartColor();
durationLabel = new JLabel();
durationField = new JSpinner();
@@ -66,14 +96,16 @@ public class FlatAnimatedIconTest
setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[]para" +
"[fill]",
"[]" +
"[fill]para" +
"[grow,fill]",
// rows
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]" +
"[grow]" +
"[]"));
@@ -81,30 +113,40 @@ public class FlatAnimatedIconTest
radioButton1.setText("radio 1");
radioButton1.setSelected(true);
add(radioButton1, "cell 0 0");
add(radioButton1ChartColor, "cell 1 0");
add(lineChartPanel, "cell 2 0 1 8,growy");
//---- radioButton2 ----
radioButton2.setText("radio 2");
add(radioButton2, "cell 0 1");
add(radioButton2ChartColor, "cell 1 1");
//---- radioButton3 ----
radioButton3.setText("radio 3");
add(radioButton3, "cell 0 2");
add(radioButton3ChartColor, "cell 1 2");
//---- checkBox1 ----
checkBox1.setText("switch");
add(checkBox1, "cell 0 3");
add(checkBox1ChartColor, "cell 1 3");
//---- checkBox3 ----
checkBox3.setText("switch ex");
add(checkBox3, "cell 0 4");
//---- checkBox2 ----
checkBox2.setText("minimal");
add(checkBox2, "cell 0 4");
add(checkBox2, "cell 0 5");
add(checkBox2ChartColor, "cell 1 5");
//---- durationLabel ----
durationLabel.setText("Duration:");
add(durationLabel, "cell 0 6 2 1");
add(durationLabel, "cell 0 7 2 1");
//---- durationField ----
durationField.setModel(new SpinnerNumberModel(200, 100, null, 50));
add(durationField, "cell 0 6 2 1");
durationField.setModel(new SpinnerNumberModel(200, 0, null, 50));
add(durationField, "cell 0 7 2 1");
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
@@ -116,10 +158,17 @@ public class FlatAnimatedIconTest
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JRadioButton radioButton1;
private FlatAnimatorTest.JChartColor radioButton1ChartColor;
private LineChartPanel lineChartPanel;
private JRadioButton radioButton2;
private FlatAnimatorTest.JChartColor radioButton2ChartColor;
private JRadioButton radioButton3;
private FlatAnimatorTest.JChartColor radioButton3ChartColor;
private JCheckBox checkBox1;
private FlatAnimatorTest.JChartColor checkBox1ChartColor;
private JCheckBox checkBox3;
private JCheckBox checkBox2;
private FlatAnimatorTest.JChartColor checkBox2ChartColor;
private JLabel durationLabel;
private JSpinner durationField;
// JFormDesigner - End of variables declaration //GEN-END:variables
@@ -146,7 +195,8 @@ public class FlatAnimatedIconTest
}
@Override
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
float animatedValue = animatedValues[0];
Color color = ColorFunctions.mix( onColor, offColor, animatedValue );
// border
@@ -162,12 +212,17 @@ public class FlatAnimatedIconTest
float dotDiameter = DOT_SIZE * animatedValue;
float xy = (SIZE - dotDiameter) / 2f;
g.setColor( color );
((Graphics2D)g).fill( new Ellipse2D.Float( xy, xy, dotDiameter, dotDiameter ) );
g.fill( new Ellipse2D.Float( xy, xy, dotDiameter, dotDiameter ) );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "radio" );
}
}
@Override
public float getValue( Component c ) {
return ((JRadioButton)c).isSelected() ? 1 : 0;
public float[] getAnimatableValues( Component c ) {
return new float[] { ((JRadioButton)c).isSelected() ? 1 : 0 };
}
@Override
@@ -178,7 +233,7 @@ public class FlatAnimatedIconTest
//---- class AnimatedSwitchIcon -------------------------------------------
public class AnimatedSwitchIcon
private class AnimatedSwitchIcon
extends FlatAnimatedIcon
{
private final Color offColor = Color.lightGray;
@@ -189,22 +244,113 @@ public class FlatAnimatedIconTest
}
@Override
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
float animatedValue = animatedValues[0];
Color color = ColorFunctions.mix( onColor, offColor, animatedValue );
// paint track
g.setColor( color );
g.fillRoundRect( x, y, width, height, height, height );
g.fillRoundRect( 0, 0, width, height, height, height );
// paint thumb
int thumbSize = height - 4;
float thumbX = x + 2 + ((width - 4 - thumbSize) * animatedValue);
int thumbY = y + 2;
float thumbX = 2 + ((width - 4 - thumbSize) * animatedValue);
int thumbY = 2;
g.setColor( Color.white );
((Graphics2D)g).fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
g.fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "switch" );
}
}
@Override
public float getValue( Component c ) {
return ((AbstractButton)c).isSelected() ? 1 : 0;
public float[] getAnimatableValues( Component c ) {
return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
}
@Override
public int getAnimationDuration() {
return (Integer) durationField.getValue();
}
}
//---- class AnimatedSwitchIconEx -----------------------------------------
private static final int HW = 8;
private class AnimatedSwitchIconEx
extends FlatAnimatedIcon
{
private final Color offColor = Color.lightGray;
private final Color onColor = Color.red;
private final Color hoverColor = new Color( 0x4400cc00, true );
private final Color pressedColor = new Color( 0x440000cc, true );
public AnimatedSwitchIconEx() {
super( 28 + HW, 16 + HW, null );
}
@Override
public void paintIconAnimated( Component c, Graphics2D g, float[] animatedValues ) {
Color color = ColorFunctions.mix( onColor, offColor, animatedValues[0] );
int hw2 = HW / 2;
int x = hw2;
int y = hw2;
int width = this.width - HW;
int height = this.height - HW;
// paint track
g.setColor( color );
g.fillRoundRect( x, y, width, height, height, height );
// paint thumb
int thumbSize = height - 4;
float thumbX = x + 2 + ((width - 4 - thumbSize) * animatedValues[0]);
int thumbY = y + 2;
g.setColor( Color.white );
g.fill( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ) );
// paint hover
if( animatedValues[1] > 0 ) {
g.setColor( hoverColor );
paintHoverOrPressed( g, thumbX, thumbY, thumbSize, animatedValues[1] );
}
// paint pressed
if( animatedValues[2] > 0 ) {
g.setColor( pressedColor );
paintHoverOrPressed( g, thumbX, thumbY, thumbSize, animatedValues[2] );
}
for( int i = 0; i < animatedValues.length; i++ ) {
float animatedValue = animatedValues[i];
if( animatedValue != 0 && animatedValue != 1 )
lineChartPanel.addValue( CHART_SWITCH_EX[i], animatedValue, Integer.MIN_VALUE, "switch ex" );
}
}
private void paintHoverOrPressed( Graphics2D g, float thumbX, int thumbY, int thumbSize, float animatedValue ) {
float hw = (HW + 4) * animatedValue;
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Ellipse2D.Float( thumbX - (hw / 2), thumbY - (hw / 2),
thumbSize + hw, thumbSize + hw ), false );
path.append( new Ellipse2D.Float( thumbX, thumbY, thumbSize, thumbSize ), false );
g.fill( path );
}
@Override
public float[] getAnimatableValues( Component c ) {
AbstractButton b = (AbstractButton) c;
ButtonModel bm = b.getModel();
return new float[] {
b.isSelected() ? 1 : 0,
bm.isRollover() ? 1 : 0,
bm.isPressed() ? 1 : 0,
};
}
@Override
@@ -223,27 +369,31 @@ public class FlatAnimatedIconTest
{
@Override
public int getIconWidth() {
return 100;
return UIScale.scale( 50 );
}
@Override
public int getIconHeight() {
return 20;
return UIScale.scale( 16 );
}
@Override
public void paintIconAnimated( Component c, Graphics g, int x, int y, float animatedValue ) {
int w = getIconWidth();
int h = getIconHeight();
public void paintAnimated( Component c, Graphics2D g, int x, int y, int width, int height, float[] animatedValues ) {
float animatedValue = animatedValues[0];
g.setColor( Color.red );
g.drawRect( x, y, w - 1, h - 1 );
g.fillRect( x, y, Math.round( w * animatedValue ), h );
g.drawRect( x, y, width - 1, height - 1 );
g.fillRect( x, y, Math.round( width * animatedValue ), height );
if( animatedValue != 0 && animatedValue != 1 ) {
Color chartColor = (Color) ((JComponent)c).getClientProperty( CHART_COLOR_KEY );
lineChartPanel.addValue( chartColor, animatedValue, Integer.MIN_VALUE, "minimal" );
}
}
@Override
public float getValue( Component c ) {
return ((AbstractButton)c).isSelected() ? 1 : 0;
public float[] getAnimatableValues( Component c ) {
return new float[] { ((AbstractButton)c).isSelected() ? 1 : 0 };
}
@Override

View File

@@ -1,12 +1,12 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[]para[fill]"
"$rowConstraints": "[][][]para[][][grow][]"
"$columnConstraints": "[][fill]para[grow,fill]"
"$rowConstraints": "[][][]para[][][][grow][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JRadioButton" ) {
@@ -17,6 +17,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "radioButton1ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
name: "lineChartPanel"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0 1 8,growy"
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "radioButton2"
"text": "radio 2"
@@ -24,6 +34,11 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "radioButton2ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "radioButton3"
"text": "radio 3"
@@ -31,37 +46,58 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "radioButton3ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "checkBox1"
"text": "switch"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "checkBox1ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "checkBox3"
"text": "switch ex"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "checkBox2"
"text": "minimal"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
"value": "cell 0 5"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "checkBox2ChartColor"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "durationLabel"
"text": "Duration:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6 2 1"
"value": "cell 0 7 2 1"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "durationField"
"model": new javax.swing.SpinnerNumberModel {
minimum: 100
minimum: 0
stepSize: 50
value: 200
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6 2 1"
"value": "cell 0 7 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 415, 350 )
"size": new java.awt.Dimension( 810, 350 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonGroup1"

View File

@@ -20,6 +20,8 @@ import java.awt.*;
import javax.swing.*;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.UIScale;
import com.formdev.flatlaf.util.Animator.Interpolator;
import net.miginfocom.swing.*;
/**
@@ -28,8 +30,13 @@ import net.miginfocom.swing.*;
public class FlatAnimatorTest
extends FlatTestPanel
{
private static final Color CHART_LINEAR = Color.blue;
private static final Color CHART_EASE_IN_OUT = Color.magenta;
private static final Color CHART_STANDARD_EASING = Color.red;
private Animator linearAnimator;
private Animator easeInOutAnimator;
private Animator standardEasingAnimator;
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
@@ -40,85 +47,132 @@ public class FlatAnimatorTest
FlatAnimatorTest() {
initComponents();
linearChartColor.setForeground( CHART_LINEAR );
easeInOutChartColor.setForeground( CHART_EASE_IN_OUT );
standardEasingChartColor.setForeground( CHART_STANDARD_EASING );
}
private void start() {
startLinear();
startEaseInOut();
linearAnimator = start( linearAnimator, null, linearScrollBar, CHART_LINEAR );
easeInOutAnimator = start( easeInOutAnimator, CubicBezierEasing.EASE_IN_OUT, easeInOutScrollBar, CHART_EASE_IN_OUT );
standardEasingAnimator = start( standardEasingAnimator, CubicBezierEasing.STANDARD_EASING, standardEasingScrollBar, CHART_STANDARD_EASING );
}
private void startLinear() {
if( linearAnimator != null ) {
linearAnimator.stop();
linearAnimator.start();
private Animator start( Animator animator, Interpolator interpolator, JScrollBar scrollBar, Color chartColor ) {
if( animator != null ) {
animator.stop();
animator.start();
} else {
linearAnimator = new Animator( 1000, fraction -> {
linearScrollBar.setValue( Math.round( fraction * linearScrollBar.getMaximum() ) );
animator = new Animator( 1000, fraction -> {
scrollBar.setValue( Math.round( fraction * scrollBar.getMaximum() ) );
lineChartPanel.addValue( chartColor, fraction, Integer.MIN_VALUE, "animator" );
} );
linearAnimator.start();
}
}
private void startEaseInOut() {
if( easeInOutAnimator != null ) {
easeInOutAnimator.stop();
easeInOutAnimator.start();
} else {
easeInOutAnimator = new Animator( 1000, fraction -> {
easeInOutScrollBar.setValue( Math.round( fraction * easeInOutScrollBar.getMaximum() ) );
} );
easeInOutAnimator.setInterpolator( CubicBezierEasing.EASE_IN_OUT );
easeInOutAnimator.start();
animator.setInterpolator( interpolator );
animator.start();
}
return animator;
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel label1 = new JLabel();
linearLabel = new JLabel();
linearChartColor = new FlatAnimatorTest.JChartColor();
linearScrollBar = new JScrollBar();
JLabel label2 = new JLabel();
easeInOutLabel = new JLabel();
easeInOutChartColor = new FlatAnimatorTest.JChartColor();
easeInOutScrollBar = new JScrollBar();
standardEasingLabel = new JLabel();
standardEasingChartColor = new FlatAnimatorTest.JChartColor();
standardEasingScrollBar = new JScrollBar();
startButton = new JButton();
lineChartPanel = new LineChartPanel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[fill]" +
"[fill]" +
"[grow,fill]",
// rows
"[]" +
"[]" +
"[]"));
"[]" +
"[]para" +
"[400,grow,fill]"));
//---- label1 ----
label1.setText("Linear:");
add(label1, "cell 0 0");
//---- linearLabel ----
linearLabel.setText("Linear:");
add(linearLabel, "cell 0 0");
add(linearChartColor, "cell 1 0");
//---- linearScrollBar ----
linearScrollBar.setOrientation(Adjustable.HORIZONTAL);
linearScrollBar.setBlockIncrement(1);
add(linearScrollBar, "cell 1 0");
add(linearScrollBar, "cell 2 0");
//---- label2 ----
label2.setText("Ease in out:");
add(label2, "cell 0 1");
//---- easeInOutLabel ----
easeInOutLabel.setText("Ease in out:");
add(easeInOutLabel, "cell 0 1");
add(easeInOutChartColor, "cell 1 1");
//---- easeInOutScrollBar ----
easeInOutScrollBar.setOrientation(Adjustable.HORIZONTAL);
easeInOutScrollBar.setBlockIncrement(1);
add(easeInOutScrollBar, "cell 1 1");
add(easeInOutScrollBar, "cell 2 1");
//---- standardEasingLabel ----
standardEasingLabel.setText("Standard easing:");
add(standardEasingLabel, "cell 0 2");
add(standardEasingChartColor, "cell 1 2");
//---- standardEasingScrollBar ----
standardEasingScrollBar.setOrientation(Adjustable.HORIZONTAL);
standardEasingScrollBar.setBlockIncrement(1);
add(standardEasingScrollBar, "cell 2 2");
//---- startButton ----
startButton.setText("Start");
startButton.addActionListener(e -> start());
add(startButton, "cell 0 2");
add(startButton, "cell 0 3");
add(lineChartPanel, "cell 0 4 3 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel linearLabel;
private FlatAnimatorTest.JChartColor linearChartColor;
private JScrollBar linearScrollBar;
private JLabel easeInOutLabel;
private FlatAnimatorTest.JChartColor easeInOutChartColor;
private JScrollBar easeInOutScrollBar;
private JLabel standardEasingLabel;
private FlatAnimatorTest.JChartColor standardEasingChartColor;
private JScrollBar standardEasingScrollBar;
private JButton startButton;
private LineChartPanel lineChartPanel;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class JChartColor --------------------------------------------------
static class JChartColor
extends JComponent
{
@Override
public Dimension getPreferredSize() {
return new Dimension( UIScale.scale( 24 ), UIScale.scale( 12 ) );
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
protected void paintComponent( Graphics g ) {
g.setColor( getForeground() );
g.fillRect( 0, 0, UIScale.scale( 24 ), UIScale.scale( 12 ) );
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -8,16 +8,27 @@ new FormModel {
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[fill][grow,fill]"
"$rowConstraints": "[][][]"
"$columnConstraints": "[fill][fill][grow,fill]"
"$rowConstraints": "[][][][]para[400,grow,fill]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
name: "linearLabel"
"text": "Linear:"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "linearChartColor"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JScrollBar" ) {
name: "linearScrollBar"
"orientation": 0
@@ -26,14 +37,25 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
"value": "cell 2 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
name: "easeInOutLabel"
"text": "Ease in out:"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "easeInOutChartColor"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JScrollBar" ) {
name: "easeInOutScrollBar"
"orientation": 0
@@ -42,7 +64,34 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "standardEasingLabel"
"text": "Standard easing:"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatAnimatorTest$JChartColor" ) {
name: "standardEasingChartColor"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JScrollBar" ) {
name: "standardEasingScrollBar"
"orientation": 0
"blockIncrement": 1
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "startButton"
@@ -52,11 +101,19 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "start", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
"value": "cell 0 3"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel" ) {
name: "lineChartPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 3 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 415, 350 )
"size": new java.awt.Dimension( 625, 625 )
} )
}
}

View File

@@ -198,17 +198,17 @@ public class FlatComponentStateTest
//---- label11 ----
label11.setText("JButton");
label11.setFont(label11.getFont().deriveFont(label11.getFont().getSize() + 4f));
label11.putClientProperty("FlatLaf.styleClass", "h3");
add(label11, "cell 1 0 2 1");
//---- label12 ----
label12.setText("JToggleButton");
label12.setFont(label12.getFont().deriveFont(label12.getFont().getSize() + 4f));
label12.putClientProperty("FlatLaf.styleClass", "h3");
add(label12, "cell 5 0 3 1");
//---- label32 ----
label32.setText("Help Button");
label32.setFont(label32.getFont().deriveFont(label32.getFont().getSize() + 4f));
label32.putClientProperty("FlatLaf.styleClass", "h3");
add(label32, "cell 9 0 2 1");
//---- label5 ----
@@ -513,12 +513,12 @@ public class FlatComponentStateTest
//---- label22 ----
label22.setText("JCheckBox");
label22.setFont(label22.getFont().deriveFont(label22.getFont().getSize() + 4f));
label22.putClientProperty("FlatLaf.styleClass", "h3");
add(label22, "cell 1 8 2 1");
//---- label27 ----
label27.setText("JRadioButton");
label27.setFont(label27.getFont().deriveFont(label27.getFont().getSize() + 4f));
label27.putClientProperty("FlatLaf.styleClass", "h3");
add(label27, "cell 5 8 2 1");
//---- label23 ----

View File

@@ -12,21 +12,21 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"text": "JButton"
"font": &SwingDerivedFont0 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label12"
"text": "JToggleButton"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 0 3 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label32"
"text": "Help Button"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 9 0 2 1"
} )
@@ -447,14 +447,14 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label22"
"text": "JCheckBox"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label27"
"text": "JRadioButton"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 8 2 1"
} )

View File

@@ -1621,6 +1621,7 @@ public class FlatComponentsTest
// customRenderer.setBorder( new LineBorder( Color.red ) );
// comboBox1.setRenderer( customRenderer );
// comboBox3.setRenderer( customRenderer );
// comboBox5.setRenderer( new DefaultListCellRenderer() );
// for testing issue #382
// spinner1.setModel( new SpinnerNumberModel( 0, null, 100, 1 ) );

View File

@@ -50,6 +50,8 @@ public class FlatContainerTest
public FlatContainerTest() {
initComponents();
tabTypeComboBox.init( TabType.class, true );
tabPlacementField.init( TabPlacement.class, true );
iconPlacementField.init( TabIconPlacement.class, true );
tabsPopupPolicyField.init( TabsPopupPolicy.class, true );
@@ -310,6 +312,12 @@ public class FlatContainerTest
tabbedPane.setTabWidthMode( value );
}
private void tabTypeChanged() {
TabType value = tabTypeComboBox.getSelectedValue();
for( FlatTabbedPane tabbedPane : allTabbedPanes )
tabbedPane.setTabType( value );
}
private void tabBackForegroundChanged() {
for( JTabbedPane tabbedPane : allTabbedPanes )
tabBackForegroundChanged( tabbedPane );
@@ -491,6 +499,8 @@ public class FlatContainerTest
tabAlignmentField = new FlatTestEnumComboBox<>();
JLabel tabWidthModeLabel = new JLabel();
tabWidthModeField = new FlatTestEnumComboBox<>();
JLabel tabTypeLabel = new JLabel();
tabTypeComboBox = new FlatTestEnumComboBox<>();
leadingComponentCheckBox = new JCheckBox();
customBorderCheckBox = new JCheckBox();
tabAreaInsetsCheckBox = new JCheckBox();
@@ -619,6 +629,7 @@ public class FlatContainerTest
"[]" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]para" +
@@ -739,75 +750,83 @@ public class FlatContainerTest
tabWidthModeField.addActionListener(e -> tabWidthModeChanged());
tabbedPaneControlPanel.add(tabWidthModeField, "cell 2 5");
//---- tabTypeLabel ----
tabTypeLabel.setText("Tab type:");
tabbedPaneControlPanel.add(tabTypeLabel, "cell 0 6");
//---- tabTypeComboBox ----
tabTypeComboBox.addActionListener(e -> tabTypeChanged());
tabbedPaneControlPanel.add(tabTypeComboBox, "cell 1 6");
//---- leadingComponentCheckBox ----
leadingComponentCheckBox.setText("Leading component");
leadingComponentCheckBox.addActionListener(e -> leadingComponentChanged());
tabbedPaneControlPanel.add(leadingComponentCheckBox, "cell 0 6");
tabbedPaneControlPanel.add(leadingComponentCheckBox, "cell 0 7");
//---- customBorderCheckBox ----
customBorderCheckBox.setText("Custom border");
customBorderCheckBox.addActionListener(e -> customBorderChanged());
tabbedPaneControlPanel.add(customBorderCheckBox, "cell 1 6");
tabbedPaneControlPanel.add(customBorderCheckBox, "cell 1 7");
//---- tabAreaInsetsCheckBox ----
tabAreaInsetsCheckBox.setText("Tab area insets (5,5,10,10)");
tabAreaInsetsCheckBox.addActionListener(e -> tabAreaInsetsChanged());
tabbedPaneControlPanel.add(tabAreaInsetsCheckBox, "cell 2 6");
tabbedPaneControlPanel.add(tabAreaInsetsCheckBox, "cell 2 7");
//---- trailingComponentCheckBox ----
trailingComponentCheckBox.setText("Trailing component");
trailingComponentCheckBox.addActionListener(e -> trailingComponentChanged());
tabbedPaneControlPanel.add(trailingComponentCheckBox, "cell 0 7");
tabbedPaneControlPanel.add(trailingComponentCheckBox, "cell 0 8");
//---- hasFullBorderCheckBox ----
hasFullBorderCheckBox.setText("Show content border");
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
tabbedPaneControlPanel.add(hasFullBorderCheckBox, "cell 1 7,alignx left,growx 0");
tabbedPaneControlPanel.add(hasFullBorderCheckBox, "cell 1 8,alignx left,growx 0");
//---- smallerTabHeightCheckBox ----
smallerTabHeightCheckBox.setText("Smaller tab height (26)");
smallerTabHeightCheckBox.addActionListener(e -> smallerTabHeightChanged());
tabbedPaneControlPanel.add(smallerTabHeightCheckBox, "cell 2 7");
tabbedPaneControlPanel.add(smallerTabHeightCheckBox, "cell 2 8");
//---- minimumTabWidthCheckBox ----
minimumTabWidthCheckBox.setText("Minimum tab width (100)");
minimumTabWidthCheckBox.addActionListener(e -> minimumTabWidthChanged());
tabbedPaneControlPanel.add(minimumTabWidthCheckBox, "cell 0 8");
tabbedPaneControlPanel.add(minimumTabWidthCheckBox, "cell 0 9");
//---- hideContentSeparatorCheckBox ----
hideContentSeparatorCheckBox.setText("Hide content separator");
hideContentSeparatorCheckBox.addActionListener(e -> hideContentSeparatorChanged());
tabbedPaneControlPanel.add(hideContentSeparatorCheckBox, "cell 1 8");
tabbedPaneControlPanel.add(hideContentSeparatorCheckBox, "cell 1 9");
//---- smallerInsetsCheckBox ----
smallerInsetsCheckBox.setText("Smaller tab insets (2,2,2,2)");
smallerInsetsCheckBox.addActionListener(e -> smallerInsetsChanged());
tabbedPaneControlPanel.add(smallerInsetsCheckBox, "cell 2 8");
tabbedPaneControlPanel.add(smallerInsetsCheckBox, "cell 2 9");
//---- maximumTabWidthCheckBox ----
maximumTabWidthCheckBox.setText("Maximum tab width (60)");
maximumTabWidthCheckBox.addActionListener(e -> maximumTabWidthChanged());
tabbedPaneControlPanel.add(maximumTabWidthCheckBox, "cell 0 9");
tabbedPaneControlPanel.add(maximumTabWidthCheckBox, "cell 0 10");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
tabbedPaneControlPanel.add(showTabSeparatorsCheckBox, "cell 1 9");
tabbedPaneControlPanel.add(showTabSeparatorsCheckBox, "cell 1 10");
//---- secondTabWiderCheckBox ----
secondTabWiderCheckBox.setText("Second Tab insets wider (4,20,4,20)");
secondTabWiderCheckBox.addActionListener(e -> secondTabWiderChanged());
tabbedPaneControlPanel.add(secondTabWiderCheckBox, "cell 2 9");
tabbedPaneControlPanel.add(secondTabWiderCheckBox, "cell 2 10");
//---- hideTabAreaWithOneTabCheckBox ----
hideTabAreaWithOneTabCheckBox.setText("Hide tab area with one tab");
hideTabAreaWithOneTabCheckBox.addActionListener(e -> hideTabAreaWithOneTabChanged());
tabbedPaneControlPanel.add(hideTabAreaWithOneTabCheckBox, "cell 1 10");
tabbedPaneControlPanel.add(hideTabAreaWithOneTabCheckBox, "cell 1 11");
//---- customWheelScrollingCheckBox ----
customWheelScrollingCheckBox.setText("Custom wheel scrolling");
customWheelScrollingCheckBox.addActionListener(e -> customWheelScrollingChanged());
tabbedPaneControlPanel.add(customWheelScrollingCheckBox, "cell 2 10");
tabbedPaneControlPanel.add(customWheelScrollingCheckBox, "cell 2 11");
}
panel9.add(tabbedPaneControlPanel, cc.xywh(1, 11, 3, 1));
}
@@ -840,6 +859,7 @@ public class FlatContainerTest
private FlatTestEnumComboBox<TabAreaAlignment> tabAreaAlignmentField;
private FlatTestEnumComboBox<TabAlignment> tabAlignmentField;
private FlatTestEnumComboBox<TabWidthMode> tabWidthModeField;
private FlatTestEnumComboBox<TabType> tabTypeComboBox;
private JCheckBox leadingComponentCheckBox;
private JCheckBox customBorderCheckBox;
private JCheckBox tabAreaInsetsCheckBox;

View File

@@ -132,7 +132,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestFrame$NoRightToLeftPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[][fill][]"
"$rowConstraints": "[center][][][][][]para[][]para[][][]"
"$rowConstraints": "[center][][][][][][]para[][]para[][][]"
} ) {
name: "tabbedPaneControlPanel"
"opaque": false
@@ -372,6 +372,22 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabTypeLabel"
"text": "Tab type:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumComboBox" ) {
name: "tabTypeComboBox"
auxiliary() {
"JavaCodeGenerator.typeParameters": "TabType"
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabTypeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "leadingComponentCheckBox"
"text": "Leading component"
@@ -380,7 +396,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "leadingComponentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
"value": "cell 0 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "customBorderCheckBox"
@@ -390,7 +406,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
"value": "cell 1 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabAreaInsetsCheckBox"
@@ -400,7 +416,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabAreaInsetsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 6"
"value": "cell 2 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "trailingComponentCheckBox"
@@ -410,7 +426,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "trailingComponentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
"value": "cell 0 8"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hasFullBorderCheckBox"
@@ -420,7 +436,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hasFullBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7,alignx left,growx 0"
"value": "cell 1 8,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "smallerTabHeightCheckBox"
@@ -430,7 +446,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "smallerTabHeightChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7"
"value": "cell 2 8"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "minimumTabWidthCheckBox"
@@ -440,7 +456,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "minimumTabWidthChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8"
"value": "cell 0 9"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hideContentSeparatorCheckBox"
@@ -450,7 +466,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideContentSeparatorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8"
"value": "cell 1 9"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "smallerInsetsCheckBox"
@@ -460,7 +476,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "smallerInsetsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 8"
"value": "cell 2 9"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "maximumTabWidthCheckBox"
@@ -470,7 +486,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "maximumTabWidthChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
"value": "cell 0 10"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTabSeparatorsCheckBox"
@@ -480,7 +496,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTabSeparatorsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9"
"value": "cell 1 10"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "secondTabWiderCheckBox"
@@ -490,7 +506,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "secondTabWiderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 9"
"value": "cell 2 10"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hideTabAreaWithOneTabCheckBox"
@@ -500,7 +516,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hideTabAreaWithOneTabChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 10"
"value": "cell 1 11"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "customWheelScrollingCheckBox"
@@ -510,7 +526,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "customWheelScrollingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 10"
"value": "cell 2 11"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 11

View File

@@ -34,7 +34,7 @@ public class FlatCustomBordersTest
private static final Color RED = new Color( 0x60ff0000, true );
private static final Color GREEN = new Color( 0x6000ff00, true );
private static final Color MAGENTA = new Color( 0x60ff00ff, true );
private static final Color ORANGE = new Color( 0x60ffc800, true );
private static final Color BLUE = new Color( 0x300000ff, true );
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
@@ -127,7 +127,7 @@ public class FlatCustomBordersTest
@SuppressWarnings( "unchecked" )
private void applySpecialComboBoxRenderers() {
BasicComboBoxRenderer sharedRenderer = new BasicComboBoxRenderer();
sharedRenderer.setBorder( new LineBorder( ORANGE, UIScale.scale( 2 ) ) );
sharedRenderer.setBorder( new LineBorder( BLUE, UIScale.scale( 2 ) ) );
comboBox29.setRenderer( sharedRenderer );
comboBox30.setRenderer( sharedRenderer );
@@ -160,7 +160,7 @@ public class FlatCustomBordersTest
}
private void applyCustomComboBoxEditorBorder( JComboBox<String> comboBox ) {
applyCustomComboBoxEditorBorder( comboBox, new LineBorder( ORANGE, UIScale.scale( 6 ) ) );
applyCustomComboBoxEditorBorder( comboBox, new LineBorder( BLUE, UIScale.scale( 6 ) ) );
}
private void applyCustomComboBoxEditorBorderWithIcon( JComboBox<String> comboBox ) {
@@ -180,7 +180,7 @@ public class FlatCustomBordersTest
}
private void applyCustomComboBoxRendererBorder( JComboBox<String> comboBox ) {
applyCustomComboBoxRendererBorder( comboBox, new LineBorder( ORANGE, UIScale.scale( 6 ) ) );
applyCustomComboBoxRendererBorder( comboBox, new LineBorder( BLUE, UIScale.scale( 6 ) ) );
}
private void applyCustomComboBoxRendererBorderWithIcon( JComboBox<String> comboBox ) {

View File

@@ -0,0 +1,242 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.*;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatFontsTest
extends JPanel
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatFontsTest" );
frame.showFrame( FlatFontsTest::new );
} );
}
FlatFontsTest() {
initComponents();
Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
TreeMap<String, TreeMap<String, Font>> familiesMap = new TreeMap<>();
for( Font font : allFonts ) {
TreeMap<String, Font> familyFontsMap = familiesMap.computeIfAbsent( font.getFamily(), key -> new TreeMap<>() );
Font old = familyFontsMap.put( font.getName(), font );
if( old != null ) {
System.err.println( "Duplicate font name '" + font.getName() + "'" );
System.err.println( " " + old );
System.err.println( " " + font );
}
}
DefaultListModel<FontFamilyInfo> model = new DefaultListModel<>();
for( Map.Entry<String, TreeMap<String, Font>> e : familiesMap.entrySet() ) {
FontFamilyInfo info = new FontFamilyInfo();
info.name = e.getKey();
info.fonts = e.getValue();
model.addElement( info );
}
familiesList.setModel( model );
familiesList.setCellRenderer( new FontFamilyRenderer() );
familiesList.setSelectedIndex( 0 );
SwingUtilities.invokeLater( () -> {
SwingUtilities.invokeLater( () -> {
familiesList.requestFocusInWindow();
} );
} );
}
private void familyChanged() {
previewFamilyNameLabel.setText( "" );
previewPanel.removeAll();
FontFamilyInfo info = familiesList.getSelectedValue();
if( info == null )
return;
previewFamilyNameLabel.setText( info.name );
for( Map.Entry<String, Font> e : info.fonts.entrySet() ) {
JLabel label = new JLabel( e.getKey() );
label.setFont( e.getValue().deriveFont( (float) UIScale.scale( 36 ) ) );
previewPanel.add( label, "wrap" );
}
previewPanel.revalidate();
previewPanel.repaint();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel familiesLabel = new JLabel();
previewFamilyNameLabel = new JLabel();
JScrollPane familiesScrollPane = new JScrollPane();
familiesList = new JList<>();
JSeparator separator1 = new JSeparator();
JScrollPane scrollPane1 = new JScrollPane();
previewPanel = new JPanel();
//======== this ========
setBorder(null);
setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[230:230,fill]para" +
"[grow,fill]",
// rows
"[top]" +
"[]" +
"[]" +
"[800,grow,fill]"));
//---- familiesLabel ----
familiesLabel.setText("Families:");
add(familiesLabel, "cell 0 0");
//---- previewFamilyNameLabel ----
previewFamilyNameLabel.setText("name");
previewFamilyNameLabel.putClientProperty("FlatLaf.styleClass", "h1");
add(previewFamilyNameLabel, "cell 1 1");
//======== familiesScrollPane ========
{
//---- familiesList ----
familiesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
familiesList.addListSelectionListener(e -> familyChanged());
familiesScrollPane.setViewportView(familiesList);
}
add(familiesScrollPane, "cell 0 1 1 3,growy");
add(separator1, "cell 1 2");
//======== scrollPane1 ========
{
scrollPane1.setBorder(BorderFactory.createEmptyBorder());
//======== previewPanel ========
{
previewPanel.setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[fill]",
// rows
"[]"));
}
scrollPane1.setViewportView(previewPanel);
}
add(scrollPane1, "cell 1 3");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel previewFamilyNameLabel;
private JList<FontFamilyInfo> familiesList;
private JPanel previewPanel;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class FontFamilyInfo -----------------------------------------------
private static class FontFamilyInfo
{
String name;
TreeMap<String, Font> fonts;
}
//---- class FontFamilyRenderer -------------------------------------------
private static class FontFamilyRenderer
extends JPanel
implements ListCellRenderer<FontFamilyInfo>
{
private FontFamilyRenderer() {
initComponents();
}
@Override
public Component getListCellRendererComponent( JList<? extends FontFamilyInfo> list,
FontFamilyInfo value, int index, boolean isSelected, boolean cellHasFocus )
{
String family = value.name;
StringBuilder buf = new StringBuilder();
for( String key : value.fonts.keySet() ) {
if( key.startsWith( family ) ) {
key = key.substring( family.length() ).trim();
if( key.isEmpty() )
key = "Regular";
}
if( buf.length() > 0 )
buf.append( ", " );
buf.append( key );
}
familyNameLabel.setText( family );
familyDescriptionLabel.setText( buf.toString() );
familyNameLabel.setFont( UIManager.getFont( "large.font" ) );
familyDescriptionLabel.setFont( UIManager.getFont( "small.font" ) );
familyDescriptionLabel.setEnabled( isSelected );
setBackground( isSelected ? list.getSelectionBackground() : list.getBackground() );
Color fg = isSelected ? list.getSelectionForeground() : list.getForeground();
familyNameLabel.setForeground( fg );
familyDescriptionLabel.setForeground( fg );
return this;
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
familyNameLabel = new JLabel();
familyDescriptionLabel = new JLabel();
//======== this ========
setLayout(new MigLayout(
"insets 2 6 2 6,hidemode 3",
// columns
"[fill]",
// rows
"[]0" +
"[]"));
//---- familyNameLabel ----
familyNameLabel.setText("text");
add(familyNameLabel, "cell 0 0");
//---- familyDescriptionLabel ----
familyDescriptionLabel.setText("text");
add(familyDescriptionLabel, "cell 0 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel familyNameLabel;
private JLabel familyDescriptionLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
}

View File

@@ -0,0 +1,103 @@
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[230:230,fill]para[grow,fill]"
"$rowConstraints": "[top][][][800,grow,fill]"
} ) {
name: "this"
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
add( new FormComponent( "javax.swing.JLabel" ) {
name: "familiesLabel"
"text": "Families:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "previewFamilyNameLabel"
"text": "name"
"$client.FlatLaf.styleClass": "h1"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
name: "familiesScrollPane"
add( new FormComponent( "javax.swing.JList" ) {
name: "familiesList"
"selectionMode": 0
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "FontFamilyInfo"
}
addEvent( new FormEvent( "javax.swing.event.ListSelectionListener", "valueChanged", "familyChanged", false ) )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1 1 3,growy"
} )
add( new FormComponent( "javax.swing.JSeparator" ) {
name: "separator1"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
name: "scrollPane1"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]"
} ) {
name: "previewPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 450, 300 )
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 2 6 2 6,hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]0[]"
} ) {
name: "panel1"
auxiliary() {
"JavaCodeGenerator.className": "FontFamilyRenderer"
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "familyNameLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "familyDescriptionLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 330 )
"size": new java.awt.Dimension( 255, 105 )
} )
}
}

View File

@@ -33,6 +33,7 @@ public class FlatOptionPaneTest
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatOptionPaneTest" );
frame.setIconImage( new ImageIcon( FlatOptionPaneTest.class.getResource( "/com/formdev/flatlaf/testing/test16.png" ) ).getImage() );
frame.showFrame( FlatOptionPaneTest::new );
} );
}
@@ -54,6 +55,10 @@ public class FlatOptionPaneTest
} );
}
private void showTitleBarIcon() {
UIManager.put( "OptionPane.showIcon", showTitleBarIconCheckBox.isSelected() );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
ScrollablePanel panel9 = new ScrollablePanel();
@@ -61,6 +66,7 @@ public class FlatOptionPaneTest
JPanel panel1 = new JPanel();
JOptionPane plainOptionPane = new JOptionPane();
plainShowDialogLabel = new FlatOptionPaneTest.ShowDialogLinkLabel();
showTitleBarIconCheckBox = new JCheckBox();
JLabel errorLabel = new JLabel();
JPanel panel2 = new JPanel();
JOptionPane errorOptionPane = new JOptionPane();
@@ -100,7 +106,7 @@ public class FlatOptionPaneTest
//======== panel9 ========
{
panel9.setLayout(new MigLayout(
"flowy,ltr,insets dialog,hidemode 3",
"ltr,insets dialog,hidemode 3",
// columns
"[]" +
"[]" +
@@ -134,7 +140,12 @@ public class FlatOptionPaneTest
//---- plainShowDialogLabel ----
plainShowDialogLabel.setOptionPane(plainOptionPane);
plainShowDialogLabel.setTitleLabel(plainLabel);
panel9.add(plainShowDialogLabel, "cell 2 0");
panel9.add(plainShowDialogLabel, "cell 1 0");
//---- showTitleBarIconCheckBox ----
showTitleBarIconCheckBox.setText("Show window title bar icon");
showTitleBarIconCheckBox.addActionListener(e -> showTitleBarIcon());
panel9.add(showTitleBarIconCheckBox, "cell 2 0");
//---- errorLabel ----
errorLabel.setText("Error");
@@ -156,7 +167,7 @@ public class FlatOptionPaneTest
//---- errorShowDialogLabel ----
errorShowDialogLabel.setTitleLabel(errorLabel);
errorShowDialogLabel.setOptionPane(errorOptionPane);
panel9.add(errorShowDialogLabel, "cell 2 1");
panel9.add(errorShowDialogLabel, "cell 1 1");
//---- informationLabel ----
informationLabel.setText("Information");
@@ -178,7 +189,7 @@ public class FlatOptionPaneTest
//---- informationShowDialogLabel ----
informationShowDialogLabel.setOptionPane(informationOptionPane);
informationShowDialogLabel.setTitleLabel(informationLabel);
panel9.add(informationShowDialogLabel, "cell 2 2");
panel9.add(informationShowDialogLabel, "cell 1 2");
//---- questionLabel ----
questionLabel.setText("Question");
@@ -222,7 +233,7 @@ public class FlatOptionPaneTest
//---- warningShowDialogLabel ----
warningShowDialogLabel.setOptionPane(warningOptionPane);
warningShowDialogLabel.setTitleLabel(warningLabel);
panel9.add(warningShowDialogLabel, "cell 2 4");
panel9.add(warningShowDialogLabel, "cell 1 4");
//---- inputLabel ----
inputLabel.setText("Input");
@@ -244,7 +255,7 @@ public class FlatOptionPaneTest
//---- inputShowDialogLabel ----
inputShowDialogLabel.setOptionPane(inputOptionPane);
inputShowDialogLabel.setTitleLabel(inputLabel);
panel9.add(inputShowDialogLabel, "cell 2 5");
panel9.add(inputShowDialogLabel, "cell 1 5");
//---- inputIconLabel ----
inputIconLabel.setText("Input + icon");
@@ -267,7 +278,7 @@ public class FlatOptionPaneTest
//---- inputIconShowDialogLabel ----
inputIconShowDialogLabel.setTitleLabel(inputIconLabel);
inputIconShowDialogLabel.setOptionPane(inputIconOptionPane);
panel9.add(inputIconShowDialogLabel, "cell 2 6");
panel9.add(inputIconShowDialogLabel, "cell 1 6");
//---- customLabel ----
customLabel.setText("Custom");
@@ -287,7 +298,7 @@ public class FlatOptionPaneTest
//---- customShowDialogLabel ----
customShowDialogLabel.setOptionPane(customOptionPane);
customShowDialogLabel.setTitleLabel(customLabel);
panel9.add(customShowDialogLabel, "cell 2 7");
panel9.add(customShowDialogLabel, "cell 1 7");
//---- rightToLeftLabel ----
rightToLeftLabel.setText("Right-to-left:");
@@ -317,6 +328,7 @@ public class FlatOptionPaneTest
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private FlatOptionPaneTest.ShowDialogLinkLabel plainShowDialogLabel;
private JCheckBox showTitleBarIconCheckBox;
private FlatOptionPaneTest.ShowDialogLinkLabel errorShowDialogLabel;
private FlatOptionPaneTest.ShowDialogLinkLabel informationShowDialogLabel;
private JOptionPane customOptionPane;
@@ -333,6 +345,7 @@ public class FlatOptionPaneTest
ShowDialogLinkLabel() {
setText( "<html><a href=\"#\">Show dialog</a></html>" );
setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) );
addMouseListener( new MouseAdapter() {
@Override

View File

@@ -10,7 +10,7 @@ new FormModel {
name: "this"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
add( new FormContainer( "com.formdev.flatlaf.demo.ScrollablePanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "flowy,ltr,insets dialog,hidemode 3"
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][][fill]"
"$rowConstraints": "[top][top][top][top][top][top][top][top][top]"
} ) {
@@ -40,6 +40,16 @@ new FormModel {
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showTitleBarIconCheckBox"
"text": "Show window title bar icon"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showTitleBarIcon", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
@@ -71,7 +81,7 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "informationLabel"
@@ -101,7 +111,7 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "questionLabel"
@@ -155,7 +165,7 @@ new FormModel {
"optionPane": new FormReference( "warningOptionPane" )
"titleLabel": new FormReference( "warningLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4"
"value": "cell 1 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputLabel"
@@ -182,7 +192,7 @@ new FormModel {
"optionPane": new FormReference( "inputOptionPane" )
"titleLabel": new FormReference( "inputLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "inputIconLabel"
@@ -210,7 +220,7 @@ new FormModel {
"titleLabel": new FormReference( "inputIconLabel" )
"optionPane": new FormReference( "inputIconOptionPane" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 6"
"value": "cell 1 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "customLabel"
@@ -238,7 +248,7 @@ new FormModel {
"optionPane": new FormReference( "customOptionPane" )
"titleLabel": new FormReference( "customLabel" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7"
"value": "cell 1 7"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "rightToLeftLabel"
@@ -273,7 +283,7 @@ new FormModel {
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 895, 1080 )
"size": new java.awt.Dimension( 995, 1080 )
} )
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Random;
import javax.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import net.miginfocom.swing.*;
@@ -73,6 +74,9 @@ public class FlatWindowDecorationsTest
menuBarCheckBox.setSelected( window instanceof JFrame );
maximizedBoundsCheckBox.setEnabled( window instanceof Frame );
menuBarEmbeddedCheckBox.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedBackgroundCheckBox.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
addMenuButton.setEnabled( menuBarCheckBox.isEnabled() );
addGlueButton.setEnabled( menuBarCheckBox.isEnabled() );
removeMenuButton.setEnabled( menuBarCheckBox.isEnabled() );
@@ -91,20 +95,35 @@ public class FlatWindowDecorationsTest
JRootPane rootPane = getWindowRootPane();
if( rootPane != null ) {
int style = rootPane.getWindowDecorationStyle();
if( style == JRootPane.NONE )
styleNoneRadioButton.setSelected( true );
else if( style == JRootPane.FRAME )
styleFrameRadioButton.setSelected( true );
else if( style == JRootPane.PLAIN_DIALOG )
stylePlainRadioButton.setSelected( true );
else if( style == JRootPane.INFORMATION_DIALOG )
styleInfoRadioButton.setSelected( true );
else
throw new RuntimeException(); // not used
updateDecorationStyleRadioButtons( rootPane );
rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> {
updateDecorationStyleRadioButtons( rootPane );
} );
}
}
private void updateDecorationStyleRadioButtons( JRootPane rootPane ) {
int style = rootPane.getWindowDecorationStyle();
if( style == JRootPane.NONE )
styleNoneRadioButton.setSelected( true );
else if( style == JRootPane.FRAME )
styleFrameRadioButton.setSelected( true );
else if( style == JRootPane.PLAIN_DIALOG )
stylePlainRadioButton.setSelected( true );
else if( style == JRootPane.INFORMATION_DIALOG )
styleInfoRadioButton.setSelected( true );
else if( style == JRootPane.ERROR_DIALOG )
styleErrorRadioButton.setSelected( true );
else if( style == JRootPane.QUESTION_DIALOG )
styleQuestionRadioButton.setSelected( true );
else if( style == JRootPane.WARNING_DIALOG )
styleWarningRadioButton.setSelected( true );
else if( style == JRootPane.COLOR_CHOOSER_DIALOG )
styleColorChooserRadioButton.setSelected( true );
else if( style == JRootPane.FILE_CHOOSER_DIALOG )
styleFileChooserRadioButton.setSelected( true );
}
private void unifiedBackgroundChanged() {
UIManager.put( "TitlePane.unifiedBackground", unifiedBackgroundCheckBox.isSelected() );
Window window = SwingUtilities.windowForComponent( this );
@@ -387,6 +406,12 @@ public class FlatWindowDecorationsTest
}
}
private void showIconChanged() {
JRootPane rootPane = getWindowRootPane();
if( rootPane != null )
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_ICON, showIconCheckBox.getChecked() );
}
private JRootPane getWindowRootPane() {
Window window = SwingUtilities.windowForComponent( this );
if( window instanceof JFrame )
@@ -435,6 +460,7 @@ public class FlatWindowDecorationsTest
iconTestRandomRadioButton = new JRadioButton();
iconTestMRIRadioButton = new JRadioButton();
iconTestDynMRIRadioButton = new JRadioButton();
showIconCheckBox = new FlatTriStateCheckBox();
JButton openDialogButton = new JButton();
JButton openFrameButton = new JButton();
menuBar = new JMenuBar();
@@ -670,12 +696,13 @@ public class FlatWindowDecorationsTest
panel2.setLayout(new MigLayout(
"ltr,insets 0,hidemode 3,gap 0 0",
// columns
"[fill]",
"[left]",
// rows
"[]" +
"[]" +
"[]" +
"[]" +
"[]rel" +
"[]"));
//---- iconNoneRadioButton ----
@@ -703,6 +730,11 @@ public class FlatWindowDecorationsTest
iconTestDynMRIRadioButton.setText("test dynamic multi-resolution (Java 9+)");
iconTestDynMRIRadioButton.addActionListener(e -> iconChanged());
panel2.add(iconTestDynMRIRadioButton, "cell 0 4");
//---- showIconCheckBox ----
showIconCheckBox.setText("show icon");
showIconCheckBox.addActionListener(e -> showIconChanged());
panel2.add(showIconCheckBox, "cell 0 5");
}
add(panel2, "cell 1 8");
@@ -940,6 +972,7 @@ public class FlatWindowDecorationsTest
private JRadioButton iconTestRandomRadioButton;
private JRadioButton iconTestMRIRadioButton;
private JRadioButton iconTestDynMRIRadioButton;
private FlatTriStateCheckBox showIconCheckBox;
private JMenuBar menuBar;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -327,8 +327,8 @@ new FormModel {
"value": "cell 0 8"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$columnConstraints": "[fill]"
"$rowConstraints": "[][][][][]"
"$columnConstraints": "[left]"
"$rowConstraints": "[][][][][]rel[]"
"$layoutConstraints": "ltr,insets 0,hidemode 3,gap 0 0"
} ) {
name: "panel2"
@@ -388,6 +388,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTriStateCheckBox" ) {
name: "showIconCheckBox"
"text": "show icon"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showIconChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8"
} )

View File

@@ -0,0 +1,883 @@
/*
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.HierarchyEvent;
import java.awt.event.MouseEvent;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HSLColor;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
class LineChartPanel
extends JPanel
{
LineChartPanel() {
initComponents();
lineChartScrollPane.putClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING, false );
oneSecondWidthChanged();
updateChartDelayedChanged();
// clear chart on startup
addHierarchyListener( e -> {
if( (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && isShowing() )
EventQueue.invokeLater( this::clearChart );
} );
// show chart tooltips immediately and forever
ToolTipManager.sharedInstance().setInitialDelay( 0 );
ToolTipManager.sharedInstance().setDismissDelay( Integer.MAX_VALUE );
}
@Override
public void addNotify() {
super.addNotify();
// allow clearing chart with Alt+C without moving focus to button
getRootPane().registerKeyboardAction(
e -> clearChart(),
KeyStroke.getKeyStroke( "alt " + (char) clearChartButton.getMnemonic() ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
}
public boolean isYZeroAtTop() {
return lineChart.yZeroAtTop;
}
public void setYZeroAtTop( boolean yZeroAtTop ) {
lineChart.yZeroAtTop = yZeroAtTop;
lineChart.repaint();
}
public boolean isAsynchron() {
return lineChart.asynchron;
}
public void setAsynchron( boolean asynchron ) {
lineChart.asynchron = asynchron;
lineChart.repaint();
}
public boolean isTemporaryValueDetection() {
return lineChart.temporaryValueDetection;
}
public void setTemporaryValueDetection( boolean temporaryValueDetection ) {
lineChart.temporaryValueDetection = temporaryValueDetection;
lineChart.repaint();
}
public String getLegendYValueText() {
return yValueLabel.getText();
}
public void setLegendYValueText( String s ) {
yValueLabel.setText( s );
}
public String getLegend1Text() {
return legend1Label.getText();
}
public void setLegend1Text( String s ) {
legend1Label.setText( s );
}
public String getLegend2Text() {
return legend2Label.getText();
}
public void setLegend2Text( String s ) {
legend2Label.setText( s );
}
public int getOneSecondWidth() {
return oneSecondWidthSlider.getValue();
}
public void setOneSecondWidth( int oneSecondWidth ) {
oneSecondWidthSlider.setValue( oneSecondWidth );
}
public boolean isUpdateChartDelayed() {
return updateChartDelayedCheckBox.isSelected();
}
public void setUpdateChartDelayed( boolean updateChartDelayed ) {
updateChartDelayedCheckBox.setSelected( updateChartDelayed );
updateChartDelayedChanged();
}
void addValue( Color chartColor, double value, int ivalue, String name ) {
lineChart.addValue( chartColor, value, ivalue, null, false, name );
}
void addValueWithDot( Color chartColor, double value, int ivalue, Color dotColor, String name ) {
if( dotColor == null )
dotColor = chartColor;
lineChart.addValue( chartColor, value, ivalue, dotColor, false, name );
}
void addDot( Color chartColor, double value, int ivalue, Color dotColor, String name ) {
if( dotColor == null )
dotColor = chartColor;
lineChart.addValue( chartColor, value, ivalue, dotColor, true, name );
}
void addMethodHighlight( String classAndMethod, String highlightColor ) {
lineChart.methodHighlightMap.put( classAndMethod, highlightColor );
}
private void oneSecondWidthChanged() {
int oneSecondWidth = oneSecondWidthSlider.getValue();
int msPerLineX =
oneSecondWidth <= 2000 ? 100 :
oneSecondWidth <= 4000 ? 50 :
oneSecondWidth <= 8000 ? 25 :
10;
lineChart.oneSecondWidth = oneSecondWidth;
lineChart.msPerLineX = msPerLineX;
lineChart.revalidate();
lineChart.repaint();
if( xLabelText == null )
xLabelText = xLabel.getText();
xLabel.setText( MessageFormat.format( xLabelText, msPerLineX ) );
}
private String xLabelText;
private void updateChartDelayedChanged() {
lineChart.updateDelayed = updateChartDelayedCheckBox.isSelected();
}
private void clearChart() {
lineChart.clear();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
lineChartScrollPane = new JScrollPane();
lineChart = new LineChartPanel.LineChart();
JPanel legendPanel = new JPanel();
xLabel = new JLabel();
legend1Label = new JLabel();
JLabel yLabel = new JLabel();
yValueLabel = new JLabel();
JLabel yLabel2 = new JLabel();
JPanel hSpacer1 = new JPanel(null);
legend2Label = new JLabel();
JLabel oneSecondWidthLabel = new JLabel();
oneSecondWidthSlider = new JSlider();
updateChartDelayedCheckBox = new JCheckBox();
clearChartButton = new JButton();
//======== this ========
setLayout(new MigLayout(
"hidemode 3",
// columns
"[grow,fill]",
// rows
"[100:300,grow,fill]" +
"[]"));
//======== lineChartScrollPane ========
{
lineChartScrollPane.setViewportView(lineChart);
}
add(lineChartScrollPane, "cell 0 0");
//======== legendPanel ========
{
legendPanel.setLayout(new MigLayout(
"insets 0,hidemode 3,gapy 0",
// columns
"[fill]para" +
"[fill]",
// rows
"[]" +
"[]"));
//---- xLabel ----
xLabel.setText("X: time ({0}ms per line)");
legendPanel.add(xLabel, "cell 0 0");
legendPanel.add(legend1Label, "cell 1 0");
//---- yLabel ----
yLabel.setText("Y: ");
legendPanel.add(yLabel, "cell 0 1,gapx 0 0");
//---- yValueLabel ----
yValueLabel.setText("value");
legendPanel.add(yValueLabel, "cell 0 1,gapx 0 0");
//---- yLabel2 ----
yLabel2.setText(" (10% per line)");
legendPanel.add(yLabel2, "cell 0 1,gapx 0 0");
legendPanel.add(hSpacer1, "cell 0 1,growx");
legendPanel.add(legend2Label, "cell 1 1");
}
add(legendPanel, "cell 0 1");
//---- oneSecondWidthLabel ----
oneSecondWidthLabel.setText("Scale X:");
oneSecondWidthLabel.setDisplayedMnemonic('A');
oneSecondWidthLabel.setLabelFor(oneSecondWidthSlider);
add(oneSecondWidthLabel, "cell 0 1,alignx right,growx 0");
//---- oneSecondWidthSlider ----
oneSecondWidthSlider.setMinimum(100);
oneSecondWidthSlider.setMaximum(10000);
oneSecondWidthSlider.setSnapToTicks(true);
oneSecondWidthSlider.setMajorTickSpacing(100);
oneSecondWidthSlider.setValue(500);
oneSecondWidthSlider.addChangeListener(e -> oneSecondWidthChanged());
add(oneSecondWidthSlider, "cell 0 1,alignx right,growx 0");
//---- updateChartDelayedCheckBox ----
updateChartDelayedCheckBox.setText("Update chart delayed");
updateChartDelayedCheckBox.setMnemonic('P');
updateChartDelayedCheckBox.addActionListener(e -> updateChartDelayedChanged());
add(updateChartDelayedCheckBox, "cell 0 1,alignx right,growx 0");
//---- clearChartButton ----
clearChartButton.setText("Clear Chart");
clearChartButton.setMnemonic('C');
clearChartButton.addActionListener(e -> clearChart());
add(clearChartButton, "cell 0 1,alignx right,growx 0");
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
private JScrollPane lineChartScrollPane;
private LineChartPanel.LineChart lineChart;
private JLabel xLabel;
private JLabel legend1Label;
private JLabel yValueLabel;
private JLabel legend2Label;
private JSlider oneSecondWidthSlider;
private JCheckBox updateChartDelayedCheckBox;
private JButton clearChartButton;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
//---- class LineChart ----------------------------------------------------
private static class LineChart
extends JComponent
implements Scrollable
{
private static final int UPDATE_DELAY_MS = 20;
private static final int NEW_SEQUENCE_TIME_LAG_MS = 500;
private static final int NEW_SEQUENCE_GAP_MS = 100;
private static final int HIT_OFFSET = 4;
private static final boolean TEST = false;
// asynchron means that chart for each color starts at x=0
private boolean asynchron;
private boolean temporaryValueDetection;
private boolean yZeroAtTop;
private int oneSecondWidth = 500;
private int msPerLineX = 100;
private final HashMap<String, String> methodHighlightMap = new HashMap<>();
private static class Data {
final double value;
final int ivalue;
final Color chartColor;
final Color dotColor;
final boolean dotOnly;
final long time; // in milliseconds
final String name;
final Exception stack;
Data( double value, int ivalue, Color chartColor, Color dotColor,
boolean dotOnly, long time, String name, Exception stack )
{
this.value = value;
this.ivalue = ivalue;
this.chartColor = chartColor;
this.dotColor = dotColor;
this.dotOnly = dotOnly;
this.time = time;
this.name = name;
this.stack = stack;
}
@Override
public String toString() {
// for debugging
return "value=" + value + ", ivalue=" + ivalue + ", dotColor=" + dotColor
+ ", dotOnly=" + dotOnly + ", time=" + time + ", name=" + name;
}
}
private final List<Data> syncChartData = new ArrayList<>();
private final Map<Color, List<Data>> asyncColor2dataMap = new HashMap<>();
private final Timer repaintTime;
private Color lastUsedChartColor;
private boolean updateDelayed;
private final List<Point> lastPoints = new ArrayList<>();
private final List<Data> lastDatas = new ArrayList<>();
private double lastSystemScaleFactor = 1;
private String lastToolTipPrinted;
LineChart() {
repaintTime = new Timer( UPDATE_DELAY_MS, e -> repaintAndRevalidate() );
repaintTime.setRepeats( false );
ToolTipManager.sharedInstance().registerComponent( this );
if( TEST )
initTestData();
}
void addValue( Color chartColor, double value, int ivalue, Color dotColor, boolean dotOnly, String name ) {
if( TEST )
return;
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
Data data = new Data( value, ivalue, chartColor, dotColor, dotOnly, System.nanoTime() / 1_000_000, name, new Exception() );
if( asynchron )
chartData.add( data );
else
syncChartData.add( data );
lastUsedChartColor = chartColor;
if( updateDelayed ) {
repaintTime.stop();
repaintTime.start();
} else
repaintAndRevalidate();
}
void clear() {
if( TEST ) {
repaint();
return;
}
syncChartData.clear();
asyncColor2dataMap.clear();
lastUsedChartColor = null;
repaint();
revalidate();
}
private void repaintAndRevalidate() {
repaint();
revalidate();
// scroll horizontally
if( lastUsedChartColor != null ) {
// compute chart width of last used color and start of last sequence
int[] lastSeqX = new int[1];
int cw = chartWidth( asynchron ? asyncColor2dataMap.get( lastUsedChartColor ) : syncChartData, lastSeqX );
// scroll to end of last sequence (of last used color)
int lastSeqWidth = cw - lastSeqX[0];
int width = Math.min( lastSeqWidth, getParent().getWidth() );
int x = cw - width;
scrollRectToVisible( new Rectangle( x, 0, width, getHeight() ) );
}
}
@Override
protected void paintComponent( Graphics g ) {
Graphics g2 = g.create();
try {
HiDPIUtils.paintAtScale1x( (Graphics2D) g2, this, this::paintAt1x );
} finally {
g2.dispose();
}
}
private void paintAt1x( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
FlatUIUtils.setRenderingHints( g );
int oneSecondWidth = (int) (UIScale.scale( this.oneSecondWidth ) * scaleFactor);
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * scaleFactor );
Color lineColor = FlatUIUtils.getUIColor( "Component.borderColor", Color.lightGray );
Color lineColor2 = FlatLaf.isLafDark()
? new HSLColor( lineColor ).adjustTone( 30 )
: new HSLColor( lineColor ).adjustShade( 30 );
g.translate( x, y );
// fill background
g.setColor( UIManager.getColor( "Table.background" ) );
g.fillRect( x, y, width, height );
// paint horizontal lines
for( int i = 1; i < 10; i++ ) {
int hy = (height * i) / 10;
g.setColor( (i != 5) ? lineColor : lineColor2 );
g.drawLine( 0, hy, width, hy );
}
// paint vertical lines
int perLineXWidth = Math.round( (oneSecondWidth / 1000f) * msPerLineX );
for( int i = 1, xv = perLineXWidth; xv < width; xv += perLineXWidth, i++ ) {
g.setColor( (i % 5 != 0) ? lineColor : lineColor2 );
g.drawLine( xv, 0, xv, height );
}
lastPoints.clear();
lastDatas.clear();
lastSystemScaleFactor = scaleFactor;
// paint lines
for( Map.Entry<Color, List<Data>> e : asyncColor2dataMap.entrySet() ) {
List<Data> chartData = asynchron ? e.getValue() : syncChartData;
Color chartColor = e.getKey();
if( FlatLaf.isLafDark() )
chartColor = new HSLColor( chartColor ).adjustTone( 50 );
Color temporaryValueColor = fade( chartColor, FlatLaf.isLafDark() ? 0.7f : 0.3f );
Color dataPointColor = fade( chartColor, FlatLaf.isLafDark() ? 0.6f : 0.2f );
// sequence start time and x coordinate
long seqStartTime = 0;
int seqStartX = 0;
// "previous" data point time and x coordinate (used for "new sequence" detection)
long ptime = Long.MIN_VALUE;
int px = 0;
// "line" data point x/y coordinates
int lx = -1;
int ly = -1;
boolean isTemporaryValue = false;
int lastTemporaryValueIndex = -1;
int size = chartData.size();
for( int i = 0; i < size; i++ ) {
Data data = chartData.get( i );
boolean useData = (data.chartColor == chartColor);
// start new sequence if there is a larger time gap to previous data point
boolean newSeq = (data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS);
ptime = data.time;
if( newSeq ) {
// start new sequence
seqStartTime = data.time;
seqStartX = (i > 0) ? px + seqGapWidth : 0;
px = seqStartX;
lx = -1;
ly = -1;
isTemporaryValue = false;
}
// x/y coordinates of current data point
int dx = (int) (seqStartX + (((data.time - seqStartTime) / 1000.) * oneSecondWidth));
int dy = (int) ((height - 1) * data.value);
if( !yZeroAtTop )
dy = height - 1 - dy;
// remember x coordinate for "new sequence" detection
px = dx;
if( !useData )
continue;
// remember data point for tooltip
lastPoints.add( new Point( dx, dy ) );
lastDatas.add( data );
// paint rectangle to indicate data point
g.setColor( dataPointColor );
g.drawRect( dx - hitOffset, dy - hitOffset, hitOffset * 2, hitOffset * 2 );
// paint dot
if( data.dotColor != null ) {
int s1 = (int) Math.round( UIScale.scale( 1 ) * scaleFactor );
int s3 = (int) Math.round( UIScale.scale( 3 ) * scaleFactor );
g.setColor( data.dotColor );
g.fillRect( dx - s1, dy - s1, s3, s3 );
if( data.dotOnly )
continue;
}
// start of line?
if( lx < 0 ) {
// remember x/y coordinates for first line
lx = dx;
ly = dy;
continue;
}
if( isTemporaryValue && i > lastTemporaryValueIndex )
isTemporaryValue = false;
// draw line in sequence
g.setColor( isTemporaryValue ? temporaryValueColor : chartColor );
g.drawLine( lx, ly, dx, dy );
// remember x/y coordinates for next line
lx = dx;
ly = dy;
// check next data points for "temporary" value(s)
if( temporaryValueDetection && !isTemporaryValue ) {
// one or two values between two equal values are considered "temporary",
// which means that they are the target value for the following scroll animation
int stage = 0;
for( int j = i + 1; j < size && stage <= 2 && !isTemporaryValue; j++ ) {
Data nextData = chartData.get( j );
if( nextData.dotOnly )
continue; // ignore dots
// check whether next data point is within 10 milliseconds
if( nextData.time > data.time + 10 )
break;
if( stage >= 1 && stage <= 2 && nextData.value == data.value ) {
isTemporaryValue = true;
lastTemporaryValueIndex = j;
}
stage++;
}
}
}
}
}
private int chartWidth() {
int width = 0;
if( asynchron ) {
for( List<Data> chartData : asyncColor2dataMap.values() )
width = Math.max( width, chartWidth( chartData, null ) );
} else
width = Math.max( width, chartWidth( syncChartData, null ) );
return width;
}
private int chartWidth( List<Data> chartData, int[] lastSeqX ) {
long seqTime = 0;
int seqX = 0;
long ptime = 0;
int px = 0;
int oneSecondWidth = UIScale.scale( this.oneSecondWidth );
int seqGapWidth = (oneSecondWidth * NEW_SEQUENCE_GAP_MS) / 1000;
int size = chartData.size();
for( int i = 0; i < size; i++ ) {
Data data = chartData.get( i );
if( data.time > ptime + NEW_SEQUENCE_TIME_LAG_MS ) {
// start new sequence
seqTime = data.time;
seqX = (i > 0) ? px + seqGapWidth : 0;
px = seqX;
} else {
// line in sequence
int dx = (int) (seqX + (((data.time - seqTime) / 1000.) * oneSecondWidth ));
px = dx;
}
ptime = data.time;
}
if( lastSeqX != null )
lastSeqX[0] = seqX;
return px;
}
@Override
public Dimension getPreferredSize() {
return new Dimension( chartWidth(), 200 );
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension( chartWidth(), 200 );
}
@Override
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
return UIScale.scale( oneSecondWidth );
}
@Override
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
return (viewport != null) ? viewport.getWidth() : 200;
}
@Override
public boolean getScrollableTracksViewportWidth() {
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass( JViewport.class, this );
return (viewport != null) ? viewport.getWidth() > chartWidth() : true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return true;
}
@Override
public String getToolTipText( MouseEvent e ) {
int x = (int) Math.round( e.getX() * lastSystemScaleFactor );
int y = (int) Math.round( e.getY() * lastSystemScaleFactor );
int hitOffset = (int) Math.round( UIScale.scale( HIT_OFFSET ) * lastSystemScaleFactor );
StringBuilder buf = null;
int pointsCount = lastPoints.size();
for( int i = 0; i < pointsCount; i++ ) {
Point pt = lastPoints.get( i );
// check X/Y coordinates
if( x < pt.x - hitOffset || x > pt.x + hitOffset ||
y < pt.y - hitOffset || y > pt.y + hitOffset )
continue;
if( buf == null ) {
buf = new StringBuilder( 5000 );
buf.append( "<html>" );
}
Data data = lastDatas.get( i );
buf.append( "<h2>" );
if( data.dotOnly )
buf.append( "DOT: " );
buf.append( data.name );
if( data.ivalue != Integer.MIN_VALUE )
buf.append( ' ' ).append( data.ivalue );
buf.append( " (" ).append( String.format( "%.3f", data.value ) ).append( ')' );
buf.append( "</h2>" );
StackTraceElement[] stackTrace = data.stack.getStackTrace();
for( int j = 0; j < stackTrace.length; j++ ) {
StackTraceElement stackElement = stackTrace[j];
String className = stackElement.getClassName();
String methodName = stackElement.getMethodName();
String classAndMethod = className + '.' + methodName;
// ignore methods from this class
if( className.startsWith( LineChartPanel.class.getName() ) )
continue;
int repeatCount = 0;
for( int k = j + 1; k < stackTrace.length; k++ ) {
if( !stackElement.equals( stackTrace[k] ) )
break;
repeatCount++;
}
j += repeatCount;
String highlight = methodHighlightMap.get( classAndMethod );
if( highlight == null )
highlight = methodHighlightMap.get( className );
if( highlight == null )
highlight = methodHighlightMap.get( methodName );
if( highlight != null )
buf.append( "<span color=\"" ).append( highlight ).append( "\">" );
// append method
buf.append( className )
.append( ".<b>" )
.append( methodName )
.append( "</b>" );
if( highlight != null )
buf.append( "</span>" );
// append source
buf.append( " <span color=\"#888888\">" );
if( stackElement.getFileName() != null ) {
buf.append( '(' );
buf.append( stackElement.getFileName() );
if( stackElement.getLineNumber() >= 0 )
buf.append( ':' ).append( stackElement.getLineNumber() );
buf.append( ')' );
} else
buf.append( "(Unknown Source)" );
buf.append( "</span>" );
// append repeat count
if( repeatCount > 0 )
buf.append( " <b>" ).append( repeatCount + 1 ).append( "x</b>" );
buf.append( "<br>" );
// break at some methods to make stack smaller
if( classAndMethod.equals( "java.awt.event.InvocationEvent.dispatch" ) ||
classAndMethod.equals( "java.awt.Component.processMouseEvent" ) ||
classAndMethod.equals( "java.awt.Component.processMouseWheelEvent" ) ||
classAndMethod.equals( "java.awt.Component.processMouseMotionEvent" ) ||
classAndMethod.equals( "javax.swing.JComponent.processKeyBinding" ) ||
classAndMethod.equals( "javax.swing.JComponent.paintComponent" ) ||
classAndMethod.equals( "com.formdev.flatlaf.util.Animator.timingEvent" ) )
break;
}
buf.append( "..." );
}
if( buf == null )
return null;
buf.append( "<html>" );
String toolTip = buf.toString();
// print to console
if( !Objects.equals( toolTip, lastToolTipPrinted ) ) {
lastToolTipPrinted = toolTip;
System.out.println( toolTip
.replace( "<br>", "\n" )
.replace( "<h2>", "\n---- " )
.replace( "</h2>", " ----\n" )
.replaceAll( "<[^>]+>", "" ) );
}
return buf.toString();
}
private void initTestData() {
// asynchron = true;
addTestSimpleLine( Color.red, 0.0, "red" );
addTestSimpleLine( Color.green, 0.1, "green" );
addTestSimpleLine( Color.blue, 0.2, "blue" );
addTestSimpleLine( Color.magenta, 0.3, "magenta" );
addTestMiddleDotOnly( Color.red, 0.0, "red" );
addTestMiddleDotOnly( Color.green, 0.1, "green" );
addTestMiddleDotOnly( Color.blue, 0.2, "blue" );
addTestMiddleDotOnly( Color.magenta, 0.3, "magenta" );
addTestLeadingDotOnly( Color.red, 0.0, "red" );
addTestLeadingDotOnly( Color.green, 0.1, "green" );
addTestLeadingDotOnly( Color.blue, 0.2, "blue" );
addTestLeadingDotOnly( Color.magenta, 0.3, "magenta" );
addTestTrailingDotOnly( Color.red, 0.0, "red" );
addTestTrailingDotOnly( Color.green, 0.1, "green" );
addTestTrailingDotOnly( Color.blue, 0.2, "blue" );
addTestTrailingDotOnly( Color.magenta, 0.3, "magenta" );
addTestSingleData( Color.red, 0.0, "red" );
addTestSingleData( Color.green, 0.1, "green" );
addTestSingleData( Color.blue, 0.2, "blue" );
addTestSingleData( Color.magenta, 0.3, "magenta" );
temporaryValueDetection = true;
addTestWithTemporaryValues( Color.red, 0.0, "red" );
addTestWithTemporaryValues( Color.green, 0.1, "green" );
addTestWithTemporaryValues( Color.blue, 0.2, "blue" );
addTestWithTemporaryValues( Color.magenta, 0.3, "magenta" );
}
private void addTestSimpleLine( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 50, chartColor, baseValue + 0.4, null, false, name );
testTime += 1000;
}
private void addTestMiddleDotOnly( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
addTestValue( 20, chartColor, baseValue + 0.3, chartColor, true, name );
addTestValue( 30, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
testTime += 1000;
}
private void addTestLeadingDotOnly( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.05, chartColor, true, name );
addTestValue( 20, chartColor, baseValue + 0.0, null, false, name );
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
testTime += 1000;
}
private void addTestTrailingDotOnly( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 30, chartColor, baseValue + 0.4, null, false, name );
addTestValue( 20, chartColor, baseValue + 0.05, chartColor, true, name );
testTime += 1000;
}
private void addTestSingleData( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.15, chartColor, false, name );
testTime += 1000;
}
private void addTestWithTemporaryValues( Color chartColor, double baseValue, String name ) {
addTestValue( 0, chartColor, baseValue + 0.0, null, false, name );
addTestValue( 50, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 5, chartColor, baseValue + 0.4, null, false, name );
addTestValue( 5, chartColor, baseValue + 0.1, null, false, name );
addTestValue( 40, chartColor, baseValue + 0.3, null, false, name );
testTime += 1000;
}
private void addTestValue( int timeDelta, Color chartColor, double value, Color dotColor, boolean dotOnly, String name ) {
testTime += timeDelta;
List<Data> chartData = asyncColor2dataMap.computeIfAbsent( chartColor, k -> new ArrayList<>() );
Data data = new Data( value, testIValue++, chartColor, dotColor, dotOnly, testTime, name, new Exception() );
if( asynchron )
chartData.add( data );
else
syncChartData.add( data );
lastUsedChartColor = chartColor;
}
private int testIValue;
private long testTime;
//TODO remove and use ColorFunctions.fade() when merging to main
private static Color fade( Color color, float amount ) {
int newAlpha = Math.round( 255 * amount );
return new Color( (color.getRGB() & 0xffffff) | (newAlpha << 24), true );
}
}
}

View File

@@ -0,0 +1,123 @@
JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[100:300,grow,fill][]"
} ) {
name: "this"
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
name: "lineChartScrollPane"
add( new FormComponent( "com.formdev.flatlaf.testing.LineChartPanel$LineChart" ) {
name: "lineChart"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3,gapy 0"
"$columnConstraints": "[fill]para[fill]"
"$rowConstraints": "[][]"
} ) {
name: "legendPanel"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "xLabel"
"text": "X: time ({0}ms per line)"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "legend1Label"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "yLabel"
"text": "Y: "
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "yValueLabel"
"text": "value"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "yLabel2"
"text": " (10% per line)"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx 0 0"
} )
add( new FormComponent( "com.jformdesigner.designer.wrapper.HSpacer" ) {
name: "hSpacer1"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "legend2Label"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "oneSecondWidthLabel"
"text": "Scale X:"
"displayedMnemonic": 65
"labelFor": new FormReference( "oneSecondWidthSlider" )
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,alignx right,growx 0"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "oneSecondWidthSlider"
"minimum": 100
"maximum": 10000
"snapToTicks": true
"majorTickSpacing": 100
"value": 500
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "oneSecondWidthChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,alignx right,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "updateChartDelayedCheckBox"
"text": "Update chart delayed"
"mnemonic": 80
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "updateChartDelayedChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,alignx right,growx 0"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "clearChartButton"
"text": "Clear Chart"
"mnemonic": 67
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "clearChart", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,alignx right,growx 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 880, 300 )
} )
}
}

View File

@@ -25,7 +25,6 @@ import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
@@ -52,6 +51,7 @@ import java.util.prefs.Preferences;
import javax.lang.model.SourceVersion;
import javax.swing.*;
import net.miginfocom.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatDarculaLaf;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
@@ -754,8 +754,7 @@ class FlatThemeFileEditor
private void about() {
JLabel titleLabel = new JLabel( "FlatLaf Theme Editor" );
Font titleFont = titleLabel.getFont();
titleLabel.setFont( titleFont.deriveFont( (float) titleFont.getSize() + UIScale.scale( 6 ) ) );
titleLabel.putClientProperty( FlatClientProperties.STYLE_CLASS, "h1" );
String link = "https://www.formdev.com/flatlaf/";
JLabel linkLabel = new JLabel( "<html><a href=\"#\">" + link + "</a></html>" );

View File

@@ -46,6 +46,7 @@ class FlatThemePreview
private final FlatThemePreviewAll allTab;
private final FlatThemePreviewButtons buttonsTab;
private final FlatThemePreviewSwitches switchesTab;
private final FlatThemePreviewFonts fontsTab;
private final Map<LazyValue, Object> lazyValueCache = new WeakHashMap<>();
private int runWithUIDefaultsGetterLevel;
@@ -61,9 +62,11 @@ class FlatThemePreview
allTab = new FlatThemePreviewAll( this );
buttonsTab = new FlatThemePreviewButtons( this );
switchesTab = new FlatThemePreviewSwitches( this );
fontsTab = new FlatThemePreviewFonts();
tabbedPane.addTab( "All", createPreviewTab( allTab ) );
tabbedPane.addTab( "Buttons", createPreviewTab( buttonsTab ) );
tabbedPane.addTab( "Switches", createPreviewTab( switchesTab ) );
tabbedPane.addTab( "Fonts", createPreviewTab( fontsTab ) );
selectRecentTab();
tabbedPane.addChangeListener( e -> selectedTabChanged() );
@@ -175,7 +178,8 @@ class FlatThemePreview
Object value = textArea.propertiesSupport.getParsedProperty( (String) key );
inGetDefaultFont = "defaultFont".equals( key );
boolean isDefaultFont = "defaultFont".equals( key );
inGetDefaultFont = isDefaultFont;
try {
if( value instanceof LazyValue ) {
value = lazyValueCache.computeIfAbsent( (LazyValue) value, k -> {
@@ -189,6 +193,12 @@ class FlatThemePreview
// System.out.println( key + " = " + value );
// for "defaultFont" never return a value that is not a font
// (e.g. a color for "defaultFont = #fff") to avoid StackOverflowError
// in Active.createValue()
if( isDefaultFont && !(value instanceof Font) )
return null;
// If value is null and is a property that is defined in a core theme,
// then force the value to null.
// This is necessary for cases where the current application Laf defines a property
@@ -196,7 +206,7 @@ class FlatThemePreview
// E.g. FlatLightLaf defines Button.focusedBackground, but in FlatDarkLaf
// it is not defined. Without this code, the preview for FlatDarkLaf would use
// Button.focusedBackground from FlatLightLaf if FlatLightLaf is the current application Laf.
if( value == null && FlatThemePropertiesBaseManager.getDefindedCoreKeys().contains( key ) && !"defaultFont".equals( key ) )
if( value == null && FlatThemePropertiesBaseManager.getDefindedCoreKeys().contains( key ) && !isDefaultFont )
return FlatLaf.NULL_VALUE;
return value;
@@ -215,6 +225,7 @@ class FlatThemePreview
{
tabbedPane.setLeadingComponent(previewLabel);
tabbedPane.setTabAreaAlignment(FlatTabbedPane.TabAreaAlignment.trailing);
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
}
add(tabbedPane, BorderLayout.CENTER);
@@ -223,8 +234,8 @@ class FlatThemePreview
add(previewSeparator, BorderLayout.LINE_START);
//---- previewLabel ----
previewLabel.setText(" Preview ");
previewLabel.setFont(previewLabel.getFont().deriveFont(previewLabel.getFont().getSize() + 6f));
previewLabel.setText(" Preview ");
previewLabel.putClientProperty("FlatLaf.styleClass", "h2");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}

View File

@@ -9,6 +9,7 @@ new FormModel {
name: "tabbedPane"
"leadingComponent": new FormReference( "previewLabel" )
"tabAreaAlignment": enum com.formdev.flatlaf.extras.components.FlatTabbedPane$TabAreaAlignment trailing
"tabLayoutPolicy": 1
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
@@ -24,8 +25,8 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "previewLabel"
"text": " Preview "
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, 6, false )
"text": " Preview "
"$client.FlatLaf.styleClass": "h2"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 235 )
"size": new java.awt.Dimension( 176, 24 )

View File

@@ -560,7 +560,7 @@ class FlatThemePreviewAll
menuUnderlineSelectionButton.setButtonType(FlatButton.ButtonType.toolBarButton);
menuUnderlineSelectionButton.setToolTipText("menu underline selection");
menuUnderlineSelectionButton.setFocusable(false);
menuUnderlineSelectionButton.setFont(menuUnderlineSelectionButton.getFont().deriveFont(menuUnderlineSelectionButton.getFont().getSize() - 2f));
menuUnderlineSelectionButton.putClientProperty("FlatLaf.styleClass", "small");
menuUnderlineSelectionButton.addActionListener(e -> menuUnderlineSelectionChanged());
add(menuUnderlineSelectionButton, "cell 0 11");

View File

@@ -307,7 +307,7 @@ new FormModel {
"buttonType": enum com.formdev.flatlaf.extras.components.FlatButton$ButtonType toolBarButton
"toolTipText": "menu underline selection"
"focusable": false
"font": new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}

View File

@@ -232,25 +232,25 @@ class FlatThemePreviewButtons
//---- noneButton ----
noneButton.setText("none");
noneButton.setSelected(true);
noneButton.setFont(noneButton.getFont().deriveFont(noneButton.getFont().getSize() - 2f));
noneButton.putClientProperty("FlatLaf.styleClass", "small");
noneButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar1.add(noneButton);
//---- squareButton ----
squareButton.setText("square");
squareButton.setFont(squareButton.getFont().deriveFont(squareButton.getFont().getSize() - 2f));
squareButton.putClientProperty("FlatLaf.styleClass", "small");
squareButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar1.add(squareButton);
//---- roundRectButton ----
roundRectButton.setText("roundRect");
roundRectButton.setFont(roundRectButton.getFont().deriveFont(roundRectButton.getFont().getSize() - 2f));
roundRectButton.putClientProperty("FlatLaf.styleClass", "small");
roundRectButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar1.add(roundRectButton);
//---- tabButton ----
tabButton.setText("tab");
tabButton.setFont(tabButton.getFont().deriveFont(tabButton.getFont().getSize() - 2f));
tabButton.putClientProperty("FlatLaf.styleClass", "small");
tabButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar1.add(tabButton);
}
@@ -263,13 +263,13 @@ class FlatThemePreviewButtons
//---- toolBarButtonButton ----
toolBarButtonButton.setText("toolBarButton");
toolBarButtonButton.setFont(toolBarButtonButton.getFont().deriveFont(toolBarButtonButton.getFont().getSize() - 2f));
toolBarButtonButton.putClientProperty("FlatLaf.styleClass", "small");
toolBarButtonButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar2.add(toolBarButtonButton);
//---- borderlessButton ----
borderlessButton.setText("borderless");
borderlessButton.setFont(borderlessButton.getFont().deriveFont(borderlessButton.getFont().getSize() - 2f));
borderlessButton.putClientProperty("FlatLaf.styleClass", "small");
borderlessButton.addActionListener(e -> buttonTypeChanged());
buttonTypeToolBar2.add(borderlessButton);
}
@@ -279,7 +279,7 @@ class FlatThemePreviewButtons
//---- label11 ----
label11.setText("JButton");
label11.setFont(label11.getFont().deriveFont(label11.getFont().getSize() + 4f));
label11.putClientProperty("FlatLaf.styleClass", "h3");
add(label11, "cell 0 1 3 1");
//---- label27 ----
@@ -292,22 +292,22 @@ class FlatThemePreviewButtons
//---- label5 ----
label5.setText("regular");
label5.setFont(label5.getFont().deriveFont(label5.getFont().getSize() - 2f));
label5.putClientProperty("FlatLaf.styleClass", "small");
add(label5, "cell 1 3,alignx center,growx 0");
//---- label7 ----
label7.setText("default");
label7.setFont(label7.getFont().deriveFont(label7.getFont().getSize() - 2f));
label7.putClientProperty("FlatLaf.styleClass", "small");
add(label7, "cell 2 3,alignx center,growx 0");
//---- label6 ----
label6.setText("regular");
label6.setFont(label6.getFont().deriveFont(label6.getFont().getSize() - 2f));
label6.putClientProperty("FlatLaf.styleClass", "small");
add(label6, "cell 3 3,alignx center,growx 0");
//---- label8 ----
label8.setText("default");
label8.setFont(label8.getFont().deriveFont(label8.getFont().getSize() - 2f));
label8.putClientProperty("FlatLaf.styleClass", "small");
add(label8, "cell 4 3,alignx center,growx 0");
//---- label1 ----
@@ -435,7 +435,7 @@ class FlatThemePreviewButtons
//---- label12 ----
label12.setText("JToggleButton");
label12.setFont(label12.getFont().deriveFont(label12.getFont().getSize() + 4f));
label12.putClientProperty("FlatLaf.styleClass", "h3");
add(label12, "cell 0 9 3 1");
//---- label29 ----
@@ -448,22 +448,22 @@ class FlatThemePreviewButtons
//---- label13 ----
label13.setText("unsel.");
label13.setFont(label13.getFont().deriveFont(label13.getFont().getSize() - 2f));
label13.putClientProperty("FlatLaf.styleClass", "small");
add(label13, "cell 1 11,alignx center,growx 0");
//---- label14 ----
label14.setText("selected");
label14.setFont(label14.getFont().deriveFont(label14.getFont().getSize() - 2f));
label14.putClientProperty("FlatLaf.styleClass", "small");
add(label14, "cell 2 11,alignx center,growx 0");
//---- label15 ----
label15.setText("unsel.");
label15.setFont(label15.getFont().deriveFont(label15.getFont().getSize() - 2f));
label15.putClientProperty("FlatLaf.styleClass", "small");
add(label15, "cell 3 11,alignx center,growx 0");
//---- label16 ----
label16.setText("selected");
label16.setFont(label16.getFont().deriveFont(label16.getFont().getSize() - 2f));
label16.putClientProperty("FlatLaf.styleClass", "small");
add(label16, "cell 4 11,alignx center,growx 0");
//---- label17 ----
@@ -576,7 +576,7 @@ class FlatThemePreviewButtons
//---- label32 ----
label32.setText("Help Button");
label32.setFont(label32.getFont().deriveFont(label32.getFont().getSize() + 4f));
label32.putClientProperty("FlatLaf.styleClass", "h3");
add(label32, "cell 0 17 2 1");
//---- label9 ----

View File

@@ -33,7 +33,7 @@ new FormModel {
"text": "none"
"$buttonGroup": new FormReference( "buttonGroup1" )
"selected": true
"font": &SwingDerivedFont0 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -43,7 +43,7 @@ new FormModel {
name: "squareButton"
"text": "square"
"$buttonGroup": new FormReference( "buttonGroup1" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -53,7 +53,7 @@ new FormModel {
name: "roundRectButton"
"text": "roundRect"
"$buttonGroup": new FormReference( "buttonGroup1" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -63,7 +63,7 @@ new FormModel {
name: "tabButton"
"text": "tab"
"$buttonGroup": new FormReference( "buttonGroup1" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -80,7 +80,7 @@ new FormModel {
name: "toolBarButtonButton"
"text": "toolBarButton"
"$buttonGroup": new FormReference( "buttonGroup1" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -90,7 +90,7 @@ new FormModel {
name: "borderlessButton"
"text": "borderless"
"$buttonGroup": new FormReference( "buttonGroup1" )
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -105,7 +105,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"text": "JButton"
"font": &SwingDerivedFont1 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1 3 1"
} )
@@ -124,28 +124,28 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"text": "regular"
"font": &SwingDerivedFont2 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"text": "default"
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "regular"
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"text": "default"
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3,alignx center,growx 0"
} )
@@ -317,7 +317,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label12"
"text": "JToggleButton"
"font": #SwingDerivedFont1
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9 3 1"
} )
@@ -336,28 +336,28 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label13"
"text": "unsel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label14"
"text": "selected"
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label15"
"text": "unsel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label16"
"text": "selected"
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 11,alignx center,growx 0"
} )
@@ -514,7 +514,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label32"
"text": "Help Button"
"font": #SwingDerivedFont1
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 17 2 1"
} )

View File

@@ -0,0 +1,289 @@
/*
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.themeeditor;
import java.awt.*;
import java.text.DecimalFormat;
import javax.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatThemePreviewFonts
extends JPanel
{
private static final DecimalFormat SCALE_FORMAT = new DecimalFormat( "0.##x" );
public FlatThemePreviewFonts() {
initComponents();
scaleValueLabel.setText( SCALE_FORMAT.format( UIScale.getUserScaleFactor() ) );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel headingsLabel = new JLabel();
FlatThemePreviewFonts.FontPreview h00Preview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview h0Preview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview h1Preview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview h2Preview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview h3Preview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview h4Preview = new FlatThemePreviewFonts.FontPreview();
JLabel textLabel = new JLabel();
FlatThemePreviewFonts.FontPreview largePreview = new FlatThemePreviewFonts.FontPreview();
defaultPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview mediumPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview smallPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview miniPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview lightPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview semiboldPreview = new FlatThemePreviewFonts.FontPreview();
FlatThemePreviewFonts.FontPreview monospacedPreview = new FlatThemePreviewFonts.FontPreview();
JLabel scaleLabel = new JLabel();
scaleValueLabel = new JLabel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
// columns
"[left]unrel",
// rows
"[]" +
"[bottom]" +
"[bottom]" +
"[bottom]" +
"[bottom]" +
"[bottom]" +
"[bottom]para" +
"[]" +
"[bottom]" +
"[bottom]" +
"[bottom]" +
"[bottom]" +
"[bottom]para" +
"[]" +
"[]para" +
"[]para" +
"[]"));
//---- headingsLabel ----
headingsLabel.setText("Headings");
headingsLabel.putClientProperty("FlatLaf.styleClass", "h3");
add(headingsLabel, "cell 0 0");
//---- h00Preview ----
h00Preview.setFontType("H00");
h00Preview.setFontStyle("h00");
add(h00Preview, "cell 0 1,gapx 12");
//---- h0Preview ----
h0Preview.setFontType("H0");
h0Preview.setFontStyle("h0");
add(h0Preview, "cell 0 2,gapx 12");
//---- h1Preview ----
h1Preview.setFontType("H1");
h1Preview.setFontStyle("h1");
h1Preview.setFontStyleRegular("h1.regular");
add(h1Preview, "cell 0 3,gapx 12");
//---- h2Preview ----
h2Preview.setFontType("H2");
h2Preview.setFontStyle("h2");
h2Preview.setFontStyleRegular("h2.regular");
add(h2Preview, "cell 0 4,gapx 12");
//---- h3Preview ----
h3Preview.setFontType("H3");
h3Preview.setFontStyle("h3");
h3Preview.setFontStyleRegular("h3.regular");
add(h3Preview, "cell 0 5,gapx 12");
//---- h4Preview ----
h4Preview.setFontType("H4");
h4Preview.setFontStyle("h4");
add(h4Preview, "cell 0 6,gapx 12");
//---- textLabel ----
textLabel.setText("Text");
textLabel.putClientProperty("FlatLaf.styleClass", "h3");
add(textLabel, "cell 0 7");
//---- largePreview ----
largePreview.setFontType("Large");
largePreview.setFontStyle("large");
add(largePreview, "cell 0 8,gapx 12");
//---- defaultPreview ----
defaultPreview.setFontType("Default");
add(defaultPreview, "cell 0 9,gapx 12");
//---- mediumPreview ----
mediumPreview.setFontType("Medium");
mediumPreview.setFontStyle("medium");
add(mediumPreview, "cell 0 10,gapx 12");
//---- smallPreview ----
smallPreview.setFontType("Small");
smallPreview.setFontStyle("small");
add(smallPreview, "cell 0 11,gapx 12");
//---- miniPreview ----
miniPreview.setFontType("Mini");
miniPreview.setFontStyle("mini");
add(miniPreview, "cell 0 12,gapx 12");
//---- lightPreview ----
lightPreview.setFontType("Light");
lightPreview.setFontStyle("light");
add(lightPreview, "cell 0 13,gapx 12");
//---- semiboldPreview ----
semiboldPreview.setFontType("Semibold");
semiboldPreview.setFontStyle("semibold");
add(semiboldPreview, "cell 0 14,gapx 12");
//---- monospacedPreview ----
monospacedPreview.setFontType("Monospaced");
monospacedPreview.setFontStyle("monospaced");
add(monospacedPreview, "cell 0 15,gapx 12");
//---- scaleLabel ----
scaleLabel.setText("Fonts in preview are scaled by:");
add(scaleLabel, "cell 0 16,gapx 12");
//---- scaleValueLabel ----
scaleValueLabel.setText("1x");
scaleValueLabel.putClientProperty("FlatLaf.styleClass", "h2");
add(scaleValueLabel, "cell 0 16");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private FlatThemePreviewFonts.FontPreview defaultPreview;
private JLabel scaleValueLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class FontPreview --------------------------------------------------
static class FontPreview
extends JPanel
{
private String fontType;
private String fontStyle;
private String fontStyleRegular;
private FontPreview() {
initComponents();
updateDescription( previewLabel.getFont() );
previewLabel.addPropertyChangeListener( "font", e -> {
updateDescription( previewLabel.getFont() );
} );
preview2Label.setVisible( false );
}
public String getFontType() {
return fontType;
}
public void setFontType( String fontType ) {
this.fontType = fontType;
previewLabel.setText( fontType );
preview2Label.setText( " / " + fontType );
}
public String getFontStyle() {
return fontStyle;
}
public void setFontStyle( String fontStyle ) {
this.fontStyle = fontStyle;
previewLabel.putClientProperty( FlatClientProperties.STYLE_CLASS, fontStyle );
}
public String getFontStyleRegular() {
return fontStyleRegular;
}
public void setFontStyleRegular( String fontStyleRegular ) {
this.fontStyleRegular = fontStyleRegular;
preview2Label.putClientProperty( FlatClientProperties.STYLE_CLASS, fontStyleRegular );
preview2Label.setVisible( fontStyleRegular != null );
}
private void updateDescription( Font font ) {
int baseSize = getDefaultFont().getSize();
int fontSize = font.getSize();
descLabel.setText( String.format( "%s %d %s%s (%+d %s)",
font.getFamily(),
fontSize,
(font.getStyle() & Font.BOLD) != 0 ? " bold" : "",
(font.getStyle() & Font.ITALIC) != 0 ? " italic" : "",
fontSize - baseSize,
SCALE_FORMAT.format( (float) fontSize / baseSize ) ) );
}
private Font getDefaultFont() {
Font font = UIManager.getFont( "defaultFont" );
if( font == null )
font = UIManager.getFont( "Label.font" );
return font;
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
previewLabel = new JLabel();
preview2Label = new JLabel();
descLabel = new JLabel();
//======== this ========
setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[90,left]" +
"[fill]",
// rows
"[]0"));
//---- previewLabel ----
previewLabel.setText("preview");
add(previewLabel, "cell 0 0");
//---- preview2Label ----
preview2Label.setText("preview");
add(preview2Label, "cell 0 0,gapx 0");
//---- descLabel ----
descLabel.setText("description");
descLabel.putClientProperty("FlatLaf.styleClass", "medium");
add(descLabel, "cell 1 0");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JLabel previewLabel;
private JLabel preview2Label;
private JLabel descLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
}

View File

@@ -0,0 +1,195 @@
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[left]unrel"
"$rowConstraints": "[][bottom][bottom][bottom][bottom][bottom][bottom]para[][bottom][bottom][bottom][bottom][bottom]para[][]para[]para[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "headingsLabel"
"text": "Headings"
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h00Preview"
"fontType": "H00"
"fontStyle": "h00"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h0Preview"
"fontType": "H0"
"fontStyle": "h0"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h1Preview"
"fontType": "H1"
"fontStyle": "h1"
"fontStyleRegular": "h1.regular"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h2Preview"
"fontType": "H2"
"fontStyle": "h2"
"fontStyleRegular": "h2.regular"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h3Preview"
"fontType": "H3"
"fontStyle": "h3"
"fontStyleRegular": "h3.regular"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "h4Preview"
"fontType": "H4"
"fontStyle": "h4"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6,gapx 12"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "textLabel"
"text": "Text"
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "largePreview"
"fontType": "Large"
"fontStyle": "large"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "defaultPreview"
"fontType": "Default"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "mediumPreview"
"fontType": "Medium"
"fontStyle": "medium"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 10,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "smallPreview"
"fontType": "Small"
"fontStyle": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "miniPreview"
"fontType": "Mini"
"fontStyle": "mini"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "lightPreview"
"fontType": "Light"
"fontStyle": "light"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "semiboldPreview"
"fontType": "Semibold"
"fontStyle": "semibold"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 14,gapx 12"
} )
add( new FormComponent( "com.formdev.flatlaf.themeeditor.FlatThemePreviewFonts$FontPreview" ) {
name: "monospacedPreview"
"fontType": "Monospaced"
"fontStyle": "monospaced"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 15,gapx 12"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleLabel"
"text": "Fonts in preview are scaled by:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 16,gapx 12"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleValueLabel"
"text": "1x"
"$client.FlatLaf.styleClass": "h2"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 16"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 0 )
"size": new java.awt.Dimension( 335, 495 )
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[90,left][fill]"
"$rowConstraints": "[]0"
} ) {
name: "panel1"
auxiliary() {
"JavaCodeGenerator.className": "FontPreview"
"JavaCodeGenerator.variableName": "fontPreview"
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "previewLabel"
"text": "preview"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "preview2Label"
"text": "preview"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0,gapx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "descLabel"
"text": "description"
"$client.FlatLaf.styleClass": "medium"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 545 )
"size": new java.awt.Dimension( 235, 65 )
} )
}
}

View File

@@ -262,25 +262,25 @@ class FlatThemePreviewSwitches
//---- zoom1xButton ----
zoom1xButton.setText("1x");
zoom1xButton.setSelected(true);
zoom1xButton.setFont(zoom1xButton.getFont().deriveFont(zoom1xButton.getFont().getSize() - 2f));
zoom1xButton.putClientProperty("FlatLaf.styleClass", "small");
zoom1xButton.addActionListener(e -> zoomChanged());
zoomToolBar.add(zoom1xButton);
//---- zoom2xButton ----
zoom2xButton.setText("2x");
zoom2xButton.setFont(zoom2xButton.getFont().deriveFont(zoom2xButton.getFont().getSize() - 2f));
zoom2xButton.putClientProperty("FlatLaf.styleClass", "small");
zoom2xButton.addActionListener(e -> zoomChanged());
zoomToolBar.add(zoom2xButton);
//---- zoom3xButton ----
zoom3xButton.setText("3x");
zoom3xButton.setFont(zoom3xButton.getFont().deriveFont(zoom3xButton.getFont().getSize() - 2f));
zoom3xButton.putClientProperty("FlatLaf.styleClass", "small");
zoom3xButton.addActionListener(e -> zoomChanged());
zoomToolBar.add(zoom3xButton);
//---- zoom4xButton ----
zoom4xButton.setText("4x");
zoom4xButton.setFont(zoom4xButton.getFont().deriveFont(zoom4xButton.getFont().getSize() - 2f));
zoom4xButton.putClientProperty("FlatLaf.styleClass", "small");
zoom4xButton.addActionListener(e -> zoomChanged());
zoomToolBar.add(zoom4xButton);
zoomToolBar.addSeparator();
@@ -288,6 +288,7 @@ class FlatThemePreviewSwitches
//---- indeterminateButton ----
indeterminateButton.setText("indeterminate");
indeterminateButton.setSelected(true);
indeterminateButton.putClientProperty("FlatLaf.styleClass", "small");
indeterminateButton.addActionListener(e -> indeterminateChanged());
zoomToolBar.add(indeterminateButton);
}
@@ -297,7 +298,7 @@ class FlatThemePreviewSwitches
//---- label22 ----
label22.setText("JCheckBox");
label22.setFont(label22.getFont().deriveFont(label22.getFont().getSize() + 4f));
label22.putClientProperty("FlatLaf.styleClass", "h3");
add(label22, "cell 0 1 3 1");
//---- label1 ----
@@ -310,32 +311,32 @@ class FlatThemePreviewSwitches
//---- label23 ----
label23.setText("unsel.");
label23.setFont(label23.getFont().deriveFont(label23.getFont().getSize() - 2f));
label23.putClientProperty("FlatLaf.styleClass", "small");
add(label23, "cell 1 3,alignx center,growx 0");
//---- label28 ----
label28.setText("sel.");
label28.setFont(label28.getFont().deriveFont(label28.getFont().getSize() - 2f));
label28.putClientProperty("FlatLaf.styleClass", "small");
add(label28, "cell 2 3,alignx center,growx 0");
//---- label37 ----
label37.setText("ind.");
label37.setFont(label37.getFont().deriveFont(label37.getFont().getSize() - 2f));
label37.putClientProperty("FlatLaf.styleClass", "small");
add(label37, "cell 3 3,alignx center,growx 0");
//---- label24 ----
label24.setText("unsel.");
label24.setFont(label24.getFont().deriveFont(label24.getFont().getSize() - 2f));
label24.putClientProperty("FlatLaf.styleClass", "small");
add(label24, "cell 4 3,alignx center,growx 0");
//---- label29 ----
label29.setText("sel.");
label29.setFont(label29.getFont().deriveFont(label29.getFont().getSize() - 2f));
label29.putClientProperty("FlatLaf.styleClass", "small");
add(label29, "cell 5 3,alignx center,growx 0");
//---- label38 ----
label38.setText("ind.");
label38.setFont(label38.getFont().deriveFont(label38.getFont().getSize() - 2f));
label38.putClientProperty("FlatLaf.styleClass", "small");
add(label38, "cell 6 3,alignx center,growx 0");
//---- label17 ----
@@ -465,7 +466,7 @@ class FlatThemePreviewSwitches
//---- label27 ----
label27.setText("JRadioButton");
label27.setFont(label27.getFont().deriveFont(label27.getFont().getSize() + 4f));
label27.putClientProperty("FlatLaf.styleClass", "h3");
add(label27, "cell 0 9 3 1");
//---- label3 ----
@@ -478,22 +479,22 @@ class FlatThemePreviewSwitches
//---- label25 ----
label25.setText("unsel.");
label25.setFont(label25.getFont().deriveFont(label25.getFont().getSize() - 2f));
label25.putClientProperty("FlatLaf.styleClass", "small");
add(label25, "cell 1 11,alignx center,growx 0");
//---- label30 ----
label30.setText("sel.");
label30.setFont(label30.getFont().deriveFont(label30.getFont().getSize() - 2f));
label30.putClientProperty("FlatLaf.styleClass", "small");
add(label30, "cell 2 11,alignx center,growx 0");
//---- label26 ----
label26.setText("unsel.");
label26.setFont(label26.getFont().deriveFont(label26.getFont().getSize() - 2f));
label26.putClientProperty("FlatLaf.styleClass", "small");
add(label26, "cell 4 11,alignx center,growx 0");
//---- label31 ----
label31.setText("sel.");
label31.setFont(label31.getFont().deriveFont(label31.getFont().getSize() - 2f));
label31.putClientProperty("FlatLaf.styleClass", "small");
add(label31, "cell 5 11,alignx center,growx 0");
//---- label36 ----

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.4.0.360" Java: "17" encoding: "UTF-8"
JFDML JFormDesigner: "7.0.5.0.382" Java: "16" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -32,7 +32,7 @@ new FormModel {
name: "zoom1xButton"
"text": "1x"
"selected": true
"font": &SwingDerivedFont0 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
"$buttonGroup": new FormReference( "buttonGroup1" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
@@ -42,7 +42,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "zoom2xButton"
"text": "2x"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
"$buttonGroup": &FormReference0 new FormReference( "buttonGroup1" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
@@ -52,7 +52,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "zoom3xButton"
"text": "3x"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
"$buttonGroup": #FormReference0
auxiliary() {
"JavaCodeGenerator.variableLocal": false
@@ -62,7 +62,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "zoom4xButton"
"text": "4x"
"font": #SwingDerivedFont0
"$client.FlatLaf.styleClass": "small"
"$buttonGroup": #FormReference0
auxiliary() {
"JavaCodeGenerator.variableLocal": false
@@ -76,6 +76,7 @@ new FormModel {
name: "indeterminateButton"
"text": "indeterminate"
"selected": true
"$client.FlatLaf.styleClass": "small"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
@@ -90,7 +91,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label22"
"text": "JCheckBox"
"font": &SwingDerivedFont1 new com.jformdesigner.model.SwingDerivedFont( null, 0, 4, false )
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1 3 1"
} )
@@ -109,42 +110,42 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label23"
"text": "unsel."
"font": &SwingDerivedFont2 new com.jformdesigner.model.SwingDerivedFont( null, 0, -2, false )
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label28"
"text": "sel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label37"
"text": "ind."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label24"
"text": "unsel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label29"
"text": "sel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 3,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label38"
"text": "ind."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 6 3,alignx center,growx 0"
} )
@@ -340,7 +341,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label27"
"text": "JRadioButton"
"font": #SwingDerivedFont1
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9 3 1"
} )
@@ -359,28 +360,28 @@ new FormModel {
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label25"
"text": "unsel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label30"
"text": "sel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label26"
"text": "unsel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 11,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label31"
"text": "sel."
"font": #SwingDerivedFont2
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 11,alignx center,growx 0"
} )

View File

@@ -531,6 +531,7 @@ OptionPane.minimumSize
OptionPane.questionIcon
OptionPane.sameSizeButtons
OptionPane.setButtonMargin
OptionPane.showIcon
OptionPane.warningIcon
OptionPane.windowBindings
OptionPaneUI
@@ -631,10 +632,13 @@ RangeSliderUI
Resizable.resizeBorder
RootPane.activeBorderColor
RootPane.ancestorInputMap
RootPane.background
RootPane.border
RootPane.borderDragThickness
RootPane.cornerDragWidth
RootPane.defaultButtonWindowKeyBindings
RootPane.font
RootPane.foreground
RootPane.honorDialogMinimumSizeOnResize
RootPane.honorFrameMinimumSizeOnResize
RootPane.inactiveBorderColor
@@ -783,6 +787,7 @@ TabbedPane.buttonArc
TabbedPane.buttonHoverBackground
TabbedPane.buttonInsets
TabbedPane.buttonPressedBackground
TabbedPane.cardTabSelectionHeight
TabbedPane.closeArc
TabbedPane.closeCrossFilledSize
TabbedPane.closeCrossLineWidth
@@ -829,6 +834,7 @@ TabbedPane.tabRunOverlay
TabbedPane.tabSelectionHeight
TabbedPane.tabSeparatorColor
TabbedPane.tabSeparatorsFullHeight
TabbedPane.tabType
TabbedPane.tabWidthMode
TabbedPane.tabsOpaque
TabbedPane.tabsOverlapBorder
@@ -969,7 +975,9 @@ TitlePane.inactiveForeground
TitlePane.maximizeIcon
TitlePane.menuBarEmbedded
TitlePane.menuBarTitleGap
TitlePane.noIconLeftGap
TitlePane.restoreIcon
TitlePane.showIcon
TitlePane.titleMargins
TitlePane.unifiedBackground
TitlePane.useWindowDecorations
@@ -1091,6 +1099,22 @@ Viewport.background
Viewport.font
Viewport.foreground
ViewportUI
[style].h0
[style].h00
[style].h1
[style].h1.regular
[style].h2
[style].h2.regular
[style].h3
[style].h3.regular
[style].h4
[style].large
[style].light
[style].medium
[style].mini
[style].monospaced
[style].semibold
[style].small
activeCaption
activeCaptionBorder
activeCaptionText
@@ -1102,6 +1126,15 @@ controlShadow
controlText
defaultFont
desktop
h0.font
h00.font
h1.font
h1.regular.font
h2.font
h2.regular.font
h3.font
h3.regular.font
h4.font
html.missingImage
html.pendingImage
inactiveCaption
@@ -1111,9 +1144,16 @@ info
infoText
laf.dark
laf.scaleFactor
large.font
light.font
medium.font
menu
menuText
mini.font
monospaced.font
scrollbar
semibold.font
small.font
swingx/TaskPaneUI
text
textHighlight

View File

@@ -27,6 +27,7 @@ include( "flatlaf-theme-editor" )
includeProject( "flatlaf-natives-windows", "flatlaf-natives/flatlaf-natives-windows" )
includeProject( "flatlaf-natives-jna", "flatlaf-natives/flatlaf-natives-jna" )
includeProject( "flatlaf-testing-modular-app", "flatlaf-testing/flatlaf-testing-modular-app" )
fun includeProject( projectPath: String, projectDir: String ) {
include( projectPath )