Compare commits

...

43 Commits
3.4.1 ... 3.5.1

Author SHA1 Message Date
Karl Tauber
438ec6ac5c release 3.5.1 2024-08-05 18:17:34 +02:00
Karl Tauber
8089e66642 SubMenuUsabilityHelper: added system property flatlaf.useSubMenuSafeTriangle to allow disabling submenu safe triangle for SWTSwing (issue #870)
also check whether EventQueue.push() succeeded; if not, disable submenu safe triangle
2024-08-05 13:56:43 +02:00
Karl Tauber
d27e0561f2 HiDPI: fixed occasional wrong repaint areas when using HiDPIUtils.installHiDPIRepaintManager() (see PR #864) 2024-08-04 15:14:46 +02:00
Karl Tauber
97b21bfa8b HTML: fixed occasional cutoff wrapped text when using multi-line text in HTML tags <h1>...<h6>, <code>, <kbd>, <big>, <small> or <samp> (issue #873; regression in 3.5) 2024-08-04 14:45:17 +02:00
Karl Tauber
ec4343ed30 TabbedPane: fixed ArrayIndexOutOfBoundsException in case of using "card" tab type and using a custom tab selection model that returns -1 for selected tab (issue #875) 2024-08-03 00:00:00 +02:00
Karl Tauber
948decb3b5 Popup: fixed UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency is not supported exception on Haiku OS when showing popup (partly) outside of window (issue #869) 2024-08-02 23:47:29 +02:00
Karl Tauber
d510fee7f6 CHANGELOG.md: moved note regarding disabled rounded popup border on macOS from 3.4.1 to 3.5 (wrong in commit 32b0f1ba10) 2024-07-17 00:23:41 +02:00
Karl Tauber
70b7a3d662 release 3.5 2024-07-16 22:17:04 +02:00
Karl Tauber
b142a6f31e FlatInspector: added FlatLaf style to tooltip 2024-07-16 13:58:53 +02:00
Karl Tauber
14705a9b30 Menu: show arrow icon for top-level JMenu if used in vertical JMenuBar (issue #867) 2024-07-16 13:48:47 +02:00
Karl Tauber
32b0f1ba10 macOS: (temporary) disabled rounded popup border (see PR #772) on macOS 14.4+ because it may freeze the application and crash the macOS WindowServer process 2024-07-16 01:25:10 +02:00
Karl Tauber
cbffdf4900 HiDPIUtils: fixed javadoc error 2024-07-15 19:01:41 +02:00
Karl Tauber
1238da5e54 Merge PR #864: HiDPI: fix incomplete component repainting at 125% or 175% scaling on Windows 2024-07-15 18:43:46 +02:00
Karl Tauber
cba203be09 Merge PR #856: Table: rounded selection 2024-07-15 18:39:12 +02:00
Karl Tauber
d89c6156b9 HiDPI: introduced (optional) repaint manager that fixes incomplete component paintings at 125% or 175% scaling on Windows (issues #860 and #582) 2024-07-10 23:02:19 +02:00
Karl Tauber
e06475b3b7 HiDPIUtils: javadoc fixes for previous commit 2024-07-05 23:29:38 +02:00
Karl Tauber
5ff99bd45e HiDPI: fixed incomplete component paintings at 125% or 175% scaling on Windows (issues #860 and #582) 2024-07-05 22:18:27 +02:00
Karl Tauber
127dd6ac41 Table: fixed repainting of rounded selection when selection changes 2024-06-27 23:48:14 +02:00
Karl Tauber
9e05384513 Table: fixed location of painted grid lines when scaled with fractional scale factors (e.g. 125%-175%) 2024-06-24 19:22:03 +02:00
Karl Tauber
9ffda72ae3 Table: support rounded selection (issue #844) 2024-06-24 19:20:55 +02:00
Karl Tauber
72a4c00e72 Window decorations: fixed black line sometimes painted on top of (native) window border on Windows 11 (issue #852)
Windows binaries built and signed locally in clean workspace
2024-06-21 19:58:36 +02:00
Karl Tauber
c95e95ef67 FileChooser: wrap shortcuts in scroll pane (issue #828)
added tooltips to shortcuts
disabled group hover effect on shortcuts
2024-06-21 19:10:39 +02:00
Karl Tauber
0c0d4bffbf ScrollPane: fixed/improved border painting at 125% - 175% scaling to avoid different border thicknesses (issue #743) 2024-06-14 19:19:22 +02:00
Karl Tauber
2a494b1d60 Testing: easier testing of various system scale factors using Alt+Shift+F1...F12 2024-06-13 12:20:32 +02:00
Karl Tauber
1463723e52 Table: Fixed painting of alternating rows below table if auto-resize mode is JTable.AUTO_RESIZE_OFF and table is smaller than scroll pane. Below alternating rows were not updated when table width changed and were painted on wrong side in right-to-left component orientation 2024-06-08 11:10:46 +02:00
Karl Tauber
9ade48d078 FlatHTML: javadoc fixes 2024-06-04 13:10:52 +02:00
Karl Tauber
7ba8274fd4 FlatLineBorder: use arc from Label or Panel, if not specified in border (issue #842) 2024-06-01 14:53:44 +02:00
Karl Tauber
238443074c Theme Editor: updated rsyntaxtextarea to latest version 2024-05-31 14:06:57 +02:00
Karl Tauber
0decbec595 Theme Editor: on macOS, use larger window title bar (PR #779) 2024-05-31 13:57:01 +02:00
Karl Tauber
0eb77c7f72 Theme Editor: fixed occasional empty window on startup on macOS 2024-05-31 13:54:50 +02:00
Karl Tauber
f05df0db0a Button and ToggleButton: added more missing border colors for selected states (issue #848) 2024-05-31 10:41:33 +02:00
Karl Tauber
13fbaf1f74 fixed errors reported by Error Prone in commit 261d2b1fe8 2024-05-30 19:33:18 +02:00
Karl Tauber
969d2642de Button and ToggleButton: added missing border colors for pressed and selected states (issue #848) 2024-05-30 19:22:25 +02:00
Karl Tauber
17ce6d39b4 Button and ToggleButton: UI properties [Toggle]Button.selectedForeground and [Toggle]Button.pressedForeground did not work for HTML text (issue #848) 2024-05-30 17:45:28 +02:00
Karl Tauber
261d2b1fe8 HTML: Fixed font sizes for HTML tags <h1>...<h6>, <code>, <kbd>, <big>, <small> and <samp> in HTML text for components Button, CheckBox, RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and JXHyperlink. Also fixed for Label and ToolTip if using Java 11+. 2024-05-30 16:39:23 +02:00
Karl Tauber
a54aeb3838 FlatTestFrame: automatically add scroll pane if content is very large (or screen is small) 2024-05-29 11:14:52 +02:00
Karl Tauber
cc4f9a9db5 Window decorations: window top border on Windows 10 in "full window content" mode was not fully repainted when activating or deactivating window (issue #809) 2024-05-28 16:16:03 +02:00
Karl Tauber
a311bac89b Popup: fixed flicker of popups (e.g. tooltips) while they are moving (e.g. following mouse pointer) (issues #832 and #672) 2024-05-22 14:02:40 +02:00
Karl Tauber
029f273dd9 Label: support painting background with rounded corners (issue #842)
Demo: added rounded panels and labels to "More Components" tab
2024-05-21 13:37:11 +02:00
Karl Tauber
bbbdd7e4d3 Panel: rounded background of panel with rounded corners is now painted even if panel is not opaque (issue #840) 2024-05-20 18:57:19 +02:00
Karl Tauber
3f3ef6b24f ProgressBar: log warning (including stack trace) when uninstalling indeterminate progress bar UI or using JProgressBar.setIndeterminate(false) not on AWT thread, because this may throw NPE in FlatProgressBarUI.paint() (issues #841 and #830) 2024-05-09 11:35:49 +02:00
Karl Tauber
8c3dfd4a36 Merge PR #834: Update flatlaf-intellij-themes/README.md 2024-04-23 13:04:13 +02:00
Hirun Chamara
af57599df9 Update README.md
install(); is deprecated
2024-04-23 11:22:13 +05:30
93 changed files with 3729 additions and 649 deletions

View File

@@ -1,6 +1,77 @@
FlatLaf Change Log
==================
## 3.5.1
#### Fixed bugs
- HTML: Fixed occasional cutoff wrapped text when using multi-line text in HTML
tags `<h1>`...`<h6>`, `<code>`, `<kbd>`, `<big>`, `<small>` or `<samp>`.
(issue #873; regression in 3.5)
- Popup: Fixed `UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency
is not supported` exception on Haiku OS when showing popup (partly) outside of
window. (issue #869)
- HiDPI: Fixed occasional wrong repaint areas when using
`HiDPIUtils.installHiDPIRepaintManager()`. (see PR #864)
- Added system property `flatlaf.useSubMenuSafeTriangle` to allow disabling
submenu safe triangle (PR #490) for
[SWTSwing](https://github.com/Chrriis/SWTSwing). (issue #870)
## 3.5
#### New features and improvements
- Table: Support rounded selection. (PR #856)
- Button and ToggleButton: Added border colors for pressed and selected states.
(issue #848)
- Label: Support painting background with rounded corners. (issue #842)
- Popup: Fixed flicker of popups (e.g. tooltips) while they are moving (e.g.
following mouse pointer). (issues #832 and #672)
- FileChooser: Wrap shortcuts in scroll pane. (issue #828)
- Theme Editor: On macOS, use larger window title bar. (PR #779)
#### Fixed bugs
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
may freeze the application and crash the macOS WindowServer process (reports
vary from Finder restarts to OS restarts). This is a temporary change until a
solution is found. See NetBeans issues
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
and
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
- FlatLaf window decorations: Window top border on Windows 10 in "full window
content" mode was not fully repainted when activating or deactivating window.
(issue #809)
- Button and ToggleButton: UI properties `[Toggle]Button.selectedForeground` and
`[Toggle]Button.pressedForeground` did not work for HTML text. (issue #848)
- HTML: Fixed font sizes for HTML tags `<h1>`...`<h6>`, `<code>`, `<kbd>`,
`<big>`, `<small>` and `<samp>` in HTML text for components Button, CheckBox,
RadioButton, MenuItem (and subclasses), JideLabel, JideButton, JXBusyLabel and
JXHyperlink. Also fixed for Label and ToolTip if using Java 11+.
- ScrollPane: Fixed/improved border painting at 125% - 175% scaling to avoid
different border thicknesses. (issue #743)
- Table: Fixed painting of alternating rows below table if auto-resize mode is
`JTable.AUTO_RESIZE_OFF` and table width is smaller than scroll pane (was not
updated when table width changed and was painted on wrong side in
right-to-left component orientation).
- Theme Editor: Fixed occasional empty window on startup on macOS.
- FlatLaf window decorations: Fixed black line sometimes painted on top of
(native) window border on Windows 11. (issue #852)
- HiDPI: Fixed incomplete component paintings at 125% or 175% scaling on Windows
where sometimes a 1px wide area at the right or bottom component edge is not
repainted. E.g. ScrollPane focus indicator border. (issues #860 and #582)
#### Incompatibilities
- ProgressBar: Log warning (including stack trace) when uninstalling
indeterminate progress bar UI or using `JProgressBar.setIndeterminate(false)`
not on AWT thread, because this may throw NPE in `FlatProgressBarUI.paint()`.
(issues #841 and #830)
- Panel: Rounded background of panel with rounded corners is now painted even if
panel is not opaque. (issue #840)
## 3.4.1
#### Fixed bugs
@@ -10,7 +81,7 @@ FlatLaf Change Log
- TabbedPane: Fixed swapped back and forward scroll buttons when using
`TabbedPane.scrollButtonsPlacement = trailing` (regression in FlatLaf 3.3).
- Fixed missing window top border on Windows 10 in "full window content" mode.
(issue 809)
(issue #809)
- Extras:
- `FlatSVGIcon` color filters now support linear gradients. (PR #817)
- `FlatSVGIcon`: Use log level `CONFIG` instead of `SEVERE` and allow
@@ -54,8 +125,8 @@ FlatLaf Change Log
- Improved log messages for loading fails.
- Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
- Table: Select all text in cell editor when starting editing using `F2` key.
(issue 652)
- Table: Select all text in cell editor when starting editing using `F2` key on
Windows or Linux. (issue #652)
#### Fixed bugs
@@ -83,7 +154,7 @@ FlatLaf Change Log
#### Fixed bugs
- Button and ToggleButton: Selected buttons did not use explicitly set
foreground color. (issue 756)
foreground color. (issue #756)
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
default Windows exe icon. (see
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
@@ -836,7 +907,7 @@ FlatLaf Change Log
- Native window decorations (Windows 10 only):
- Fixed occasional application crash in `flatlaf-windows.dll`. (issue #357)
- When window is initially shown, fill background with window background color
(instead of white), which avoids flickering in dark themes. (issue 339)
(instead of white), which avoids flickering in dark themes. (issue #339)
- When resizing a window at the right/bottom edge, then first fill the new
space with the window background color (instead of black) before the layout
is updated.

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 3.4
#Version 3.5.1
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -296,6 +296,7 @@ fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flat
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
anno 0 java.lang.Deprecated()
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle"
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"
@@ -629,16 +630,33 @@ hfds alpha,hsl,rgb
CLSS public com.formdev.flatlaf.util.HiDPIUtils
cons public init()
innr public abstract interface static DirtyRegionCallback
innr public abstract interface static Painter
innr public static HiDPIRepaintManager
meth public static float computeTextYCorrection(java.awt.Graphics2D)
meth public static java.awt.Graphics2D createGraphicsTextYCorrection(java.awt.Graphics2D)
meth public static void addDirtyRegion(javax.swing.JComponent,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback)
meth public static void drawStringUnderlineCharAtWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int,int)
meth public static void drawStringWithYCorrection(javax.swing.JComponent,java.awt.Graphics2D,java.lang.String,int,int)
meth public static void installHiDPIRepaintManager()
meth public static void paintAtScale1x(java.awt.Graphics2D,int,int,int,int,com.formdev.flatlaf.util.HiDPIUtils$Painter)
meth public static void paintAtScale1x(java.awt.Graphics2D,javax.swing.JComponent,com.formdev.flatlaf.util.HiDPIUtils$Painter)
meth public static void repaint(java.awt.Component)
meth public static void repaint(java.awt.Component,int,int,int,int)
meth public static void repaint(java.awt.Component,java.awt.Rectangle)
supr java.lang.Object
hfds CORRECTION_INTER,CORRECTION_OPEN_SANS,CORRECTION_SEGOE_UI,CORRECTION_TAHOMA,SCALE_FACTORS,useDebugScaleFactor,useTextYCorrection
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$DirtyRegionCallback
outer com.formdev.flatlaf.util.HiDPIUtils
meth public abstract void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
CLSS public static com.formdev.flatlaf.util.HiDPIUtils$HiDPIRepaintManager
outer com.formdev.flatlaf.util.HiDPIUtils
cons public init()
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
supr javax.swing.RepaintManager
CLSS public abstract interface static com.formdev.flatlaf.util.HiDPIUtils$Painter
outer com.formdev.flatlaf.util.HiDPIUtils
meth public abstract void paint(java.awt.Graphics2D,int,int,int,int,double)
@@ -1137,6 +1155,31 @@ meth public void provideErrorFeedback(java.awt.Component)
meth public void uninitialize()
supr java.lang.Object
CLSS public javax.swing.RepaintManager
cons public init()
meth public boolean isCompletelyDirty(javax.swing.JComponent)
meth public boolean isDoubleBufferingEnabled()
meth public java.awt.Dimension getDoubleBufferMaximumSize()
meth public java.awt.Image getOffscreenBuffer(java.awt.Component,int,int)
meth public java.awt.Image getVolatileOffscreenBuffer(java.awt.Component,int,int)
meth public java.awt.Rectangle getDirtyRegion(javax.swing.JComponent)
meth public java.lang.String toString()
meth public static javax.swing.RepaintManager currentManager(java.awt.Component)
meth public static javax.swing.RepaintManager currentManager(javax.swing.JComponent)
meth public static void setCurrentManager(javax.swing.RepaintManager)
meth public void addDirtyRegion(java.applet.Applet,int,int,int,int)
meth public void addDirtyRegion(java.awt.Window,int,int,int,int)
meth public void addDirtyRegion(javax.swing.JComponent,int,int,int,int)
meth public void addInvalidComponent(javax.swing.JComponent)
meth public void markCompletelyClean(javax.swing.JComponent)
meth public void markCompletelyDirty(javax.swing.JComponent)
meth public void paintDirtyRegions()
meth public void removeInvalidComponent(javax.swing.JComponent)
meth public void setDoubleBufferMaximumSize(java.awt.Dimension)
meth public void setDoubleBufferingEnabled(boolean)
meth public void validateInvalidComponents()
supr java.lang.Object
CLSS public abstract javax.swing.border.AbstractBorder
cons public init()
intf java.io.Serializable

View File

@@ -204,6 +204,15 @@ public interface FlatSystemProperties
*/
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
/**
* Specifies whether safe triangle is used to improve usability of submenus.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
* @since 3.5.1
*/
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";
/**
* Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}.

View File

@@ -45,6 +45,7 @@ import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
* Improves usability of submenus by using a
@@ -64,6 +65,7 @@ class SubMenuUsabilityHelper
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
private static SubMenuUsabilityHelper instance;
private boolean eventQueuePushNotSupported;
private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter;
private boolean changePending;
@@ -83,6 +85,9 @@ class SubMenuUsabilityHelper
if( instance != null )
return false;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SUB_MENU_SAFE_TRIANGLE, true ) )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
@@ -99,7 +104,7 @@ class SubMenuUsabilityHelper
@Override
public void stateChanged( ChangeEvent e ) {
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
if( eventQueuePushNotSupported || !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
return;
// handle menu selection change later, but only once in case of temporary changes
@@ -173,8 +178,29 @@ debug*/
targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to suppress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue();
if( subMenuEventQueue == null ) {
SubMenuEventQueue queue = new SubMenuEventQueue();
try {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemEventQueue().push( queue );
// check whether push() worked
// (e.g. SWTSwing uses own event queue that does not support push())
if( toolkit.getSystemEventQueue() != queue ) {
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", null );
return;
}
subMenuEventQueue = queue;
} catch( RuntimeException ex ) {
// catch runtime exception from EventQueue.push()
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", ex );
return;
}
}
// create safe triangle painter
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
@@ -247,8 +273,6 @@ debug*/
}
} );
timeoutTimer.setRepeats( false );
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
}
void uninstall() {

View File

@@ -638,14 +638,18 @@ class UIDefaultsLoader
// Syntax: top,left,bottom,right[,lineColor[,lineThickness[,arc]]]
List<String> parts = splitFunctionParams( value, ',' );
Insets insets = parseInsets( value );
ColorUIResource lineColor = (parts.size() >= 5)
ColorUIResource lineColor = (parts.size() >= 5 && !parts.get( 4 ).isEmpty())
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver )
: null;
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty()) ? parseFloat( parts.get( 5 ) ) : 1f;
int arc = (parts.size() >= 7) ? parseInteger( parts.get( 6 ) ) : 0;
float lineThickness = (parts.size() >= 6 && !parts.get( 5 ).isEmpty())
? parseFloat( parts.get( 5 ) )
: 1f;
int arc = (parts.size() >= 7) && !parts.get( 6 ).isEmpty()
? parseInteger( parts.get( 6 ) )
: -1;
return (LazyValue) t -> {
return (lineColor != null)
return (lineColor != null || arc > 0)
? new FlatLineBorder( insets, lineColor, lineThickness, arc )
: new FlatEmptyBorder( insets );
};

View File

@@ -135,7 +135,7 @@ public class FlatBorder
Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
focusWidth, 1, focusInnerWidth, borderWidth, arc,
focusColor, borderColor, null );
focusColor, borderColor, null, c instanceof JScrollPane );
} finally {
g2.dispose();
}
@@ -277,7 +277,7 @@ public class FlatBorder
}
/**
* Returns the (unscaled) arc diameter of the border.
* Returns the (unscaled) arc diameter of the border corners.
*/
protected int getArc( Component c ) {
return 0;

View File

@@ -42,6 +42,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.disabledBorderColor Color
* @uiDefault Button.focusedBorderColor Color
* @uiDefault Button.hoverBorderColor Color optional
* @uiDefault Button.pressedBorderColor Color optional
*
* @uiDefault Button.selectedBorderColor Color optional
* @uiDefault Button.disabledSelectedBorderColor Color optional
* @uiDefault Button.focusedSelectedBorderColor Color optional
* @uiDefault Button.hoverSelectedBorderColor Color optional
* @uiDefault Button.pressedSelectedBorderColor Color optional
*
* @uiDefault Button.default.borderWidth int or float
* @uiDefault Button.default.borderColor Color
@@ -49,6 +56,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Button.default.endBorderColor Color optional; if set, a gradient paint is used
* @uiDefault Button.default.focusedBorderColor Color
* @uiDefault Button.default.focusColor Color
* @uiDefault Button.default.pressedBorderColor Color optional
* @uiDefault Button.default.hoverBorderColor Color optional
*
* @uiDefault Button.toolbar.focusWidth int or float optional; default is 1.5
@@ -65,6 +73,13 @@ public class FlatButtonBorder
protected Color endBorderColor = UIManager.getColor( "Button.endBorderColor" );
@Styleable protected Color hoverBorderColor = UIManager.getColor( "Button.hoverBorderColor" );
/** @since 3.5 */ @Styleable protected Color pressedBorderColor = UIManager.getColor( "Button.pressedBorderColor" );
/** @since 3.5 */ @Styleable protected Color selectedBorderColor = UIManager.getColor( "Button.selectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color disabledSelectedBorderColor = UIManager.getColor( "Button.disabledSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color focusedSelectedBorderColor = UIManager.getColor( "Button.focusedSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color hoverSelectedBorderColor = UIManager.getColor( "Button.hoverSelectedBorderColor" );
/** @since 3.5 */ @Styleable protected Color pressedSelectedBorderColor = UIManager.getColor( "Button.pressedSelectedBorderColor" );
@Styleable(dot=true) protected float defaultBorderWidth = FlatUIUtils.getUIFloat( "Button.default.borderWidth", 1 );
@Styleable(dot=true) protected Color defaultBorderColor = FlatUIUtils.getUIColor( "Button.default.startBorderColor", "Button.default.borderColor" );
@@ -72,6 +87,7 @@ public class FlatButtonBorder
@Styleable(dot=true) protected Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
@Styleable(dot=true) protected Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
@Styleable(dot=true) protected Color defaultHoverBorderColor = UIManager.getColor( "Button.default.hoverBorderColor" );
/** @since 3.5 */ @Styleable(dot=true) protected Color defaultPressedBorderColor = UIManager.getColor( "Button.default.pressedBorderColor" );
/** @since 1.4 */ @Styleable(dot=true) protected float toolbarFocusWidth = FlatUIUtils.getUIFloat( "Button.toolbar.focusWidth", 1.5f );
/** @since 1.4 */ @Styleable(dot=true) protected Color toolbarFocusColor = UIManager.getColor( "Button.toolbar.focusColor" );
@@ -139,12 +155,13 @@ public class FlatButtonBorder
@Override
protected Paint getBorderColor( Component c ) {
boolean def = FlatButtonUI.isDefaultButton( c );
boolean selected = (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
Paint color = FlatButtonUI.buttonStateColor( c,
def ? defaultBorderColor : borderColor,
disabledBorderColor,
def ? defaultFocusedBorderColor : focusedBorderColor,
def ? defaultHoverBorderColor : hoverBorderColor,
null );
def ? defaultBorderColor : ((selected && selectedBorderColor != null) ? selectedBorderColor : borderColor),
(selected && disabledSelectedBorderColor != null) ? disabledSelectedBorderColor : disabledBorderColor,
def ? defaultFocusedBorderColor : ((selected && focusedSelectedBorderColor != null) ? focusedSelectedBorderColor : focusedBorderColor),
def ? defaultHoverBorderColor : ((selected && hoverSelectedBorderColor != null) ? hoverSelectedBorderColor : hoverBorderColor),
def ? defaultPressedBorderColor : ((selected && pressedSelectedBorderColor != null) ? pressedSelectedBorderColor : pressedBorderColor) );
// change to gradient paint if start/end colors are specified
Color startBg = def ? defaultBorderColor : borderColor;

View File

@@ -29,6 +29,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.util.Map;
@@ -61,6 +62,7 @@ import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -300,6 +302,10 @@ public class FlatButtonUI
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case BasicHTML.propertyKey:
FlatHTML.updateRendererCSSFontBaseSize( b );
break;
case SQUARE_SIZE:
case MINIMUM_WIDTH:
case MINIMUM_HEIGHT:
@@ -308,11 +314,11 @@ public class FlatButtonUI
case BUTTON_TYPE:
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
case OUTLINE:
b.repaint();
HiDPIUtils.repaint( b );
break;
case STYLE:
@@ -324,7 +330,7 @@ public class FlatButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}
@@ -583,9 +589,16 @@ public class FlatButtonUI
// paint text
if( clippedText != null && !clippedText.isEmpty() ) {
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null )
if( view != null ) {
// update foreground color in HTML view, which is necessary
// for selected and pressed states
// (only for enabled buttons, because UIManager.getColor("textInactiveText")
// is used for disabled components; see: javax.swing.text.GlyphView.paint())
if( b.isEnabled() )
FlatHTML.updateRendererCSSForeground( view, getForeground( b ) );
view.paint( g, textR ); // HTML text
else
} else
paintText( g, b, textR, clippedText );
}
@@ -631,8 +644,6 @@ public class FlatButtonUI
}
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
if(foreground == null)
foreground=Color.red;
FontMetrics fm = b.getFontMetrics( b.getFont() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -906,7 +917,7 @@ public class FlatButtonUI
@Override
public void stateChanged( ChangeEvent e ) {
super.stateChanged( e );
HiDPIUtils.repaint( b );
// if button is in toolbar, repaint button groups
AbstractButton b = (AbstractButton) e.getSource();
@@ -918,5 +929,17 @@ public class FlatButtonUI
((FlatToolBarUI)ui).repaintButtonGroup( b );
}
}
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( b );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( b );
}
}
}

View File

@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
@@ -102,13 +103,23 @@ public class FlatCheckBoxMenuItemUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */

View File

@@ -78,6 +78,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
@@ -220,7 +221,7 @@ public class FlatComboBoxUI
private void repaintArrowButton() {
if( arrowButton != null && !comboBox.isEditable() )
arrowButton.repaint();
HiDPIUtils.repaint( arrowButton );
}
};
comboBox.addMouseListener( hoverListener );
@@ -351,15 +352,15 @@ public class FlatComboBoxUI
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
};
}
@@ -386,12 +387,12 @@ public class FlatComboBoxUI
switch( propertyName ) {
case PLACEHOLDER_TEXT:
if( editor != null )
editor.repaint();
HiDPIUtils.repaint( editor );
break;
case COMPONENT_ROUND_RECT:
case OUTLINE:
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
case MINIMUM_WIDTH:
@@ -402,7 +403,7 @@ public class FlatComboBoxUI
case STYLE_CLASS:
installStyle();
comboBox.revalidate();
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
}
}

View File

@@ -171,7 +171,7 @@ public class FlatEditorPaneUI
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
}

View File

@@ -24,6 +24,7 @@ import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -32,6 +33,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.function.Function;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
@@ -46,6 +48,7 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
@@ -164,6 +167,7 @@ public class FlatFileChooserUI
{
private final FlatFileView fileView = new FlatFileView();
private FlatShortcutsPanel shortcutsPanel;
private JScrollPane shortcutsScrollPane;
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
@@ -183,7 +187,10 @@ public class FlatFileChooserUI
FlatShortcutsPanel panel = createShortcutsPanel( fc );
if( panel.getComponentCount() > 0 ) {
shortcutsPanel = panel;
fc.add( shortcutsPanel, BorderLayout.LINE_START );
shortcutsScrollPane = new JScrollPane( shortcutsPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
shortcutsScrollPane.setBorder( BorderFactory.createEmptyBorder() );
fc.add( shortcutsScrollPane, BorderLayout.LINE_START );
fc.addPropertyChangeListener( shortcutsPanel );
}
}
@@ -196,6 +203,7 @@ public class FlatFileChooserUI
if( shortcutsPanel != null ) {
fc.removePropertyChangeListener( shortcutsPanel );
shortcutsPanel = null;
shortcutsScrollPane = null;
}
}
@@ -324,7 +332,7 @@ public class FlatFileChooserUI
public Dimension getPreferredSize( JComponent c ) {
Dimension prefSize = super.getPreferredSize( c );
Dimension minSize = getMinimumSize( c );
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
int shortcutsPanelWidth = (shortcutsScrollPane != null) ? shortcutsScrollPane.getPreferredSize().width : 0;
return new Dimension(
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
Math.max( prefSize.height, minSize.height ) );
@@ -401,7 +409,7 @@ public class FlatFileChooserUI
/** @since 2.3 */
public static class FlatShortcutsPanel
extends JToolBar
implements PropertyChangeListener
implements PropertyChangeListener, Scrollable
{
private final JFileChooser fc;
@@ -420,6 +428,7 @@ public class FlatFileChooserUI
super( JToolBar.VERTICAL );
this.fc = fc;
setFloatable( false );
putClientProperty( FlatClientProperties.STYLE, "hoverButtonGroupBackground: null" );
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
@@ -461,7 +470,7 @@ public class FlatFileChooserUI
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
// create button
JToggleButton button = createButton( name, icon );
JToggleButton button = createButton( name, icon, file.toString() );
File f = file;
button.addActionListener( e -> {
fc.setCurrentDirectory( f );
@@ -487,8 +496,10 @@ public class FlatFileChooserUI
return size;
}
protected JToggleButton createButton( String name, Icon icon ) {
/** @since 3.5 */
protected JToggleButton createButton( String name, Icon icon, String toolTip ) {
JToggleButton button = new JToggleButton( name, icon );
button.setToolTipText( toolTip );
button.setVerticalTextPosition( SwingConstants.BOTTOM );
button.setHorizontalTextPosition( SwingConstants.CENTER );
button.setAlignmentX( Component.CENTER_ALIGNMENT );
@@ -566,6 +577,8 @@ public class FlatFileChooserUI
buttonGroup.clearSelection();
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
@@ -574,6 +587,41 @@ public class FlatFileChooserUI
break;
}
}
//---- interface Scrollable ----
@Override
public Dimension getPreferredScrollableViewportSize() {
if( getComponentCount() > 0 ) {
Insets insets = getInsets();
int height = (getComponent( 0 ).getPreferredSize().height * 5) + insets.top + insets.bottom;
return new Dimension( getPreferredSize().width, height );
}
return getPreferredSize();
}
@Override
public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) {
if( orientation == SwingConstants.VERTICAL && getComponentCount() > 0 )
return getComponent( 0 ).getPreferredSize().height;
return getScrollableBlockIncrement( visibleRect, orientation, direction ) / 10;
}
@Override
public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) {
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
//---- class ShortcutIcon -------------------------------------------------

View File

@@ -0,0 +1,255 @@
/*
* Copyright 2024 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JToolTip;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.LabelView;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
/**
* @author Karl Tauber
* @since 3.5
*/
public class FlatHTML
{
private FlatHTML() {}
/**
* Adds CSS rule BASE_SIZE to the style sheet of the HTML view,
* which re-calculates font sizes based on current component font size.
* This is necessary for "absolute-size" keywords (e.g. "x-large")
* for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css).
* See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values">CSS font-size</a>.
* <p>
* This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}.
*/
public static void updateRendererCSSFontBaseSize( JComponent c ) {
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
if( view == null )
return;
// dumpViews( view, 0 );
Document doc = view.getDocument();
if( !(doc instanceof HTMLDocument) )
return;
// add BASE_SIZE rule if necessary
// - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule
// - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
/*debug
for( int i = 1; i <= 7; i++ )
System.out.println( i+": "+ styleSheet.getPointSize( i ) );
debug*/
int fontBaseSize = c.getFont().getSize();
if( styleSheet.getPointSize( 7 ) != 36f ||
styleSheet.getPointSize( 4 ) == fontBaseSize )
return;
// check whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
if( !usesAbsoluteSizeKeywordForFontSize( view ) )
return;
// get HTML text from component
String text;
if( c instanceof JLabel )
text = ((JLabel)c).getText();
else if( c instanceof AbstractButton )
text = ((AbstractButton)c).getText();
else if( c instanceof JToolTip )
text = ((JToolTip)c).getTipText();
else
return;
if( text == null )
return;
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
String openTag = "";
String closeTag = "";
String lowerText = text.toLowerCase( Locale.ENGLISH );
int headIndex;
int styleIndex;
int insertIndex;
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
// there is a <head> tag --> insert after <head> tag
insertIndex = headIndex + "<head>".length();
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
// there is a <style> tag --> insert before <style> tag
insertIndex = styleIndex;
} else {
// no <head> or <style> tag --> insert <head> tag after <html> tag
insertIndex = "<html>".length();
openTag = "<head>";
closeTag = "</head>";
}
String newText = text.substring( 0, insertIndex )
+ openTag + style + closeTag
+ text.substring( insertIndex );
BasicHTML.updateRenderer( c, newText );
}
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" ) );
/**
* Checks whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
* (see javax/swing/text/html/default.css).
*/
private static boolean usesAbsoluteSizeKeywordForFontSize( View view ) {
AttributeSet attributes = view.getAttributes();
if( attributes != null ) {
Object fontSize = attributes.getAttribute( CSS.Attribute.FONT_SIZE );
if( fontSize != null ) {
if( absoluteSizeKeywordsSet.contains( fontSize.toString() ) )
return true;
}
}
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ ) {
if( usesAbsoluteSizeKeywordForFontSize( view.getView( i ) ) )
return true;
}
return false;
}
/**
* Updates foreground in style sheet of the HTML view.
* Adds "body { color: #&lt;foreground-hex&gt;; }"
*/
public static void updateRendererCSSForeground( View view, Color foreground ) {
Document doc = view.getDocument();
if( !(doc instanceof HTMLDocument) || foreground == null )
return;
// add foreground rule if necessary
// - use tag 'body' because BasicHTML.createHTMLView() also uses this tag
// to set font and color styles to component font/color
// see: SwingUtilities2.displayPropertiesToCSS()
// - this color is not used if component is disabled;
// JTextComponent.getDisabledTextColor() is used for disabled text components;
// UIManager.getColor("textInactiveText") is used for other disabled components
// see: javax.swing.text.GlyphView.paint()
Style bodyStyle = ((HTMLDocument)doc).getStyle( "body" );
if( bodyStyle == null ) {
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
styleSheet.addRule( String.format( "body { color: #%06x; }", foreground.getRGB() & 0xffffff ) );
clearViewCaches( view );
} else if( !foreground.equals( bodyStyle.getAttribute( StyleConstants.Foreground ) ) ) {
bodyStyle.addAttribute( StyleConstants.Foreground, foreground );
clearViewCaches( view );
}
}
/**
* Clears cached values in view so that CSS changes take effect.
*/
private static void clearViewCaches( View view ) {
if( view instanceof LabelView )
((LabelView)view).changedUpdate( null, null, null );
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ )
clearViewCaches( view.getView( i ) );
}
public static PropertyChangeListener createPropertyChangeListener( PropertyChangeListener superListener ) {
return e -> {
if( superListener != null )
superListener.propertyChange( e );
propertyChange( e );
};
}
/**
* Invokes {@link #updateRendererCSSFontBaseSize(JComponent)}
* for {@link BasicHTML#propertyKey} property change events,
* which are fired when {@link BasicHTML#updateRenderer(JComponent, String)}
* updates the HTML view.
*/
public static void propertyChange( PropertyChangeEvent e ) {
if( BasicHTML.propertyKey.equals( e.getPropertyName() ) && e.getNewValue() instanceof View )
updateRendererCSSFontBaseSize( (JComponent) e.getSource() );
}
/*debug
public static void dumpView( JComponent c ) {
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
if( view != null )
dumpViews( view, 0 );
}
public static void dumpViews( View view, int indent ) {
for( int i = 0; i < indent; i++ )
System.out.print( " " );
System.out.printf( "%s @%-8x %3d,%2d",
view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName(),
System.identityHashCode( view ),
(int) view.getPreferredSpan( View.X_AXIS ),
(int) view.getPreferredSpan( View.Y_AXIS ) );
AttributeSet attrs = view.getAttributes();
if( attrs != null ) {
Object fontSize = attrs.getAttribute( CSS.Attribute.FONT_SIZE );
System.out.printf( " %-8s", fontSize );
}
if( view instanceof javax.swing.text.GlyphView ) {
javax.swing.text.GlyphView gview = ((javax.swing.text.GlyphView)view);
java.awt.Font font = gview.getFont();
System.out.printf( " %3d-%-3d %s %2d (@%x) #%06x '%s'",
gview.getStartOffset(), gview.getEndOffset() - 1,
font.getName(), font.getSize(), System.identityHashCode( font ),
gview.getForeground().getRGB() & 0xffffff,
gview.getText( gview.getStartOffset(), gview.getEndOffset() ) );
}
System.out.println();
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ ) {
View child = view.getView( i );
dumpViews( child, indent + 1 );
}
}
debug*/
}

View File

@@ -22,11 +22,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
@@ -64,6 +60,9 @@ public class FlatLabelUI
{
@Styleable protected Color disabledForeground;
// only used via styling (not in UI defaults)
/** @since 3.5 */ @Styleable protected int arc = -1;
private final boolean shared;
private boolean defaults_initialized = false;
private Map<String, Object> oldStyleValues;
@@ -110,16 +109,13 @@ public class FlatLabelUI
super.installComponents( c );
// update HTML renderer if necessary
updateHTMLRenderer( c, c.getText(), false );
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
String name = e.getPropertyName();
if( name == "text" || name == "font" || name == "foreground" ) {
JLabel label = (JLabel) e.getSource();
updateHTMLRenderer( label, label.getText(), true );
} else if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
if( name.equals( FlatClientProperties.STYLE ) || name.equals( FlatClientProperties.STYLE_CLASS ) ) {
JLabel label = (JLabel) e.getSource();
if( shared && FlatStylingSupport.hasStyleProperty( label ) ) {
// unshare component UI if necessary
@@ -128,9 +124,11 @@ public class FlatLabelUI
} else
installStyle( label );
label.revalidate();
label.repaint();
} else
super.propertyChange( e );
HiDPIUtils.repaint( label );
}
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
/** @since 2 */
@@ -165,83 +163,10 @@ public class FlatLabelUI
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}
/**
* Checks whether text contains HTML tags that use "absolute-size" keywords
* (e.g. "x-large") for font-size in default style sheet
* (see javax/swing/text/html/default.css).
* If yes, adds a special CSS rule (BASE_SIZE) to the HTML text, which
* re-calculates font sizes based on current component font size.
*/
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
if( BasicHTML.isHTMLString( text ) &&
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
needsFontBaseSize( text ) )
{
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
String lowerText = text.toLowerCase( Locale.ENGLISH );
int headIndex;
int styleIndex;
int insertIndex;
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
// there is a <head> tag --> insert after <head> tag
insertIndex = headIndex + "<head>".length();
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
// there is a <style> tag --> insert before <style> tag
insertIndex = styleIndex;
} else {
// no <head> or <style> tag --> insert <head> tag after <html> tag
style = "<head>" + style + "</head>";
insertIndex = "<html>".length();
}
text = text.substring( 0, insertIndex )
+ style
+ text.substring( insertIndex );
} else if( !always )
return; // not necessary to invoke BasicHTML.updateRenderer()
BasicHTML.updateRenderer( c, text );
}
private static Set<String> tagsUseFontSizeSet;
private static boolean needsFontBaseSize( String text ) {
if( tagsUseFontSizeSet == null ) {
// tags that use font-size in javax/swing/text/html/default.css
tagsUseFontSizeSet = new HashSet<>( Arrays.asList(
"h1", "h2", "h3", "h4", "h5", "h6", "code", "kbd", "big", "small", "samp" ) );
}
// search for tags in HTML text
int textLength = text.length();
for( int i = 6; i < textLength - 1; i++ ) {
if( text.charAt( i ) == '<' ) {
switch( text.charAt( i + 1 ) ) {
// first letters of tags in tagsUseFontSizeSet
case 'b': case 'B':
case 'c': case 'C':
case 'h': case 'H':
case 'k': case 'K':
case 's': case 'S':
int tagBegin = i + 1;
for( i += 2; i < textLength; i++ ) {
if( !Character.isLetterOrDigit( text.charAt( i ) ) ) {
String tag = text.substring( tagBegin, i ).toLowerCase( Locale.ENGLISH );
if( tagsUseFontSizeSet.contains( tag ) )
return true;
break;
}
}
break;
}
}
}
return false;
@Override
public void update( Graphics g, JComponent c ) {
FlatPanelUI.fillRoundedBackground( g, c, arc );
paint( g, c );
}
static Graphics createGraphicsHTMLTextYCorrection( Graphics g, JComponent c ) {

View File

@@ -23,13 +23,20 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.plaf.ComponentUI;
/**
* Line border for various components.
*
* <p>
* Paints a scaled (usually 1px thick) line around the component.
* The line thickness is not added to the border insets.
* The insets should be at least have line thickness (usually 1,1,1,1).
* <p>
* For {@link javax.swing.JPanel} and {@link javax.swing.JLabel}, this border
* can be used paint rounded background (if line color is {@code null}) or
* paint rounded line border with rounded background.
*
* @author Karl Tauber
*/
@@ -41,7 +48,7 @@ public class FlatLineBorder
/** @since 2 */ private final int arc;
public FlatLineBorder( Insets insets, Color lineColor ) {
this( insets, lineColor, 1f, 0 );
this( insets, lineColor, 1f, -1 );
}
/** @since 2 */
@@ -52,15 +59,28 @@ public class FlatLineBorder
this.arc = arc;
}
/** @since 3.5 */
public FlatLineBorder( Insets insets, int arc ) {
this( insets, null, 0, arc );
}
public Color getLineColor() {
return lineColor;
}
/**
* Returns the (unscaled) line thickness used to paint the border.
* The line thickness does not affect the border insets.
*/
public float getLineThickness() {
return lineThickness;
}
/** @since 2 */
/**
* Returns the (unscaled) arc diameter of the border corners.
*
* @since 2
*/
public int getArc() {
return arc;
}
@@ -70,11 +90,31 @@ public class FlatLineBorder
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
return;
Color lineColor = getLineColor();
float lineThickness = getLineThickness();
if( lineColor == null || lineThickness <= 0 )
return;
int arc = getArc();
if( arc < 0 ) {
// get arc from label or panel
ComponentUI ui = (c instanceof JLabel)
? ((JLabel)c).getUI()
: (c instanceof JPanel ? ((JPanel)c).getUI() : null);
if( ui instanceof FlatLabelUI )
arc = ((FlatLabelUI)ui).arc;
else if( ui instanceof FlatPanelUI )
arc = ((FlatPanelUI)ui).arc;
if( arc < 0 )
arc = 0;
}
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
0, 0, 0, scale( getLineThickness() ), scale( getArc() ), null, getLineColor(), null );
0, 0, 0, scale( lineThickness ), scale( arc ), null, lineColor, null );
} finally {
g2.dispose();
}

View File

@@ -43,6 +43,7 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -182,7 +183,7 @@ public class FlatListUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
list.revalidate();
list.repaint();
HiDPIUtils.repaint( list );
break;
}
};
@@ -205,7 +206,7 @@ public class FlatListUI
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
if( r != null ) {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
}
}
};
@@ -324,8 +325,7 @@ public class FlatListUI
(rendererComponent instanceof DefaultListCellRenderer ||
rendererComponent instanceof BasicComboBoxRenderer) &&
(selectionArc > 0 ||
(selectionInsets != null &&
(selectionInsets.top != 0 || selectionInsets.left != 0 || selectionInsets.bottom != 0 || selectionInsets.right != 0))) )
(selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets ))) )
{
// Because selection painting is done in the cell renderer, it would be
// necessary to require a FlatLaf specific renderer to implement rounded selection.
@@ -374,7 +374,15 @@ public class FlatListUI
rendererPane.paintComponent( g, rendererComponent, list, cx, rowBounds.y, cw, rowBounds.height, true );
}
/** @since 3 */
/**
* Paints (rounded) cell selection.
* Supports {@link #selectionArc} and {@link #selectionInsets}.
* <p>
* <b>Note:</b> This method is only invoked if either selection arc
* is greater than zero or if selection insets are not empty.
*
* @since 3
*/
protected void paintCellSelection( Graphics g, int row, int x, int y, int width, int height ) {
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
@@ -440,7 +448,8 @@ public class FlatListUI
* Paints a cell selection at the given coordinates.
* The selection color must be set on the graphics context.
* <p>
* This method is intended for use in custom cell renderers.
* This method is intended for use in custom cell renderers
* to support {@link #selectionArc} and {@link #selectionInsets}.
*
* @since 3
*/

View File

@@ -25,7 +25,9 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
@@ -35,6 +37,7 @@ import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
@@ -222,7 +225,7 @@ public class FlatMenuItemRenderer
}
// arrow size
if( !isTopLevelMenu && arrowIcon != null ) {
if( arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )) ) {
// gap between text and arrow
if( accelText == null )
width += scale( textNoAcceleratorGap );
@@ -254,7 +257,8 @@ public class FlatMenuItemRenderer
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
// layout arrow
if( !isTopLevelMenu && arrowIcon != null ) {
boolean showArrowIcon = (arrowIcon != null && (!isTopLevelMenu || isInVerticalMenuBar( menuItem )));
if( showArrowIcon ) {
arrowRect.width = arrowIcon.getIconWidth();
arrowRect.height = arrowIcon.getIconHeight();
} else
@@ -288,7 +292,7 @@ public class FlatMenuItemRenderer
int accelArrowWidth = accelRect.width + arrowRect.width;
if( accelText != null )
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
if( !isTopLevelMenu && arrowIcon != null ) {
if( showArrowIcon ) {
if( accelText == null )
accelArrowWidth += scale( textNoAcceleratorGap );
accelArrowWidth += scale( acceleratorArrowGap );
@@ -355,7 +359,7 @@ debug*/
paintIcon( g, iconRect, getIconForPainting(), underlineSelection ? underlineSelectionCheckBackground : checkBackground, selectionBackground );
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
if( !isTopLevelMenu( menuItem ) )
if( arrowIcon != null && (!isTopLevelMenu( menuItem ) || isInVerticalMenuBar( menuItem )) )
paintArrowIcon( g, arrowRect, arrowIcon );
}
@@ -520,6 +524,15 @@ debug*/
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
}
/** @since 3.5 */
public static boolean isInVerticalMenuBar( JMenuItem menuItem ) {
if( !(menuItem instanceof JMenu) || !(menuItem.getParent() instanceof JMenuBar) )
return false;
LayoutManager layout = menuItem.getParent().getLayout();
return layout instanceof GridLayout && ((GridLayout)layout).getRows() != 1;
}
protected boolean isUnderlineSelection() {
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
}

View File

@@ -103,13 +103,23 @@ public class FlatMenuItemUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicMenuUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -136,6 +137,14 @@ public class FlatMenuUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@@ -159,7 +168,7 @@ public class FlatMenuUI
JMenu menu = (JMenu) e.getSource();
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
menu.getModel().setRollover( rollover );
menu.repaint();
HiDPIUtils.repaint( menu );
}
}
};
@@ -167,7 +176,9 @@ public class FlatMenuUI
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */

View File

@@ -25,11 +25,13 @@ import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -110,7 +112,7 @@ public class FlatPanelUI
} else
installStyle( c );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
@@ -160,32 +162,38 @@ public class FlatPanelUI
@Override
public void update( Graphics g, JComponent c ) {
// fill background
if( c.isOpaque() ) {
int width = c.getWidth();
int height = c.getHeight();
int arc = (this.arc >= 0)
? this.arc
: ((c.getBorder() instanceof FlatLineBorder)
? ((FlatLineBorder)c.getBorder()).getArc()
: 0);
fillRoundedBackground( g, c, arc );
paint( g, c );
}
// fill background with parent color to avoid garbage in rounded corners
if( arc > 0 )
FlatUIUtils.paintParentBackground( g, c );
g.setColor( c.getBackground() );
if( arc > 0 ) {
// fill rounded rectangle if having rounded corners
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, width, height,
0, UIScale.scale( arc ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} else
g.fillRect( 0, 0, width, height );
/** @since 3.5 */
public static void fillRoundedBackground( Graphics g, JComponent c, int arc ) {
if( arc < 0 ) {
Border border = c.getBorder();
arc = ((border instanceof FlatLineBorder)
? ((FlatLineBorder)border).getArc()
: 0);
}
paint( g, c );
// fill background
if( c.isOpaque() ) {
if( arc > 0 ) {
// fill background with parent color to avoid garbage in rounded corners
FlatUIUtils.paintParentBackground( g, c );
} else {
g.setColor( c.getBackground() );
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
}
}
// fill rounded rectangle if having rounded corners
if( arc > 0 ) {
g.setColor( c.getBackground() );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(),
0, UIScale.scale( arc ) );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
}
@Override

View File

@@ -43,6 +43,7 @@ import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -163,7 +164,7 @@ public class FlatPasswordFieldUI
}
private void repaint( KeyEvent e ) {
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
e.getComponent().repaint();
HiDPIUtils.repaint( e.getComponent() );
scrollCaretToVisible();
}
}
@@ -326,7 +327,7 @@ public class FlatPasswordFieldUI
if( visible != revealButton.isVisible() ) {
revealButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
if( !visible ) {
revealButton.setSelected( false );

View File

@@ -17,6 +17,7 @@
package com.formdev.flatlaf.ui;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
@@ -41,6 +42,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
@@ -76,6 +78,8 @@ public class FlatPopupFactory
private MethodHandle java8getPopupMethod;
private MethodHandle java9getPopupMethod;
private final ArrayList<NonFlashingPopup> stillShownHeavyWeightPopups = new ArrayList<>();
@Override
public Popup getPopup( Component owner, Component contents, int x, int y )
throws IllegalArgumentException
@@ -88,15 +92,34 @@ public class FlatPopupFactory
fixLinuxWaylandJava21focusIssue( owner );
// reuse a heavy weight popup window, which is still shown on screen,
// to avoid flicker when popup (e.g. tooltip) is moving while mouse is moved
for( NonFlashingPopup popup : stillShownHeavyWeightPopups ) {
if( popup.delegate != null &&
popup.owner == owner &&
(popup.contents == contents ||
(popup.contents instanceof JToolTip && contents instanceof JToolTip)) )
{
stillShownHeavyWeightPopups.remove( popup );
return reuseStillShownHeavyWeightPopups( popup, contents, x, y );
}
}
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
// macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) {
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null && SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() )
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
if( popup.popupWindow != null && SystemInfo.isMacOS &&
// do not use rounded border on macOS 14.4+ because it may freeze the application
// and crash the macOS WindowServer process (reports vary from Finder restarts to OS restarts)
// https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215
// https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442
SystemInfo.osVersion < SystemInfo.toVersion( 14, 4, 0, 0 ) &&
FlatNativeMacLibrary.isLoaded() )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
}
@@ -105,7 +128,7 @@ public class FlatPopupFactory
if( isWindows11BorderSupported() &&
getBorderCornerRadius( owner, contents ) > 0 )
{
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), owner, contents );
if( popup.popupWindow != null )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
@@ -116,7 +139,10 @@ public class FlatPopupFactory
forceHeavyWeight = true;
// create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight );
return owner.getGraphicsConfiguration().isTranslucencyCapable()
? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
: new NonFlashingPopup( popupForScreenOfOwner, owner, contents );
}
/**
@@ -227,6 +253,24 @@ public class FlatPopupFactory
return UIManager.get( uiKey );
}
/**
* Reuse a heavy weight popup window, which is still shown on screen,
* by updating window location and contents.
* This avoid flicker when popup (e.g. a tooltip) is moving while mouse is moved.
* E.g. overridden JComponent.getToolTipLocation(MouseEvent).
* See ToolTipManager.checkForTipChange(MouseEvent).
*/
private static NonFlashingPopup reuseStillShownHeavyWeightPopups(
NonFlashingPopup reusePopup, Component contents, int ownerX, int ownerY )
{
// clone popup because PopupFactory.getPopup() should not return old instance
NonFlashingPopup popup = reusePopup.cloneForReuse();
// update popup location, size and contents
popup.reset( contents, ownerX, ownerY );
return popup;
}
//---- tooltips -----------------------------------------------------------
/**
@@ -490,18 +534,31 @@ public class FlatPopupFactory
//---- class NonFlashingPopup ---------------------------------------------
private static class NonFlashingPopup
/**
* Fixes popup background flashing effect when using dark theme on light platform theme,
* where the light popup background is shown for a fraction of a second before
* the dark popup content is shown.
* This is fixed by setting popup background to content background.
* <p>
* Defers hiding of heavy weight popup window for an event cycle,
* which allows reusing popup window to avoid flicker when "moving" popup.
*/
private class NonFlashingPopup
extends Popup
{
private Popup delegate;
Component owner;
private Component contents;
// heavy weight
protected Window popupWindow;
Window popupWindow;
private Color oldPopupWindowBackground;
NonFlashingPopup( Popup delegate, Component contents ) {
private boolean disposed;
NonFlashingPopup( Popup delegate, Component owner, Component contents ) {
this.delegate = delegate;
this.owner = owner;
this.contents = contents;
popupWindow = SwingUtilities.windowForComponent( contents );
@@ -515,8 +572,27 @@ public class FlatPopupFactory
}
}
private NonFlashingPopup( NonFlashingPopup reusePopup ) {
delegate = reusePopup.delegate;
owner = reusePopup.owner;
contents = reusePopup.contents;
popupWindow = reusePopup.popupWindow;
oldPopupWindowBackground = reusePopup.oldPopupWindowBackground;
}
NonFlashingPopup cloneForReuse() {
return new NonFlashingPopup( this );
}
@Override
public void show() {
public final void show() {
if( disposed )
return;
showImpl();
}
void showImpl() {
if( delegate != null ) {
showPopupAndFixLocation( delegate, popupWindow );
@@ -540,13 +616,36 @@ public class FlatPopupFactory
}
@Override
public void hide() {
public final void hide() {
if( disposed )
return;
disposed = true;
// immediately hide non-heavy weight popups or combobox popups
if( !(popupWindow instanceof JWindow) || contents instanceof BasicComboPopup ) {
hideImpl();
return;
}
// defer hiding of heavy weight popup window for an event cycle,
// which allows reusing popup window to avoid flicker when "moving" popup
((JWindow)popupWindow).getContentPane().removeAll();
stillShownHeavyWeightPopups.add( this );
EventQueue.invokeLater( () -> {
// hide popup if it was not reused
if( stillShownHeavyWeightPopups.remove( this ) )
hideImpl();
} );
}
void hideImpl() {
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
if( delegate != null ) {
delegate.hide();
delegate = null;
owner = null;
contents = null;
}
@@ -557,6 +656,30 @@ public class FlatPopupFactory
popupWindow = null;
}
}
void reset( Component contents, int ownerX, int ownerY ) {
// update popup window location
popupWindow.setLocation( ownerX, ownerY );
// replace component in content pane
Container contentPane = ((JWindow)popupWindow).getContentPane();
contentPane.removeAll();
contentPane.add( contents, BorderLayout.CENTER );
popupWindow.invalidate();
popupWindow.validate();
popupWindow.pack();
// update client property on contents
if( this.contents != contents ) {
Object old = (this.contents instanceof JComponent)
? ((JComponent)this.contents).getClientProperty( KEY_POPUP_USES_NATIVE_BORDER )
: null;
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, old );
this.contents = contents;
}
}
}
//---- class DropShadowPopup ----------------------------------------------
@@ -564,8 +687,6 @@ public class FlatPopupFactory
private class DropShadowPopup
extends NonFlashingPopup
{
private final Component owner;
// light weight
private JComponent lightComp;
private Border oldBorder;
@@ -580,11 +701,11 @@ public class FlatPopupFactory
// heavy weight
private Popup dropShadowDelegate;
private Window dropShadowWindow;
private JPanel dropShadowPanel2;
private Color oldDropShadowWindowBackground;
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
super( delegate, contents );
this.owner = owner;
super( delegate, owner, contents );
Dimension size = contents.getPreferredSize();
if( size.width <= 0 || size.height <= 0 )
@@ -600,24 +721,24 @@ public class FlatPopupFactory
// the drop shadow and is positioned behind the popup window.
// create panel that paints the drop shadow
JPanel dropShadowPanel = new JPanel();
dropShadowPanel.setBorder( createDropShadowBorder() );
dropShadowPanel.setOpaque( false );
dropShadowPanel2 = new JPanel();
dropShadowPanel2.setBorder( createDropShadowBorder() );
dropShadowPanel2.setOpaque( false );
// set preferred size of drop shadow panel
Dimension prefSize = popupWindow.getPreferredSize();
Insets insets = dropShadowPanel.getInsets();
dropShadowPanel.setPreferredSize( new Dimension(
Insets insets = dropShadowPanel2.getInsets();
dropShadowPanel2.setPreferredSize( new Dimension(
prefSize.width + insets.left + insets.right,
prefSize.height + insets.top + insets.bottom ) );
// create heavy weight popup for drop shadow
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel, x, y, true );
dropShadowDelegate = getPopupForScreenOfOwner( owner, dropShadowPanel2, x, y, true );
// make drop shadow popup window translucent
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel2 );
if( dropShadowWindow != null ) {
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
dropShadowWindow.setBackground( new Color( 0, true ) );
@@ -654,6 +775,23 @@ public class FlatPopupFactory
}
}
private DropShadowPopup( DropShadowPopup reusePopup ) {
super( reusePopup );
// not necessary to clone fields used for light/medium weight popups
// heavy weight
dropShadowDelegate = reusePopup.dropShadowDelegate;
dropShadowWindow = reusePopup.dropShadowWindow;
dropShadowPanel2 = reusePopup.dropShadowPanel2;
oldDropShadowWindowBackground = reusePopup.oldDropShadowWindowBackground;
}
@Override
NonFlashingPopup cloneForReuse() {
return new DropShadowPopup( this );
}
private Border createDropShadowBorder() {
return new FlatDropShadowBorder(
UIManager.getColor( "Popup.dropShadowColor" ),
@@ -662,14 +800,14 @@ public class FlatPopupFactory
}
@Override
public void show() {
void showImpl() {
if( dropShadowDelegate != null )
showPopupAndFixLocation( dropShadowDelegate, dropShadowWindow );
if( mediumWeightPanel != null )
showMediumWeightDropShadow();
super.show();
super.showImpl();
// fix location of light weight popup in case it has left or top drop shadow
if( lightComp != null ) {
@@ -680,10 +818,11 @@ public class FlatPopupFactory
}
@Override
public void hide() {
void hideImpl() {
if( dropShadowDelegate != null ) {
dropShadowDelegate.hide();
dropShadowDelegate = null;
dropShadowPanel2 = null;
}
if( mediumWeightPanel != null ) {
@@ -692,7 +831,7 @@ public class FlatPopupFactory
mediumWeightPanel = null;
}
super.hide();
super.hideImpl();
if( dropShadowWindow != null ) {
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
@@ -776,5 +915,25 @@ public class FlatPopupFactory
if( dropShadowPanel != null && mediumWeightPanel != null )
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
}
@Override
void reset( Component contents, int ownerX, int ownerY ) {
super.reset( contents, ownerX, ownerY );
if( dropShadowWindow != null ) {
// set preferred size of drop shadow panel
Dimension prefSize = popupWindow.getPreferredSize();
Insets insets = dropShadowPanel2.getInsets();
int w = prefSize.width + insets.left + insets.right;
int h = prefSize.height + insets.top + insets.bottom;
dropShadowPanel2.setPreferredSize( new Dimension( w, h ) );
// update drop shadow popup window location and size
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowWindow.setBounds( x, y, w, h );
dropShadowWindow.pack();
}
}
}
}

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -86,6 +87,17 @@ public class FlatProgressBarUI
installStyle();
}
@Override
public void uninstallUI( JComponent c ) {
if( !EventQueue.isDispatchThread() && progressBar.isIndeterminate() ) {
LoggingFacade.INSTANCE.logSevere(
"FlatLaf: Uninstalling indeterminate progress bar UI not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
new IllegalStateException() );
}
super.uninstallUI( c );
}
@Override
protected void installDefaults() {
super.installDefaults();
@@ -110,17 +122,25 @@ public class FlatProgressBarUI
propertyChangeListener = e -> {
switch( e.getPropertyName() ) {
case "indeterminate":
if( !EventQueue.isDispatchThread() && !progressBar.isIndeterminate() ) {
LoggingFacade.INSTANCE.logSevere(
"FlatLaf: Using JProgressBar.setIndeterminate(false) not on AWT thread may throw NPE in FlatProgressBarUI.paint(). Use SwingUtilities.invokeLater().",
new IllegalStateException() );
}
break;
case PROGRESS_BAR_LARGE_HEIGHT:
case PROGRESS_BAR_SQUARE:
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
case STYLE:
case STYLE_CLASS:
installStyle();
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
}
};
@@ -274,6 +294,6 @@ public class FlatProgressBarUI
// Only solution is to repaint whole progress bar.
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
if( (int) systemScaleFactor != systemScaleFactor )
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
}
}

View File

@@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
@@ -102,13 +103,23 @@ public class FlatRadioButtonMenuItemUI
oldStyleValues = null;
}
@Override
protected void installComponents( JMenuItem menuItem ) {
super.installComponents( menuItem );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( menuItem );
}
protected FlatMenuItemRenderer createRenderer() {
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
return FlatStylingSupport.createPropertyChangeListener( c, this::installStyle, super.createPropertyChangeListener( c ) );
return FlatHTML.createPropertyChangeListener(
FlatStylingSupport.createPropertyChangeListener( c, this::installStyle,
super.createPropertyChangeListener( c ) ) );
}
/** @since 2 */

View File

@@ -40,12 +40,14 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicRadioButtonUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -159,6 +161,10 @@ public class FlatRadioButtonUI
/** @since 2 */
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case BasicHTML.propertyKey:
FlatHTML.updateRendererCSSFontBaseSize( b );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
@@ -168,7 +174,7 @@ public class FlatRadioButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -43,6 +43,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -212,14 +213,14 @@ public class FlatScrollBarUI
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case "componentOrientation":
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
private void repaint() {
if( scrollbar.isEnabled() )
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
}
}

View File

@@ -55,6 +55,7 @@ import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -297,11 +298,11 @@ public class FlatScrollPaneUI
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
if( vsb != null ) {
vsb.revalidate();
vsb.repaint();
HiDPIUtils.repaint( vsb );
}
if( hsb != null ) {
hsb.revalidate();
hsb.repaint();
HiDPIUtils.repaint( hsb );
}
break;
@@ -321,14 +322,14 @@ public class FlatScrollPaneUI
break;
case FlatClientProperties.OUTLINE:
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case "border":
@@ -339,7 +340,7 @@ public class FlatScrollPaneUI
borderShared = null;
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
break;
}
@@ -538,14 +539,14 @@ public class FlatScrollPaneUI
public void focusGained( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
}

View File

@@ -32,6 +32,7 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -134,7 +135,7 @@ public class FlatSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -25,6 +25,8 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
@@ -191,6 +193,23 @@ public class FlatSliderUI
return new FlatTrackListener();
}
@Override
protected FocusListener createFocusListener( JSlider slider ) {
return new BasicSliderUI.FocusHandler() {
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( slider );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( slider );
}
};
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
@@ -422,7 +441,7 @@ debug*/
Color thumbColor, Color thumbBorderColor, Color focusedColor, float thumbBorderWidth, int focusWidth )
{
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( systemScaleFactor != (int) systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( (Graphics2D) g, thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
@@ -579,15 +598,15 @@ debug*/
@Override
public void setThumbLocation( int x, int y ) {
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
if( !isRoundThumb() ) {
// the needle of the directional thumb is painted outside of thumbRect
// --> must increase repaint rectangle
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
// increase union rectangle for repaint
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
if( slider.getOrientation() == JSlider.HORIZONTAL )
@@ -597,10 +616,9 @@ debug*/
if( !slider.getComponentOrientation().isLeftToRight() )
r.x -= extra;
}
}
slider.repaint( r );
} else
super.setThumbLocation( x, y );
HiDPIUtils.repaint( slider, r );
}
//---- class FlatTrackListener --------------------------------------------
@@ -688,21 +706,21 @@ debug*/
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
{
calculateThumbLocation();
slider.repaint();
HiDPIUtils.repaint( slider );
}
}
protected void setThumbHover( boolean hover ) {
if( hover != thumbHover ) {
thumbHover = hover;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}
protected void setThumbPressed( boolean pressed ) {
if( pressed != thumbPressed ) {
thumbPressed = pressed;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -586,7 +587,7 @@ public class FlatSpinnerUI
@Override
public void focusGained( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
// if spinner gained focus, transfer it to the editor text field
if( e.getComponent() == spinner ) {
@@ -599,7 +600,7 @@ public class FlatSpinnerUI
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
}
//---- interface PropertyChangeListener ----
@@ -614,7 +615,7 @@ public class FlatSpinnerUI
case FlatClientProperties.COMPONENT_ROUND_RECT:
case FlatClientProperties.OUTLINE:
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
case FlatClientProperties.MINIMUM_WIDTH:
@@ -625,7 +626,7 @@ public class FlatSpinnerUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
spinner.revalidate();
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
}
}

View File

@@ -40,6 +40,7 @@ import javax.swing.UIManager;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -709,7 +710,7 @@ public class FlatStylingSupport
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
};

View File

@@ -98,6 +98,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
@@ -895,7 +896,7 @@ public class FlatTabbedPaneUI
}
}
tabPane.repaint( r );
HiDPIUtils.repaint( tabPane, r );
}
private boolean inCalculateEqual;
@@ -1684,7 +1685,7 @@ debug*/
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 ) {
if( getTabType() == TAB_TYPE_CARD && selectedIndex >= 0 ) {
float csh = scale( (float) contentSeparatorHeight );
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
@@ -2581,19 +2582,19 @@ debug*/
@Override
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
popupVisible = true;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuCanceled( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
}
@@ -3102,7 +3103,7 @@ debug*/
case TABBED_PANE_SHOW_TAB_SEPARATORS:
case TABBED_PANE_TAB_TYPE:
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
@@ -3125,14 +3126,14 @@ debug*/
case TABBED_PANE_TAB_ICON_PLACEMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3140,7 +3141,7 @@ debug*/
uninstallTrailingComponent();
installTrailingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3148,7 +3149,7 @@ debug*/
case STYLE_CLASS:
installStyle();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}
@@ -3172,7 +3173,7 @@ debug*/
case TABBED_PANE_TAB_ALIGNMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}

View File

@@ -65,8 +65,28 @@ public class FlatTableCellBorder
return super.getLineColor();
}
@Override
public int getArc() {
if( c != null ) {
Integer selectionArc = getStyleFromTableUI( c, ui -> ui.selectionArc );
if( selectionArc != null )
return selectionArc;
}
return super.getArc();
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( c != null ) {
Insets selectionInsets = getStyleFromTableUI( c, ui -> ui.selectionInsets );
if( selectionInsets != null ) {
x += selectionInsets.left;
y += selectionInsets.top;
width -= selectionInsets.left + selectionInsets.right;
height -= selectionInsets.top + selectionInsets.bottom;
}
}
this.c = c;
super.paintBorder( c, g, x, y, width, height );
this.c = null;

View File

@@ -45,6 +45,7 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -234,8 +235,8 @@ public class FlatTableHeaderUI
@Override
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
header.repaint( header.getHeaderRect( oldColumn ) );
header.repaint( header.getHeaderRect( newColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( oldColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( newColumn ) );
}
@Override

View File

@@ -24,7 +24,13 @@ import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
@@ -38,21 +44,29 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -92,6 +106,8 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.selectionInsets Insets
* @uiDefault Table.selectionArc int
* @uiDefault Table.paintOutsideAlternateRows boolean
* @uiDefault Table.editorSelectAllOnStartEditing boolean
*
@@ -120,6 +136,8 @@ public class FlatTableUI
@Styleable protected Color selectionForeground;
@Styleable protected Color selectionInactiveBackground;
@Styleable protected Color selectionInactiveForeground;
/** @since 3.5 */ @Styleable protected Insets selectionInsets;
/** @since 3.5 */ @Styleable protected int selectionArc;
// for FlatTableCellBorder
/** @since 2 */ @Styleable protected Insets cellMargins;
@@ -132,6 +150,9 @@ public class FlatTableUI
private TableCellRenderer oldBooleanRenderer;
private PropertyChangeListener propertyChangeListener;
private ComponentListener outsideAlternateRowsListener;
private ListSelectionListener rowSelectionListener;
private TableColumnModelListener columnSelectionListener;
private Map<String, Object> oldStyleValues;
public static ComponentUI createUI( JComponent c ) {
@@ -158,6 +179,8 @@ public class FlatTableUI
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
selectionInsets = UIManager.getInsets( "Table.selectionInsets" );
selectionArc = UIManager.getInt( "Table.selectionArc" );
toggleSelectionColors();
@@ -245,6 +268,28 @@ public class FlatTableUI
propertyChangeListener = e -> {
switch( e.getPropertyName() ) {
case "selectionModel":
if( rowSelectionListener != null ) {
Object oldModel = e.getOldValue();
Object newModel = e.getNewValue();
if( oldModel != null )
((ListSelectionModel)oldModel).removeListSelectionListener( rowSelectionListener );
if( newModel != null )
((ListSelectionModel)newModel).addListSelectionListener( rowSelectionListener );
}
break;
case "columnModel":
if( columnSelectionListener != null ) {
Object oldModel = e.getOldValue();
Object newModel = e.getNewValue();
if( oldModel != null )
((TableColumnModel)oldModel).removeColumnModelListener( columnSelectionListener );
if( newModel != null )
((TableColumnModel)newModel).addColumnModelListener( columnSelectionListener );
}
break;
case FlatClientProperties.COMPONENT_FOCUS_OWNER:
toggleSelectionColors();
break;
@@ -253,11 +298,14 @@ public class FlatTableUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
table.revalidate();
table.repaint();
HiDPIUtils.repaint( table );
break;
}
};
table.addPropertyChangeListener( propertyChangeListener );
if( selectionArc > 0 )
installRepaintRoundedSelectionListeners();
}
@Override
@@ -266,6 +314,19 @@ public class FlatTableUI
table.removePropertyChangeListener( propertyChangeListener );
propertyChangeListener = null;
if( outsideAlternateRowsListener != null ) {
table.removeComponentListener( outsideAlternateRowsListener );
outsideAlternateRowsListener = null;
}
if( rowSelectionListener != null ) {
table.getSelectionModel().removeListSelectionListener( rowSelectionListener );
rowSelectionListener = null;
}
if( columnSelectionListener != null ) {
table.getColumnModel().removeColumnModelListener( columnSelectionListener );
columnSelectionListener = null;
}
}
@Override
@@ -342,6 +403,8 @@ public class FlatTableUI
protected Object applyStyleProperty( String key, Object value ) {
if( "rowHeight".equals( key ) && value instanceof Integer )
value = UIScale.scale( (Integer) value );
else if( "selectionArc".equals( key ) && value instanceof Integer && (Integer) value > 0 )
installRepaintRoundedSelectionListeners();
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, table, key, value );
}
@@ -404,6 +467,7 @@ public class FlatTableUI
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
double lineThickness = (1. / systemScaleFactor) * (int) systemScaleFactor;
double lineOffset = (1. - lineThickness) + 0.05; // adding 0.05 to fix line location in some cases
// Java 8 uses drawLine() to paint grid lines
// Java 9+ uses fillRect() to paint grid lines (except for dragged column)
@@ -446,11 +510,11 @@ public class FlatTableUI
// reduce line thickness to avoid unstable painted line thickness
if( lineThickness != 1 ) {
if( horizontalLines && height == 1 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, width, lineThickness ) );
super.fill( new Rectangle2D.Double( x, y + lineOffset, width, lineThickness ) );
return;
}
if( verticalLines && width == 1 && y == 0 && wasInvokedFromPaintGrid() ) {
super.fill( new Rectangle2D.Double( x, y, lineThickness, height ) );
super.fill( new Rectangle2D.Double( x + lineOffset, y, lineThickness, height ) );
return;
}
}
@@ -468,6 +532,10 @@ public class FlatTableUI
};
}
// rounded selection or selection insets
if( selectionArc > 0 || (selectionInsets != null && !FlatUIUtils.isInsetsEmpty( selectionInsets )) )
g = new RoundedSelectionGraphics( g, UIManager.getColor( "Table.alternateRowColor" ) );
super.paint( g, c );
}
@@ -513,8 +581,6 @@ public class FlatTableUI
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
Color alternateColor;
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
g.setColor( alternateColor );
int rowCount = table.getRowCount();
// paint alternating empty rows below the table
@@ -523,10 +589,350 @@ public class FlatTableUI
int tableWidth = table.getWidth();
int rowHeight = table.getRowHeight();
g.setColor( alternateColor );
int x = viewport.getComponentOrientation().isLeftToRight() ? 0 : viewportWidth - tableWidth;
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
if( row % 2 != 0 )
g.fillRect( 0, y, tableWidth, rowHeight );
paintAlternateRowBackground( g, -1, -1, x, y, tableWidth, rowHeight );
}
// add listener on demand
if( outsideAlternateRowsListener == null && table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF ) {
outsideAlternateRowsListener = new FlatOutsideAlternateRowsListener();
table.addComponentListener( outsideAlternateRowsListener );
}
}
}
}
/**
* Paints (rounded) alternate row background.
* Supports {@link #selectionArc} and {@link #selectionInsets}.
* <p>
* <b>Note:</b> This method is only invoked if either selection arc
* is greater than zero or if selection insets are not empty.
*
* @since 3.5
*/
protected void paintAlternateRowBackground( Graphics g, int row, int column, int x, int y, int width, int height ) {
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( column >= 0 ) {
// selection insets
// selection arc
if( column > 0 ) {
if( insets != null )
insets.left = 0;
if( table.getComponentOrientation().isLeftToRight() )
arcTopLeft = arcBottomLeft = 0;
else
arcTopRight = arcBottomRight = 0;
}
if( column < table.getColumnCount() - 1 ) {
if( insets != null )
insets.right = 0;
if( table.getComponentOrientation().isLeftToRight() )
arcTopRight = arcBottomRight = 0;
else
arcTopLeft = arcBottomLeft = 0;
}
}
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
}
/**
* Paints (rounded) cell selection.
* Supports {@link #selectionArc} and {@link #selectionInsets}.
* <p>
* <b>Note:</b> This method is only invoked if either selection arc
* is greater than zero or if selection insets are not empty.
*
* @since 3.5
*/
protected void paintCellSelection( Graphics g, int row, int column, int x, int y, int width, int height ) {
boolean rowSelAllowed = table.getRowSelectionAllowed();
boolean colSelAllowed = table.getColumnSelectionAllowed();
boolean rowSelOnly = rowSelAllowed && !colSelAllowed;
boolean colSelOnly = colSelAllowed && !rowSelAllowed;
boolean cellOnlySel = rowSelAllowed && colSelAllowed;
// get selection state of surrounding cells
boolean leftSelected = (column > 0 && (rowSelOnly || table.isCellSelected( row, column - 1 )));
boolean topSelected = (row > 0 && (colSelOnly || table.isCellSelected( row - 1, column )));
boolean rightSelected = (column < table.getColumnCount() - 1 && (rowSelOnly || table.isCellSelected( row, column + 1 )));
boolean bottomSelected = (row < table.getRowCount() - 1 && (colSelOnly || table.isCellSelected( row + 1, column )));
if( !table.getComponentOrientation().isLeftToRight() ) {
boolean temp = leftSelected;
leftSelected = rightSelected;
rightSelected = temp;
}
// selection insets
// (insets are applied to whole row if row-only selection is used,
// or to whole column if column-only selection is used,
// or to cell if cell selection is used)
Insets insets = (selectionInsets != null) ? (Insets) selectionInsets.clone() : null;
if( insets != null ) {
if( rowSelOnly && leftSelected )
insets.left = 0;
if( rowSelOnly && rightSelected )
insets.right = 0;
if( colSelOnly && topSelected )
insets.top = 0;
if( colSelOnly && bottomSelected )
insets.bottom = 0;
}
// selection arc
float arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight;
arcTopLeft = arcTopRight = arcBottomLeft = arcBottomRight = UIScale.scale( selectionArc / 2f );
if( selectionArc > 0 ) {
// note that intercellSpacing is not considered as a gap because
// grid lines are usually painted to intercell space
boolean hasRowGap = (rowSelOnly || cellOnlySel) && insets != null && (insets.top != 0 || insets.bottom != 0);
boolean hasColGap = (colSelOnly || cellOnlySel) && insets != null && (insets.left != 0 || insets.right != 0);
if( leftSelected && !hasColGap )
arcTopLeft = arcBottomLeft = 0;
if( rightSelected && !hasColGap )
arcTopRight = arcBottomRight = 0;
if( topSelected && !hasRowGap )
arcTopLeft = arcTopRight = 0;
if( bottomSelected && !hasRowGap )
arcBottomLeft = arcBottomRight = 0;
}
FlatUIUtils.paintSelection( (Graphics2D) g, x, y, width, height,
UIScale.scale( insets ), arcTopLeft, arcTopRight, arcBottomLeft, arcBottomRight, 0 );
}
/**
* Paints a cell selection at the given coordinates.
* The selection color must be set on the graphics context.
* <p>
* This method is intended for use in custom cell renderers to support
* {@link #selectionArc} and {@link #selectionInsets}.
*
* @since 3.5
*/
public static void paintCellSelection( JTable table, Graphics g, int row, int column, int x, int y, int width, int height ) {
if( !(table.getUI() instanceof FlatTableUI) )
return;
FlatTableUI ui = (FlatTableUI) table.getUI();
ui.paintCellSelection( g, row, column, x, y, width, height );
}
private void installRepaintRoundedSelectionListeners() {
if( rowSelectionListener == null ) {
rowSelectionListener = this::repaintRoundedRowSelection;
table.getSelectionModel().addListSelectionListener( rowSelectionListener );
}
if( columnSelectionListener == null ) {
columnSelectionListener = new TableColumnModelListener() {
@Override
public void columnSelectionChanged( ListSelectionEvent e ) {
repaintRoundedColumnSelection( e );
}
@Override public void columnRemoved( TableColumnModelEvent e ) {}
@Override public void columnMoved( TableColumnModelEvent e ) {}
@Override public void columnMarginChanged( ChangeEvent e ) {}
@Override public void columnAdded( TableColumnModelEvent e ) {}
};
table.getColumnModel().addColumnModelListener( columnSelectionListener );
}
}
private void repaintRoundedRowSelection( ListSelectionEvent e ) {
if( selectionArc <= 0 || !table.getRowSelectionAllowed() )
return;
int rowCount = table.getRowCount();
int columnCount = table.getColumnCount();
if( rowCount <= 0 || columnCount <= 0 )
return;
// repaint including rows before and after changed selection
int firstRow = Math.max( 0, Math.min( e.getFirstIndex() - 1, rowCount - 1 ) );
int lastRow = Math.max( 0, Math.min( e.getLastIndex() + 1, rowCount - 1 ) );
Rectangle firstRect = table.getCellRect( firstRow, 0, false );
Rectangle lastRect = table.getCellRect( lastRow, columnCount - 1, false );
table.repaint( firstRect.union( lastRect ) );
}
private void repaintRoundedColumnSelection( ListSelectionEvent e ) {
if( selectionArc <= 0 || !table.getColumnSelectionAllowed() )
return;
int rowCount = table.getRowCount();
int columnCount = table.getColumnCount();
if( rowCount <= 0 || columnCount <= 0 )
return;
// limit to selected rows for cell selection
int firstRow = 0;
int lastRow = rowCount - 1;
if( table.getRowSelectionAllowed() ) {
firstRow = table.getSelectionModel().getMinSelectionIndex();
lastRow = table.getSelectionModel().getMaxSelectionIndex();
}
// repaint including columns before and after changed selection
int firstColumn = Math.max( 0, Math.min( e.getFirstIndex() - 1, columnCount - 1 ) );
int lastColumn = Math.max( 0, Math.min( e.getLastIndex() + 1, columnCount - 1 ) );
Rectangle firstRect = table.getCellRect( firstRow, firstColumn, false );
Rectangle lastRect = table.getCellRect( lastRow, lastColumn, false );
table.repaint( firstRect.union( lastRect ) );
}
//---- class RoundedSelectionGraphics -------------------------------------
/**
* Because selection painting is done in the cell renderer, it would be
* necessary to require a FlatLaf specific renderer to implement rounded selection.
* Using a LaF specific renderer was avoided because often a custom renderer is
* already used in applications. Then either the rounded selection is not used,
* or the application has to be changed to extend a FlatLaf renderer.
* <p>
* To solve this, a graphics proxy is used that paints rounded selection
* if row/column/cell is selected and the renderer wants to fill the background.
*/
private class RoundedSelectionGraphics
extends Graphics2DProxy
{
private final Color alternateRowColor;
// used to avoid endless loop in case that paintCellSelection() invokes
// g.fillRect() with full bounds (selectionInsets is 0,0,0,0)
private boolean inPaintSelection;
RoundedSelectionGraphics( Graphics delegate, Color alternateRowColor ) {
super( (Graphics2D) delegate );
this.alternateRowColor = alternateRowColor;
}
@Override
public Graphics create() {
return new RoundedSelectionGraphics( super.create(), alternateRowColor );
}
@Override
public Graphics create( int x, int y, int width, int height ) {
return new RoundedSelectionGraphics( super.create( x, y, width, height ), alternateRowColor );
}
@Override
public void fillRect( int x, int y, int width, int height ) {
if( fillCellSelection( x, y, width, height ) )
return;
super.fillRect( x, y, width, height );
}
@Override
public void fill( Shape shape ) {
if( shape instanceof Rectangle2D ) {
Rectangle2D r = (Rectangle2D) shape;
double x = r.getX();
double y = r.getY();
double width = r.getWidth();
double height = r.getHeight();
if( x == (int) x && y == (int) y && width == (int) width && height == (int) height ) {
if( fillCellSelection( (int) x, (int) y, (int) width, (int) height ) )
return;
}
}
super.fill( shape );
}
private boolean fillCellSelection( int x, int y, int width, int height ) {
if( inPaintSelection )
return false;
Color color;
Component rendererComponent;
if( x == 0 && y == 0 &&
((color = getColor()) == table.getSelectionBackground() ||
(alternateRowColor != null && color == alternateRowColor)) &&
(rendererComponent = findActiveRendererComponent()) != null &&
width == rendererComponent.getWidth() &&
height == rendererComponent.getHeight() )
{
Point location = rendererComponent.getLocation();
int row = table.rowAtPoint( location );
int column = table.columnAtPoint( location );
if( row >= 0 && column >= 0 ) {
inPaintSelection = true;
if( color == table.getSelectionBackground() )
paintCellSelection( this, row, column, x, y, width, height );
else
paintAlternateRowBackground( this, row, column, x, y, width, height );
inPaintSelection = false;
return true;
}
}
return false;
}
/**
* A CellRendererPane may contain multiple components, if multiple renderers
* are used. Inactive renderer components have size {@code 0x0}.
*/
private Component findActiveRendererComponent() {
int count = rendererPane.getComponentCount();
for( int i = 0; i < count; i++ ) {
Component c = rendererPane.getComponent( i );
if( c.getWidth() > 0 && c.getHeight() > 0 )
return c;
}
return null;
}
}
//---- class OutsideAlternateRowsListener ---------------------------------
/**
* Used if table auto-resize-mode is off to repaint outside alternate rows
* when table width changed (column resized) or component orientation changed.
*/
private class FlatOutsideAlternateRowsListener
extends ComponentAdapter
{
@Override
public void componentHidden( ComponentEvent e ) {
Container viewport = SwingUtilities.getUnwrappedParent( table );
if( viewport instanceof JViewport )
HiDPIUtils.repaint( viewport );
}
@Override
public void componentMoved( ComponentEvent e ) {
repaintAreaBelowTable();
}
@Override
public void componentResized( ComponentEvent e ) {
repaintAreaBelowTable();
}
private void repaintAreaBelowTable() {
Container viewport = SwingUtilities.getUnwrappedParent( table );
if( viewport instanceof JViewport ) {
int viewportHeight = viewport.getHeight();
int tableHeight = table.getHeight();
if( tableHeight < viewportHeight )
HiDPIUtils.repaint( viewport, 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
}
}
}

View File

@@ -239,7 +239,7 @@ public class FlatTextFieldUI
case COMPONENT_ROUND_RECT:
case OUTLINE:
case TEXT_FIELD_PADDING:
c.repaint();
HiDPIUtils.repaint( c );
break;
case MINIMUM_WIDTH:
@@ -250,38 +250,38 @@ public class FlatTextFieldUI
case STYLE_CLASS:
installStyle();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_ICON:
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_ICON:
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_COMPONENT:
uninstallTrailingComponent();
installTrailingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
uninstallClearButton();
installClearButton();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case "enabled":
@@ -815,7 +815,7 @@ debug*/
if( visible != clearButton.isVisible() ) {
clearButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
}
}

View File

@@ -222,7 +222,7 @@ public class FlatTitlePane
windowTopBorderLayer = new JPanel();
windowTopBorderLayer.setVisible( false );
windowTopBorderLayer.setOpaque( false );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( FlatNativeWindowBorder.WindowTopBorder.getInstance() ) );
windowTopBorderLayer.setBorder( FlatUIUtils.nonUIResource( WindowTopBorder.getInstance() ) );
} else
windowTopBorderLayer = null;
@@ -745,16 +745,6 @@ public class FlatTitlePane
g.fillRect( 0, 0, getWidth(), getHeight() );
}
protected void repaintWindowBorder() {
int width = rootPane.getWidth();
int height = rootPane.getHeight();
Insets insets = rootPane.getInsets();
rootPane.repaint( 0, 0, width, insets.top ); // top
rootPane.repaint( 0, 0, insets.left, height ); // left
rootPane.repaint( 0, height - insets.bottom, width, insets.bottom ); // bottom
rootPane.repaint( width - insets.right, 0, insets.right, height ); // right
}
/**
* Iconifies the window.
*/
@@ -1352,10 +1342,7 @@ public class FlatTitlePane
activeChanged( true );
updateNativeTitleBarHeightAndHitTestSpots();
if( isWindowTopBorderNeeded() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
repaintBorder();
}
@Override
@@ -1363,10 +1350,22 @@ public class FlatTitlePane
activeChanged( false );
updateNativeTitleBarHeightAndHitTestSpots();
if( isWindowTopBorderNeeded() )
repaintBorder();
}
private void repaintBorder() {
// Windows 10 top border
if( windowTopBorderLayer != null && windowTopBorderLayer.isShowing())
WindowTopBorder.getInstance().repaintBorder( windowTopBorderLayer );
else if( isWindowTopBorderNeeded() && !isWindowMaximized() && !isFullWindowContent() )
WindowTopBorder.getInstance().repaintBorder( FlatTitlePane.this );
repaintWindowBorder();
// Window border used for non-native window decorations
if( rootPane.getBorder() instanceof FlatRootPaneUI.FlatWindowBorder ) {
// not repainting four areas on the four sides because RepaintManager
// unions dirty regions, which also results in repaint of whole rootpane
rootPane.repaint();
}
}
@Override

View File

@@ -26,6 +26,7 @@ import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -159,14 +160,14 @@ public class FlatToggleButtonUI
b.revalidate();
}
b.repaint();
HiDPIUtils.repaint( b );
break;
case TAB_BUTTON_UNDERLINE_PLACEMENT:
case TAB_BUTTON_UNDERLINE_HEIGHT:
case TAB_BUTTON_UNDERLINE_COLOR:
case TAB_BUTTON_SELECTED_BACKGROUND:
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -36,6 +36,7 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -131,7 +132,7 @@ public class FlatToolBarSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -443,7 +444,7 @@ public class FlatToolBarUI
// repaint button group
if( gr != null )
toolBar.repaint( gr );
HiDPIUtils.repaint(toolBar, gr );
}
private ButtonGroup getButtonGroup( AbstractButton b ) {

View File

@@ -61,7 +61,7 @@ public class FlatToolTipUI
super.installUI( c );
// update HTML renderer if necessary
FlatLabelUI.updateHTMLRenderer( c, ((JToolTip)c).getTipText(), false );
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
@@ -81,11 +81,7 @@ public class FlatToolTipUI
/** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
String name = e.getPropertyName();
if( name == "tiptext" || name == "font" || name == "foreground" ) {
JToolTip toolTip = (JToolTip) e.getSource();
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
}
FlatHTML.propertyChange( e );
}
@Override

View File

@@ -47,6 +47,7 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -310,7 +311,7 @@ public class FlatTreeUI
switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION:
case TREE_PAINT_SELECTION:
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "dropLocation":
@@ -325,7 +326,7 @@ public class FlatTreeUI
case STYLE_CLASS:
installStyle();
tree.revalidate();
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "enabled":
@@ -353,7 +354,7 @@ public class FlatTreeUI
Rectangle r = tree.getPathBounds( loc.getPath() );
if( r != null )
tree.repaint( 0, r.y, tree.getWidth(), r.height );
HiDPIUtils.repaint( tree, 0, r.y, tree.getWidth(), r.height );
}
@Override
@@ -370,14 +371,14 @@ public class FlatTreeUI
{
if( changedPaths.length > 4 ) {
// same is done in BasicTreeUI.Handler.valueChanged()
tree.repaint();
HiDPIUtils.repaint( tree );
} else {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
for( TreePath path : changedPaths ) {
Rectangle r = getPathBounds( tree, path );
if( r != null )
tree.repaint( r.x, r.y - arc, r.width, r.height + (arc * 2) );
HiDPIUtils.repaint( tree, r.x, r.y - arc, r.width, r.height + (arc * 2) );
}
}
}

View File

@@ -124,6 +124,11 @@ public class FlatUIUtils
dest.right = src.right;
}
/** @since 3.5 */
public static boolean isInsetsEmpty( Insets insets ) {
return insets.top == 0 && insets.left == 0 && insets.bottom == 0 && insets.right == 0;
}
public static Color getUIColor( String key, int defaultColorRGB ) {
Color color = UIManager.getColor( key );
return (color != null) ? color : new Color( defaultColorRGB );
@@ -601,28 +606,55 @@ public class FlatUIUtils
public static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
{
paintOutlinedComponent( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
borderWidth, arc, focusColor, borderColor, background, false );
}
static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane )
{
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( (int) systemScaleFactor != systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintOutlinedComponentImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), focusWidthFraction, (float) (focusInnerWidth * scaleFactor),
(float) (borderWidth * scaleFactor), (float) (arc * scaleFactor),
focusColor, borderColor, background );
focusColor, borderColor, background, scrollPane, scaleFactor );
} );
return;
}
paintOutlinedComponentImpl( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
borderWidth, arc, focusColor, borderColor, background );
borderWidth, arc, focusColor, borderColor, background, scrollPane, systemScaleFactor );
}
@SuppressWarnings( "SelfAssignment" ) // Error Prone
private static void paintOutlinedComponentImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
Paint focusColor, Paint borderColor, Paint background, boolean scrollPane, double scaleFactor )
{
// Special handling for scrollpane and fractional scale factors (e.g. 1.25 - 1.75),
// where Swing scales one "logical" pixel (border insets) to either one or two physical pixels.
// Antialiasing is used to paint the border, which usually needs two physical pixels
// at small scale factors. 1px for the solid border and another 1px for antialiasing.
// But scrollpane view is painted over the border, which results in a painted border
// that is 1px thick at some sides and 2px thick at other sides.
if( scrollPane && scaleFactor != (int) scaleFactor ) {
if( focusWidth > 0 ) {
// reduce outer border thickness (focusWidth) so that inner side of
// component border (focusWidth + borderWidth) is at a full pixel
int totalWidth = (int) (focusWidth + borderWidth);
focusWidth = totalWidth - borderWidth;
} else {// if( scaleFactor > 1 && scaleFactor < 2 ) {
// reduce component border thickness (borderWidth) to full pixels
borderWidth = (int) borderWidth;
}
}
// outside bounds of the border and the background
float x1 = x + focusWidth;
float y1 = y + focusWidth;
@@ -780,7 +812,7 @@ public class FlatUIUtils
if( arcTopLeft > 0 || arcTopRight > 0 || arcBottomLeft > 0 || arcBottomRight > 0 ) {
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
if( systemScaleFactor != (int) systemScaleFactor ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
@@ -1315,13 +1347,13 @@ debug*/
@Override
public void focusGained( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
@Override
public void focusLost( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
}

View File

@@ -16,14 +16,17 @@
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import javax.swing.JComponent;
import javax.swing.RepaintManager;
import com.formdev.flatlaf.FlatSystemProperties;
/**
@@ -322,4 +325,243 @@ public class HiDPIUtils
}
};
}
/**
* Repaints the given component.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c ) {
repaint( c, 0, 0, c.getWidth(), c.getHeight() );
}
/**
* Repaints the given component area.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c, Rectangle r ) {
repaint( c, r.x, r.y, r.width, r.height );
}
/**
* Repaints the given component area.
* <p>
* Invokes {@link Component#repaint(int, int, int, int)} on the given component,
* <p>
* Use this method, instead of {@code Component.repaint(...)},
* to fix a problem in Swing when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The problem may occur under following conditions:
* <ul>
* <li>using Java 9 or later
* <li>system scale factor is 125%, 175%, 225%, ...
* (Windows only; Java on macOS and Linux does not support fractional scale factors)
* <li>repaint whole component or right/bottom area of component
* <li>component is opaque; or component is contained in a opaque container
* that has same right/bottom bounds as component
* <li>component has bounds that Java/Swing scales different when repainting components
* </ul>
*
* @since 3.5
*/
public static void repaint( Component c, int x, int y, int width, int height ) {
// repaint given component area
// Always invoke repaint() on given component, even if also invoked (below)
// on one of its ancestors, for the case that component overrides that method.
// Also RepaintManager "merges" the two repaints into one.
c.repaint( x, y, width, height );
if( RepaintManager.currentManager( c ) instanceof HiDPIRepaintManager )
return;
// if necessary, also repaint given area in first ancestor that is larger than component
// to avoid clipping issue (see needsSpecialRepaint())
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() ) {
p.repaint( x2, y2, width, height );
break;
}
}
}
}
/**
* There is a problem in Swing, when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The component is first painted to an in-memory image,
* and then that image is copied to the screen.
* See {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* There are two clipping rectangles involved when copying the image to the screen:
* {@code sun.java2d.SunGraphics2D#devClip} and
* {@code sun.java2d.SunGraphics2D#usrClip}.
* <p>
* {@code devClip} is the device clipping in physical pixels.
* It gets the bounds of the painting component, which is either the passed component,
* or if it is non-opaque, then the first opaque ancestor of the passed component.
* It is calculated in {@code sun.java2d.SunGraphics2D#constrain()} while
* getting a graphics context via {@link JComponent#getGraphics()}.
* <p>
* {@code usrClip} is the user clipping, which is set via {@link Graphics} clipping methods.
* This is done in {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* The intersection of {@code devClip} and {@code usrClip}
* (computed in {@code sun.java2d.SunGraphics2D#validateCompClip()})
* is used to copy the image to the screen.
* <p>
* Unfortunately different scaling/rounding strategies are used to calculate
* the two clipping rectangles, which is the reason of the issue.
* <p>
* {@code devClip} (see {@code sun.java2d.SunGraphics2D#constrain()}):
* <pre>{@code
* int devX = (int) (x * scale);
* int devWidth = Math.round( width * scale )
* }</pre>
* {@code usrClip} (see {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}):
* <pre>{@code
* int usrX = (int) Math.ceil( (x * scale) - 0.5 );
* int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;
* }</pre>
* X/Y coordinates are always rounded down for {@code devClip}, but rounded up for {@code usrClip}.
* Width/height calculation is also different.
*/
private static boolean needsSpecialRepaint( Component c, int x, int y, int width, int height ) {
// no special repaint necessary for Java 8 or for macOS and Linux
// (Java on those platforms does not support fractional scale factors)
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return false;
// check whether repaint area is empty or no component given
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
if( width <= 0 || height <= 0 || c == null )
return false;
// check whether component has zero size
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
int compWidth = c.getWidth();
int compHeight = c.getHeight();
if( compWidth <= 0 || compHeight <= 0 )
return false;
// check whether repaint area does span to right or bottom component edges
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x + width < compWidth && y + height < compHeight )
return false;
// if component is not opaque, Swing uses the first opaque ancestor for painting
if( !c.isOpaque() ) {
int x2 = x;
int y2 = y;
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( p.isOpaque() ) {
// check whether repaint area does span to right or bottom edges
// of the opaque ancestor component
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() )
return false;
break;
}
}
}
// check whether Special repaint is necessary for current scale factor
// (doing this check late because it temporary allocates some memory)
double scaleFactor = UIScale.getSystemScaleFactor( c.getGraphicsConfiguration() );
double fraction = scaleFactor - (int) scaleFactor;
if( fraction == 0 || fraction == 0.5 )
return false;
return true;
}
/**
* Installs a {@link HiDPIRepaintManager} on Windows when running in Java 9+,
* but only if default repaint manager is currently installed.
* <p>
* Invoke once on application startup.
* Compatible with all/other LaFs.
*
* @since 3.5
*/
public static void installHiDPIRepaintManager() {
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return;
RepaintManager manager = RepaintManager.currentManager( (Component) null );
if( manager.getClass() == RepaintManager.class )
RepaintManager.setCurrentManager( new HiDPIRepaintManager() );
}
/**
* Similar to {@link #repaint(Component, int, int, int, int)},
* but invokes callback instead of invoking {@link Component#repaint(int, int, int, int)}.
* <p>
* For use in custom repaint managers.
*
* @since 3.5
*/
public static void addDirtyRegion( JComponent c, int x, int y, int width, int height, DirtyRegionCallback callback ) {
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() && p instanceof JComponent ) {
callback.addDirtyRegion( (JComponent) p, x2, y2, width, height );
return;
}
x2 += p.getX();
y2 += p.getY();
}
}
callback.addDirtyRegion( c, x, y, width, height );
}
//---- interface DirtyRegionCallback --------------------------------------
/**
* For {@link HiDPIUtils#addDirtyRegion(JComponent, int, int, int, int, DirtyRegionCallback)}.
*
* @since 3.5
*/
public interface DirtyRegionCallback {
void addDirtyRegion( JComponent c, int x, int y, int w, int h );
}
//---- class HiDPIRepaintManager ------------------------------------------
/**
* A repaint manager that fixes a problem in Swing when repainting components
* at some scale factors (e.g. 125%, 175%, etc) on Windows.
* <p>
* Use {@link HiDPIUtils#installHiDPIRepaintManager()} to install it.
* <p>
* See {@link HiDPIUtils#repaint(Component, int, int, int, int)} for details.
*
* @since 3.5
*/
public static class HiDPIRepaintManager
extends RepaintManager
{
@Override
public void addDirtyRegion( JComponent c, int x, int y, int w, int h ) {
HiDPIUtils.addDirtyRegion( c, x, y, w, h, super::addDirtyRegion );
}
}
}

View File

@@ -84,10 +84,12 @@ public class TestUIDefaultsLoader
void parseBorders() {
Insets insets = new Insets( 1,2,3,4 );
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4" );
assertBorderEquals( new FlatEmptyBorder( insets ), "1,2,3,4,,," );
assertBorderEquals( new FlatLineBorder( insets, Color.red ), "1,2,3,4,#f00" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 0 ), "1,2,3,4,#f00,2.5" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, -1 ), "1,2,3,4,#f00,2.5" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 2.5f, 6 ), "1,2,3,4,#f00,2.5,6" );
assertBorderEquals( new FlatLineBorder( insets, Color.red, 1, 6 ), "1,2,3,4,#f00,,6" );
assertBorderEquals( new FlatLineBorder( insets, null, 1, 6 ), "1,2,3,4,,,6" );
}
private void assertBorderEquals( Border expected, String actualStyle ) {

View File

@@ -253,7 +253,8 @@ public class TestFlatStyleableInfo
FlatLabelUI ui = (FlatLabelUI) c.getUI();
Map<String, Class<?>> expected = expectedMap(
"disabledForeground", Color.class
"disabledForeground", Color.class,
"arc", int.class
);
assertMapEquals( expected, ui.getStyleableInfos( c ) );
@@ -799,6 +800,8 @@ public class TestFlatStyleableInfo
"selectionForeground", Color.class,
"selectionInactiveBackground", Color.class,
"selectionInactiveForeground", Color.class,
"selectionInsets", Insets.class,
"selectionArc", int.class,
// FlatTableCellBorder
"cellMargins", Insets.class,
@@ -993,12 +996,20 @@ public class TestFlatStyleableInfo
"disabledBorderColor", Color.class,
"focusedBorderColor", Color.class,
"hoverBorderColor", Color.class,
"pressedBorderColor", Color.class,
"selectedBorderColor", Color.class,
"disabledSelectedBorderColor", Color.class,
"focusedSelectedBorderColor", Color.class,
"hoverSelectedBorderColor", Color.class,
"pressedSelectedBorderColor", Color.class,
"default.borderWidth", float.class,
"default.borderColor", Color.class,
"default.focusedBorderColor", Color.class,
"default.focusColor", Color.class,
"default.hoverBorderColor", Color.class,
"default.pressedBorderColor", Color.class,
"toolbar.focusWidth", float.class,
"toolbar.focusColor", Color.class,

View File

@@ -305,6 +305,9 @@ public class TestFlatStyleableValue
testColor( c, ui, "buttonPressedArrowColor", 0x123456 );
testColor( c, ui, "popupBackground", 0x123456 );
testInsets( c, ui, "popupInsets", 1,2,3,4 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// border
flatRoundBorder( c, ui );
@@ -355,6 +358,7 @@ public class TestFlatStyleableValue
FlatLabelUI ui = (FlatLabelUI) c.getUI();
testColor( c, ui, "disabledForeground", 0x123456 );
testInteger( c, ui, "arc", 123 );
}
@Test
@@ -366,6 +370,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// FlatListCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -801,6 +807,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionForeground", 0x123456 );
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x901324 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
// FlatTableCellBorder
testInsets( c, ui, "cellMargins", 1,2,3,4 );
@@ -930,6 +938,8 @@ public class TestFlatStyleableValue
testColor( c, ui, "selectionInactiveBackground", 0x123456 );
testColor( c, ui, "selectionInactiveForeground", 0x123456 );
testColor( c, ui, "selectionBorderColor", 0x123456 );
testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 );
testBoolean( c, ui, "wideSelection", true );
testBoolean( c, ui, "showCellFocusIndicator", true );
@@ -955,12 +965,20 @@ public class TestFlatStyleableValue
testColor( c, ui, "disabledBorderColor", 0x123456 );
testColor( c, ui, "focusedBorderColor", 0x123456 );
testColor( c, ui, "hoverBorderColor", 0x123456 );
testColor( c, ui, "pressedBorderColor", 0x123456 );
testColor( c, ui, "selectedBorderColor", 0x123456 );
testColor( c, ui, "disabledSelectedBorderColor", 0x123456 );
testColor( c, ui, "focusedSelectedBorderColor", 0x123456 );
testColor( c, ui, "hoverSelectedBorderColor", 0x123456 );
testColor( c, ui, "pressedSelectedBorderColor", 0x123456 );
testFloat( c, ui, "default.borderWidth", 1.23f );
testColor( c, ui, "default.borderColor", 0x123456 );
testColor( c, ui, "default.focusedBorderColor", 0x123456 );
testColor( c, ui, "default.focusColor", 0x123456 );
testColor( c, ui, "default.hoverBorderColor", 0x123456 );
testColor( c, ui, "default.pressedBorderColor", 0x123456 );
testFloat( c, ui, "toolbar.focusWidth", 1.23f );
testColor( c, ui, "toolbar.focusColor", 0x123456 );
@@ -1025,12 +1043,20 @@ public class TestFlatStyleableValue
testValue( border, "disabledBorderColor", Color.WHITE );
testValue( border, "focusedBorderColor", Color.WHITE );
testValue( border, "hoverBorderColor", Color.WHITE );
testValue( border, "pressedBorderColor", Color.WHITE );
testValue( border, "selectedBorderColor", Color.WHITE );
testValue( border, "disabledSelectedBorderColor", Color.WHITE );
testValue( border, "focusedSelectedBorderColor", Color.WHITE );
testValue( border, "hoverSelectedBorderColor", Color.WHITE );
testValue( border, "pressedSelectedBorderColor", Color.WHITE );
testValue( border, "default.borderWidth", 2f );
testValue( border, "default.borderColor", Color.WHITE );
testValue( border, "default.focusedBorderColor", Color.WHITE );
testValue( border, "default.focusColor", Color.WHITE );
testValue( border, "default.hoverBorderColor", Color.WHITE );
testValue( border, "default.pressedBorderColor", Color.WHITE );
testValue( border, "toolbar.focusWidth", 1.5f );
testValue( border, "toolbar.focusColor", Color.WHITE );

View File

@@ -412,6 +412,7 @@ public class TestFlatStyling
FlatLabelUI ui = (FlatLabelUI) c.getUI();
ui.applyStyle( c, "disabledForeground: #fff" );
ui.applyStyle( c, "arc: 8" );
// JComponent properties
ui.applyStyle( c, "background: #fff" );
@@ -986,6 +987,8 @@ public class TestFlatStyling
ui.applyStyle( "selectionForeground: #fff" );
ui.applyStyle( "selectionInactiveBackground: #fff" );
ui.applyStyle( "selectionInactiveForeground: #fff" );
ui.applyStyle( "selectionInsets: 1,2,3,4" );
ui.applyStyle( "selectionArc: 8" );
// FlatTableCellBorder
ui.applyStyle( "cellMargins: 1,2,3,4" );
@@ -1294,12 +1297,20 @@ public class TestFlatStyling
border.applyStyleProperty( "disabledBorderColor", Color.WHITE );
border.applyStyleProperty( "focusedBorderColor", Color.WHITE );
border.applyStyleProperty( "hoverBorderColor", Color.WHITE );
border.applyStyleProperty( "pressedBorderColor", Color.WHITE );
border.applyStyleProperty( "selectedBorderColor", Color.WHITE );
border.applyStyleProperty( "disabledSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "focusedSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "hoverSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "pressedSelectedBorderColor", Color.WHITE );
border.applyStyleProperty( "default.borderWidth", 2 );
border.applyStyleProperty( "default.borderColor", Color.WHITE );
border.applyStyleProperty( "default.focusedBorderColor", Color.WHITE );
border.applyStyleProperty( "default.focusColor", Color.WHITE );
border.applyStyleProperty( "default.hoverBorderColor", Color.WHITE );
border.applyStyleProperty( "default.pressedBorderColor", Color.WHITE );
border.applyStyleProperty( "toolbar.focusWidth", 1.5f );
border.applyStyleProperty( "toolbar.focusColor", Color.WHITE );

View File

@@ -140,8 +140,8 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F11, NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F12, MetalLookAndFeel.class.getName() );
// register Alt+UP and Alt+DOWN to switch to previous/next theme
((JComponent)frame.getContentPane()).registerKeyboardAction(
@@ -153,6 +153,9 @@ class ControlBar
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
// register Alt+Shift+F1, F2, ... keys to change system scale factor
DemoPrefs.registerSystemScaleFactors( frame );
// register ESC key to close frame
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> {

View File

@@ -25,6 +25,8 @@ import javax.swing.*;
import javax.swing.table.*;
import javax.swing.tree.*;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.ColorFunctions;
import net.miginfocom.swing.*;
/**
@@ -93,10 +95,12 @@ class DataComponentsPanel
private void rowSelectionChanged() {
table1.setRowSelectionAllowed( rowSelectionCheckBox.isSelected() );
roundedSelectionChanged();
}
private void columnSelectionChanged() {
table1.setColumnSelectionAllowed( columnSelectionCheckBox.isSelected() );
roundedSelectionChanged();
}
private void showHorizontalLinesChanged() {
@@ -127,6 +131,28 @@ class DataComponentsPanel
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
}
private void roundedSelectionChanged() {
String style = null;
if( roundedSelectionCheckBox.isSelected() ) {
style = rowSelectionCheckBox.isSelected()
? "selectionArc: 6; selectionInsets: 0,1,0,1"
: "selectionArc: 6";
}
table1.putClientProperty( FlatClientProperties.STYLE, style );
}
private void alternatingRowsChanged() {
Color alternateRowColor = null;
if( alternatingRowsCheckBox.isSelected() ) {
Color background = table1.getBackground();
alternateRowColor = FlatLaf.isLafDark()
? ColorFunctions.lighten( background, 0.05f )
: ColorFunctions.darken( background, 0.05f );
}
UIManager.put( "Table.alternateRowColor", alternateRowColor );
table1.repaint();
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
@@ -151,12 +177,14 @@ class DataComponentsPanel
JScrollPane scrollPane5 = new JScrollPane();
table1 = new JTable();
JPanel tableOptionsPanel = new JPanel();
roundedSelectionCheckBox = new JCheckBox();
showHorizontalLinesCheckBox = new JCheckBox();
showVerticalLinesCheckBox = new JCheckBox();
intercellSpacingCheckBox = new JCheckBox();
redGridColorCheckBox = new JCheckBox();
rowSelectionCheckBox = new JCheckBox();
columnSelectionCheckBox = new JCheckBox();
alternatingRowsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPopupMenu popupMenu2 = new JPopupMenu();
JMenuItem menuItem3 = new JMenuItem();
@@ -403,44 +431,56 @@ class DataComponentsPanel
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0" +
"[]0"));
//---- roundedSelectionCheckBox ----
roundedSelectionCheckBox.setText("rounded selection");
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
tableOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
//---- showHorizontalLinesCheckBox ----
showHorizontalLinesCheckBox.setText("show horizontal lines");
showHorizontalLinesCheckBox.addActionListener(e -> showHorizontalLinesChanged());
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 0");
tableOptionsPanel.add(showHorizontalLinesCheckBox, "cell 0 1");
//---- showVerticalLinesCheckBox ----
showVerticalLinesCheckBox.setText("show vertical lines");
showVerticalLinesCheckBox.addActionListener(e -> showVerticalLinesChanged());
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 1");
tableOptionsPanel.add(showVerticalLinesCheckBox, "cell 0 2");
//---- intercellSpacingCheckBox ----
intercellSpacingCheckBox.setText("intercell spacing");
intercellSpacingCheckBox.addActionListener(e -> intercellSpacingChanged());
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 2");
tableOptionsPanel.add(intercellSpacingCheckBox, "cell 0 3");
//---- redGridColorCheckBox ----
redGridColorCheckBox.setText("red grid color");
redGridColorCheckBox.addActionListener(e -> redGridColorChanged());
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 3");
tableOptionsPanel.add(redGridColorCheckBox, "cell 0 4");
//---- rowSelectionCheckBox ----
rowSelectionCheckBox.setText("row selection");
rowSelectionCheckBox.setSelected(true);
rowSelectionCheckBox.addActionListener(e -> rowSelectionChanged());
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 4");
tableOptionsPanel.add(rowSelectionCheckBox, "cell 0 5");
//---- columnSelectionCheckBox ----
columnSelectionCheckBox.setText("column selection");
columnSelectionCheckBox.addActionListener(e -> columnSelectionChanged());
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 5");
tableOptionsPanel.add(columnSelectionCheckBox, "cell 0 6");
//---- alternatingRowsCheckBox ----
alternatingRowsCheckBox.setText("alternating rows");
alternatingRowsCheckBox.addActionListener(e -> alternatingRowsChanged());
tableOptionsPanel.add(alternatingRowsCheckBox, "cell 0 7");
//---- dndCheckBox ----
dndCheckBox.setText("enable drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
tableOptionsPanel.add(dndCheckBox, "cell 0 6");
tableOptionsPanel.add(dndCheckBox, "cell 0 8");
}
add(tableOptionsPanel, "cell 4 3");
@@ -477,12 +517,14 @@ class DataComponentsPanel
private JTree tree3;
private JTree tree2;
private JTable table1;
private JCheckBox roundedSelectionCheckBox;
private JCheckBox showHorizontalLinesCheckBox;
private JCheckBox showVerticalLinesCheckBox;
private JCheckBox intercellSpacingCheckBox;
private JCheckBox redGridColorCheckBox;
private JCheckBox rowSelectionCheckBox;
private JCheckBox columnSelectionCheckBox;
private JCheckBox alternatingRowsCheckBox;
private JCheckBox dndCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -343,9 +343,19 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[]"
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0"
"$rowConstraints": "[]0[]0[]0[]0[]0[]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "roundedSelectionCheckBox"
"text": "rounded selection"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showHorizontalLinesCheckBox"
"text": "show horizontal lines"
@@ -354,7 +364,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showHorizontalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showVerticalLinesCheckBox"
@@ -364,7 +374,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showVerticalLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "intercellSpacingCheckBox"
@@ -374,7 +384,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "intercellSpacingChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "redGridColorCheckBox"
@@ -384,7 +394,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "redGridColorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rowSelectionCheckBox"
@@ -395,7 +405,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "rowSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "columnSelectionCheckBox"
@@ -405,7 +415,17 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "columnSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "alternatingRowsCheckBox"
"text": "alternating rows"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alternatingRowsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
@@ -416,7 +436,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
"value": "cell 0 8"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3"

View File

@@ -19,6 +19,10 @@ package com.formdev.flatlaf.demo;
import java.io.File;
import java.io.FileInputStream;
import java.util.prefs.Preferences;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
@@ -27,6 +31,7 @@ import com.formdev.flatlaf.IntelliJTheme;
import com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
@@ -35,6 +40,7 @@ public class DemoPrefs
{
public static final String KEY_LAF = "laf";
public static final String KEY_LAF_THEME = "lafTheme";
public static final String KEY_SYSTEM_SCALE_FACTOR = "systemScaleFactor";
public static final String RESOURCE_PREFIX = "res:";
public static final String FILE_PREFIX = "file:";
@@ -96,4 +102,66 @@ public class DemoPrefs
state.put( KEY_LAF, UIManager.getLookAndFeel().getClass().getName() );
} );
}
public static void initSystemScale() {
if( System.getProperty( "sun.java2d.uiScale" ) == null ) {
String scaleFactor = getState().get( KEY_SYSTEM_SCALE_FACTOR, null );
if( scaleFactor != null )
System.setProperty( "sun.java2d.uiScale", scaleFactor );
}
}
/**
* register Alt+Shift+F1, F2, ... F12 keys to change system scale factor
*/
public static void registerSystemScaleFactors( JFrame frame ) {
registerSystemScaleFactor( frame, "alt shift F1", null );
registerSystemScaleFactor( frame, "alt shift F2", "1" );
if( SystemInfo.isWindows ) {
registerSystemScaleFactor( frame, "alt shift F3", "1.25" );
registerSystemScaleFactor( frame, "alt shift F4", "1.5" );
registerSystemScaleFactor( frame, "alt shift F5", "1.75" );
registerSystemScaleFactor( frame, "alt shift F6", "2" );
registerSystemScaleFactor( frame, "alt shift F7", "2.25" );
registerSystemScaleFactor( frame, "alt shift F8", "2.5" );
registerSystemScaleFactor( frame, "alt shift F9", "2.75" );
registerSystemScaleFactor( frame, "alt shift F10", "3" );
registerSystemScaleFactor( frame, "alt shift F11", "3.5" );
registerSystemScaleFactor( frame, "alt shift F12", "4" );
} else {
// Java on macOS and Linux supports only integer scale factors
registerSystemScaleFactor( frame, "alt shift F3", "2" );
registerSystemScaleFactor( frame, "alt shift F4", "3" );
registerSystemScaleFactor( frame, "alt shift F5", "4" );
}
}
private static void registerSystemScaleFactor( JFrame frame, String keyStrokeStr, String scaleFactor ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
if( keyStroke == null )
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
((JComponent)frame.getContentPane()).registerKeyboardAction(
e -> applySystemScaleFactor( frame, scaleFactor ),
keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
}
private static void applySystemScaleFactor( JFrame frame, String scaleFactor ) {
if( JOptionPane.showConfirmDialog( frame,
"Change system scale factor to "
+ (scaleFactor != null ? scaleFactor : "default")
+ " and exit?",
frame.getTitle(), JOptionPane.YES_NO_OPTION ) != JOptionPane.YES_OPTION )
return;
if( scaleFactor != null )
DemoPrefs.getState().put( KEY_SYSTEM_SCALE_FACTOR, scaleFactor );
else
DemoPrefs.getState().remove( KEY_SYSTEM_SCALE_FACTOR );
System.exit( 0 );
}
}

View File

@@ -70,9 +70,10 @@ public class FlatLafDemo
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
System.setProperty( "flatlaf.uiScale", "2x" );
SwingUtilities.invokeLater( () -> {
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.initSystemScale();
SwingUtilities.invokeLater( () -> {
// install fonts for lazy loading
FlatInterFont.installLazy();
FlatJetBrainsMonoFont.installLazy();
@@ -95,6 +96,9 @@ public class FlatLafDemo
// use Roboto Mono font
// FlatLaf.setPreferredMonospacedFontFamily( FlatRobotoMonoFont.FAMILY );
// install own repaint manager to fix repaint issues at 125%, 175%, 225%, ... on Windows
// HiDPIUtils.installHiDPIRepaintManager();
// application specific UI defaults
FlatLaf.registerCustomDefaultsSource( "com.formdev.flatlaf.demo" );

View File

@@ -118,6 +118,14 @@ class MoreComponentsPanel
JLabel label5 = new JLabel();
JPanel panel13 = new JPanel();
JLabel label6 = new JLabel();
JLabel panelLabel = new JLabel();
JPanel panel5 = new JPanel();
JLabel label9 = new JLabel();
JPanel panel4 = new JPanel();
JLabel label8 = new JLabel();
JLabel labelLabel = new JLabel();
JLabel label13 = new JLabel();
JLabel label10 = new JLabel();
//======== this ========
setLayout(new MigLayout(
@@ -140,7 +148,9 @@ class MoreComponentsPanel
"[]" +
"[]" +
"[]" +
"[100,top]"));
"[100,top]" +
"[50,top]" +
"[]"));
//---- scrollPaneLabel ----
scrollPaneLabel.setText("JScrollPane:");
@@ -441,7 +451,7 @@ class MoreComponentsPanel
//======== panel10 ========
{
panel10.setBackground(new Color(217, 163, 67));
panel10.setBackground(new Color(0xd9a343));
panel10.setLayout(new BorderLayout());
//---- label1 ----
@@ -454,7 +464,7 @@ class MoreComponentsPanel
//======== panel11 ========
{
panel11.setBackground(new Color(98, 181, 67));
panel11.setBackground(new Color(0x62b543));
panel11.setLayout(new BorderLayout());
//---- label2 ----
@@ -474,7 +484,7 @@ class MoreComponentsPanel
//======== panel12 ========
{
panel12.setBackground(new Color(242, 101, 34));
panel12.setBackground(new Color(0xf26522));
panel12.setLayout(new BorderLayout());
//---- label5 ----
@@ -487,7 +497,7 @@ class MoreComponentsPanel
//======== panel13 ========
{
panel13.setBackground(new Color(64, 182, 224));
panel13.setBackground(new Color(0x40b6e0));
panel13.setLayout(new BorderLayout());
//---- label6 ----
@@ -502,6 +512,52 @@ class MoreComponentsPanel
}
add(splitPane3, "cell 1 11 4 1,grow");
//---- panelLabel ----
panelLabel.setText("JPanel:");
add(panelLabel, "cell 0 12");
//======== panel5 ========
{
panel5.putClientProperty("FlatLaf.style", "arc: 16; background: darken($Panel.background,5%)");
panel5.setLayout(new BorderLayout());
//---- label9 ----
label9.setText("rounded background");
label9.setHorizontalAlignment(SwingConstants.CENTER);
panel5.add(label9, BorderLayout.CENTER);
}
add(panel5, "cell 1 12 4 1,growy,width 150");
//======== panel4 ========
{
panel4.putClientProperty("FlatLaf.style", "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)");
panel4.setLayout(new BorderLayout());
//---- label8 ----
label8.setText("rounded border");
label8.setHorizontalAlignment(SwingConstants.CENTER);
panel4.add(label8, BorderLayout.CENTER);
}
add(panel4, "cell 1 12 4 1,growy,width 150");
//---- labelLabel ----
labelLabel.setText("JLabel:");
add(labelLabel, "cell 0 13");
//---- label13 ----
label13.setText("rounded background");
label13.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10");
label13.setBackground(new Color(0xb8e4f3));
label13.setForeground(new Color(0x135b76));
add(label13, "cell 1 13 4 1");
//---- label10 ----
label10.setText("rounded border");
label10.putClientProperty("FlatLaf.style", "arc: 999; border: 2,10,2,10,#135b76");
label10.setBackground(new Color(0xb8e4f3));
label10.setForeground(new Color(0x135b76));
add(label10, "cell 1 13 4 1");
//---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup();
buttonGroup1.add(toggleButton1);

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[][][][][]"
"$rowConstraints": "[][][][][][][][][][][][100,top]"
"$rowConstraints": "[][][][][][][][][][][][100,top][50,top][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -467,6 +467,62 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11 4 1,grow"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "panelLabel"
"text": "JPanel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel5"
"$client.FlatLaf.style": "arc: 16; background: darken($Panel.background,5%)"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"text": "rounded background"
"horizontalAlignment": 0
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12 4 1,growy,width 150"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "panel4"
"$client.FlatLaf.style": "border: 1,1,1,1,@disabledForeground,1,16; background: darken($Panel.background,5%)"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"text": "rounded border"
"horizontalAlignment": 0
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "Center"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12 4 1,growy,width 150"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "labelLabel"
"text": "JLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label13"
"text": "rounded background"
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10"
"background": new java.awt.Color( 184, 228, 243, 255 )
"foreground": new java.awt.Color( 19, 91, 118, 255 )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13 4 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"text": "rounded border"
"$client.FlatLaf.style": "arc: 999; border: 2,10,2,10,#135b76"
"background": new java.awt.Color( 184, 228, 243, 255 )
"foreground": new java.awt.Color( 19, 91, 118, 255 )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13 4 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 700, 550 )
@@ -474,7 +530,7 @@ new FormModel {
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "buttonGroup1"
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 560 )
"location": new java.awt.Point( 0, 600 )
} )
}
}

View File

@@ -547,6 +547,12 @@ public class FlatInspector
appendRow( buf, "Left-to-right", String.valueOf( c.getComponentOrientation().isLeftToRight() ) );
appendRow( buf, "Parent", (c.getParent() != null ? toString( c.getParent().getClass(), classHierarchy ) : "null") );
if( c instanceof JComponent ) {
Object style = ((JComponent)c).getClientProperty( FlatClientProperties.STYLE );
if( style != null )
appendRow( buf, "FlatLaf Style", style.toString() );
}
// append parent level
buf.append( "<tr><td colspan=\"2\">" );
if( parentLevel > 0 )

View File

@@ -28,10 +28,10 @@ Otherwise, download `flatlaf-intellij-themes-<version>.jar` here:
How to use?
-----------
Choose a theme (see list below) and invoke its `install` method. E.g.:
Choose a theme (see list below) and invoke its `setup` method. E.g.:
~~~java
FlatArcOrangeIJTheme.install();
FlatArcOrangeIJTheme.setup();
~~~

View File

@@ -16,9 +16,14 @@
package com.formdev.flatlaf.jideoss.ui;
import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import com.formdev.flatlaf.ui.FlatHTML;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideButtonListener;
import com.jidesoft.plaf.basic.BasicJideButtonUI;
/**
@@ -37,4 +42,26 @@ public class FlatJideButtonUI
return new FlatJideButtonUI();
}
@Override
protected BasicButtonListener createButtonListener( AbstractButton b ) {
return new FlatJideButtonListener( b );
}
//---- class FlatJideButtonListener ---------------------------------------
/** @since 3.5 */
protected static class FlatJideButtonListener
extends BasicJideButtonListener
{
protected FlatJideButtonListener( AbstractButton b ) {
super( b );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
}
}

View File

@@ -18,11 +18,13 @@ package com.formdev.flatlaf.jideoss.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.basic.BasicJideLabelUI;
@@ -65,6 +67,20 @@ public class FlatJideLabelUI
defaults_initialized = false;
}
@Override
protected void installComponents( JLabel c ) {
super.installComponents( c );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -60,8 +60,11 @@ import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinUser.HMONITOR;
import com.sun.jna.platform.win32.WinUser.WindowProc;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
//
@@ -615,6 +618,14 @@ public class FlatWindowsNativeWindowBorder
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params.rgrc[0].right--;
}
} else if( SystemInfo.isWindows_11_orLater ) {
// For Windows 11, add border thickness to top, which is necessary to make the whole Java area visible.
// This also avoids that a black line is sometimes painted on top window border.
// Note: Do not increase top on Windows 10 because this would not hide Windows title bar.
IntByReference borderThickness = new IntByReference();
if( DWMApi.INSTANCE.DwmGetWindowAttribute( hwnd, DWMApi.DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
borderThickness.getPointer(), 4 ) == WinError.S_OK.intValue() )
params.rgrc[0].top += borderThickness.getValue();
}
// write changed params back to native memory
@@ -898,6 +909,18 @@ public class FlatWindowsNativeWindowBorder
HBRUSH CreateSolidBrush( DWORD color );
}
//---- interface DWMApi ---------------------------------------------------
private interface DWMApi
extends StdCallLibrary
{
DWMApi INSTANCE = Native.load( "dwmapi", DWMApi.class, W32APIOptions.DEFAULT_OPTIONS );
int DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;
int DwmGetWindowAttribute( HWND hwnd, int dwAttribute, Pointer pvAttribute, int cbAttribute );
}
//---- class NCCALCSIZE_PARAMS --------------------------------------------
@FieldOrder( { "rgrc" } )

View File

@@ -20,6 +20,7 @@
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <dwmapi.h>
#include <jawt.h>
#include <jawt_md.h>
#include "FlatWndProc.h"
@@ -76,6 +77,7 @@ jmethodID FlatWndProc::isFullscreenMID;
jmethodID FlatWndProc::fireStateChangedLaterOnceMID;
HWNDMap* FlatWndProc::hwndMap;
DWORD FlatWndProc::osBuildNumber = 0;
#define java_awt_Frame_ICONIFIED 1
#define java_awt_Frame_MAXIMIZED_BOTH (4 | 2)
@@ -107,6 +109,14 @@ HWND FlatWndProc::install( JNIEnv *env, jobject obj, jobject window ) {
return 0;
}
// get OS build number
if( osBuildNumber == 0 ) {
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof( info );
if( ::GetVersionEx( &info ) )
osBuildNumber = info.dwBuildNumber;
}
// get window handle
HWND hwnd = getWindowHandle( env, window );
if( hwnd == NULL || hwndMap->get( hwnd ) != NULL )
@@ -391,6 +401,13 @@ LRESULT FlatWndProc::WmNcCalcSize( HWND hwnd, int uMsg, WPARAM wParam, LPARAM lP
if( hasAutohideTaskbar( ABE_RIGHT, monitorInfo.rcMonitor ) )
params->rgrc[0].right--;
}
} else if( osBuildNumber >= 22000 ) {
// For Windows 11, add border thickness to top, which is necessary to make the whole Java area visible.
// This also avoids that a black line is sometimes painted on top window border.
// Note: Do not increase top on Windows 10 because this would not hide Windows title bar.
UINT borderThickness = 0;
if( ::DwmGetWindowAttribute( hwnd, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &borderThickness, sizeof( borderThickness ) ) == S_OK )
params->rgrc[0].top += borderThickness;
}
return lResult;

View File

@@ -35,6 +35,7 @@ private:
static jmethodID fireStateChangedLaterOnceMID;
static HWNDMap* hwndMap;
static DWORD osBuildNumber;
JavaVM* jvm;
JNIEnv* env; // attached to AWT-Windows/Win32 thread

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.swingx.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
@@ -25,6 +26,7 @@ import javax.swing.plaf.ComponentUI;
import org.jdesktop.swingx.JXBusyLabel;
import org.jdesktop.swingx.plaf.basic.BasicBusyLabelUI;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
//TODO scale busy spinner
@@ -70,6 +72,20 @@ public class FlatBusyLabelUI
disabledForeground = null;
}
@Override
protected void installComponents( JLabel c ) {
super.installComponents( c );
// update HTML renderer if necessary
FlatHTML.updateRendererCSSFontBaseSize( c );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
@Override
protected void paintDisabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;

View File

@@ -21,12 +21,15 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonListener;
import org.jdesktop.swingx.plaf.basic.BasicHyperlinkUI;
import com.formdev.flatlaf.ui.FlatButtonUI;
import com.formdev.flatlaf.ui.FlatHTML;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale;
@@ -60,6 +63,11 @@ public class FlatHyperlinkUI
disabledText = null;
}
@Override
protected BasicButtonListener createButtonListener( AbstractButton b ) {
return new FlatHyperlinkListener( b );
}
@Override
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
FlatButtonUI.paintText( g, b, textRect, text, b.isEnabled() ? b.getForeground() : disabledText );
@@ -79,4 +87,21 @@ public class FlatHyperlinkUI
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
//---- class FlatHyperlinkListener ----------------------------------------
/** @since 3.5 */
protected static class FlatHyperlinkListener
extends BasicHyperlinkListener
{
protected FlatHyperlinkListener( AbstractButton b ) {
super( b );
}
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
FlatHTML.propertyChange( e );
}
}
}

View File

@@ -79,6 +79,7 @@ Button.default.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.Colo
Button.default.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedBorderColor #8888ff HSL 240 100 77 javax.swing.plaf.ColorUIResource [UI]
Button.default.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Button.default.startBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
@@ -87,6 +88,7 @@ Button.disabledBackground #e0e0e0 HSL 0 0 88 javax.swing.plaf.Colo
Button.disabledBorderColor #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBackground #112233 HSL 210 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedBorderColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.disabledSelectedForeground #ffcccc HSL 0 100 90 javax.swing.plaf.ColorUIResource [UI]
Button.disabledText #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
Button.endBackground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
@@ -94,21 +96,26 @@ Button.endBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.Colo
Button.focusedBackground #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedBorderColor #466d94 HSL 210 36 43 javax.swing.plaf.ColorUIResource [UI]
Button.focusedForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.focusedSelectedBorderColor #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.font [active] $defaultFont [UI]
Button.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverBorderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.hoverSelectedBorderColor #aaaaaa HSL 0 0 67 javax.swing.plaf.ColorUIResource [UI]
Button.iconTextGap 4
Button.innerFocusWidth 1
Button.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI]
Button.margin 2,14,2,14 javax.swing.plaf.InsetsUIResource [UI]
Button.minimumWidth 72
Button.pressedBackground #ffc800 HSL 47 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedBorderColor #00ffff HSL 180 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedForeground #0080ff HSL 210 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.pressedSelectedBorderColor #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI]
Button.rollover true
Button.selectedBackground #ffbbbb HSL 0 100 87 javax.swing.plaf.ColorUIResource [UI]
Button.selectedBorderColor #ff00ff HSL 300 100 50 javax.swing.plaf.ColorUIResource [UI]
Button.selectedForeground #332211 HSL 30 50 13 javax.swing.plaf.ColorUIResource [UI]
Button.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI]
Button.startBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]

View File

@@ -47,6 +47,7 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatMenuArrowIcon;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
import com.formdev.flatlaf.ui.FlatListUI;
import com.formdev.flatlaf.ui.FlatTableUI;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
import com.jidesoft.swing.CheckBoxTreeCellRenderer;
@@ -131,6 +132,14 @@ public class FlatComponents2Test
initTableEditors( table1 );
initTableEditors( xTable1 );
// table selection listeners
table1.getSelectionModel().addListSelectionListener( e -> {
System.out.printf( "row sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
} );
table1.getColumnModel().getSelectionModel().addListSelectionListener( e -> {
System.out.printf( "column sel %d-%d adj=%b\n", e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting() );
} );
// JXTable
Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
PatternPredicate patternPredicate = new PatternPredicate( "^J", 2 );
@@ -164,6 +173,8 @@ public class FlatComponents2Test
JComboBox<String> editableComboBox = new JComboBox<>( months );
editableComboBox.setEditable( true );
cm.getColumn(3).setCellEditor( new DefaultCellEditor( editableComboBox ) );
// table.setDefaultRenderer( Object.class, new TestLabelRoundedTableCellRenderer() );
}
private void expandTree( JTree tree ) {
@@ -229,6 +240,65 @@ public class FlatComponents2Test
FlatLaf.updateUILater();
}
private void roundedSelectionChanged() {
String style = roundedSelectionCheckBox.isSelected() ? "selectionArc: 12; " : "";
int left = leftSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int right = rightSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int top = topSelectionInsetsCheckBox.isSelected() ? 2 : 0;
int bottom = bottomSelectionInsetsCheckBox.isSelected() ? 2 : 0;
if( left > 0 || right > 0 || top > 0 || bottom > 0 )
style += "selectionInsets: " + top + ',' + left + ',' + bottom + ',' + right;
if( style.isEmpty() )
style = null;
list1.putClientProperty( FlatClientProperties.STYLE, style );
list2.putClientProperty( FlatClientProperties.STYLE, style );
tree1.putClientProperty( FlatClientProperties.STYLE, style );
tree2.putClientProperty( FlatClientProperties.STYLE, style );
xTree1.putClientProperty( FlatClientProperties.STYLE, style );
checkBoxTree1.putClientProperty( FlatClientProperties.STYLE, style );
table1.putClientProperty( FlatClientProperties.STYLE, style );
xTable1.putClientProperty( FlatClientProperties.STYLE, style );
xTreeTable1.putClientProperty( FlatClientProperties.STYLE, style );
// initial selection
if( style != null ) {
initSelection( list1 );
initSelection( list2 );
initSelection( tree1 );
initSelection( tree2 );
initSelection( xTree1 );
initSelection( checkBoxTree1 );
initSelection( table1 );
initSelection( xTable1 );
initSelection( xTreeTable1 );
}
if( paintOutsideAlternateRowsCheckBox.isSelected() )
table1ScrollPane.repaint();
}
private static void initSelection( JList<?> list ) {
if( list.isSelectionEmpty() ) {
list.addSelectionInterval( 1, 2 );
list.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTree tree ) {
if( tree.isSelectionEmpty() ) {
tree.addSelectionInterval( 1, 2 );
tree.addSelectionInterval( 5, 5 );
}
}
private static void initSelection( JTable table ) {
if( table.getSelectedRowCount() == 0 ) {
table.addRowSelectionInterval( 1, 2 );
table.addRowSelectionInterval( 5, 5 );
}
}
private void dndChanged() {
boolean dnd = dndCheckBox.isSelected();
list1.setDragEnabled( dnd );
@@ -535,6 +605,12 @@ public class FlatComponents2Test
public void applyComponentOrientation( ComponentOrientation o ) {
super.applyComponentOrientation( o );
// always use left-to-right for options panels
generalOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
listOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
treeOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
tableOptionsPanel.applyComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT );
// swap upper right and left corners (other corners are not used in this app)
Component leftCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_LEFT_CORNER );
Component rightCorner = table1ScrollPane.getCorner( ScrollPaneConstants.UPPER_RIGHT_CORNER );
@@ -596,16 +672,22 @@ public class FlatComponents2Test
JLabel label2 = new JLabel();
xTreeTable1ScrollPane = new JScrollPane();
xTreeTable1 = new JXTreeTable();
JPanel panel5 = new JPanel();
generalOptionsPanel = new JPanel();
roundedSelectionCheckBox = new JCheckBox();
JLabel label6 = new JLabel();
topSelectionInsetsCheckBox = new JCheckBox();
bottomSelectionInsetsCheckBox = new JCheckBox();
leftSelectionInsetsCheckBox = new JCheckBox();
rightSelectionInsetsCheckBox = new JCheckBox();
dndCheckBox = new JCheckBox();
JPanel panel6 = new JPanel();
listOptionsPanel = new JPanel();
JLabel listRendererLabel = new JLabel();
listRendererComboBox = new JComboBox<>();
JLabel listLayoutOrientationLabel = new JLabel();
listLayoutOrientationField = new JComboBox<>();
JLabel listVisibleRowCountLabel = new JLabel();
listVisibleRowCountSpinner = new JSpinner();
JPanel treeOptionsPanel = new JPanel();
treeOptionsPanel = new JPanel();
JLabel treeRendererLabel = new JLabel();
treeRendererComboBox = new JComboBox<>();
treeWideSelectionCheckBox = new JCheckBox();
@@ -614,7 +696,7 @@ public class FlatComponents2Test
treeRedLinesCheckBox = new JCheckBox();
treeEditableCheckBox = new JCheckBox();
treeShowDefaultIconsCheckBox = new JCheckBox();
JPanel tableOptionsPanel = new JPanel();
tableOptionsPanel = new JPanel();
JLabel autoResizeModeLabel = new JLabel();
autoResizeModeField = new JComboBox<>();
JLabel sortIconPositionLabel = new JLabel();
@@ -872,30 +954,68 @@ public class FlatComponents2Test
}
add(xTreeTable1ScrollPane, "cell 4 3 2 1");
//======== panel5 ========
//======== generalOptionsPanel ========
{
panel5.setBorder(new TitledBorder("General Control"));
panel5.putClientProperty("FlatLaf.internal.testing.ignore", true);
panel5.setLayout(new MigLayout(
"hidemode 3",
generalOptionsPanel.setBorder(new TitledBorder("General Control"));
generalOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
generalOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]",
"[left]",
// rows
"[]" +
"[]0" +
"[]0" +
"[]rel" +
"[]"));
//---- roundedSelectionCheckBox ----
roundedSelectionCheckBox.setText("rounded selection");
roundedSelectionCheckBox.setMnemonic('D');
roundedSelectionCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(roundedSelectionCheckBox, "cell 0 0");
//---- label6 ----
label6.setText("Selection insets:");
generalOptionsPanel.add(label6, "cell 0 1");
//---- topSelectionInsetsCheckBox ----
topSelectionInsetsCheckBox.setText("top");
topSelectionInsetsCheckBox.setMnemonic('D');
topSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(topSelectionInsetsCheckBox, "cell 0 2,gapx ind");
//---- bottomSelectionInsetsCheckBox ----
bottomSelectionInsetsCheckBox.setText("bottom");
bottomSelectionInsetsCheckBox.setMnemonic('D');
bottomSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(bottomSelectionInsetsCheckBox, "cell 0 2");
//---- leftSelectionInsetsCheckBox ----
leftSelectionInsetsCheckBox.setText("left");
leftSelectionInsetsCheckBox.setMnemonic('D');
leftSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(leftSelectionInsetsCheckBox, "cell 0 3,gapx ind");
//---- rightSelectionInsetsCheckBox ----
rightSelectionInsetsCheckBox.setText("right");
rightSelectionInsetsCheckBox.setMnemonic('D');
rightSelectionInsetsCheckBox.addActionListener(e -> roundedSelectionChanged());
generalOptionsPanel.add(rightSelectionInsetsCheckBox, "cell 0 3");
//---- dndCheckBox ----
dndCheckBox.setText("drag and drop");
dndCheckBox.setMnemonic('D');
dndCheckBox.addActionListener(e -> dndChanged());
panel5.add(dndCheckBox, "cell 0 0");
generalOptionsPanel.add(dndCheckBox, "cell 0 4");
}
add(panel5, "cell 0 4 4 1");
add(generalOptionsPanel, "cell 0 4 4 1");
//======== panel6 ========
//======== listOptionsPanel ========
{
panel6.setBorder(new TitledBorder("JList Control"));
panel6.setLayout(new MigLayout(
"hidemode 3",
listOptionsPanel.setBorder(new TitledBorder("JList Control"));
listOptionsPanel.setLayout(new MigLayout(
"insets 8,hidemode 3",
// columns
"[fill]" +
"[fill]",
@@ -906,7 +1026,7 @@ public class FlatComponents2Test
//---- listRendererLabel ----
listRendererLabel.setText("Renderer:");
panel6.add(listRendererLabel, "cell 0 0");
listOptionsPanel.add(listRendererLabel, "cell 0 0");
//---- listRendererComboBox ----
listRendererComboBox.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -916,11 +1036,11 @@ public class FlatComponents2Test
"labelRounded"
}));
listRendererComboBox.addActionListener(e -> listRendererChanged());
panel6.add(listRendererComboBox, "cell 1 0");
listOptionsPanel.add(listRendererComboBox, "cell 1 0");
//---- listLayoutOrientationLabel ----
listLayoutOrientationLabel.setText("Orientation:");
panel6.add(listLayoutOrientationLabel, "cell 0 1");
listOptionsPanel.add(listLayoutOrientationLabel, "cell 0 1");
//---- listLayoutOrientationField ----
listLayoutOrientationField.setModel(new DefaultComboBoxModel<>(new String[] {
@@ -929,25 +1049,25 @@ public class FlatComponents2Test
"horzontal wrap"
}));
listLayoutOrientationField.addActionListener(e -> listLayoutOrientationChanged());
panel6.add(listLayoutOrientationField, "cell 1 1");
listOptionsPanel.add(listLayoutOrientationField, "cell 1 1");
//---- listVisibleRowCountLabel ----
listVisibleRowCountLabel.setText("Visible row count:");
panel6.add(listVisibleRowCountLabel, "cell 0 2");
listOptionsPanel.add(listVisibleRowCountLabel, "cell 0 2");
//---- listVisibleRowCountSpinner ----
listVisibleRowCountSpinner.setModel(new SpinnerNumberModel(8, 0, null, 1));
listVisibleRowCountSpinner.addChangeListener(e -> listVisibleRowCountChanged());
panel6.add(listVisibleRowCountSpinner, "cell 1 2");
listOptionsPanel.add(listVisibleRowCountSpinner, "cell 1 2");
}
add(panel6, "cell 0 4 4 1");
add(listOptionsPanel, "cell 0 4 4 1");
//======== treeOptionsPanel ========
{
treeOptionsPanel.setBorder(new TitledBorder("JTree Control"));
treeOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
treeOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[left]",
// rows
@@ -1014,7 +1134,7 @@ public class FlatComponents2Test
tableOptionsPanel.setBorder(new TitledBorder("JTable Control"));
tableOptionsPanel.putClientProperty("FlatLaf.internal.testing.ignore", true);
tableOptionsPanel.setLayout(new MigLayout(
"hidemode 3",
"insets 8,hidemode 3",
// columns
"[]" +
"[fill]" +
@@ -1132,10 +1252,18 @@ public class FlatComponents2Test
private CheckBoxTree checkBoxTree1;
private JScrollPane xTreeTable1ScrollPane;
private JXTreeTable xTreeTable1;
private JPanel generalOptionsPanel;
private JCheckBox roundedSelectionCheckBox;
private JCheckBox topSelectionInsetsCheckBox;
private JCheckBox bottomSelectionInsetsCheckBox;
private JCheckBox leftSelectionInsetsCheckBox;
private JCheckBox rightSelectionInsetsCheckBox;
private JCheckBox dndCheckBox;
private JPanel listOptionsPanel;
private JComboBox<String> listRendererComboBox;
private JComboBox<String> listLayoutOrientationField;
private JSpinner listVisibleRowCountSpinner;
private JPanel treeOptionsPanel;
private JComboBox<String> treeRendererComboBox;
private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treePaintSelectionCheckBox;
@@ -1143,6 +1271,7 @@ public class FlatComponents2Test
private JCheckBox treeRedLinesCheckBox;
private JCheckBox treeEditableCheckBox;
private JCheckBox treeShowDefaultIconsCheckBox;
private JPanel tableOptionsPanel;
private JComboBox<String> autoResizeModeField;
private JComboBox<String> sortIconPositionComboBox;
private JCheckBox showHorizontalLinesCheckBox;
@@ -1683,4 +1812,46 @@ public class FlatComponents2Test
return this;
}
}
//---- class TestLabelRoundedTableCellRenderer ----------------------------
@SuppressWarnings( "unused" )
private static class TestLabelRoundedTableCellRenderer
extends JLabel
implements TableCellRenderer
{
private JTable table;
private int row;
private int column;
private boolean isSelected;
TestLabelRoundedTableCellRenderer() {
setBorder( new FlatEmptyBorder( 1, 6, 1, 6 ) );
}
@Override
public Component getTableCellRendererComponent( JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column )
{
this.table = table;
this.row = row;
this.column = column;
this.isSelected = isSelected;
setText( String.valueOf( value ) );
setBackground( isSelected ? Color.green : table.getBackground() );
setForeground( isSelected ? Color.blue : table.getForeground() );
return this;
}
@Override
protected void paintComponent( Graphics g ) {
if( isSelected ) {
g.setColor( getBackground() );
FlatTableUI.paintCellSelection( table, g, row, column, 0, 0, getWidth(), getHeight() );
}
super.paintComponent( g );
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -297,13 +297,77 @@ new FormModel {
"value": "cell 4 3 2 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]rel[]"
} ) {
name: "panel5"
name: "generalOptionsPanel"
"border": new javax.swing.border.TitledBorder( "General Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "roundedSelectionCheckBox"
"text": "rounded selection"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "Selection insets:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "topSelectionInsetsCheckBox"
"text": "top"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "bottomSelectionInsetsCheckBox"
"text": "bottom"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "leftSelectionInsetsCheckBox"
"text": "left"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx ind"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "rightSelectionInsetsCheckBox"
"text": "right"
"mnemonic": 68
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "roundedSelectionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "dndCheckBox"
"text": "drag and drop"
@@ -313,18 +377,21 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "dndChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
"value": "cell 0 4"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[fill][fill]"
"$rowConstraints": "[][][]"
} ) {
name: "panel6"
name: "listOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JList Control" )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "listRendererLabel"
"text": "Renderer:"
@@ -392,13 +459,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]0[]"
} ) {
name: "treeOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTree Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "treeRendererLabel"
"text": "Renderer:"
@@ -491,13 +561,16 @@ new FormModel {
"value": "cell 0 4 4 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets 8,hidemode 3"
"$columnConstraints": "[][fill][fill]"
"$rowConstraints": "[][]0[]0[]0[]0"
} ) {
name: "tableOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTable Control" )
"$client.FlatLaf.internal.testing.ignore": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "autoResizeModeLabel"
"text": "Auto resize mode:"

View File

@@ -40,6 +40,8 @@ import net.miginfocom.swing.*;
public class FlatFileChooserTest
extends FlatTestPanel
{
private static ShortcutsCount shortcutsCount = ShortcutsCount.home;
public static void main( String[] args ) {
// Locale.setDefault( Locale.FRENCH );
// Locale.setDefault( Locale.GERMAN );
@@ -52,8 +54,21 @@ public class FlatFileChooserTest
FlatTestFrame frame = FlatTestFrame.create( args, "FlatFileChooserTest" );
UIManager.put( "FileChooser.shortcuts.filesFunction", (Function<File[], File[]>) files -> {
if( shortcutsCount == null )
return files;
if( shortcutsCount == ShortcutsCount.empty )
return new File[0];
ArrayList<File> list = new ArrayList<>( Arrays.asList( files ) );
list.add( 0, new File( System.getProperty( "user.home" ) ) );
if( shortcutsCount == ShortcutsCount.home )
list.add( 0, new File( System.getProperty( "user.home" ) ) );
else {
File home = new File( System.getProperty( "user.home" ) );
File[] homeFiles = home.listFiles();
int count = shortcutsCount.value;
for( int i = 0; i < count; i++ )
list.add( i < homeFiles.length ? homeFiles[i] : new File( "Dummy " + i ) );
}
return list.toArray( new File[list.size()] );
} );
@@ -78,6 +93,8 @@ public class FlatFileChooserTest
dialogTypeField.init( DialogType.class, false );
fileSelectionModeField.init( FileSelectionMode.class, false );
localesField.init( Locales.class, false );
shortcutsCountField.init( ShortcutsCount.class, true );
shortcutsCountField.setSelectedValue( shortcutsCount );
showControlButtonsCheckBox.setSelected( fileChooser1.getControlButtonsAreShown() );
multiSelectionCheckBox.setSelected( fileChooser1.isMultiSelectionEnabled() );
@@ -197,6 +214,11 @@ public class FlatFileChooserTest
} );
}
private void shortcutsCountChanged() {
shortcutsCount = shortcutsCountField.getSelectedValue();
fileChooser1.updateUI();
}
private void printShortcutFiles() {
printFiles( JavaCompatibility2.getChooserShortcutPanelFiles( fileChooser1.getFileSystemView() ) );
}
@@ -263,6 +285,8 @@ public class FlatFileChooserTest
printRootsButton = new JButton();
JLabel localesLabel = new JLabel();
localesField = new FlatTestEnumSelector<>();
JLabel label12 = new JLabel();
shortcutsCountField = new FlatTestEnumSelector<>();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JLabel label3 = new JLabel();
@@ -291,6 +315,7 @@ public class FlatFileChooserTest
"[]" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]"));
@@ -432,49 +457,57 @@ public class FlatFileChooserTest
localesField.addActionListener(e -> localesChanged());
add(localesField, "cell 1 8 2 1");
//---- label12 ----
label12.setText("Shortcuts:");
add(label12, "cell 0 9");
//---- shortcutsCountField ----
shortcutsCountField.addActionListener(e -> shortcutsCountChanged());
add(shortcutsCountField, "cell 1 9 2 1");
//---- label1 ----
label1.setText("icons:");
add(label1, "cell 0 9");
add(label1, "cell 0 10");
//---- label2 ----
label2.setIcon(UIManager.getIcon("FileView.directoryIcon"));
add(label2, "cell 1 9 2 1");
add(label2, "cell 1 10 2 1");
//---- label3 ----
label3.setIcon(UIManager.getIcon("FileView.fileIcon"));
add(label3, "cell 1 9 2 1");
add(label3, "cell 1 10 2 1");
//---- label4 ----
label4.setIcon(UIManager.getIcon("FileView.computerIcon"));
add(label4, "cell 1 9 2 1");
add(label4, "cell 1 10 2 1");
//---- label5 ----
label5.setIcon(UIManager.getIcon("FileView.hardDriveIcon"));
add(label5, "cell 1 9 2 1");
add(label5, "cell 1 10 2 1");
//---- label6 ----
label6.setIcon(UIManager.getIcon("FileView.floppyDriveIcon"));
add(label6, "cell 1 9 2 1");
add(label6, "cell 1 10 2 1");
//---- label7 ----
label7.setIcon(UIManager.getIcon("FileChooser.newFolderIcon"));
add(label7, "cell 1 9 2 1");
add(label7, "cell 1 10 2 1");
//---- label8 ----
label8.setIcon(UIManager.getIcon("FileChooser.upFolderIcon"));
add(label8, "cell 1 9 2 1");
add(label8, "cell 1 10 2 1");
//---- label9 ----
label9.setIcon(UIManager.getIcon("FileChooser.homeFolderIcon"));
add(label9, "cell 1 9 2 1");
add(label9, "cell 1 10 2 1");
//---- label10 ----
label10.setIcon(UIManager.getIcon("FileChooser.detailsViewIcon"));
add(label10, "cell 1 9 2 1");
add(label10, "cell 1 10 2 1");
//---- label11 ----
label11.setIcon(UIManager.getIcon("FileChooser.listViewIcon"));
add(label11, "cell 1 9 2 1");
add(label11, "cell 1 10 2 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
@@ -500,6 +533,7 @@ public class FlatFileChooserTest
private JButton printComboBoxFilesButton;
private JButton printRootsButton;
private FlatTestEnumSelector<Locales> localesField;
private FlatTestEnumSelector<ShortcutsCount> shortcutsCountField;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- enum DialogType ----------------------------------------------------
@@ -553,4 +587,26 @@ public class FlatFileChooserTest
this.value = value;
}
}
//---- enum ShortcutsCount ------------------------------------------------
enum ShortcutsCount {
empty( -1 ),
home( -2 ),
zero( 0 ),
one( 1 ),
two( 2 ),
three( 3 ),
four( 4 ),
five( 5 ),
ten( 10 ),
twenty( 20 ),
thirty( 30 );
public final int value;
ShortcutsCount( int value ) {
this.value = value;
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][][grow]"
"$rowConstraints": "[grow,fill][][][][][][][][]para[]"
"$rowConstraints": "[grow,fill][][][][][][][][][]para[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -273,71 +273,87 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label12"
"text": "Shortcuts:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatTestEnumSelector" ) {
name: "shortcutsCountField"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "ShortcutsCount"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "shortcutsCountChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "icons:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
"value": "cell 0 10"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.directoryIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.fileIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.computerIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.hardDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileView.floppyDriveIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.newFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.upFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label9"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.homeFolderIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label10"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.detailsViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label11"
"icon": new com.jformdesigner.model.SwingIcon( 2, "FileChooser.listViewIcon" )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9 2 1"
"value": "cell 1 10 2 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )

View File

@@ -0,0 +1,390 @@
/*
* Copyright 2024 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.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.function.Supplier;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.SystemInfo;
/**
* @author Karl Tauber
*/
public class FlatHiDPITest
{
private static final double scale = 1.25;
private final JFrame frame;
private final JPanel testPanel;
private final Insets frameInsets;
public static void main( String[] args ) {
System.setProperty( FlatSystemProperties.USE_WINDOW_DECORATIONS, "false" );
System.setProperty( "sun.java2d.uiScale", Double.toString( scale ) );
System.out.println( "Scale factor: " + scale );
for( int x = 0; x <= 100; x++ ) {
int devX = devScaleXY( x, scale );
int usrX = usrScaleXY( x, scale );
if( usrX != devX )
System.out.printf( "%d: %d != %d\n", x, devX, usrX );
/*
for( int w = 0; w <= 10; w++ ) {
int devW = devScaleWH( w, scale );
int usrW = usrScaleWH( x, w, scale );
if( usrW != devW )
System.out.printf( " %d %d: %d != %d\n", x, w, devW, usrW );
}
*/
}
SwingUtilities.invokeLater( () -> {
if( !SystemInfo.isJava_9_orLater ) {
JOptionPane.showMessageDialog( null, "Use Java 9+" );
return;
}
// HiDPIUtils.installHiDPIRepaintManager();
FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
FlatLightLaf.setup();
UIManager.put( "Button.pressedBorderColor", Color.blue );
UIManager.put( "TextField.caretBlinkRate", 0 );
UIManager.put( "FormattedTextField.caretBlinkRate", 0 );
new FlatHiDPITest();
} );
}
FlatHiDPITest() {
frame = new JFrame( "FlatHiDPITest " + scale ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "JFrame" );
}
};
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// get frame insets
frame.addNotify();
frameInsets = frame.getInsets();
testPanel = new JPanel( null ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "JPanel" );
}
};
int y = 0;
addAtProblematicXY( 0, y, 40, 16, 48, "TestComp", TestComp::new );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JButton", () -> new JButton( "B" ) );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JTextField", () -> new JTextField( "Text" ) );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JComboBox", JComboBox<String>::new );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JComboBox editable", () -> {
JComboBox<String> c = new JComboBox<>();
c.setEditable( true );
return c;
} );
y += 20;
addAtProblematicXY( 0, y, 40, 16, 48, "JSpinner", JSpinner::new );
y += 20;
addAtProblematicXY( 0, y, 80, 16, 88, "JSlider", JSlider::new );
y += 20;
addAtProblematicXY( 0, y, 80, 16, 88, "JScrollBar", () -> new JScrollBar( JScrollBar.HORIZONTAL ) );
y += 20;
addAtProblematicXY( 0, y, 16, 40, 20, "JScrollBar", () -> new JScrollBar( JScrollBar.VERTICAL ) );
y += 60;
addAtProblematicXY( 0, y, 82, 60, 88, "JScrollPane", () -> new JScrollPane( new JTree() ) );
y += 80;
addAtProblematicXY( 0, y, 80, 16, 88, "JProgressBar", () -> {
JProgressBar c = new JProgressBar();
c.setValue( 60 );
c.addMouseListener( new MouseAdapter() {
@Override
public void mousePressed( MouseEvent e ) {
int value = c.getValue();
c.setValue( (value >= 20) ? value - 20 : 100 );
}
} );
return c;
} );
frame.getContentPane().add( testPanel );
frame.setSize( 400, 400 );
frame.setVisible( true );
}
private void addAtProblematicXY( int x, int y, int w, int h, int offset, String text, Supplier<Component> generator ) {
// plain component
addAtProblematicXY( x, y, w, h, generator.get() );
// component in (opaque) panel which has same bounds as component
addAtProblematicXY( x + offset, y, w, h, wrapInPanel( generator.get(), false ) );
// component in (opaque) panel which is 1px larger than component
addAtProblematicXY( x + (offset * 2), y, w + 1, h + 1, wrapInPanel( generator.get(), true ) );
JLabel l = new JLabel( text );
testPanel.add( l );
l.setLocation( x + (offset * 3) + 20, y );
l.setSize( l.getPreferredSize() );
}
private void addAtProblematicXY( int x, int y, int w, int h, Component c ) {
int px = nextProblematicXY( x + frameInsets.left ) - frameInsets.left;
int py = nextProblematicXY( y + frameInsets.top ) - frameInsets.top;
testPanel.add( c );
c.setBounds( px, py, w, h );
}
private Component wrapInPanel( Component c, boolean emptyBorder ) {
JPanel p = new JPanel( new BorderLayout() ) {
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "wrapping JPanel" );
}
};
if( emptyBorder )
p.setBorder( new EmptyBorder( 0, 0, 1, 1 ) );
p.add( c, BorderLayout.CENTER );
return p;
}
private static int nextProblematicXY( int xy ) {
for( int i = xy; i < xy + 20; i++ ) {
if( devScaleXY( i, scale ) != usrScaleXY( i, scale ) )
return i;
}
throw new IllegalArgumentException();
}
private static int devScaleXY( int xy, double scale ) {
return (int) (xy * scale);
}
private static int usrScaleXY( int xy, double scale ) {
// see sun.java2d.pipe.Region.clipRound(double);
return (int) Math.ceil( (xy * scale) - 0.5 );
}
@SuppressWarnings( "unused" )
private static int devScaleWH( int wh, double scale ) {
return (int) Math.round( wh * scale );
}
@SuppressWarnings( "unused" )
private static int usrScaleWH( int xy, int wh, double scale ) {
int usrXY = usrScaleXY( xy, scale );
return ((int) Math.ceil( ((xy + wh) * scale) - 0.5 )) - usrXY;
}
//---- class TestComp -----------------------------------------------------
private static class TestComp
extends JComponent
implements FocusListener
{
// used to avoid repainting when window is deactivated and activated (for easier debugging)
private boolean permanentFocused;
TestComp() {
setOpaque( true );
setFocusable( true );
addFocusListener( this );
addMouseListener( new MouseAdapter() {
@Override
public void mouseClicked( MouseEvent e ) {
requestFocusInWindow();
}
} );
}
@Override
protected void paintComponent( Graphics g ) {
g.setColor( isFocusOwner() ? Color.green : Color.red );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
@Override
public void focusGained( FocusEvent e ) {
if( permanentFocused )
return;
if( !e.isTemporary() ) {
repaint();
permanentFocused = true;
}
}
@Override
public void focusLost( FocusEvent e ) {
if( !e.isTemporary() ) {
repaint();
permanentFocused = false;
}
}
@Override
public Graphics getGraphics() {
return TestGraphics2D.install( super.getGraphics(), "TestComp" );
}
}
//---- TestGraphics2D -----------------------------------------------------
private static class TestGraphics2D
extends Graphics2DProxy
{
private final Graphics2D delegate;
private final String id;
static Graphics install( Graphics g, String id ) {
return wasInvokedFrom_safelyGetGraphics()
? new TestGraphics2D( (Graphics2D) g, id )
: g;
}
private static boolean wasInvokedFrom_safelyGetGraphics() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[4];
return "javax.swing.JComponent".equals( stackTraceElement.getClassName() ) &&
"safelyGetGraphics".equals( stackTraceElement.getMethodName() );
}
private TestGraphics2D( Graphics2D delegate, String id ) {
super( delegate );
this.delegate = delegate;
this.id = id;
System.out.println();
System.out.println( "---------------------------------------- " );
System.out.println( id + ": construct" );
printClipRects();
}
private void printClipRects() {
try {
Class<?> sunGraphics2DClass = Class.forName( "sun.java2d.SunGraphics2D" );
if( !sunGraphics2DClass.isInstance( delegate ) ) {
System.out.println( " not a SunGraphics2D: " + delegate.getClass().getName() );
return;
}
Rectangle devClip = region2rect( getFieldValue( sunGraphics2DClass, delegate, "devClip" ) );
Shape usrClip = (Shape) getFieldValue( sunGraphics2DClass, delegate, "usrClip" );
Rectangle clipRegion = region2rect( getFieldValue( sunGraphics2DClass, delegate, "clipRegion" ) );
printField( devClip, "devClip" );
printField( usrClip, "usrClip" );
printField( clipRegion, "clipRegion" );
if( (usrClip instanceof Rectangle && !devClip.contains( (Rectangle) usrClip )) ||
(usrClip instanceof Rectangle2D && !devClip.contains( (Rectangle2D) usrClip )) )
{
System.out.flush();
System.err.println( "WARNING: devClip smaller than usrClip" );
System.err.flush();
}
} catch( Exception ex ) {
ex.printStackTrace();
}
}
private void printField( Object value, String name ) throws Exception {
System.out.printf( " %-16s", name );
if( value instanceof Rectangle ) {
Rectangle r = (Rectangle) value;
System.out.printf( "xy %3d %3d -> %3d %3d wh %3d %3d\n",
r.x, r.y, r.x + r.width, r.y + r.height, r.width, r.height );
} else if( value instanceof Rectangle2D ) {
Rectangle2D r = (Rectangle2D) value;
System.out.printf( "xy %.2f %.2f -> %.2f %.2f wh %.2f %.2f\n",
r.getX(), r.getY(), r.getX() + r.getWidth(), r.getY() + r.getHeight(), r.getWidth(), r.getHeight() );
} else
System.out.println( value );
}
private static Rectangle region2rect( Object region ) throws Exception {
Class<?> regionClass = Class.forName( "sun.java2d.pipe.Region" );
int loX = (int) getMethodValue( regionClass, region, "getLoX" );
int loY = (int) getMethodValue( regionClass, region, "getLoY" );
int hiX = (int) getMethodValue( regionClass, region, "getHiX" );
int hiY = (int) getMethodValue( regionClass, region, "getHiY" );
return new Rectangle( loX, loY, hiX - loX, hiY - loY );
}
private static Object getFieldValue( Class<?> cls, Object object, String name ) throws Exception {
Field f = cls.getDeclaredField( name );
f.setAccessible( true );
return f.get( object );
}
private static Object getMethodValue( Class<?> cls, Object object, String name ) throws Exception {
Method m = cls.getDeclaredMethod( name );
m.setAccessible( true );
return m.invoke( object );
}
@Override
public void clipRect( int x, int y, int width, int height ) {
System.out.printf( "\n%s: clipRect( %d, %d, %d, %d )\n", id, x, y, width, height );
super.clipRect( x, y, width, height );
printClipRects();
}
@Override
public void setClip( int x, int y, int width, int height ) {
System.out.printf( "\n%s: setClip( %d, %d, %d, %d )\n", id, x, y, width, height );
super.setClip( x, y, width, height );
printClipRects();
}
@Override
public void setClip( Shape clip ) {
System.out.printf( "\n%s: setClip( %s )\n", id, clip );
super.setClip( clip );
printClipRects();
}
@Override
public void clip( Shape s ) {
System.out.printf( "\n%s: clip( %s )\n", id, s );
super.clip( s );
printClipRects();
}
}
}

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.testing;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.UIScale;
import com.jidesoft.swing.*;
import net.miginfocom.swing.*;
import org.jdesktop.swingx.*;
@@ -53,6 +55,58 @@ public class FlatHtmlTest
increaseFontSize();
}
private void changeHtmlText() {
changeHtmlText( this );
}
private void changeHtmlText( Component c ) {
if( c instanceof AbstractButton )
((AbstractButton)c).setText( changeHtmlText( ((AbstractButton)c).getText() ) );
else if( c instanceof JLabel )
((JLabel)c).setText( changeHtmlText( ((JLabel)c).getText() ) );
else if( c instanceof JTextComponent )
((JTextComponent)c).setText( changeHtmlText( ((JTextComponent)c).getText() ) );
else if( c instanceof JToolTip )
((JToolTip)c).setTipText( changeHtmlText( ((JToolTip)c).getTipText() ) );
else if( c instanceof JComboBox ) {
@SuppressWarnings( "unchecked" )
JComboBox<String> cb = (JComboBox<String>) c;
DefaultComboBoxModel<String> model = (DefaultComboBoxModel<String>) cb.getModel();
String text = model.getElementAt( 0 );
String newText = changeHtmlText( text );
if( newText != text ) {
model.insertElementAt( newText, 1 );
model.removeElementAt( 0 );
}
}
if( c instanceof Container ) {
for( Component child : ((Container)c).getComponents() )
changeHtmlText( child );
}
}
private String changeHtmlText( String text ) {
String htmlTag = "<html>";
if( !text.startsWith( htmlTag ) )
return text;
String bodyTag = "<body>";
int bodyIndex = text.indexOf( bodyTag );
if( bodyIndex < 0 )
bodyIndex = htmlTag.length();
else
bodyIndex += bodyTag.length();
int insertIndex = text.indexOf( '>', bodyIndex );
if( insertIndex < 0 )
insertIndex = bodyIndex;
else
insertIndex++;
return text.substring( 0, insertIndex ) + "X" + text.substring( insertIndex );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel labelLabel = new JLabel();
@@ -88,14 +142,28 @@ public class FlatHtmlTest
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem2 = new JRadioButtonMenuItem();
JLabel label14 = new JLabel();
JLabel label15 = new JLabel();
JLabel label16 = new JLabel();
JToolTip toolTip3 = new JToolTip();
JToolTip toolTip4 = new JToolTip();
JLabel label17 = new JLabel();
JComboBox<String> comboBox1 = new JComboBox<>();
JComboBox<String> comboBox2 = new JComboBox<>();
JLabel label56 = new JLabel();
JXBusyLabel xBusyLabel1 = new JXBusyLabel();
JXBusyLabel xBusyLabel2 = new JXBusyLabel();
JLabel label18 = new JLabel();
JXHyperlink xHyperlink1 = new JXHyperlink();
JXHyperlink xHyperlink2 = new JXHyperlink();
JLabel label33 = new JLabel();
JideLabel jideLabel1 = new JideLabel();
JideLabel jideLabel2 = new JideLabel();
JLabel label16 = new JLabel();
JideButton jideButton1 = new JideButton();
JideButton jideButton2 = new JideButton();
JLabel label54 = new JLabel();
JideToggleButton jideToggleButton1 = new JideToggleButton();
JideToggleButton jideToggleButton2 = new JideToggleButton();
JButton changeHtmlTextButton = new JButton();
JLabel label15 = new JLabel();
label1 = new JLabel();
JScrollPane scrollPane15 = new JScrollPane();
editorPane1 = new JEditorPane();
@@ -143,13 +211,10 @@ public class FlatHtmlTest
JLabel label47 = new JLabel();
JLabel label53 = new JLabel();
JLabel label48 = new JLabel();
JLabel label54 = new JLabel();
JLabel label56 = new JLabel();
JLabel label57 = new JLabel();
//======== this ========
setLayout(new MigLayout(
"ltr,insets dialog,hidemode 3",
"flowy,ltr,insets dialog,hidemode 3",
// columns
"[grow,sizegroup 1,fill]" +
"[grow,sizegroup 1,fill]" +
@@ -196,6 +261,12 @@ public class FlatHtmlTest
"[]" +
"[]" +
"[]" +
"[]unrel" +
"[]" +
"[]unrel" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]"));
@@ -204,7 +275,7 @@ public class FlatHtmlTest
panel1.add(label5, "cell 0 0");
//---- label6 ----
label6.setText("<html>Some <b>Bold</b> Text");
label6.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(label6, "cell 1 0");
//---- label7 ----
@@ -212,11 +283,11 @@ public class FlatHtmlTest
panel1.add(label7, "cell 2 0");
//---- label3 ----
label3.setText("JButon:");
label3.setText("JButton:");
panel1.add(label3, "cell 0 1");
//---- button1 ----
button1.setText("<html>Some <b>Bold</b> Text");
button1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(button1, "cell 1 1");
//---- button2 ----
@@ -228,7 +299,7 @@ public class FlatHtmlTest
panel1.add(label11, "cell 0 2");
//---- toggleButton1 ----
toggleButton1.setText("<html>Some <b>Bold</b> Text");
toggleButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
toggleButton1.setSelected(true);
panel1.add(toggleButton1, "cell 1 2");
@@ -242,7 +313,7 @@ public class FlatHtmlTest
panel1.add(label12, "cell 0 3");
//---- checkBox1 ----
checkBox1.setText("<html>Some <b>Bold</b> Text");
checkBox1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(checkBox1, "cell 1 3");
//---- checkBox2 ----
@@ -254,7 +325,7 @@ public class FlatHtmlTest
panel1.add(label13, "cell 0 4");
//---- radioButton1 ----
radioButton1.setText("<html>Some <b>Bold</b> Text");
radioButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(radioButton1, "cell 1 4");
//---- radioButton2 ----
@@ -267,7 +338,7 @@ public class FlatHtmlTest
//======== menu1 ========
{
menu1.setText("<html>Some <b>Bold</b> Text");
menu1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
}
panel1.add(menu1, "cell 1 5");
@@ -282,7 +353,7 @@ public class FlatHtmlTest
panel1.add(label4, "cell 0 6");
//---- menuItem1 ----
menuItem1.setText("<html>Some <b>Bold</b> Text");
menuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(menuItem1, "cell 1 6");
//---- menuItem2 ----
@@ -294,7 +365,7 @@ public class FlatHtmlTest
panel1.add(label9, "cell 0 7");
//---- checkBoxMenuItem1 ----
checkBoxMenuItem1.setText("<html>Some <b>Bold</b> Text");
checkBoxMenuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
checkBoxMenuItem1.setSelected(true);
panel1.add(checkBoxMenuItem1, "cell 1 7");
@@ -308,7 +379,7 @@ public class FlatHtmlTest
panel1.add(label10, "cell 0 8");
//---- radioButtonMenuItem1 ----
radioButtonMenuItem1.setText("<html>Some <b>Bold</b> Text");
radioButtonMenuItem1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
radioButtonMenuItem1.setSelected(true);
panel1.add(radioButtonMenuItem1, "cell 1 8");
@@ -321,15 +392,13 @@ public class FlatHtmlTest
label14.setText("JToolTip:");
panel1.add(label14, "cell 0 9");
//---- label15 ----
label15.setText("(move mouse here)");
label15.setToolTipText("<html>Some <b>Bold</b> Text");
panel1.add(label15, "cell 1 9");
//---- toolTip3 ----
toolTip3.setTipText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(toolTip3, "cell 1 9");
//---- label16 ----
label16.setText("(move mouse here)");
label16.setToolTipText("Some text");
panel1.add(label16, "cell 2 9");
//---- toolTip4 ----
toolTip4.setTipText("Some text");
panel1.add(toolTip4, "cell 2 9");
//---- label17 ----
label17.setText("JComboBox:");
@@ -337,7 +406,7 @@ public class FlatHtmlTest
//---- comboBox1 ----
comboBox1.setModel(new DefaultComboBoxModel<>(new String[] {
"<html>Some <b>Bold</b> Text",
"<html>Some <b>Bold</b> Text <kbd>kbd</kbd>",
"abc",
"def"
}));
@@ -351,19 +420,76 @@ public class FlatHtmlTest
}));
panel1.add(comboBox2, "cell 2 10");
//---- label56 ----
label56.setText("JXBusyLabel:");
panel1.add(label56, "cell 0 11");
//---- xBusyLabel1 ----
xBusyLabel1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(xBusyLabel1, "cell 1 11");
//---- xBusyLabel2 ----
xBusyLabel2.setText("Some text");
panel1.add(xBusyLabel2, "cell 2 11");
//---- label18 ----
label18.setText("JXHyperlink:");
panel1.add(label18, "cell 0 11");
panel1.add(label18, "cell 0 12");
//---- xHyperlink1 ----
xHyperlink1.setText("<html>Some <b>Bold</b> Text");
panel1.add(xHyperlink1, "cell 1 11");
xHyperlink1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(xHyperlink1, "cell 1 12");
//---- xHyperlink2 ----
xHyperlink2.setText("Some text");
panel1.add(xHyperlink2, "cell 2 11");
panel1.add(xHyperlink2, "cell 2 12");
//---- label33 ----
label33.setText("JideLabel:");
panel1.add(label33, "cell 0 13");
//---- jideLabel1 ----
jideLabel1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideLabel1, "cell 1 13");
//---- jideLabel2 ----
jideLabel2.setText("Some text");
panel1.add(jideLabel2, "cell 2 13");
//---- label16 ----
label16.setText("JideButton:");
panel1.add(label16, "cell 0 14");
//---- jideButton1 ----
jideButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideButton1, "cell 1 14");
//---- jideButton2 ----
jideButton2.setText("Some text");
panel1.add(jideButton2, "cell 2 14");
//---- label54 ----
label54.setText("JideToggleButton:");
panel1.add(label54, "cell 0 15");
//---- jideToggleButton1 ----
jideToggleButton1.setText("<html>Some <b>Bold</b> Text <kbd>kbd</kbd>");
panel1.add(jideToggleButton1, "cell 1 15");
//---- jideToggleButton2 ----
jideToggleButton2.setText("Some text");
panel1.add(jideToggleButton2, "cell 2 15");
//---- changeHtmlTextButton ----
changeHtmlTextButton.setText("Change HTML Text");
changeHtmlTextButton.addActionListener(e -> changeHtmlText());
panel1.add(changeHtmlTextButton, "cell 0 16");
//---- label15 ----
label15.setText("(use to check whether CSS is updated on text changes)");
panel1.add(label15, "cell 0 17 3 1");
}
add(panel1, "cell 4 0 1 2,aligny top,growy 0");
add(panel1, "cell 4 0 1 3,aligny top,growy 0");
//---- label1 ----
label1.setText("<html>HTML<br>Sample <b>content</b><br> <u>text</u> with <a href=\"#\">link</a><h1>Header 1</h1><h2>Header 2</h2><h3>Header 3</h3><h4>Header 4</h4><h5>Header 5</h5><h6>Header 6</h6><p>Paragraph</p><address>Address</address><hr><table border=\"1\"><tr><th>Col 1</th><th>Col 2</th></tr><tr><td>abc</td><td>def</td></tr></table><ul><li>item 1</li><li>item 2</li></ul></html>");
@@ -441,10 +567,6 @@ public class FlatHtmlTest
"[]" +
"[]" +
"[]" +
"[]para" +
"[]para" +
"[]" +
"[]" +
"[]"));
//---- label22 ----
@@ -582,20 +704,8 @@ public class FlatHtmlTest
//---- label48 ----
label48.setText("<html><address>address</address></html>");
panel2.add(label48, "cell 1 5");
//---- label54 ----
label54.setText("Test whether inserted rule affects display:");
panel2.add(label54, "cell 0 7 7 1");
//---- label56 ----
label56.setText("<html><head><style>body { color: red }</style></head>leading <big>red</big> trailing</html>");
panel2.add(label56, "cell 0 8 7 1");
//---- label57 ----
label57.setText("<html><style>body { color: red }</style><p>leading <big>red</big> trailing</p></html>");
panel2.add(label57, "cell 0 9 7 1");
}
add(panel2, "cell 4 2");
add(panel2, "cell 4 0 1 3");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.3.1.342" Java: "15" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -7,7 +7,7 @@ new FormModel {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$layoutConstraints": "flowy,ltr,insets dialog,hidemode 3"
"$columnConstraints": "[grow,sizegroup 1,fill][grow,sizegroup 1,fill][grow,sizegroup 1,fill][grow,sizegroup 1,fill][fill]"
"$rowConstraints": "[][fill][grow,fill]"
} ) {
@@ -39,7 +39,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[][][][][][][][][][][][]"
"$rowConstraints": "[][][][][][][][][][][]unrel[][]unrel[][][]para[][]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -50,7 +50,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
@@ -62,13 +62,13 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "JButon:"
"text": "JButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
@@ -86,7 +86,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JToggleButton" ) {
name: "toggleButton1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
@@ -106,7 +106,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "checkBox1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
@@ -124,7 +124,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JRadioButton" ) {
name: "radioButton1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
@@ -142,7 +142,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
@@ -160,7 +160,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
@@ -178,7 +178,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "checkBoxMenuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7"
@@ -198,7 +198,7 @@ new FormModel {
} )
add( new FormComponent( "javax.swing.JRadioButtonMenuItem" ) {
name: "radioButtonMenuItem1"
"text": "<html>Some <b>Bold</b> Text"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
"selected": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8"
@@ -216,17 +216,15 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label15"
"text": "(move mouse here)"
"toolTipText": "<html>Some <b>Bold</b> Text"
add( new FormComponent( "javax.swing.JToolTip" ) {
name: "toolTip3"
"tipText": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 9"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label16"
"text": "(move mouse here)"
"toolTipText": "Some text"
add( new FormComponent( "javax.swing.JToolTip" ) {
name: "toolTip4"
"tipText": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 9"
} )
@@ -239,8 +237,8 @@ new FormModel {
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "comboBox1"
"model": new javax.swing.DefaultComboBoxModel {
selectedItem: "<html>Some <b>Bold</b> Text"
addElement( "<html>Some <b>Bold</b> Text" )
selectedItem: "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
addElement( "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>" )
addElement( "abc" )
addElement( "def" )
}
@@ -259,25 +257,110 @@ new FormModel {
"value": "cell 2 10"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label18"
"text": "JXHyperlink:"
name: "label56"
"text": "JXBusyLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink1"
"text": "<html>Some <b>Bold</b> Text"
add( new FormComponent( "org.jdesktop.swingx.JXBusyLabel" ) {
name: "xBusyLabel1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11"
} )
add( new FormComponent( "org.jdesktop.swingx.JXBusyLabel" ) {
name: "xBusyLabel2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label18"
"text": "JXHyperlink:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 12"
} )
add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) {
name: "xHyperlink2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 11"
"value": "cell 2 12"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label33"
"text": "JideLabel:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 13"
} )
add( new FormComponent( "com.jidesoft.swing.JideLabel" ) {
name: "jideLabel1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 13"
} )
add( new FormComponent( "com.jidesoft.swing.JideLabel" ) {
name: "jideLabel2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 13"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label16"
"text": "JideButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 14"
} )
add( new FormComponent( "com.jidesoft.swing.JideButton" ) {
name: "jideButton1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 14"
} )
add( new FormComponent( "com.jidesoft.swing.JideButton" ) {
name: "jideButton2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 14"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label54"
"text": "JideToggleButton:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 15"
} )
add( new FormComponent( "com.jidesoft.swing.JideToggleButton" ) {
name: "jideToggleButton1"
"text": "<html>Some <b>Bold</b> Text <kbd>kbd</kbd>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15"
} )
add( new FormComponent( "com.jidesoft.swing.JideToggleButton" ) {
name: "jideToggleButton2"
"text": "Some text"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 15"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "changeHtmlTextButton"
"text": "Change HTML Text"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeHtmlText", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 16"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label15"
"text": "(use to check whether CSS is updated on text changes)"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 17 3 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0 1 2,aligny top,growy 0"
"value": "cell 4 0 1 3,aligny top,growy 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
@@ -372,7 +455,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][fill][fill][fill][fill]"
"$rowConstraints": "[][][][][][]para[]para[][][]"
"$rowConstraints": "[][][][][][]"
} ) {
name: "panel2"
auxiliary() {
@@ -582,30 +665,12 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label54"
"text": "Test whether inserted rule affects display:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 7 7 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label56"
"text": "<html><head><style>body { color: red }</style></head>leading <big>red</big> trailing</html>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 8 7 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label57"
"text": "<html><style>body { color: red }</style><p>leading <big>red</big> trailing</p></html>"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 9 7 1"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 2"
"value": "cell 4 0 1 3"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 905, 815 )
"size": new java.awt.Dimension( 905, 880 )
} )
}
}

View File

@@ -22,6 +22,7 @@ import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.*;
import java.util.function.Supplier;
import javax.swing.*;
@@ -49,6 +50,7 @@ public class FlatMenusTest
initComponents();
largerCheckBox.setSelected( LargerMenuItem.useLargerSize );
verticalMenuBar.setLayout( new GridLayout( 0, 1 ) );
}
private void armedChanged() {
@@ -246,7 +248,15 @@ public class FlatMenusTest
JLabel popupMenuLabel = new JLabel();
JButton showPopupMenuButton = new JButton();
showScrollingPopupMenuButton = new JButton();
JLabel label1 = new JLabel();
armedCheckBox = new JCheckBox();
verticalMenuBar = new JMenuBar();
JMenu menu14 = new JMenu();
JMenuItem menuItem53 = new JMenuItem();
JMenu menu15 = new JMenu();
JMenuItem menuItem54 = new JMenuItem();
JMenu menu16 = new JMenu();
JMenuItem menuItem55 = new JMenuItem();
underlineCheckBox = new JCheckBox();
popupMenubackgroundCheckBox = new JCheckBox();
@@ -884,6 +894,10 @@ public class FlatMenusTest
showScrollingPopupMenuButton.addActionListener(e -> showScrollingPopupMenu(e));
add(showScrollingPopupMenuButton, "cell 2 2");
//---- label1 ----
label1.setText("Vertical JMenuBar:");
add(label1, "cell 4 2");
//---- armedCheckBox ----
armedCheckBox.setText("armed");
armedCheckBox.setMnemonic('A');
@@ -891,6 +905,42 @@ public class FlatMenusTest
armedCheckBox.addActionListener(e -> armedChanged());
add(armedCheckBox, "cell 0 3");
//======== verticalMenuBar ========
{
//======== menu14 ========
{
menu14.setText("menu");
//---- menuItem53 ----
menuItem53.setText("text");
menu14.add(menuItem53);
}
verticalMenuBar.add(menu14);
//======== menu15 ========
{
menu15.setText("another menu");
//---- menuItem54 ----
menuItem54.setText("text");
menu15.add(menuItem54);
}
verticalMenuBar.add(menu15);
//======== menu16 ========
{
menu16.setText("menu 3");
menu16.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/testing/test16.png")));
//---- menuItem55 ----
menuItem55.setText("text");
menu16.add(menuItem55);
}
verticalMenuBar.add(menu16);
}
add(verticalMenuBar, "cell 4 3 1 3");
//---- underlineCheckBox ----
underlineCheckBox.setText("underline menu selection");
underlineCheckBox.putClientProperty("FlatLaf.internal.testing.ignore", true);
@@ -931,6 +981,7 @@ public class FlatMenusTest
private JCheckBox accelCheckBox;
private JButton showScrollingPopupMenuButton;
private JCheckBox armedCheckBox;
private JMenuBar verticalMenuBar;
private JCheckBox underlineCheckBox;
private JCheckBox popupMenubackgroundCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "16" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -662,6 +662,12 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "Vertical JMenuBar:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "armedCheckBox"
"text": "armed"
@@ -674,6 +680,39 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "verticalMenuBar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu14"
"text": "menu"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem53"
"text": "text"
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu15"
"text": "another menu"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem54"
"text": "text"
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu16"
"text": "menu 3"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test16.png" )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem55"
"text": "text"
} )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 3 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "underlineCheckBox"
"text": "underline menu selection"

View File

@@ -36,6 +36,8 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.extras.components.FlatTriStateCheckBox;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.ui.FlatLineBorder;
import com.formdev.flatlaf.util.SystemInfo;
import net.miginfocom.swing.*;
@@ -161,6 +163,8 @@ public class FlatNativeWindowBorderTest
registerSwitchToLookAndFeel( "F2", FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F3", FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( "F4", FlatDarculaLaf.class.getName() );
registerSwitchToLookAndFeel( "F5", FlatMacLightLaf.class.getName() );
registerSwitchToLookAndFeel( "F6", FlatMacDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F8", FlatTestLaf.class.getName() );
@@ -170,8 +174,8 @@ public class FlatNativeWindowBorderTest
registerSwitchToLookAndFeel( "F9", "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( "F9", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F11", NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
}
private void updateInfo() {

View File

@@ -40,10 +40,15 @@ public class FlatPaintingHiDPITest
FlatPaintingHiDPITest() {
initComponents();
reset();
sliderChanged();
}
@Override
public void addNotify() {
super.addNotify();
reset();
}
private void sliderChanged() {
painter.originX = originXSlider.getValue();
painter.originY = originYSlider.getValue();
@@ -212,7 +217,7 @@ public class FlatPaintingHiDPITest
scaleXSlider.setPaintTicks(true);
scaleXSlider.setMajorTickSpacing(50);
scaleXSlider.setSnapToTicks(true);
scaleXSlider.setMinorTickSpacing(10);
scaleXSlider.setMinorTickSpacing(5);
scaleXSlider.setMinimum(-100);
scaleXSlider.addChangeListener(e -> sliderChanged());
add(scaleXSlider, "cell 1 4");
@@ -228,7 +233,7 @@ public class FlatPaintingHiDPITest
scaleYSlider.setPaintLabels(true);
scaleYSlider.setMajorTickSpacing(50);
scaleYSlider.setSnapToTicks(true);
scaleYSlider.setMinorTickSpacing(10);
scaleYSlider.setMinorTickSpacing(5);
scaleYSlider.setMinimum(-100);
scaleYSlider.addChangeListener(e -> sliderChanged());
add(scaleYSlider, "cell 1 5");

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.0.0.0.122" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -108,7 +108,7 @@ new FormModel {
"paintTicks": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minorTickSpacing": 5
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
@@ -131,7 +131,7 @@ new FormModel {
"paintLabels": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minorTickSpacing": 5
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false

View File

@@ -17,8 +17,12 @@
package com.formdev.flatlaf.testing;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*;
/**
@@ -27,7 +31,8 @@ import net.miginfocom.swing.*;
public class FlatPopupTest
extends FlatTestPanel
{
private Popup popup;
private Popup[] popups;
private JPanel[] popupPanels;
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
@@ -54,19 +59,25 @@ public class FlatPopupTest
private void showPopup( int xoffset, int yoffset ) {
hidePopup();
createPopupPanels();
int xoffset2 = popupPanels[0].getPreferredSize().width + UIScale.scale( 10 );
Point pt = showPopupButton.getLocationOnScreen();
popup = PopupFactory.getSharedInstance().getPopup( showPopupButton, popupPanel,
pt.x + xoffset, pt.y + showPopupButton.getHeight() + yoffset );
popup.show();
popups = new Popup[popupPanels.length];
for( int i = 0; i < popupPanels.length; i++ ) {
popups[i] = PopupFactory.getSharedInstance().getPopup( this, popupPanels[i],
pt.x + xoffset + (xoffset2 * i), pt.y + showPopupButton.getHeight() + yoffset );
popups[i].show();
}
}
private void hidePopup() {
if( popup == null )
if( popups == null )
return;
popup.hide();
popup = null;
for( Popup popup : popups )
popup.hide();
popups = null;
}
private void movePopupDown() {
@@ -80,13 +91,29 @@ public class FlatPopupTest
private void movePopup( int xoffset, int yoffset ) {
showPopup();
Animator animator = new Animator( 1000, fraction -> {
System.out.println(fraction);
Animator animator = new Animator( 1500, fraction -> {
// System.out.println(fraction);
showPopup( (int) (fraction * xoffset), (int) (fraction * yoffset) );
} );
animator.start();
}
private void createPopupPanels() {
int count = (int) countField.getValue();
if( popupPanels != null && popupPanels.length == count )
return;
Random random = new Random();
popupPanels = new JPanel[count];
for( int i = 0; i < popupPanels.length; i++ ) {
JLabel l = new JLabel( "popup " + (i + 1) );
JPanel p = new JPanel();
p.setBackground( new Color( random.nextInt( 0xffffff ) ) );
p.add( l );
popupPanels[i] = p;
}
}
@Override
public void updateUI() {
super.updateUI();
@@ -94,8 +121,15 @@ public class FlatPopupTest
if( popupMenu1 != null ) {
SwingUtilities.updateComponentTreeUI( popupMenu1 );
SwingUtilities.updateComponentTreeUI( popupMenu2 );
SwingUtilities.updateComponentTreeUI( popupPanel );
}
if( popupPanels != null ) {
for( JPanel popupPanel : popupPanels )
SwingUtilities.updateComponentTreeUI( popupPanel );
}
}
private void countChanged() {
// TODO add your code here
}
private void initComponents() {
@@ -107,16 +141,17 @@ public class FlatPopupTest
showPopupButton = new JButton();
hidePopupButton = new JButton();
movePopupDownButton = new JButton();
movePopuprightButton = new JButton();
movePopupRightButton = new JButton();
countLabel = new JLabel();
countField = new JSpinner();
label4 = new JLabel();
movingToolTipPanel = new MovingToolTipPanel();
popupMenu1 = new JPopupMenu();
menuItem1 = new JMenuItem();
menuItem2 = new JMenuItem();
menu1 = new JMenu();
menuItem3 = new JMenuItem();
menuItem4 = new JMenuItem();
popupPanel = new JPanel();
label3 = new JLabel();
popupMenu2 = new JPopupMenu();
menuItem5 = new JMenuItem();
menuItem6 = new JMenuItem();
@@ -146,14 +181,18 @@ public class FlatPopupTest
"[fill]" +
"[fill]" +
"[fill]" +
"[fill]",
"[fill]" +
"[fill]" +
"[fill]" +
"[grow,fill]",
// rows
"[]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]"));
"[]" +
"[grow,fill]"));
//---- label1 ----
label1.setText("Label with light-weight tooltip");
@@ -190,14 +229,24 @@ public class FlatPopupTest
movePopupDownButton.addActionListener(e -> movePopupDown());
add(movePopupDownButton, "cell 2 4");
//---- movePopuprightButton ----
movePopuprightButton.setText("move right");
movePopuprightButton.addActionListener(e -> movePopupRight());
add(movePopuprightButton, "cell 3 4");
//---- movePopupRightButton ----
movePopupRightButton.setText("move right");
movePopupRightButton.addActionListener(e -> movePopupRight());
add(movePopupRightButton, "cell 3 4");
//---- countLabel ----
countLabel.setText("Count:");
add(countLabel, "cell 4 4");
//---- countField ----
countField.setModel(new SpinnerNumberModel(1, 1, null, 1));
countField.addChangeListener(e -> countChanged());
add(countField, "cell 5 4");
//---- label4 ----
label4.setText("(switches to heavy-weight when moving outside of window)");
add(label4, "cell 0 5 4 1,alignx right,growx 0");
add(movingToolTipPanel, "cell 0 6 7 1");
//======== popupMenu1 ========
{
@@ -225,21 +274,6 @@ public class FlatPopupTest
popupMenu1.add(menu1);
}
//======== popupPanel ========
{
popupPanel.setBackground(new Color(153, 255, 153));
popupPanel.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]",
// rows
"[]"));
//---- label3 ----
label3.setText("popup");
popupPanel.add(label3, "cell 0 0");
}
//======== popupMenu2 ========
{
@@ -336,16 +370,17 @@ public class FlatPopupTest
private JButton showPopupButton;
private JButton hidePopupButton;
private JButton movePopupDownButton;
private JButton movePopuprightButton;
private JButton movePopupRightButton;
private JLabel countLabel;
private JSpinner countField;
private JLabel label4;
private MovingToolTipPanel movingToolTipPanel;
private JPopupMenu popupMenu1;
private JMenuItem menuItem1;
private JMenuItem menuItem2;
private JMenu menu1;
private JMenuItem menuItem3;
private JMenuItem menuItem4;
private JPanel popupPanel;
private JLabel label3;
private JPopupMenu popupMenu2;
private JMenuItem menuItem5;
private JMenuItem menuItem6;
@@ -368,4 +403,45 @@ public class FlatPopupTest
private JMenuItem menuItem22;
private JMenuItem menuItem23;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class MovingToolTipPanel -------------------------------------------
private static class MovingToolTipPanel
extends JPanel
{
private MovingToolTipPanel() {
initComponents();
}
@Override
public String getToolTipText( MouseEvent e ) {
return e.getX() + "," + e.getY();
}
@Override
public Point getToolTipLocation( MouseEvent e ) {
// multiply Y by two to make it possible to move tooltip outside of window,
// which forces use of heavy weight popups for all Lafs
return new Point( e.getX() , e.getY() * 2 );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
label6 = new JLabel();
//======== this ========
setBorder(new LineBorder(Color.red));
setToolTipText("text");
setLayout(new FlowLayout());
//---- label6 ----
label6.setText("moving tooltip area");
add(label6);
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
private JLabel label6;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
}
}

View File

@@ -1,12 +1,12 @@
JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" 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": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[fill][fill][fill][fill]"
"$rowConstraints": "[][][][][][]"
"$columnConstraints": "[fill][fill][fill][fill][fill][fill][grow,fill]"
"$rowConstraints": "[][][][][][][grow,fill]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -59,21 +59,51 @@ new FormModel {
"value": "cell 2 4"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "movePopuprightButton"
name: "movePopupRightButton"
"text": "move right"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "movePopupRight", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "countLabel"
"text": "Count:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 4"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "countField"
"model": new javax.swing.SpinnerNumberModel {
minimum: 1
value: 1
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "countChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"text": "(switches to heavy-weight when moving outside of window)"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5 4 1,alignx right,growx 0"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "movingToolTipPanel"
"border": new javax.swing.border.LineBorder( sfield java.awt.Color red, 1, false )
"toolTipText": "text"
auxiliary() {
"JavaCodeGenerator.className": "MovingToolTipPanel"
}
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "moving tooltip area"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6 7 1"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 490, 350 )
"size": new java.awt.Dimension( 700, 350 )
} )
add( new FormContainer( "javax.swing.JPopupMenu", new FormLayoutManager( class javax.swing.JPopupMenu ) ) {
name: "popupMenu1"
@@ -100,23 +130,6 @@ new FormModel {
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 395 )
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[]"
} ) {
name: "popupPanel"
"background": new java.awt.Color( 153, 255, 153, 255 )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "popup"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 225, 395 )
"size": new java.awt.Dimension( 200, 200 )
} )
add( new FormContainer( "javax.swing.JPopupMenu", new FormLayoutManager( class javax.swing.JPopupMenu ) ) {
name: "popupMenu2"
add( new FormComponent( "javax.swing.JMenuItem" ) {

View File

@@ -17,14 +17,19 @@
package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.tree.*;
@@ -227,6 +232,38 @@ public class FlatRoundedScrollPaneTest
}
}
private void emptyViewportChanged() {
boolean empty = emptyViewportCheckBox.isSelected();
for( JScrollPane scrollPane : allJScrollPanes ) {
JViewport viewport = scrollPane.getViewport();
Component view = viewport.getView();
if( empty ) {
scrollPane.putClientProperty( getClass().getName(), view );
JComponent emptyView = new JComponent() {
};
emptyView.setBorder( new EmptyViewBorder() );
emptyView.setFocusable( true );
emptyView.addMouseListener( new MouseAdapter() {
@Override
public void mousePressed( MouseEvent e ) {
emptyView.requestFocusInWindow();
}
} );
viewport.setView( emptyView );
} else {
Object oldView = scrollPane.getClientProperty( getClass().getName() );
scrollPane.putClientProperty( getClass().getName(), null );
if( oldView instanceof Component )
viewport.setView( (Component) oldView );
else
viewport.setView( null );
}
viewport.setOpaque( !empty );
scrollPane.revalidate();
scrollPane.repaint();
}
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
splitPane2 = new JSplitPane();
@@ -265,6 +302,7 @@ public class FlatRoundedScrollPaneTest
viewportBorderCheckBox = new JCheckBox();
rowHeaderCheckBox = new JCheckBox();
verticalScrollBarCheckBox = new JCheckBox();
emptyViewportCheckBox = new JCheckBox();
//======== this ========
setLayout(new MigLayout(
@@ -420,6 +458,7 @@ public class FlatRoundedScrollPaneTest
"[]",
// rows
"[]" +
"[]" +
"[]"));
//---- arcLabel ----
@@ -468,6 +507,11 @@ public class FlatRoundedScrollPaneTest
verticalScrollBarCheckBox.setSelected(true);
verticalScrollBarCheckBox.addActionListener(e -> verticalScrollBarChanged());
panel3.add(verticalScrollBarCheckBox, "cell 4 1");
//---- emptyViewportCheckBox ----
emptyViewportCheckBox.setText("Empty viewport");
emptyViewportCheckBox.addActionListener(e -> emptyViewportChanged());
panel3.add(emptyViewportCheckBox, "cell 2 2");
}
add(panel3, "cell 0 1");
// JFormDesigner - End of component initialization //GEN-END:initComponents
@@ -509,6 +553,7 @@ public class FlatRoundedScrollPaneTest
private JCheckBox viewportBorderCheckBox;
private JCheckBox rowHeaderCheckBox;
private JCheckBox verticalScrollBarCheckBox;
private JCheckBox emptyViewportCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class Corner -------------------------------------------------------
@@ -525,4 +570,29 @@ public class FlatRoundedScrollPaneTest
// do not change background when checkbox "explicit colors" is selected
}
}
//---- class EmptyViewBorder ----------------------------------------------
private static class EmptyViewBorder
extends EmptyBorder
{
public EmptyViewBorder() {
super( 0, 0, 0, 0 );
}
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
g.setColor( Color.red );
int x2 = x + width - 1;
int y2 = y + height - 1;
for( int px = x; px <= x2; px += 4 ) {
g.fillRect( px, y, 1, 1 );
g.fillRect( px, y2, 1, 1 );
}
for( int py = y; py <= y2; py += 4 ) {
g.fillRect( x, py, 1, 1 );
g.fillRect( x2, py, 1, 1 );
}
}
}
}

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.1.1.0.298" Java: "19.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -169,7 +169,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][grow,fill]para[][][]"
"$rowConstraints": "[][]"
"$rowConstraints": "[][][]"
} ) {
name: "panel3"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -238,6 +238,13 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "emptyViewportCheckBox"
"text": "Empty viewport"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "emptyViewportChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )

View File

@@ -22,13 +22,13 @@ import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalLookAndFeel;
@@ -42,6 +42,8 @@ import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.demo.DemoPrefs;
import com.formdev.flatlaf.extras.FlatInspector;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.MigLayout;
@@ -63,8 +65,9 @@ public class FlatSingleComponentTest
public static void main( String[] args ) {
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.initSystemScale();
// set scale factor
// set user scale factor
if( System.getProperty( FlatSystemProperties.UI_SCALE ) == null ) {
String scaleFactor = DemoPrefs.getState().get( KEY_SCALE_FACTOR, null );
if( scaleFactor != null )
@@ -95,7 +98,8 @@ public class FlatSingleComponentTest
JComponent c = createSingleComponent();
Container contentPane = getContentPane();
contentPane.setLayout( new MigLayout( null, null, "[][grow]") );
contentPane.setLayout( new MigLayout( null, null,
c instanceof JScrollPane ? "[grow,fill][]" : "[][grow]" ) );
contentPane.add( c );
infoLabel = new JLabel();
@@ -107,6 +111,8 @@ public class FlatSingleComponentTest
registerSwitchToLookAndFeel( "F2", FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F3", FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( "F4", FlatDarculaLaf.class.getName() );
registerSwitchToLookAndFeel( "F5", FlatMacLightLaf.class.getName() );
registerSwitchToLookAndFeel( "F6", FlatMacDarkLaf.class.getName() );
registerSwitchToLookAndFeel( "F8", FlatTestLaf.class.getName() );
@@ -116,42 +122,37 @@ public class FlatSingleComponentTest
registerSwitchToLookAndFeel( "F9", "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( "F9", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F11", NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
// register Alt+F1, F2, ... keys to change scale factor
registerScaleFactor( "alt F1", "1" );
registerScaleFactor( "alt F2", "1.25" );
registerScaleFactor( "alt F3", "1.5" );
registerScaleFactor( "alt F4", "1.75" );
registerScaleFactor( "alt F5", "2" );
registerScaleFactor( "alt F6", "2.5" );
registerScaleFactor( "alt F7", "3" );
registerScaleFactor( "alt F8", "3.5" );
registerScaleFactor( "alt F9", "4" );
registerScaleFactor( "alt F10", "5" );
registerScaleFactor( "alt F11", "6" );
registerScaleFactor( "alt F12", null );
// register Alt+F1, F2, ... keys to change user scale factor
registerScaleFactor( "alt F1", null );
registerScaleFactor( "alt F2", "1" );
registerScaleFactor( "alt F3", "1.25" );
registerScaleFactor( "alt F4", "1.5" );
registerScaleFactor( "alt F5", "1.75" );
registerScaleFactor( "alt F6", "2" );
registerScaleFactor( "alt F7", "2.5" );
registerScaleFactor( "alt F8", "3" );
registerScaleFactor( "alt F9", "3.5" );
registerScaleFactor( "alt F10", "4" );
registerScaleFactor( "alt F11", "5" );
registerScaleFactor( "alt F12", "6" );
// register Alt+Shift+F1, F2, ... keys to change system scale factor
DemoPrefs.registerSystemScaleFactors( this );
// register Alt+R key to toggle component orientation
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
applyComponentOrientation( getComponentOrientation().isLeftToRight()
? ComponentOrientation.RIGHT_TO_LEFT
: ComponentOrientation.LEFT_TO_RIGHT );
revalidate();
repaint();
},
KeyStroke.getKeyStroke( "alt R" ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( "alt R", () -> {
applyComponentOrientation( getComponentOrientation().isLeftToRight()
? ComponentOrientation.RIGHT_TO_LEFT
: ComponentOrientation.LEFT_TO_RIGHT );
revalidate();
repaint();
} );
// register ESC key to close frame
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
dispose();
},
KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, false ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( "ESCAPE", () -> dispose() );
// update info
addWindowListener( new WindowAdapter() {
@@ -199,19 +200,23 @@ public class FlatSingleComponentTest
+ ")" );
}
private void registerSwitchToLookAndFeel( String keyStrokeStr, String lafClassName ) {
private void registerKey( String keyStrokeStr, Runnable runnable ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
if( keyStroke == null )
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
applyLookAndFeel( lafClassName );
runnable.run();
},
keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
}
private void registerSwitchToLookAndFeel( String keyStrokeStr, String lafClassName ) {
registerKey( keyStrokeStr, () -> applyLookAndFeel( lafClassName ) );
}
private void applyLookAndFeel( String lafClassName ) {
try {
UIManager.setLookAndFeel( lafClassName );
@@ -222,16 +227,7 @@ public class FlatSingleComponentTest
}
private void registerScaleFactor( String keyStrokeStr, String scaleFactor ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
if( keyStroke == null )
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
applyScaleFactor( scaleFactor );
},
keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( keyStrokeStr, () -> applyScaleFactor( scaleFactor ) );
}
private void applyScaleFactor( String scaleFactor ) {

View File

@@ -21,6 +21,7 @@ import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.Beans;
import java.util.Objects;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
@@ -85,6 +86,17 @@ public class FlatTestEnumSelector<E>
return null;
}
public void setSelectedValue( E value ) {
for( Component c : toolBar.getComponents() ) {
if( c instanceof JToggleButton &&
Objects.equals( value, ((JToggleButton)c).getClientProperty( "FlatTestEnumSelector.value" ) ) )
{
((JToggleButton)c).setSelected( true );
return;
}
}
}
public void addActionListener( ActionListener l ) {
listenerList.add( ActionListener.class, l );
}

View File

@@ -19,7 +19,6 @@ package com.formdev.flatlaf.testing;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
@@ -81,6 +80,7 @@ public class FlatTestFrame
// System.setProperty( "awt.useSystemAAFontSettings", "off" );
DemoPrefs.init( PREFS_ROOT_PATH );
DemoPrefs.initSystemScale();
// set scale factor
if( System.getProperty( FlatSystemProperties.UI_SCALE ) == null ) {
@@ -169,41 +169,23 @@ public class FlatTestFrame
registerSwitchToLookAndFeel( "F9", "com.apple.laf.AquaLookAndFeel" );
else if( SystemInfo.isLinux )
registerSwitchToLookAndFeel( "F9", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel" );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F11", NimbusLookAndFeel.class.getName() );
registerSwitchToLookAndFeel( "F12", MetalLookAndFeel.class.getName() );
// register Alt+Shift+F1, F2, ... keys to change system scale factor
DemoPrefs.registerSystemScaleFactors( this );
// register Ctrl+0, Ctrl++ and Ctrl+- to change font size
int menuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
((JComponent)getContentPane()).registerKeyboardAction(
e -> restoreFont(),
KeyStroke.getKeyStroke( KeyEvent.VK_0, menuShortcutKeyMask ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
((JComponent)getContentPane()).registerKeyboardAction(
e -> incrFont(),
KeyStroke.getKeyStroke( KeyEvent.VK_PLUS, menuShortcutKeyMask ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
((JComponent)getContentPane()).registerKeyboardAction(
e -> decrFont(),
KeyStroke.getKeyStroke( KeyEvent.VK_MINUS, menuShortcutKeyMask ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( SystemInfo.isMacOS ? "meta 0" : "ctrl 0", () -> restoreFont() );
registerKey( SystemInfo.isMacOS ? "meta PLUS" : "ctrl PLUS", () -> incrFont() );
registerKey( SystemInfo.isMacOS ? "meta MINUS" : "ctrl MINUS", () -> decrFont() );
// register Alt+UP and Alt+DOWN to switch to previous/next theme
((JComponent)getContentPane()).registerKeyboardAction(
e -> themesPanel.selectPreviousTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
((JComponent)getContentPane()).registerKeyboardAction(
e -> themesPanel.selectNextTheme(),
KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( "alt UP", () -> themesPanel.selectPreviousTheme() );
registerKey( "alt DOWN", () -> themesPanel.selectNextTheme() );
// register ESC key to close frame
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
dispose();
},
KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, false ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
registerKey( "ESCAPE", () -> dispose() );
// make the "close" button the default button
getRootPane().setDefaultButton( closeButton );
@@ -274,19 +256,23 @@ public class FlatTestFrame
setTitle( newTitle );
}
private void registerSwitchToLookAndFeel( String keyStrokeStr, String lafClassName ) {
private void registerKey( String keyStrokeStr, Runnable runnable ) {
KeyStroke keyStroke = KeyStroke.getKeyStroke( keyStrokeStr );
if( keyStroke == null )
throw new IllegalArgumentException( "Invalid key stroke '" + keyStrokeStr + "'" );
((JComponent)getContentPane()).registerKeyboardAction(
e -> {
selectLookAndFeel( lafClassName );
runnable.run();
},
keyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
}
private void registerSwitchToLookAndFeel( String keyStrokeStr, String lafClassName ) {
registerKey( keyStrokeStr, () -> selectLookAndFeel( lafClassName ) );
}
private void loadLafs( DefaultComboBoxModel<LookAndFeelInfo> lafModel ) {
Properties properties = new Properties();
try( InputStream in = new FileInputStream( "lafs.properties" ) ) {
@@ -340,7 +326,8 @@ public class FlatTestFrame
if( menuBarFactory != null )
setJMenuBar( menuBarFactory.apply( content ) );
contentPanel.add( content );
addContentToContentPanel();
pack();
setLocationRelativeTo( null );
setVisible( true );
@@ -350,6 +337,27 @@ public class FlatTestFrame
} );
}
private void addContentToContentPanel() {
if( content instanceof JScrollPane ) {
contentPanel.add( content );
return;
}
Dimension contentSize = content.getPreferredSize();
int buttonBarHeight = buttonBar.getPreferredSize().height;
Rectangle screenBounds = getGraphicsConfiguration().getBounds();
// add scroll pane if content is larger than screen
if( contentSize.width > screenBounds.width ||
contentSize.height + buttonBarHeight > screenBounds.height )
{
JScrollPane scrollPane = new JScrollPane( content );
scrollPane.setBorder( BorderFactory.createEmptyBorder() );
contentPanel.add( scrollPane );
} else
contentPanel.add( content );
}
private void selectLookAndFeel( String lafClassName ) {
lookAndFeelComboBox.setSelectedLookAndFeel( lafClassName );
}
@@ -697,9 +705,9 @@ public class FlatTestFrame
}
private void recreateContent() {
contentPanel.remove( content );
contentPanel.removeAll();
content = contentFactory.get();
contentPanel.add( content );
addContentToContentPanel();
if( rightToLeftCheckBox.isSelected() )
rightToLeftChanged();

View File

@@ -94,6 +94,13 @@ Button.endBorderColor = #f00
Button.disabledBorderColor = #008
Button.focusedBorderColor = #466d94
Button.hoverBorderColor = #f00
Button.pressedBorderColor = #0ff
Button.selectedBorderColor = #f0f
Button.disabledSelectedBorderColor = #ff0
Button.focusedSelectedBorderColor = #fff
Button.hoverSelectedBorderColor = #aaa
Button.pressedSelectedBorderColor = #888
Button.innerFocusWidth = 1
@@ -109,6 +116,7 @@ Button.default.pressedForeground = #0080ff
Button.default.startBorderColor = #f00
Button.default.endBorderColor = #00f
Button.default.hoverBorderColor = #f00
Button.default.pressedBorderColor = #88f
Button.default.focusedBorderColor = #537699
Button.default.focusColor = #f00

View File

@@ -25,6 +25,7 @@ import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
@@ -191,22 +192,20 @@ class FlatThemeFileEditor
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING, FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
controlPanel.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
rootPane.putClientProperty( "apple.awt.fullscreenable", true );
}
// integrate into macOS screen menu
@@ -929,6 +928,12 @@ class FlatThemeFileEditor
y = Math.max( Math.min( y, r.height - h ), r.y );
}
// On macOS, the window may be empty if it spans the whole screen height
// and client property apple.awt.fullWindowContent is set to true.
// Invoking addNotify() before setting window bounds fixes this issue.
if( SystemInfo.isMacOS && !isDisplayable() )
addNotify();
setBounds( x, y, w, h );
return;
} catch( NumberFormatException ex ) {
@@ -1015,6 +1020,7 @@ class FlatThemeFileEditor
helpMenu = new JMenu();
aboutMenuItem = new JMenuItem();
controlPanel = new JPanel();
JPanel macFullWindowContentButtonsPlaceholder = new JPanel();
directoryLabel = new JLabel();
directoryField = new FlatThemeFileEditor.DirectoryComboBox();
openDirectoryButton = new JButton();
@@ -1218,7 +1224,7 @@ class FlatThemeFileEditor
//======== controlPanel ========
{
controlPanel.setLayout(new MigLayout(
"hidemode 3",
"insets panel,hidemode 3",
// columns
"[fill]" +
"[grow,fill]" +
@@ -1226,6 +1232,12 @@ class FlatThemeFileEditor
// rows
"[]"));
//======== macFullWindowContentButtonsPlaceholder ========
{
macFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
}
controlPanel.add(macFullWindowContentButtonsPlaceholder, "west");
//---- directoryLabel ----
directoryLabel.setText("Directory:");
controlPanel.add(directoryLabel, "cell 0 0");
@@ -1258,6 +1270,9 @@ class FlatThemeFileEditor
lafButtonGroup.add(lightLafMenuItem);
lafButtonGroup.add(darkLafMenuItem);
// JFormDesigner - End of component initialization //GEN-END:initComponents
// on macOS, panel on left side of control bar is a placeholder for title bar buttons in fullWindowContent mode
macFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables

View File

@@ -13,11 +13,19 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.WindowListener", "windowDeactivated", "windowDeactivated", false ) )
addEvent( new FormEvent( "java.awt.event.WindowListener", "windowActivated", "windowActivated", false ) )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$layoutConstraints": "insets panel,hidemode 3"
"$columnConstraints": "[fill][grow,fill][fill]"
"$rowConstraints": "[]"
} ) {
name: "controlPanel"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "macFullWindowContentButtonsPlaceholder"
auxiliary() {
"JavaCodeGenerator.variableLocal": true
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "west"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "directoryLabel"
"text": "Directory:"

View File

@@ -29,6 +29,7 @@ Button.default.hoverBackground
Button.default.hoverBorderColor
Button.default.hoverForeground
Button.default.pressedBackground
Button.default.pressedBorderColor
Button.default.pressedForeground
Button.default.startBackground
Button.default.startBorderColor
@@ -37,6 +38,7 @@ Button.disabledBackground
Button.disabledBorderColor
Button.disabledForeground
Button.disabledSelectedBackground
Button.disabledSelectedBorderColor
Button.disabledSelectedForeground
Button.disabledText
Button.endBackground
@@ -45,21 +47,26 @@ Button.focusInputMap
Button.focusedBackground
Button.focusedBorderColor
Button.focusedForeground
Button.focusedSelectedBorderColor
Button.font
Button.foreground
Button.highlight
Button.hoverBackground
Button.hoverBorderColor
Button.hoverForeground
Button.hoverSelectedBorderColor
Button.iconTextGap
Button.innerFocusWidth
Button.light
Button.margin
Button.minimumWidth
Button.pressedBackground
Button.pressedBorderColor
Button.pressedForeground
Button.pressedSelectedBorderColor
Button.rollover
Button.selectedBackground
Button.selectedBorderColor
Button.selectedForeground
Button.shadow
Button.startBackground

View File

@@ -14,8 +14,8 @@
# limitations under the License.
#
flatlaf.releaseVersion = 3.4.1
flatlaf.developmentVersion = 3.5-SNAPSHOT
flatlaf.releaseVersion = 3.5.1
flatlaf.developmentVersion = 3.6-SNAPSHOT
org.gradle.parallel = true
# org.gradle.warning.mode = all

View File

@@ -38,7 +38,7 @@ miglayout-swing = "com.miglayout:miglayout-swing:5.3"
jgoodies-forms = "com.jgoodies:jgoodies-forms:1.9.0"
# flatlaf-theme-editor
fifesoft-rsyntaxtextarea = "com.fifesoft:rsyntaxtextarea:3.3.4"
fifesoft-rsyntaxtextarea = "com.fifesoft:rsyntaxtextarea:3.4.0"
fifesoft-autocomplete = "com.fifesoft:autocomplete:3.3.1"
# flatlaf-testing