Compare commits

...

29 Commits
2.3 ... 2.4

Author SHA1 Message Date
Karl Tauber
f842530537 release 2.4 2022-07-13 23:43:02 +02:00
Karl Tauber
63077bbb19 Merge PR #565: Window title bar usability improvements (Windows 10/11 only) 2022-07-13 23:28:38 +02:00
Karl Tauber
4dad337377 Window decorations: fixed app icon hit test bounds if icon is shown beside title 2022-07-13 23:11:32 +02:00
Karl Tauber
10a965d765 Window decorations: option to show window icon beside window title, if menu bar is embedded or title is centered 2022-07-13 17:58:25 +02:00
Karl Tauber
3e9c9c9066 execute FlatLaf.initialize() and uninitialize() only for current laf
For NetBeans GUI builder, which invokes `FlatLaf.initialize()` multiple times for preview, but never invokes `FlatLaf.uninitialize()`.

(https://github.com/apache/netbeans/issues/4231)
2022-07-12 12:13:19 +02:00
Karl Tauber
8b5a738e65 Menus: avoid that SubMenuUsabilityHelper can be installed multiple times, which can freeze the application caused pushing multiple event queues and popping wrong event queue first
(e.g. NetBeans Form Editor invokes `FlatLaf.initialize()` but not `uninitialize()`)

(PR #490; https://github.com/apache/netbeans/issues/4231)
2022-07-12 10:33:53 +02:00
Karl Tauber
2c041dce3a Window decorations: add small resize area at top of embedded menu bar only if frame is resizable 2022-07-11 17:47:04 +02:00
Karl Tauber
ef151c68f4 Window decorations:
- improved title bar usability by using larger gaps and minimum sizes
- added minimum gap between embedded menu bar and window title
- fixed oscillating title while resizing window width
- fixed lost right-to-left component orientation in title bar when switching Laf
2022-07-11 17:28:30 +02:00
Karl Tauber
52feaac92a Window decorations: no longer reduce height of window title bar if it has an embedded menu bar and is maximized 2022-07-10 14:03:45 +02:00
Karl Tauber
cddbb3d7d4 Window decorations: make sure that a horizontal glue in embedded menu bar has a minimum width and is always visible 2022-07-10 13:57:37 +02:00
Karl Tauber
42764550e6 Window decorations: improved window title bar layout for small window widths:
- width of iconify/maximize/close buttons is reduced to give more space to embedded menu bar and title
- window title now has a minimum width to always allow moving window
2022-07-09 19:54:29 +02:00
Karl Tauber
6ee737b314 Window decorations: small area at top of embedded menu bar to resize window 2022-07-09 10:30:33 +02:00
Karl Tauber
f460ef7685 UI defaults dumps updated for commits b82ee2ef61 and 93e0496fd2 2022-07-09 10:19:54 +02:00
Karl Tauber
9977bcb468 Window decorations: do not center window title if embedded menu bar is empty or has no menus at left side, but some components at right side (issue #558) 2022-07-09 00:04:51 +02:00
Karl Tauber
7437d984c7 Theme Editor: accept @ as identifier character, which includes it in selection when double clicking e.g. on @background 2022-07-08 17:53:49 +02:00
Karl Tauber
5cd0b2403c Theme Editor: find/replace bar improvements:
- always use editor selection to search if `Ctrl+F` is pressed
- keep find/replace bar open if switching to another editor
- mark matches when switching to another editor
2022-07-08 17:52:08 +02:00
Karl Tauber
a372da22f3 Extras: FlatInspector:
- support embedding into SWT
- added "MigLayout visual padding" to tooltip

fixed typo in MigLayoutVisualPadding.java
2022-07-04 11:09:06 +02:00
Karl Tauber
8b10d3ba5a Native window decorations: fixed missing top window border in dark themes if window drop shadows are disabled in system settings (issue #554) 2022-07-02 23:26:34 +02:00
Karl Tauber
a8b15c6a12 MenuItem: fixed sometimes wrapped HTML text on HiDPI screens on Windows 2022-07-02 22:38:37 +02:00
Karl Tauber
23bac7e5fd Native window decorations: do not use window decorations if system property sun.java2d.opengl is true on Windows 10 (issue #540) 2022-07-02 00:29:29 +02:00
Karl Tauber
b82ee2ef61 Typography: no longer use Consolas or Courier New as monospaced font on Windows because they have bad vertically placement 2022-07-02 00:25:07 +02:00
Karl Tauber
b7761f4b71 HiDPIUtils: support rotated graphics (issue #557) 2022-07-01 15:34:04 +02:00
Karl Tauber
f9a4f9771c Testing: FlatPaintingStringTest:
- added "Fonts" combobox to test various fonts
- reworked/fixed text painting/sizing to get correct results
2022-06-07 11:03:34 +02:00
Karl Tauber
d2acb2c98a HiDPIUtils: reimplemented HiDPIUtils.scale() to make it easier to read and more understandable
(no longer re-using dx1 and dy2 variables for different kind of values)
2022-06-05 23:43:13 +02:00
Karl Tauber
d60bd5df14 FlatEmptyBorder: fixed possible NPE if passed component is null 2022-06-05 00:46:43 +02:00
Karl Tauber
73b6ca3762 ComboBox: fixed vertical alignment of text in popup list with text in combo box in IntelliJ/Darcula themes 2022-06-04 20:15:31 +02:00
Karl Tauber
6c18431a30 TableHeader: fixed exception when changing table structure (e.g. removing column) from a table header popup menu action (issue #532) 2022-05-31 18:56:06 +02:00
Karl Tauber
a49d20249f Gradle: do not set Multi-Release: true in META-INF/MANIFEST.MF if not needed 2022-05-31 15:35:56 +02:00
Karl Tauber
ad384acd57 updated sigtest for FlatLaf 2.3
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-28 18:43:37 +02:00
41 changed files with 1717 additions and 232 deletions

View File

@@ -1,6 +1,50 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 2.4
#### New features and improvements
- Native window decorations (Windows 10/11 only):
- There is now a small area at top of the embedded menu bar to resize the
window.
- Improved window title bar layout for small window widths:
- Width of iconify/maximize/close buttons is reduced (if necessary) to give
more space to embedded menu bar and title.
- Window title now has a minimum width to always allow moving window
(click-and-drag on window title). Instead, embedded menu bar is made
smaller.
- Option to show window icon beside window title, if menu bar is embedded or
title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`.
- No longer reduce height of window title bar if it has an embedded menu bar
and is maximized.
#### Fixed bugs
- ComboBox: Fixed vertical alignment of text in popup list with text in combo
box in IntelliJ/Darcula themes.
- Menus: Fixed application freeze under very special conditions (invoking
`FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has
submenus. See
[NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682)
for details.
- MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows.
- TableHeader: Fixed exception when changing table structure (e.g. removing
column) from a table header popup menu action. (issue #532)
- `HiDPIUtils.paintAtScale1x()` now supports rotated graphics. (issue #557)
- Typography: No longer use `Consolas` or `Courier New` as monospaced font on
Windows because they have bad vertically placement.
- Native window decorations (Windows 10/11 only):
- Do not center window title if embedded menu bar is empty or has no menus at
left side, but some components at right side. (issue #558)
- Do not use window decorations if system property `sun.java2d.opengl` is
`true` on Windows 10. (issue #540)
- Fixed missing top window border in dark themes if window drop shadows are
disabled in system settings. (issue #554; Windows 10 only)
- Right-to-left component orientation of title bar was lost when switching
theme.
## 2.3 ## 2.3
#### New features and improvements #### New features and improvements

View File

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

View File

@@ -61,8 +61,6 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
} }
jar { jar {
manifest.attributes( "Multi-Release" to "true" )
from( sourceSets["module-info"].output ) { from( sourceSets["module-info"].output ) {
include( "module-info.class" ) include( "module-info.class" )
} }

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 2.2 #Version 2.3
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType" fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -75,6 +75,7 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground" fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor" fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight" fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement"
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback" fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent" fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon" fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
@@ -665,6 +666,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init() cons public init()
fld public final static boolean isAARCH64 fld public final static boolean isAARCH64
fld public final static boolean isJava_11_orLater fld public final static boolean isJava_11_orLater
fld public final static boolean isJava_12_orLater
fld public final static boolean isJava_15_orLater fld public final static boolean isJava_15_orLater
fld public final static boolean isJava_17_orLater fld public final static boolean isJava_17_orLater
fld public final static boolean isJava_18_orLater fld public final static boolean isJava_18_orLater
@@ -673,6 +675,7 @@ fld public final static boolean isJetBrainsJVM
fld public final static boolean isJetBrainsJVM_11_orLater fld public final static boolean isJetBrainsJVM_11_orLater
fld public final static boolean isKDE fld public final static boolean isKDE
fld public final static boolean isLinux fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_orLater fld public final static boolean isMacOS_10_11_ElCapitan_orLater
fld public final static boolean isMacOS_10_14_Mojave_orLater fld public final static boolean isMacOS_10_14_Mojave_orLater

View File

@@ -103,7 +103,7 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory; private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler; private MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper; private boolean subMenuUsabilityHelperInstalled;
private Consumer<UIDefaults> postInitialization; private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters; private List<Function<Object, Object>> uiDefaultsGetters;
@@ -232,6 +232,15 @@ public abstract class FlatLaf
@Override @Override
public void initialize() { public void initialize() {
// do not initialize if this is not the current look and feel
// This is only necessary for special Laf usage. E.g. in GUI builders,
// which may use multiple Lafs and may invoke this method directly.
// This avoids that listeners and factories are installed multiple times.
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
// this also avoids that listeners stay registered in the system.
if( UIManager.getLookAndFeel() != this )
return;
if( SystemInfo.isMacOS ) if( SystemInfo.isMacOS )
initializeAqua(); initializeAqua();
@@ -246,8 +255,7 @@ public abstract class FlatLaf
mnemonicHandler.install(); mnemonicHandler.install();
// install submenu usability helper // install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper(); subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
subMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes // listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) { if( SystemInfo.isWindows ) {
@@ -304,6 +312,10 @@ public abstract class FlatLaf
@Override @Override
public void uninitialize() { public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener // remove desktop property listener
if( desktopPropertyListener != null ) { if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit(); Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -329,9 +341,9 @@ public abstract class FlatLaf
} }
// uninstall submenu usability helper // uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) { if( subMenuUsabilityHelperInstalled ) {
subMenuUsabilityHelper.uninstall(); SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null; subMenuUsabilityHelperInstalled = false;
} }
// restore default link color // restore default link color

View File

@@ -59,6 +59,11 @@ class SubMenuUsabilityHelper
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle"; private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle"; private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
// Using a static field to ensure that there is only one instance in the system.
// Multiple instances would freeze the application.
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
private static SubMenuUsabilityHelper instance;
private SubMenuEventQueue subMenuEventQueue; private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter; private SafeTrianglePainter safeTrianglePainter;
private boolean changePending; private boolean changePending;
@@ -74,13 +79,22 @@ class SubMenuUsabilityHelper
private Rectangle invokerBounds; private Rectangle invokerBounds;
void install() { static synchronized boolean install() {
MenuSelectionManager.defaultManager().addChangeListener( this ); if( instance != null )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
} }
void uninstall() { static synchronized void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this ); if( instance == null )
uninstallEventQueue(); return;
MenuSelectionManager.defaultManager().removeChangeListener( instance );
instance.uninstallEventQueue();
instance = null;
} }
@Override @Override

View File

@@ -608,7 +608,7 @@ public class FlatComboBoxUI
boolean shouldValidate = (c instanceof JPanel); boolean shouldValidate = (c instanceof JPanel);
paddingBorder.install( c ); paddingBorder.install( c, 0 );
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate ); currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
paddingBorder.uninstall(); paddingBorder.uninstall();
@@ -682,7 +682,7 @@ public class FlatComboBoxUI
@Override @Override
protected Dimension getSizeForComponent( Component comp ) { protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp ); paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp ); Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall(); paddingBorder.uninstall();
return size; return size;
@@ -900,7 +900,7 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() ); c.applyComponentOrientation( comboBox.getComponentOrientation() );
paddingBorder.install( c ); paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
return c; return c;
} }
@@ -923,6 +923,7 @@ public class FlatComboBoxUI
private Insets padding; private Insets padding;
private JComponent rendererComponent; private JComponent rendererComponent;
private Border rendererBorder; private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) { CellPaddingBorder( Insets padding ) {
this.padding = padding; this.padding = padding;
@@ -930,10 +931,12 @@ public class FlatComboBoxUI
// using synchronized to avoid problems with code that modifies combo box // using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc) not on AWT thread (which should be not done) // (model, selection, etc) not on AWT thread (which should be not done)
synchronized void install( Component c ) { synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) ) if( !(c instanceof JComponent) )
return; return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c; JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder(); Border oldBorder = jc.getBorder();
if( oldBorder == this ) if( oldBorder == this )
@@ -987,6 +990,12 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom; insets.bottom = padding.bottom;
insets.right = padding.right; insets.right = padding.right;
} }
// if used in popup list, add focus width for exact vertical alignment
// of text in popup list with text in combobox
insets.left += focusWidth;
insets.right += focusWidth;
return insets; return insets;
} }

View File

@@ -56,7 +56,7 @@ public class FlatEmptyBorder
protected static Insets scaleInsets( Component c, Insets insets, protected static Insets scaleInsets( Component c, Insets insets,
int top, int left, int bottom, int right ) int top, int left, int bottom, int right )
{ {
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight(); boolean leftToRight = left == right || c == null || c.getComponentOrientation().isLeftToRight();
insets.left = scale( leftToRight ? left : right ); insets.left = scale( leftToRight ? left : right );
insets.top = scale( top ); insets.top = scale( top );
insets.right = scale( leftToRight ? right : left ); insets.right = scale( leftToRight ? right : left );

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@@ -27,6 +29,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.ActionMap; import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI; import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}. * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -100,6 +105,10 @@ public class FlatMenuBarUI
super.installDefaults(); super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false ); LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
} }
@Override @Override
@@ -221,6 +230,130 @@ public class FlatMenuBarUI
rootPane.getWindowDecorationStyle() != JRootPane.NONE; rootPane.getWindowDecorationStyle() != JRootPane.NONE;
} }
//---- class FlatMenuBarLayout --------------------------------------------
/**
* @since 2.4
*/
protected static class FlatMenuBarLayout
extends DefaultMenuLayout
{
public FlatMenuBarLayout( Container target ) {
super( target, BoxLayout.LINE_AXIS );
}
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// The only purpose of the code below is to make sure that a horizontal glue,
// which can be used to move window and displays the window title in embedded menu bar,
// is always visible within the menu bar bounds and has a minimum width.
// If this is not the case, the horizontal glue is made larger and
// components that are on the left side of the glue are made smaller.
// get root pane and check whether this menu bar is the root pane menu bar
JRootPane rootPane = SwingUtilities.getRootPane( target );
if( rootPane == null || rootPane.getJMenuBar() != target )
return;
// get title pane and check whether menu bar is embedded
FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane );
if( titlePane == null || !titlePane.isMenuBarEmbedded() )
return;
// check whether there is a horizontal glue (used for window title in embedded menu bar)
// and check minimum width of horizontal glue
Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target );
int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth );
if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) {
// get index of glue component
int glueIndex = -1;
Component[] components = target.getComponents();
for( int i = components.length - 1; i >= 0; i-- ) {
if( components[i] == horizontalGlue ) {
glueIndex = i;
break;
}
}
if( glueIndex < 0 )
return; // should never happen
if( target.getComponentOrientation().isLeftToRight() ) {
// left-to-right
// make horizontal glue wider (minimum title width)
int offset = minTitleWidth - horizontalGlue.getWidth();
horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() );
// check whether glue is fully visible
int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth;
if( minGlueX < horizontalGlue.getX() ) {
// move glue to the left to make it fully visible
offset -= (horizontalGlue.getX() - minGlueX);
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
// shrink and move components that are on the left side of the glue
for( int i = glueIndex - 1; i >= 0; i-- ) {
Component c = components[i];
if( c.getX() > minGlueX ) {
// move component and set width to zero
c.setBounds( minGlueX, c.getY(), 0, c.getHeight() );
} else {
// reduce size of component
c.setSize( minGlueX - c.getX(), c.getHeight() );
break;
}
}
}
// move components that are on the right side of the glue
for( int i = glueIndex + 1; i < components.length; i++ ) {
Component c = components[i];
c.setLocation( c.getX() + offset, c.getY() );
}
} else {
// right-to-left
// make horizontal glue wider (minimum title width)
int offset = minTitleWidth - horizontalGlue.getWidth();
horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(),
minTitleWidth, horizontalGlue.getHeight() );
// check whether glue is fully visible
int minGlueX = target.getInsets().left;
if( minGlueX > horizontalGlue.getX() ) {
// move glue to the right to make it fully visible
offset -= (horizontalGlue.getX() - minGlueX);
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
// shrink and move components that are on the right side of the glue
int x = horizontalGlue.getX() + horizontalGlue.getWidth();
for( int i = glueIndex - 1; i >= 0; i-- ) {
Component c = components[i];
if( c.getX() + c.getWidth() < x ) {
// move component and set width to zero
c.setBounds( x, c.getY(), 0, c.getHeight() );
} else {
// move component and reduce size
c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() );
break;
}
}
}
// move components that are on the left side of the glue
for( int i = glueIndex + 1; i < components.length; i++ ) {
Component c = components[i];
c.setLocation( c.getX() - offset, c.getY() );
}
}
}
}
}
//---- class TakeFocus ---------------------------------------------------- //---- class TakeFocus ----------------------------------------------------
/** /**

View File

@@ -446,6 +446,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem, protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
Rectangle textRect, View htmlView, Color selectionForeground ) Rectangle textRect, View htmlView, Color selectionForeground )
{ {
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
// the width of the HTML view may be initially too small (because component
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
//
// If using a too small width for htmlView.paint(), the view would rearrange
// its children and wrap them to two lines. To avoid this, use view preferred X span
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
//
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
textRect = new Rectangle( textRect );
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
if( isArmedOrSelected( menuItem ) && selectionForeground != null ) if( isArmedOrSelected( menuItem ) && selectionForeground != null )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground ); g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );

View File

@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatNativeWindowBorder public class FlatNativeWindowBorder
{ {
// can use window decorations if: // can use window decorations if:
// - on Windows 10 // - on Windows 10 or later
// - not if system property "sun.java2d.opengl" is true on Windows 10
// - not when running in JetBrains Projector, Webswing or WinPE // - not when running in JetBrains Projector, Webswing or WinPE
// - not disabled via system property // - not disabled via system property
private static final boolean canUseWindowDecorations = private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater && SystemInfo.isWindows_10_orLater &&
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
!SystemInfo.isProjector && !SystemInfo.isProjector &&
!SystemInfo.isWebswing && !SystemInfo.isWebswing &&
!SystemInfo.isWinPE && !SystemInfo.isWinPE &&

View File

@@ -130,7 +130,7 @@ public class FlatPopupMenuUI
LayoutManager layout = popupMenu.getLayout(); LayoutManager layout = popupMenu.getLayout();
if( layout == null || layout instanceof UIResource ) if( layout == null || layout instanceof UIResource )
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
} }
@Override @Override
@@ -230,12 +230,15 @@ public class FlatPopupMenuUI
return screenBounds.height - screenInsets.top - screenInsets.bottom; return screenBounds.height - screenInsets.top - screenInsets.bottom;
} }
//---- class FlatMenuLayout ----------------------------------------------- //---- class FlatPopupMenuLayout ------------------------------------------
protected static class FlatMenuLayout /**
* @since 2.4
*/
protected static class FlatPopupMenuLayout
extends DefaultMenuLayout extends DefaultMenuLayout
{ {
public FlatMenuLayout( Container target, int axis ) { public FlatPopupMenuLayout( Container target, int axis ) {
super( target, axis ); super( target, axis );
} }

View File

@@ -364,6 +364,12 @@ public class FlatRootPaneUI
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded(); ((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
} }
/** @since 2.4 */
protected static FlatTitlePane getTitlePane( JRootPane rootPane ) {
RootPaneUI ui = rootPane.getUI();
return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null;
}
//---- class FlatRootLayout ----------------------------------------------- //---- class FlatRootLayout -----------------------------------------------
protected class FlatRootLayout protected class FlatRootLayout

View File

@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI; import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -195,6 +197,8 @@ public class FlatTableHeaderUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
fixDraggedAndResizingColumns( header );
TableColumnModel columnModel = header.getColumnModel(); TableColumnModel columnModel = header.getColumnModel();
if( columnModel.getColumnCount() <= 0 ) if( columnModel.getColumnCount() <= 0 )
return; return;
@@ -269,6 +273,32 @@ public class FlatTableHeaderUI
return size; return size;
} }
static void fixDraggedAndResizingColumns( JTableHeader header ) {
if( header == null )
return;
// Dragged column may be outdated in the case that the table structure
// was changed from a table header popup menu action. In this case
// the paint methods in BasicTableHeaderUI and BasicTableUI would throw exceptions.
TableColumn draggedColumn = header.getDraggedColumn();
if( draggedColumn != null && !isValidColumn( header.getColumnModel(), draggedColumn ) )
header.setDraggedColumn( null );
// also clear outdated resizing column (although this seems not cause exceptions)
TableColumn resizingColumn = header.getResizingColumn();
if( resizingColumn != null && !isValidColumn( header.getColumnModel(), resizingColumn ) )
header.setResizingColumn( null );
}
private static boolean isValidColumn( TableColumnModel cm, TableColumn column ) {
int count = cm.getColumnCount();
for( int i = 0; i < count; i++ ) {
if( cm.getColumn( i ) == column )
return true;
}
return false;
}
//---- class FlatTableCellHeaderRenderer ---------------------------------- //---- class FlatTableCellHeaderRenderer ----------------------------------
/** /**

View File

@@ -314,6 +314,8 @@ public class FlatTableUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
FlatTableHeaderUI.fixDraggedAndResizingColumns( table.getTableHeader() );
boolean horizontalLines = table.getShowHorizontalLines(); boolean horizontalLines = table.getShowHorizontalLines();
boolean verticalLines = table.getShowVerticalLines(); boolean verticalLines = table.getShowVerticalLines();
if( horizontalLines || verticalLines ) { if( horizontalLines || verticalLines ) {

View File

@@ -24,6 +24,7 @@ import java.awt.Container;
import java.awt.Dialog; import java.awt.Dialog;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.GraphicsConfiguration; import java.awt.GraphicsConfiguration;
@@ -50,13 +51,13 @@ import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.AbstractBorder; import javax.swing.border.AbstractBorder;
@@ -83,10 +84,14 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.iconMargins Insets * @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean * @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.titleMinimumWidth int
* @uiDefault TitlePane.buttonMinimumWidth int
* @uiDefault TitlePane.buttonMaximizedHeight int * @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean * @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean * @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int * @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon * @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon * @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon * @uiDefault TitlePane.maximizeIcon Icon
@@ -107,10 +112,15 @@ public class FlatTitlePane
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true ); /** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 ); /** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
/** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 60 );
/** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 30 );
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 ); /** @since 2.4 */ protected final boolean showIconBesideTitle = UIManager.getBoolean( "TitlePane.showIconBesideTitle" );
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 );
/** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 );
/** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 );
protected final JRootPane rootPane; protected final JRootPane rootPane;
@@ -142,6 +152,8 @@ public class FlatTitlePane
// necessary for closing window with double-click on icon // necessary for closing window with double-click on icon
iconLabel.addMouseListener( handler ); iconLabel.addMouseListener( handler );
applyComponentOrientation( rootPane.getComponentOrientation() );
} }
protected FlatTitlePaneBorder createTitlePaneBorder() { protected FlatTitlePaneBorder createTitlePaneBorder() {
@@ -163,7 +175,6 @@ public class FlatTitlePane
}; };
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) ); iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) ); titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) ); leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false ); leftPanel.setOpaque( false );
@@ -183,18 +194,52 @@ public class FlatTitlePane
setLayout( new BorderLayout() { setLayout( new BorderLayout() {
@Override @Override
public void layoutContainer( Container target ) { public void layoutContainer( Container target ) {
super.layoutContainer( target ); // compute available bounds
// make left panel (with embedded menu bar) smaller if horizontal space is rare
// to avoid that embedded menu bar overlaps button bar
Insets insets = target.getInsets(); Insets insets = target.getInsets();
int width = target.getWidth() - insets.left - insets.right; int x = insets.left;
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) { int y = insets.top;
int oldWidth = leftPanel.getWidth(); int w = target.getWidth() - insets.left - insets.right;
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 ); int h = target.getHeight() - insets.top - insets.bottom;
leftPanel.setSize( newWidth, leftPanel.getHeight() );
if( !getComponentOrientation().isLeftToRight() ) // compute widths
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() ); int leftWidth = leftPanel.getPreferredSize().width;
int buttonsWidth = buttonPanel.getPreferredSize().width;
int titleWidth = w - leftWidth - buttonsWidth;
int minTitleWidth = UIScale.scale( titleMinimumWidth );
// increase minimum width if icon is show besides the title
Icon icon = titleLabel.getIcon();
if( icon != null ) {
Insets iconInsets = iconLabel.getInsets();
int iconTextGap = titleLabel.getComponentOrientation().isLeftToRight() ? iconInsets.right : iconInsets.left;
minTitleWidth += icon.getIconWidth() + iconTextGap;
}
// if title is too small, reduce width of buttons
if( titleWidth < minTitleWidth ) {
buttonsWidth = Math.max( buttonsWidth - (minTitleWidth - titleWidth), buttonPanel.getMinimumSize().width );
titleWidth = w - leftWidth - buttonsWidth;
}
// if title is still too small, reduce width of left panel (icon and embedded menu bar)
if( titleWidth < minTitleWidth ) {
int minLeftWidth = iconLabel.isVisible()
? iconLabel.getWidth() - iconLabel.getInsets().right
: UIScale.scale( noIconLeftGap );
leftWidth = Math.max( leftWidth - (minTitleWidth - titleWidth), minLeftWidth );
titleWidth = w - leftWidth - buttonsWidth;
}
if( target.getComponentOrientation().isLeftToRight() ) {
// left-to-right
leftPanel.setBounds( x, y, leftWidth, h );
titleLabel.setBounds( x + leftWidth, y, titleWidth, h );
buttonPanel.setBounds( x + leftWidth + titleWidth, y, buttonsWidth, h );
} else {
// right-to-left
buttonPanel.setBounds( x, y, buttonsWidth, h );
titleLabel.setBounds( x + buttonsWidth, y, titleWidth, h );
leftPanel.setBounds( x + buttonsWidth + titleWidth, y, leftWidth, h );
} }
// If menu bar is embedded and contains a horizontal glue component, // If menu bar is embedded and contains a horizontal glue component,
@@ -233,10 +278,7 @@ public class FlatTitlePane
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize(); Dimension size = super.getPreferredSize();
if( buttonMaximizedHeight > 0 && if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
// make title pane height smaller when frame is maximized // make title pane height smaller when frame is maximized
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) ); size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
} }
@@ -259,7 +301,13 @@ public class FlatTitlePane
} }
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) { protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
JButton button = new JButton( UIManager.getIcon( iconKey ) ); JButton button = new JButton( UIManager.getIcon( iconKey ) ) {
@Override
public Dimension getMinimumSize() {
// allow the button to shrink if space is rare
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
}
};
button.setFocusable( false ); button.setFocusable( false );
button.setContentAreaFilled( false ); button.setContentAreaFilled( false );
button.setBorder( BorderFactory.createEmptyBorder() ); button.setBorder( BorderFactory.createEmptyBorder() );
@@ -356,11 +404,12 @@ public class FlatTitlePane
boolean hasIcon = (images != null && !images.isEmpty()); boolean hasIcon = (images != null && !images.isEmpty());
// set icon // set icon
iconLabel.setIcon( hasIcon ? new FlatTitlePaneIcon( images, iconSize ) : null ); iconLabel.setIcon( hasIcon && !showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
titleLabel.setIcon( hasIcon && showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
// show/hide icon // show/hide icon
iconLabel.setVisible( hasIcon ); iconLabel.setVisible( hasIcon && !showIconBesideTitle );
leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) ); leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
updateNativeTitleBarHeightAndHitTestSpotsLater(); updateNativeTitleBarHeightAndHitTestSpotsLater();
} }
@@ -567,6 +616,11 @@ debug*/
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED ); frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
} }
/** @since 2.4 */
protected boolean isWindowMaximized() {
return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
/** /**
* Maximizes the window. * Maximizes the window.
*/ */
@@ -729,7 +783,7 @@ debug*/
List<Rectangle> hitTestSpots = new ArrayList<>(); List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null; Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) { if( !showIconBesideTitle && iconLabel.isVisible() ) {
// compute real icon size (without insets; 1px larger for easier hitting) // compute real icon size (without insets; 1px larger for easier hitting)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window ); Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets(); Insets iconInsets = iconLabel.getInsets();
@@ -741,9 +795,7 @@ debug*/
// if frame is maximized, increase icon bounds to upper-left corner // if frame is maximized, increase icon bounds to upper-left corner
// of window to allow closing window via double-click in upper-left corner // of window to allow closing window via double-click in upper-left corner
if( window instanceof Frame && if( isWindowMaximized() ) {
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
iconBounds.height += iconBounds.y; iconBounds.height += iconBounds.y;
iconBounds.y = 0; iconBounds.y = 0;
@@ -758,6 +810,38 @@ debug*/
hitTestSpots.add( iconBounds ); hitTestSpots.add( iconBounds );
else else
appIconBounds = iconBounds; appIconBounds = iconBounds;
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
// compute real icon bounds
Insets insets = titleLabel.getInsets();
Rectangle viewR = new Rectangle( insets.left, insets.top,
titleLabel.getWidth() - insets.left - insets.right,
titleLabel.getHeight() - insets.top - insets.bottom );
Rectangle iconR = new Rectangle();
Rectangle textR = new Rectangle();
ui.layoutCL( titleLabel, titleLabel.getFontMetrics( titleLabel.getFont() ),
titleLabel.getText(), titleLabel.getIcon(),
viewR, iconR, textR );
// Windows shows the window system menu only in the upper-left corner
if( iconR.x == 0 ) {
// convert icon location to window coordinates
Point location = SwingUtilities.convertPoint( titleLabel, 0, 0, window );
iconR.x += location.x;
iconR.y += location.y;
// make icon bounds 1px larger for easier hitting
iconR.x -= 1;
iconR.y -= 1;
iconR.width += 2;
iconR.height += 2;
if( hasJBRCustomDecoration() )
hitTestSpots.add( iconR );
else
appIconBounds = iconR;
}
} }
Rectangle r = getNativeHitTestSpot( buttonPanel ); Rectangle r = getNativeHitTestSpot( buttonPanel );
@@ -768,6 +852,15 @@ debug*/
if( hasVisibleEmbeddedMenuBar( menuBar ) ) { if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar ); r = getNativeHitTestSpot( menuBar );
if( r != null ) { if( r != null ) {
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
// to have a small area above the menu bar to resize the window
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
// limit to 8, because Windows does not use a larger height
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
r.y += resizeHeight;
r.height -= resizeHeight;
}
Component horizontalGlue = findHorizontalGlue( menuBar ); Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) { if( horizontalGlue != null ) {
// If menu bar is embedded and contains a horizontal glue component, // If menu bar is embedded and contains a horizontal glue component,
@@ -854,7 +947,7 @@ debug*/
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
insets.bottom += UIScale.scale( 1 ); insets.bottom += UIScale.scale( 1 );
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() ); insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
return insets; return insets;
@@ -873,7 +966,7 @@ debug*/
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight ); FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
} }
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) ) if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
} }
@@ -881,10 +974,6 @@ debug*/
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null; return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
} }
protected boolean isWindowMaximized( Component c ) {
return window instanceof Frame && (((Frame) window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
} }
//---- class FlatTitleLabelUI --------------------------------------------- //---- class FlatTitleLabelUI ---------------------------------------------
@@ -898,32 +987,101 @@ debug*/
} }
@Override @Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) { protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon,
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ); Rectangle viewR, Rectangle iconR, Rectangle textR )
int labelWidth = l.getWidth(); {
int textWidth = labelWidth - (textX * 2); JMenuBar menuBar = rootPane.getJMenuBar();
int gap = UIScale.scale( menuBarTitleGap ); boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar );
boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar );
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise, leave it centered within free space (label bounds).
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
textX = centeredTextX;
} else {
// leading aligned
boolean leftToRight = getComponentOrientation().isLeftToRight(); boolean leftToRight = getComponentOrientation().isLeftToRight();
Insets insets = l.getInsets();
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right); if( hasEmbeddedMenuBar ) {
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth; int minGap = UIScale.scale( menuBarTitleMinimumGap );
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
textX = leadingTextX; // apply minimum leading gap (between embedded menu bar and title)
if( hasEmbeddedLeadingMenus ) {
if( leftToRight )
viewR.x += minGap;
viewR.width -= minGap;
} }
super.paintEnabledText( l, g, s, textX, textY ); // apply minimum trailing gap (between title and right aligned components of embedded menu bar)
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null && menuBar.getComponent( menuBar.getComponentCount() - 1 ) != horizontalGlue ) {
if( !leftToRight )
viewR.x += minGap;
viewR.width -= minGap;
}
}
// compute icon width and gap (if icon is show besides the title)
int iconTextGap = 0;
int iconWidthAndGap = 0;
if( icon != null ) {
Insets iconInsets = iconLabel.getInsets();
iconTextGap = leftToRight ? iconInsets.right : iconInsets.left;
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
}
// layout title and icon (if show besides the title)
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
viewR, iconR, textR,
iconTextGap );
// compute text X location
if( !clippedText.equals( text ) ) {
// if text is clipped, align to left (or right)
textR.x = leftToRight
? viewR.x + iconWidthAndGap
: viewR.x + viewR.width - iconWidthAndGap - textR.width;
} else {
int leadingGap = hasEmbeddedLeadingMenus ? UIScale.scale( menuBarTitleGap - menuBarTitleMinimumGap ) : 0;
boolean center = hasEmbeddedLeadingMenus ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise, center within free space (label bounds).
Container parent = label.getParent();
int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap - label.getX() : -1;
textR.x = (centeredTextX >= viewR.x + leadingGap && centeredTextX + textR.width <= viewR.x + viewR.width - leadingGap)
? centeredTextX
: viewR.x + ((viewR.width - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap;
} else {
// leading aligned with leading gap, which is reduced if space is rare
textR.x = leftToRight
? Math.min( viewR.x + leadingGap + iconWidthAndGap, viewR.x + viewR.width - textR.width )
: Math.max( viewR.x + viewR.width - leadingGap - iconWidthAndGap - textR.width, viewR.x );
}
}
// compute icon X location (relative to text X location)
if( icon != null ) {
iconR.x = leftToRight
? textR.x - iconWidthAndGap
: textR.x + textR.width + iconTextGap;
}
return clippedText;
}
private boolean hasLeadingMenus( JMenuBar menuBar ) {
// check whether menu bar is empty
if( menuBar.getComponentCount() == 0 || menuBar.getWidth() == 0 )
return false;
// check whether menu bar has a leading glue component
// (no menus/components at left side)
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
boolean leftToRight = getComponentOrientation().isLeftToRight();
if( (leftToRight && horizontalGlue.getX() == 0) ||
(!leftToRight && horizontalGlue.getX() + horizontalGlue.getWidth() == menuBar.getWidth()) )
return false;
}
return true;
} }
} }

View File

@@ -195,11 +195,13 @@ public class JBRCustomDecorations
{ {
private static JBRWindowTopBorder instance; private static JBRWindowTopBorder instance;
private final Color defaultActiveBorder = new Color( 0x707070 ); private final Color activeLightColor = new Color( 0x707070 );
private final Color activeDarkColor = new Color( 0x2D2E2F );
private final Color inactiveLightColor = new Color( 0xaaaaaa ); private final Color inactiveLightColor = new Color( 0xaaaaaa );
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders; private boolean colorizationAffectsBorders;
private Color activeColor = defaultActiveBorder; private Color activeColor;
static JBRWindowTopBorder getInstance() { static JBRWindowTopBorder getInstance() {
if( instance == null ) if( instance == null )
@@ -250,7 +252,7 @@ public class JBRCustomDecorations
private Color calculateActiveBorderColor() { private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders ) if( !colorizationAffectsBorders )
return defaultActiveBorder; return null;
Color colorizationColor = getColorizationColor(); Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) { if( colorizationColor != null ) {
@@ -285,15 +287,11 @@ public class JBRCustomDecorations
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
Window window = SwingUtilities.windowForComponent( c ); Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive(); boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
// paint top border g.setColor( active
// - in light themes ? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
// - in dark themes only for active windows if colorization affects borders : (dark ? inactiveDarkColor : inactiveLightColor) );
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
if( !paintTopBorder )
return;
g.setColor( active ? activeColor : inactiveLightColor );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl ); HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
} }

View File

@@ -143,8 +143,8 @@ public class MigLayoutVisualPadding
//---- class FlatMigInsets ------------------------------------------------ //---- class FlatMigInsets ------------------------------------------------
/** /**
* Marker class to identify our visual paddings and leaf paddings, * Marker class to identify our visual paddings and leave paddings
* which were set from outside, untouched. * set from outside untouched.
*/ */
private static class FlatMigInsets private static class FlatMigInsets
extends Insets extends Insets

View File

@@ -48,30 +48,55 @@ public class HiDPIUtils
*/ */
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) { public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
// save original transform // save original transform
AffineTransform transform = g.getTransform(); AffineTransform t = g.getTransform();
// get scale X/Y and shear X/Y
double scaleX = t.getScaleX();
double scaleY = t.getScaleY();
double shearX = t.getShearX();
double shearY = t.getShearY();
// check whether rotated
// (also check for negative scale X/Y because shear X/Y are zero for 180 degrees rotation)
boolean rotated = (shearX != 0 || shearY != 0 || scaleX <= 0 || scaleY <= 0);
if( rotated ) {
// resulting scale X/Y values are always positive
scaleX = Math.hypot( scaleX, shearX );
scaleY = Math.hypot( scaleY, shearY );
} else {
// make scale X/Y positive
scaleX = Math.abs( scaleX );
scaleY = Math.abs( scaleY );
}
// check whether scaled // check whether scaled
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) { if( scaleX == 1 && scaleY == 1 ) {
painter.paint( g, x, y, width, height, 1 ); painter.paint( g, x, y, width, height, 1 );
return; return;
} }
// scale rectangle // scale rectangle
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height ); Rectangle2D.Double scaledRect = scale( scaleX, scaleY, t, x, y, width, height );
try { try {
// unscale to factor 1.0 and move origin (to whole numbers) // unscale to factor 1.0, keep rotation and move origin (to whole numbers)
g.setTransform( new AffineTransform( 1, 0, 0, 1, AffineTransform t1x;
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) ); if( rotated ) {
t1x = new AffineTransform( t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(),
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
t1x.scale( 1. / scaleX, 1. / scaleY );
} else
t1x = new AffineTransform( 1, 0, 0, 1, Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
g.setTransform( t1x );
int swidth = (int) scaledRect.width; int swidth = (int) scaledRect.width;
int sheight = (int) scaledRect.height; int sheight = (int) scaledRect.height;
// paint // paint
painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() ); painter.paint( g, 0, 0, swidth, sheight, scaleX );
} finally { } finally {
// restore original transform // restore original transform
g.setTransform( transform ); g.setTransform( t );
} }
} }
@@ -80,20 +105,16 @@ public class HiDPIUtils
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(), * sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect(). * which is used by Graphics.fillRect().
*/ */
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) { private static Rectangle2D.Double scale( double scaleX, double scaleY, AffineTransform t, int x, int y, int width, int height ) {
double dx1 = transform.getScaleX(); double px = (x * scaleX) + t.getTranslateX();
double dy2 = transform.getScaleY(); double py = (y * scaleY) + t.getTranslateY();
double px = x * dx1 + transform.getTranslateX();
double py = y * dy2 + transform.getTranslateY();
dx1 *= width;
dy2 *= height;
double newx = normalize( px ); double newX = normalize( px );
double newy = normalize( py ); double newY = normalize( py );
dx1 = normalize( px + dx1 ) - newx; double newWidth = normalize( px + (width * scaleX) ) - newX;
dy2 = normalize( py + dy2 ) - newy; double newHeight = normalize( py + (height * scaleY) ) - newY;
return new Rectangle2D.Double( newx, newy, dx1, dy2 ); return new Rectangle2D.Double( newX, newY, newWidth, newHeight );
} }
private static double normalize( double value ) { private static double normalize( double value ) {

View File

@@ -64,7 +64,7 @@ light.font = +0
semibold.font = +0 semibold.font = +0
# monospaced # monospaced
[win]monospaced.font = Consolas, "Courier New", Monospaced [win]monospaced.font = Monospaced
[mac]monospaced.font = Menlo, Monospaced [mac]monospaced.font = Menlo, Monospaced
[linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced [linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced
monospaced.font = Monospaced monospaced.font = Monospaced
@@ -789,11 +789,16 @@ TitlePane.noIconLeftGap = 8
TitlePane.iconSize = 16,16 TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,8 TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0 TitlePane.titleMargins = 3,0,3,0
TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30 TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22 TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.menuBarTitleGap = 20 TitlePane.showIconBesideTitle = false
TitlePane.menuBarTitleGap = 40
TitlePane.menuBarTitleMinimumGap = 12
TitlePane.menuBarResizeHeight = 4
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

@@ -342,8 +342,8 @@ class DemoFrame
// add font families // add font families
fontMenu.addSeparator(); fontMenu.addSeparator();
ArrayList<String> families = new ArrayList<>( Arrays.asList( ArrayList<String> families = new ArrayList<>( Arrays.asList(
"Arial", "Cantarell", "Comic Sans MS", "Courier New", "DejaVu Sans", "Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans",
"Dialog", "Liberation Sans", "Monospaced", "Noto Sans", "Roboto", "Dialog", "Liberation Sans", "Noto Sans", "Roboto",
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) ); "SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
if( !families.contains( currentFamily ) ) if( !families.contains( currentFamily ) )
families.add( currentFamily ); families.add( currentFamily );

View File

@@ -65,6 +65,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.MigLayoutVisualPadding;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -113,6 +114,7 @@ public class FlatInspector
private int inspectParentLevel; private int inspectParentLevel;
private boolean wasModifierKeyPressed; private boolean wasModifierKeyPressed;
private boolean showClassHierarchy; private boolean showClassHierarchy;
private long lastWhen;
private JComponent highlightFigure; private JComponent highlightFigure;
private Popup popup; private Popup popup;
@@ -131,8 +133,22 @@ public class FlatInspector
(((KeyEvent)e).getModifiersEx() & KEY_MODIFIERS_MASK) == (keyStroke.getModifiers() & KEY_MODIFIERS_MASK) ) (((KeyEvent)e).getModifiersEx() & KEY_MODIFIERS_MASK) == (keyStroke.getModifiers() & KEY_MODIFIERS_MASK) )
{ {
Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if( activeWindow instanceof RootPaneContainer ) { RootPaneContainer rootPaneContainer = null;
JRootPane rootPane = ((RootPaneContainer)activeWindow).getRootPane(); if( activeWindow instanceof RootPaneContainer )
rootPaneContainer = (RootPaneContainer) activeWindow;
else {
// search for root pain container in children
// (e.g. for Swing embedded into SWT)
for( Component child : activeWindow.getComponents() ) {
if( child instanceof RootPaneContainer ) {
rootPaneContainer = (RootPaneContainer) child;
break;
}
}
}
if( rootPaneContainer != null ) {
JRootPane rootPane = rootPaneContainer.getRootPane();
FlatInspector inspector = (FlatInspector) rootPane.getClientProperty( FlatInspector.class ); FlatInspector inspector = (FlatInspector) rootPane.getClientProperty( FlatInspector.class );
if( inspector == null ) { if( inspector == null ) {
inspector = new FlatInspector( rootPane ); inspector = new FlatInspector( rootPane );
@@ -172,6 +188,11 @@ public class FlatInspector
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT ) if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT )
wasModifierKeyPressed = true; wasModifierKeyPressed = true;
} else if( id == KeyEvent.KEY_RELEASED && wasModifierKeyPressed ) { } else if( id == KeyEvent.KEY_RELEASED && wasModifierKeyPressed ) {
// ignore duplicate events (for Swing embedded into SWT)
if( (keyEvent.getWhen() - lastWhen) <= 5 )
return;
lastWhen = keyEvent.getWhen();
if( keyCode == KeyEvent.VK_CONTROL ) { if( keyCode == KeyEvent.VK_CONTROL ) {
inspectParentLevel++; inspectParentLevel++;
int parentLevel = inspect( lastX, lastY ); int parentLevel = inspect( lastX, lastY );
@@ -464,6 +485,15 @@ public class FlatInspector
if( margin != null ) if( margin != null )
appendRow( buf, "Margin", toString( margin ) ); appendRow( buf, "Margin", toString( margin ) );
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( MigLayoutVisualPadding.VISUAL_PADDING_PROPERTY );
Insets visualPadding = (value instanceof int[])
? new Insets( ((int[])value)[0], ((int[])value)[1], ((int[])value)[2], ((int[])value)[3] )
: (value instanceof Insets ? (Insets) value : null);
if( visualPadding != null )
appendRow( buf, "Mig visual padding", toString( visualPadding ) );
}
Dimension prefSize = c.getPreferredSize(); Dimension prefSize = c.getPreferredSize();
Dimension minSize = c.getMinimumSize(); Dimension minSize = c.getMinimumSize();
Dimension maxSize = c.getMaximumSize(); Dimension maxSize = c.getMaximumSize();

View File

@@ -129,7 +129,7 @@
#---- monospaced ---- #---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI] - monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI] + monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]

View File

@@ -1214,6 +1214,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #303234 HSL 210 4 20 javax.swing.plaf.ColorUIResource [UI] TitlePane.background #303234 HSL 210 4 20 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse) TitlePane.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse)
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonPressedBackground #484c4f HSL 206 5 30 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse) TitlePane.buttonPressedBackground #484c4f HSL 206 5 30 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false TitlePane.centerTitle false
@@ -1233,11 +1234,15 @@ TitlePane.inactiveBackground #303234 HSL 210 4 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20 TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true
@@ -1545,7 +1550,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ---- #---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI] monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ---- #---- ----

View File

@@ -129,7 +129,7 @@
#---- monospaced ---- #---- monospaced ----
- monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI] - monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
+ monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI] + monospaced.font [active] Menlo plain 13 javax.swing.plaf.FontUIResource [UI]

View File

@@ -1219,6 +1219,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.background #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse) TitlePane.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse)
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonPressedBackground #ebebeb HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] darken(8% autoInverse) TitlePane.buttonPressedBackground #ebebeb HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] darken(8% autoInverse)
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false TitlePane.centerTitle false
@@ -1238,11 +1239,15 @@ TitlePane.inactiveBackground #ffffff HSL 0 0 100 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20 TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true
@@ -1550,7 +1555,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ---- #---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI] monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ---- #---- ----

View File

@@ -1249,6 +1249,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI
TitlePane.background #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.background #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
TitlePane.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonMaximizedHeight 22 TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false TitlePane.centerTitle false
TitlePane.centerTitleIfMenuBarEmbedded true TitlePane.centerTitleIfMenuBarEmbedded true
@@ -1266,11 +1267,15 @@ TitlePane.inactiveBackground #008800 HSL 120 100 27 javax.swing.plaf.Colo
TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20 TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true TitlePane.useWindowDecorations true
@@ -1314,11 +1319,11 @@ ToggleButton.selectedForeground #000000 HSL 0 0 0 javax.swing.plaf.Col
ToggleButton.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI] ToggleButton.shadow #a0a0a0 HSL 0 0 63 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.disabledUnderlineColor #7a7a7a HSL 0 0 48 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.disabledUnderlineColor #7a7a7a HSL 0 0 48 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.focusBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.focusBackground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.focusForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.focusForeground #008800 HSL 120 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.hoverBackground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.hoverBackground #eeeeee HSL 0 0 93 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.hoverForeground #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.selectedBackground #008800 HSL 120 100 27 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedForeground #00aaff HSL 200 100 50 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.selectedForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.underlineColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI] ToggleButton.tab.underlineColor #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.underlineHeight 2 ToggleButton.tab.underlineHeight 2
ToggleButton.textIconGap 4 ToggleButton.textIconGap 4
@@ -1589,7 +1594,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- monospaced ---- #---- monospaced ----
monospaced.font [active] Consolas plain 12 javax.swing.plaf.FontUIResource [UI] monospaced.font [active] Monospaced plain 12 javax.swing.plaf.FontUIResource [UI]
#---- ---- #---- ----

View File

@@ -106,6 +106,20 @@ public class FlatComponents2Test
table1.setModel( tableModel ); table1.setModel( tableModel );
xTable1.setModel( tableModel ); xTable1.setModel( tableModel );
// table header popup menu
JMenuItem addMenuItem = new JMenuItem( "Add column" );
addMenuItem.addActionListener( e -> {
tableModel.setColumnCount( tableModel.getColumnCount() + 1 );
});
JMenuItem removeMenuItem = new JMenuItem( "Remove last column" );
removeMenuItem.addActionListener( e -> {
tableModel.setColumnCount( tableModel.getColumnCount() - 1 );
});
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( addMenuItem );
popupMenu.add( removeMenuItem );
table1.getTableHeader().setComponentPopupMenu( popupMenu );
// table column editors // table column editors
initTableEditors( table1 ); initTableEditors( table1 );
initTableEditors( xTable1 ); initTableEditors( xTable1 );
@@ -1174,6 +1188,7 @@ public class FlatComponents2Test
{ "item 12", null, "December", null, null, null }, { "item 12", null, "December", null, null, null },
}; };
private int columnCount = columnNames.length;
private int rowCount = rows.length; private int rowCount = rows.length;
private final Map<Integer, Object[]> moreRowsMap = new HashMap<>(); private final Map<Integer, Object[]> moreRowsMap = new HashMap<>();
@@ -1181,6 +1196,16 @@ public class FlatComponents2Test
setRowCount( rowCount ); setRowCount( rowCount );
} }
void setColumnCount( int columnCount ) {
if( columnCount > columnNames.length )
columnCount = columnNames.length;
this.columnCount = columnCount;
// fire event
fireTableStructureChanged();
}
void setRowCount( int rowCount ) { void setRowCount( int rowCount ) {
int oldRowCount = this.rowCount; int oldRowCount = this.rowCount;
this.rowCount = rowCount; this.rowCount = rowCount;
@@ -1199,7 +1224,7 @@ public class FlatComponents2Test
@Override @Override
public int getColumnCount() { public int getColumnCount() {
return columnNames.length; return columnCount;
} }
@Override @Override

View File

@@ -181,6 +181,10 @@ public class FlatMenusTest
JMenuItem menuItem45 = new JMenuItem(); JMenuItem menuItem45 = new JMenuItem();
JMenuItem menuItem46 = new JMenuItem(); JMenuItem menuItem46 = new JMenuItem();
JMenuItem menuItem47 = new JMenuItem(); JMenuItem menuItem47 = new JMenuItem();
JMenu menu13 = new JMenu();
JMenuItem menuItem48 = new JMenuItem();
JMenuItem menuItem49 = new JMenuItem();
JMenuItem menuItem50 = new JMenuItem();
menuBar2 = new JMenuBar(); menuBar2 = new JMenuBar();
JMenu menu8 = new JMenu(); JMenu menu8 = new JMenu();
FlatMenusTest.LargerMenuItem menuItem13 = new FlatMenusTest.LargerMenuItem(); FlatMenusTest.LargerMenuItem menuItem13 = new FlatMenusTest.LargerMenuItem();
@@ -442,6 +446,24 @@ public class FlatMenusTest
menu12.add(menuItem47); menu12.add(menuItem47);
} }
menuBar1.add(menu12); menuBar1.add(menu12);
//======== menu13 ========
{
menu13.setText("HTML");
//---- menuItem48 ----
menuItem48.setText("<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem48);
//---- menuItem49 ----
menuItem49.setText("<html>some longer <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem49);
//---- menuItem50 ----
menuItem50.setText("<html>another <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>");
menu13.add(menuItem50);
}
menuBar1.add(menu13);
} }
add(menuBar1, "cell 1 0 2 1,growx"); add(menuBar1, "cell 1 0 2 1,growx");

View File

@@ -194,6 +194,22 @@ new FormModel {
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test64.png" ) "icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/testing/test64.png" )
} ) } )
} ) } )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "menu13"
"text": "HTML"
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem48"
"text": "<html>some <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem49"
"text": "<html>some longer <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem50"
"text": "<html>another <b color=\"red\">HTML</b> <i color=\"blue\">text</i></html>"
} )
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0 2 1,growx" "value": "cell 1 0 2 1,growx"
} ) } )

View File

@@ -0,0 +1,376 @@
/*
* Copyright 2022 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.testing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.HiDPIUtils;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatPaintingHiDPITest
extends JPanel
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatPaintingHiDPITest" );
frame.showFrame( FlatPaintingHiDPITest::new );
} );
}
FlatPaintingHiDPITest() {
initComponents();
sliderChanged();
}
private void sliderChanged() {
painter.translateX = translateXSlider.getValue();
painter.translateY = translateYSlider.getValue();
painter.scaleX = scaleXSlider.getValue();
painter.scaleY = scaleYSlider.getValue();
painter.rotate = rotateSlider.getValue();
painter.repaint();
AffineTransform t = new AffineTransform();
t.translate( painter.translateX, painter.translateY );
t.scale( painter.scaleX / 100., painter.scaleY / 100. );
t.rotate( Math.toRadians( painter.rotate ) );
tScaleXLabel.setText( Double.toString( t.getScaleX() ) );
tScaleYLabel.setText( Double.toString( t.getScaleY() ) );
tShearXLabel.setText( Double.toString( t.getShearX() ) );
tShearYLabel.setText( Double.toString( t.getShearY() ) );
tTranslateXLabel.setText( Double.toString( t.getTranslateX() ) );
tTranslateYLabel.setText( Double.toString( t.getTranslateY() ) );
double scaleX = Math.hypot( t.getScaleX(), t.getShearX() );
double scaleY = Math.hypot( t.getScaleY(), t.getShearY() );
if( t.getScaleX() < 0 )
scaleX = -scaleX;
if( t.getScaleY() < 0 )
scaleY = -scaleY;
double rotation = Math.atan2( t.getShearY(), t.getScaleY() );
double rotationDegrees = Math.toDegrees( rotation );
cScaleXLabel.setText( Double.toString( scaleX ) );
cScaleYLabel.setText( Double.toString( scaleY ) );
cRotationDegreesLabel.setText( Double.toString( rotationDegrees ) );
}
private void reset() {
translateXSlider.setValue( 100 );
translateYSlider.setValue( 100 );
scaleXSlider.setValue( 100 );
scaleYSlider.setValue( 100 );
rotateSlider.setValue( 0 );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JLabel translateXLabel = new JLabel();
translateXSlider = new JSlider();
JLabel translateYLabel = new JLabel();
translateYSlider = new JSlider();
JLabel scaleXLabel = new JLabel();
scaleXSlider = new JSlider();
JLabel scaleYLabel = new JLabel();
scaleYSlider = new JSlider();
JLabel rotateLabel = new JLabel();
rotateSlider = new JSlider();
JPanel panel1 = new JPanel();
JLabel tLabel = new JLabel();
JLabel xLabel = new JLabel();
JLabel yLabel = new JLabel();
JLabel tScaleLabel = new JLabel();
tScaleXLabel = new JLabel();
tScaleYLabel = new JLabel();
JLabel tShearLabel = new JLabel();
tShearXLabel = new JLabel();
tShearYLabel = new JLabel();
JLabel tTranslateLabel = new JLabel();
tTranslateXLabel = new JLabel();
tTranslateYLabel = new JLabel();
JLabel cLabel = new JLabel();
JLabel cScaleLabel = new JLabel();
cScaleXLabel = new JLabel();
cScaleYLabel = new JLabel();
JLabel cRotationLabel = new JLabel();
cRotationDegreesLabel = new JLabel();
JButton resetButton = new JButton();
painter = new FlatPaintingHiDPITest.HiDPI1xPainter();
//======== this ========
setBorder(null);
setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[400,fill]para" +
"[grow,fill]",
// rows
"[fill]" +
"[]" +
"[]" +
"[]" +
"[]" +
"[grow]"));
//---- translateXLabel ----
translateXLabel.setText("TranslateX:");
add(translateXLabel, "cell 0 0");
//---- translateXSlider ----
translateXSlider.setMaximum(500);
translateXSlider.setPaintLabels(true);
translateXSlider.setPaintTicks(true);
translateXSlider.setMajorTickSpacing(100);
translateXSlider.setMinorTickSpacing(25);
translateXSlider.setValue(100);
translateXSlider.addChangeListener(e -> sliderChanged());
add(translateXSlider, "cell 1 0");
//---- translateYLabel ----
translateYLabel.setText("TranslateY:");
add(translateYLabel, "cell 0 1");
//---- translateYSlider ----
translateYSlider.setMaximum(500);
translateYSlider.setPaintLabels(true);
translateYSlider.setPaintTicks(true);
translateYSlider.setMajorTickSpacing(100);
translateYSlider.setMinorTickSpacing(25);
translateYSlider.setValue(100);
translateYSlider.addChangeListener(e -> sliderChanged());
add(translateYSlider, "cell 1 1");
//---- scaleXLabel ----
scaleXLabel.setText("ScaleX:");
add(scaleXLabel, "cell 0 2");
//---- scaleXSlider ----
scaleXSlider.setMaximum(300);
scaleXSlider.setValue(100);
scaleXSlider.setPaintTicks(true);
scaleXSlider.setPaintLabels(true);
scaleXSlider.setMajorTickSpacing(50);
scaleXSlider.setSnapToTicks(true);
scaleXSlider.setMinorTickSpacing(10);
scaleXSlider.setMinimum(-100);
scaleXSlider.addChangeListener(e -> sliderChanged());
add(scaleXSlider, "cell 1 2");
//---- scaleYLabel ----
scaleYLabel.setText("ScaleY:");
add(scaleYLabel, "cell 0 3");
//---- scaleYSlider ----
scaleYSlider.setMaximum(300);
scaleYSlider.setValue(100);
scaleYSlider.setPaintTicks(true);
scaleYSlider.setPaintLabels(true);
scaleYSlider.setMajorTickSpacing(50);
scaleYSlider.setSnapToTicks(true);
scaleYSlider.setMinorTickSpacing(10);
scaleYSlider.setMinimum(-100);
scaleYSlider.addChangeListener(e -> sliderChanged());
add(scaleYSlider, "cell 1 3");
//---- rotateLabel ----
rotateLabel.setText("Rotate:");
add(rotateLabel, "cell 0 4");
//---- rotateSlider ----
rotateSlider.setMaximum(360);
rotateSlider.setMinimum(-360);
rotateSlider.setValue(0);
rotateSlider.setMajorTickSpacing(90);
rotateSlider.setPaintLabels(true);
rotateSlider.setPaintTicks(true);
rotateSlider.addChangeListener(e -> sliderChanged());
add(rotateSlider, "cell 1 4");
//======== panel1 ========
{
panel1.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[50,fill]" +
"[50,fill]",
// rows
"[]" +
"[]0" +
"[]0" +
"[]para" +
"[]" +
"[]0" +
"[]"));
//---- tLabel ----
tLabel.setText("AffineTransform:");
panel1.add(tLabel, "cell 0 0");
//---- xLabel ----
xLabel.setText("X");
panel1.add(xLabel, "cell 1 0,alignx center,growx 0");
//---- yLabel ----
yLabel.setText("Y");
panel1.add(yLabel, "cell 2 0,alignx center,growx 0");
//---- tScaleLabel ----
tScaleLabel.setText("Scale:");
panel1.add(tScaleLabel, "cell 0 1,gapx indent");
//---- tScaleXLabel ----
tScaleXLabel.setText("text");
panel1.add(tScaleXLabel, "cell 1 1");
//---- tScaleYLabel ----
tScaleYLabel.setText("text");
panel1.add(tScaleYLabel, "cell 2 1");
//---- tShearLabel ----
tShearLabel.setText("Shear:");
panel1.add(tShearLabel, "cell 0 2,gapx indent");
//---- tShearXLabel ----
tShearXLabel.setText("text");
panel1.add(tShearXLabel, "cell 1 2");
//---- tShearYLabel ----
tShearYLabel.setText("text");
panel1.add(tShearYLabel, "cell 2 2");
//---- tTranslateLabel ----
tTranslateLabel.setText("Translate:");
panel1.add(tTranslateLabel, "cell 0 3,gapx indent");
//---- tTranslateXLabel ----
tTranslateXLabel.setText("text");
panel1.add(tTranslateXLabel, "cell 1 3");
//---- tTranslateYLabel ----
tTranslateYLabel.setText("text");
panel1.add(tTranslateYLabel, "cell 2 3");
//---- cLabel ----
cLabel.setText("Computed:");
panel1.add(cLabel, "cell 0 4");
//---- cScaleLabel ----
cScaleLabel.setText("Scale:");
panel1.add(cScaleLabel, "cell 0 5,gapx indent");
//---- cScaleXLabel ----
cScaleXLabel.setText("text");
panel1.add(cScaleXLabel, "cell 1 5");
//---- cScaleYLabel ----
cScaleYLabel.setText("text");
panel1.add(cScaleYLabel, "cell 2 5");
//---- cRotationLabel ----
cRotationLabel.setText("Rotation:");
panel1.add(cRotationLabel, "cell 0 6,gapx indent");
//---- cRotationDegreesLabel ----
cRotationDegreesLabel.setText("text");
panel1.add(cRotationDegreesLabel, "cell 1 6");
}
add(panel1, "cell 2 0 1 4");
//---- resetButton ----
resetButton.setText("Reset");
resetButton.addActionListener(e -> reset());
add(resetButton, "cell 2 4,align left bottom,grow 0 0");
add(painter, "cell 0 5 3 1,grow,width 600,height 400");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JSlider translateXSlider;
private JSlider translateYSlider;
private JSlider scaleXSlider;
private JSlider scaleYSlider;
private JSlider rotateSlider;
private JLabel tScaleXLabel;
private JLabel tScaleYLabel;
private JLabel tShearXLabel;
private JLabel tShearYLabel;
private JLabel tTranslateXLabel;
private JLabel tTranslateYLabel;
private JLabel cScaleXLabel;
private JLabel cScaleYLabel;
private JLabel cRotationDegreesLabel;
private FlatPaintingHiDPITest.HiDPI1xPainter painter;
// JFormDesigner - End of variables declaration //GEN-END:variables
//---- class HiDPI1xPainter -----------------------------------------------
public static class HiDPI1xPainter
extends JComponent
{
int translateX;
int translateY;
int scaleX;
int scaleY;
int rotate;
public HiDPI1xPainter() {
}
@Override
protected void paintComponent( Graphics g ) {
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D) g.create();
try {
FlatUIUtils.setRenderingHints( g2 );
g2.setColor( Color.blue );
g2.drawRect( 0, 0, width - 1, height - 1 );
g2.setColor( Color.cyan );
g2.drawLine( 0, translateY, width, translateY );
g2.drawLine( translateX, 0, translateX, height );
g2.translate( translateX, translateY );
g2.scale( scaleX / 100., scaleY / 100. );
g2.rotate( Math.toRadians( rotate ) );
g2.setColor( Color.red );
g2.fillRect( 0, 0, 100, 50 );
g2.setColor( Color.green );
HiDPIUtils.paintAtScale1x( g2, 0, 0, 100, 50,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
g2d.fillRect( x2 + 10, y2 + 10, width2 - 20, height2 - 20 );
} );
} finally {
g2.dispose();
}
}
}
}

View File

@@ -0,0 +1,289 @@
JFDML JFormDesigner: "8.0.0.0.122" Java: "17.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][400,fill]para[grow,fill]"
"$rowConstraints": "[fill][][][][][grow]"
} ) {
name: "this"
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE
add( new FormComponent( "javax.swing.JLabel" ) {
name: "translateXLabel"
"text": "TranslateX:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "translateXSlider"
"maximum": 500
"paintLabels": true
"paintTicks": true
"majorTickSpacing": 100
"minorTickSpacing": 25
"value": 100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "translateYLabel"
"text": "TranslateY:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "translateYSlider"
"maximum": 500
"paintLabels": true
"paintTicks": true
"majorTickSpacing": 100
"minorTickSpacing": 25
"value": 100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleXLabel"
"text": "ScaleX:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "scaleXSlider"
"maximum": 300
"value": 100
"paintTicks": true
"paintLabels": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "scaleYLabel"
"text": "ScaleY:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "scaleYSlider"
"maximum": 300
"value": 100
"paintTicks": true
"paintLabels": true
"majorTickSpacing": 50
"snapToTicks": true
"minorTickSpacing": 10
"minimum": -100
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "rotateLabel"
"text": "Rotate:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "rotateSlider"
"maximum": 360
"minimum": -360
"value": 0
"majorTickSpacing": 90
"paintLabels": true
"paintTicks": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "sliderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][50,fill][50,fill]"
"$rowConstraints": "[][]0[]0[]para[][]0[]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tLabel"
"text": "AffineTransform:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "xLabel"
"text": "X"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "yLabel"
"text": "Y"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0,alignx center,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleLabel"
"text": "Scale:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tScaleYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearLabel"
"text": "Shear:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tShearYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateLabel"
"text": "Translate:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tTranslateYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 3"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cLabel"
"text": "Computed:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleLabel"
"text": "Scale:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleXLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cScaleYLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cRotationLabel"
"text": "Rotation:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6,gapx indent"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "cRotationDegreesLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 6"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0 1 4"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "resetButton"
"text": "Reset"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "reset", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 4,align left bottom,grow 0 0"
} )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatPaintingHiDPITest$HiDPI1xPainter" ) {
name: "painter"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5 3 1,grow,width 600,height 400"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 685, 680 )
} )
}
}

View File

@@ -22,12 +22,14 @@ import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.util.Arrays;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.text.StyleContext;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility; import com.formdev.flatlaf.util.JavaCompatibility;
@@ -57,18 +59,60 @@ public class FlatPaintingStringTest
FlatPaintingStringTest() { FlatPaintingStringTest() {
initComponents(); initComponents();
String[] availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames().clone();
Arrays.sort( availableFontFamilyNames );
Font currentFont = UIManager.getFont( "Label.font" );
String currentFamily = currentFont.getFamily();
// initialize font families combobox
String[] families = {
// regular
"Arial", "Cantarell", "DejaVu Sans",
"Dialog", "Helvetica Neue", "Inter", "Liberation Sans", "Noto Sans", "Open Sans", "Roboto",
"SansSerif", "Segoe UI", "Tahoma", "Ubuntu", "Verdana", ".SF NS Text",
// light, semibold
"Segoe UI Light", "Segoe UI Semibold",
"HelveticaNeue-Thin", "HelveticaNeue-Medium",
"Lato Light", "Ubuntu Light", "Cantarell Light",
"Lato Semibold", "Ubuntu Medium", "Montserrat SemiBold",
// monospaced
"Monospaced", "Consolas", "Courier New", "Menlo", "Liberation Mono", "Ubuntu Mono",
};
Arrays.sort( families, String.CASE_INSENSITIVE_ORDER );
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
model.addElement( currentFamily );
for( String family : families ) {
if( !family.equals( currentFamily ) && Arrays.binarySearch( availableFontFamilyNames, family ) >= 0 )
model.addElement( family );
}
fontField.setModel( model );
fontField.setSelectedItem( currentFamily );
updateFontMetricsLabel();
add( new JLabel() ); add( new JLabel() );
add( new JLabel( "none" ) ); add( new JLabel( "none" ) );
add( new JLabel( "flatlaf" ) ); add( new JLabel( "flatlaf" ) );
add( new JLabel( "0.25*scale" ) ); add( new JLabel() );
add( new JLabel( "0.5*scale" ) );
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
add( new JLabel( "0.125" ) );
add( new JLabel( "0.25" ) ); add( new JLabel( "0.25" ) );
add( new JLabel( "0.5" ) ); add( new JLabel( "0.5" ) );
add( new JLabel( "0.625" ) ); add( new JLabel( "0.625" ) );
add( new JLabel( "0.75" ) ); add( new JLabel( "0.75" ) );
add( new JLabel( "0.875" ) ); add( new JLabel( "0.875" ) );
add( new JLabel( "1" ) );
add( new JLabel( "1.25" ) );
add( new JLabel() );
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel( "ty" ) );
} else { } else {
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel( "0.625*scale" ) ); add( new JLabel( "0.625*scale" ) );
add( new JLabel( "0.75*scale" ) ); add( new JLabel( "0.75*scale" ) );
add( new JLabel( "0.875*scale" ) ); add( new JLabel( "0.875*scale" ) );
@@ -80,16 +124,14 @@ public class FlatPaintingStringTest
? HiDPIUtils.computeTextYCorrection( g ) ? HiDPIUtils.computeTextYCorrection( g )
: (scaleFactor > 1 ? -(0.625f * scaleFactor) : 0); : (scaleFactor > 1 ? -(0.625f * scaleFactor) : 0);
}; };
YCorrectionFunction oneQSysScale = (g, scaleFactor) -> -(0.25f * scaleFactor); YCorrectionFunction ty = (g, scaleFactor) -> {
YCorrectionFunction halfSysScale = (g, scaleFactor) -> -(0.5f * scaleFactor); // Based on translateY, which is the scaled Y coordinate translation of the graphics context.
YCorrectionFunction fiveEightsQSysScale = (g, scaleFactor) -> -(0.625f * scaleFactor); // When painting whole window, translateY is from top of window, and this works fine.
YCorrectionFunction threeQSysScale = (g, scaleFactor) -> -(0.75f * scaleFactor); // But when repainting only parts of the window, then translateY starts somewhere
YCorrectionFunction sevenEightsSysScale = (g, scaleFactor) -> -(0.875f * scaleFactor); // else and the text if (re-)painted at the wrong Y location.
YCorrectionFunction oneQ = (g, scaleFactor) -> -0.25f; double y = g.getTransform().getTranslateY();
YCorrectionFunction half = (g, scaleFactor) -> -0.5f; return (float) -(y - (int) y);
YCorrectionFunction fiveEights = (g, scaleFactor) -> -0.625f; };
YCorrectionFunction threeQ = (g, scaleFactor) -> -0.75f;
YCorrectionFunction sevenEights = (g, scaleFactor) -> -0.875f;
float[] scaleFactors = new float[] { 1f, 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 3f, 3.5f, 4f }; float[] scaleFactors = new float[] { 1f, 1.25f, 1.5f, 1.75f, 2f, 2.25f, 2.5f, 3f, 3.5f, 4f };
@@ -98,34 +140,72 @@ public class FlatPaintingStringTest
add( scaleFactor, none ); add( scaleFactor, none );
add( scaleFactor, flatlaf ); add( scaleFactor, flatlaf );
add( scaleFactor, oneQSysScale ); add( new JLabel( " " ) );
add( scaleFactor, halfSysScale );
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
add( scaleFactor, oneQ ); add( scaleFactor, (g, sf) -> -0.125f );
add( scaleFactor, half ); add( scaleFactor, (g, sf) -> -0.25f );
add( scaleFactor, fiveEights ); add( scaleFactor, (g, sf) -> -0.5f );
add( scaleFactor, threeQ ); add( scaleFactor, (g, sf) -> -0.625f );
add( scaleFactor, sevenEights ); add( scaleFactor, (g, sf) -> -0.75f );
add( scaleFactor, (g, sf) -> -0.875f );
add( scaleFactor, (g, sf) -> -1f );
add( scaleFactor, (g, sf) -> -1.25f );
add( new JLabel( " " ) );
add( scaleFactor, (g, sf) -> -(0.25f * sf) );
add( scaleFactor, (g, sf) -> -(0.5f * sf) );
add( scaleFactor, ty );
} else { } else {
add( scaleFactor, fiveEightsQSysScale ); add( scaleFactor, (g, sf) -> -(0.25f * sf) );
add( scaleFactor, threeQSysScale ); add( scaleFactor, (g, sf) -> -(0.5f * sf) );
add( scaleFactor, sevenEightsSysScale ); add( scaleFactor, (g, sf) -> -(0.625f * sf) );
add( scaleFactor, (g, sf) -> -(0.75f * sf) );
add( scaleFactor, (g, sf) -> -(0.875f * sf) );
} }
add( new JLabel( String.valueOf( scaleFactor ) ) );
} }
} }
private void add( float scaleFactor, YCorrectionFunction correctionFunction ) { private void add( float scaleFactor, YCorrectionFunction correctionFunction ) {
if( SystemInfo.isJava_9_orLater ) { if( SystemInfo.isJava_9_orLater ) {
add( new Painter( scaleFactor, correctionFunction, 0 ), "split 4, gapx 0 0" ); add( new Painter( scaleFactor, correctionFunction, 0 ), "split 4, gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.25f ), "gapx 0 0" ); add( new Painter( scaleFactor, correctionFunction, 1 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.5f ), "gapx 0 0" ); add( new Painter( scaleFactor, correctionFunction, 2 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.75f ), "gapx 0 0" ); add( new Painter( scaleFactor, correctionFunction, 3 ), "gapx 0 0" );
} else } else
add( new Painter( scaleFactor, correctionFunction, 0 ) ); add( new Painter( scaleFactor, correctionFunction, 0 ) );
} }
private void fontChanged() {
String family = (String) fontField.getSelectedItem();
Font font = UIManager.getFont( "defaultFont" );
if( font.getFamily().equals( family ) )
return;
Font newFont = StyleContext.getDefaultStyleContext().getFont( family, font.getStyle(), font.getSize() );
UIManager.put( "defaultFont", newFont );
updateFontMetricsLabel();
FlatLaf.updateUILater();
}
private void updateFontMetricsLabel() {
Font font = UIManager.getFont( "defaultFont" );
FontMetrics fm = getFontMetrics( font );
fontMetricsLabel.setText( String.format( "%s %d height %d ascent %d descent %d max ascent %d max descent %d leading %d",
font.getFamily(), font.getSize(),
fm.getHeight(), fm.getAscent(), fm.getDescent(),
fm.getMaxAscent(), fm.getMaxDescent(), fm.getLeading()
) );
}
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JPanel panel1 = new JPanel();
JLabel fontLabel = new JLabel();
fontField = new JComboBox<>();
fontMetricsLabel = new JLabel();
//======== this ======== //======== this ========
setBorder(null); setBorder(null);
@@ -134,11 +214,40 @@ public class FlatPaintingStringTest
// columns // columns
"[fill]", "[fill]",
// rows // rows
"[top]unrel" +
"[top]")); "[top]"));
//======== panel1 ========
{
panel1.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]" +
"[fill]",
// rows
"[]"));
//---- fontLabel ----
fontLabel.setText("Font:");
panel1.add(fontLabel, "cell 0 0");
//---- fontField ----
fontField.setMaximumRowCount(20);
fontField.addActionListener(e -> fontChanged());
panel1.add(fontField, "cell 1 0");
//---- fontMetricsLabel ----
fontMetricsLabel.setText("text");
panel1.add(fontMetricsLabel, "cell 2 0");
}
add(panel1, "north");
// JFormDesigner - End of component initialization //GEN-END:initComponents // JFormDesigner - End of component initialization //GEN-END:initComponents
} }
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JComboBox<String> fontField;
private JLabel fontMetricsLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
private interface YCorrectionFunction { private interface YCorrectionFunction {
@@ -152,17 +261,29 @@ public class FlatPaintingStringTest
{ {
private final float scaleFactor; private final float scaleFactor;
private final YCorrectionFunction correctionFunction; private final YCorrectionFunction correctionFunction;
private final float yOffset; private final int yOffset;
public Painter( float scaleFactor, YCorrectionFunction correctionFunction, float yOffset ) { public Painter( float scaleFactor, YCorrectionFunction correctionFunction, int yOffset ) {
super( "E" ); super( "E" );
this.scaleFactor = scaleFactor; this.scaleFactor = scaleFactor;
this.correctionFunction = correctionFunction; this.correctionFunction = correctionFunction;
this.yOffset = yOffset; this.yOffset = yOffset;
setBorder( new EmptyBorder( 2, 0, 2, 0 ) );
updateFont();
}
@Override
public void updateUI() {
super.updateUI();
updateFont();
}
private void updateFont() {
if( scaleFactor == 0 )
return; // invoked from super constructor
if( !SystemInfo.isJava_9_orLater ) { if( !SystemInfo.isJava_9_orLater ) {
Font font = getFont(); Font font = UIManager.getFont( "defaultFont" );
setFont( font.deriveFont( (float) Math.round( font.getSize() * scaleFactor ) ) ); setFont( font.deriveFont( (float) Math.round( font.getSize() * scaleFactor ) ) );
} }
} }
@@ -170,35 +291,36 @@ public class FlatPaintingStringTest
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize(); Dimension size = super.getPreferredSize();
Insets insets = getInsets(); if( SystemInfo.isJava_9_orLater ) {
int leftRight = insets.left + insets.right; // compute component size using JRE scaling
return new Dimension( //
scale( size.width -leftRight ) + leftRight, // The y offset is used to simulate different vertical component positions,
scale( size.height ) ); // which may result in different component heights.
// E.g. scaling following bounds by 150% results in different heights:
// 0,0, 10,15 --> [x=0,y=0,width=15,height=22]
// 0,1, 10,15 --> [x=0,y=1,width=15,height=23]
Rectangle r = scaleTo1x( scaleFactor, 0, yOffset, size.width, size.height );
return new Dimension( r.width, r.height );
} else
return size;
} }
@Override @Override
protected void paintComponent( Graphics g ) { protected void paintComponent( Graphics g ) {
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g2 );
// simulate component y position at a fraction
if( scaleFactor > 1 && SystemInfo.isJava_9_orLater )
g2.translate( 0, yOffset );
int width = getWidth(); int width = getWidth();
int height = getHeight(); int height = getHeight();
Insets insets = getInsets();
FontMetrics fm = getFontMetrics( getFont() ); FontMetrics fm = getFontMetrics( getFont() );
// paint lines at 1x // paint lines at 1x
HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height, HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height,
(g2d, x2, y2, width2, height2, scaleFactor2) -> { (g2d, x2, y2, width2, height2, scaleFactor2) -> {
// g.setColor( Color.blue ); g.setColor( Color.blue );
// g.drawLine( 0, 0, width2, 0 ); g.drawLine( 0, 0, width2, 0 );
// g.drawLine( 0, height2 - 1, width2, height2 - 1 ); g.drawLine( 0, height2 - 1, width2, height2 - 1 );
int baseline = (int) Math.round( (insets.top + fm.getAscent()) * scaleFactor2 int baseline = (int) Math.round( fm.getAscent() * scaleFactor2
* (SystemInfo.isJava_9_orLater ? scaleFactor : 1f) ) - 1; * (SystemInfo.isJava_9_orLater ? scaleFactor : 1f) ) - 1;
int topline = height2 - baseline - 1; int topline = height2 - baseline - 1;
@@ -207,8 +329,15 @@ public class FlatPaintingStringTest
g.drawLine( 0, topline, width2, topline ); g.drawLine( 0, topline, width2, topline );
} ); } );
// move x before scaling to have same left inset at all scale factors // simulate different vertical component positions
g.translate( insets.left, 0 ); if( yOffset > 0 && SystemInfo.isJava_9_orLater ) {
double ty = yOffset * scaleFactor;
ty -= (int) ty;
if( ty == 0 )
return; // no need to paint
g2.translate( 0, ty );
}
// scale // scale
if( SystemInfo.isJava_9_orLater ) if( SystemInfo.isJava_9_orLater )
@@ -232,21 +361,36 @@ public class FlatPaintingStringTest
// draw string // draw string
g.setColor( getForeground() ); g.setColor( getForeground() );
int y = insets.top + fm.getAscent(); int y = fm.getAscent();
JavaCompatibility.drawStringUnderlineCharAt( this, cg, "E", -1, 0, y ); JavaCompatibility.drawStringUnderlineCharAt( this, cg, "E", -1, 0, y );
// set tooltip text // set tooltip text
if( getToolTipText() == null ) {
AffineTransform t = g2.getTransform(); AffineTransform t = g2.getTransform();
double textY = t.getTranslateY() + (y * t.getScaleY()); double textY = t.getTranslateY() + (y * t.getScaleY());
setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) ); setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) );
} }
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints ); /**
* Scales a rectangle in the same way as the JRE does in
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*
* This is a copy of HiDPIUtils.scale()
*/
public static Rectangle scaleTo1x( double scaleFactor, int x, int y, int width, int height ) {
double px = (x * scaleFactor);
double py = (y * scaleFactor);
double newX = normalize( px );
double newY = normalize( py );
double newWidth = normalize( px + (width * scaleFactor) ) - newX;
double newHeight = normalize( py + (height * scaleFactor) ) - newY;
return new Rectangle( (int) Math.floor( newX ), (int) Math.floor( newY ), (int) newWidth, (int) newHeight );
} }
private int scale( int value ) { private static double normalize( double value ) {
return SystemInfo.isJava_9_orLater ? Math.round( value * scaleFactor ) : value; return Math.floor( value + 0.25 ) + 0.25;
} }
} }
} }

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8" JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -9,10 +9,45 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3" "$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[fill]" "$columnConstraints": "[fill]"
"$rowConstraints": "[top]" "$rowConstraints": "[top]unrel[top]"
} ) { } ) {
name: "this" name: "this"
"border": sfield com.jformdesigner.model.FormObject NULL_VALUE "border": sfield com.jformdesigner.model.FormObject NULL_VALUE
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[]"
} ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontLabel"
"text": "Font:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JComboBox" ) {
name: "fontField"
"maximumRowCount": 20
auxiliary() {
"JavaCodeGenerator.variableLocal": false
"JavaCodeGenerator.typeParameters": "String"
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fontChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontMetricsLabel"
"text": "text"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "north"
} )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 ) "location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 450, 300 ) "size": new java.awt.Dimension( 450, 300 )

View File

@@ -241,6 +241,23 @@ public class FlatWindowDecorationsTest
if( c instanceof Box.Filler ) if( c instanceof Box.Filler )
return; return;
} }
/*debug
menuBar.add( new Box.Filler( new Dimension(), new Dimension(),
new Dimension( Short.MAX_VALUE, Short.MAX_VALUE ) )
{
@Override
protected void paintComponent( Graphics g ) {
int w = getWidth();
int h = getHeight();
g.setColor( Color.blue );
g.drawRect( 0, 0, w - 1, h - 1 );
g.drawLine( 0, 0, w, h );
g.drawLine( 0, h, w, 0 );
}
} );
debug*/
menuBar.add( Box.createGlue() ); menuBar.add( Box.createGlue() );
menuBar.revalidate(); menuBar.revalidate();
} }

View File

@@ -25,7 +25,6 @@ dependencies {
implementation( "com.miglayout:miglayout-swing:5.3" ) implementation( "com.miglayout:miglayout-swing:5.3" )
implementation( "com.fifesoft:rsyntaxtextarea:3.1.4" ) implementation( "com.fifesoft:rsyntaxtextarea:3.1.4" )
implementation( "com.fifesoft:autocomplete:3.1.3" ) implementation( "com.fifesoft:autocomplete:3.1.3" )
implementation( "com.fifesoft:rstaui:3.1.3" )
} }
tasks { tasks {

View File

@@ -16,7 +16,7 @@
package com.formdev.flatlaf.themeeditor; package com.formdev.flatlaf.themeeditor;
import java.awt.Container; import java.awt.EventQueue;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.List; import java.util.List;
@@ -26,7 +26,6 @@ import javax.swing.border.MatteBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import com.formdev.flatlaf.extras.components.*; import com.formdev.flatlaf.extras.components.*;
import org.fife.rsta.ui.CollapsibleSectionPanel;
import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
@@ -44,9 +43,13 @@ import net.miginfocom.swing.*;
class FlatFindReplaceBar class FlatFindReplaceBar
extends JPanel extends JPanel
{ {
static final String PROP_CLOSED = "closed";
private final RSyntaxTextArea textArea; private final RSyntaxTextArea textArea;
private SearchContext context; private SearchContext context;
private boolean inSetContext;
private boolean markAllPending;
FlatFindReplaceBar( RSyntaxTextArea textArea ) { FlatFindReplaceBar( RSyntaxTextArea textArea ) {
this.textArea = textArea; this.textArea = textArea;
@@ -74,6 +77,10 @@ class FlatFindReplaceBar
regexToggleButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/regex.svg" ) ); regexToggleButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/regex.svg" ) );
closeButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/close.svg" ) ); closeButton.setIcon( new FlatSVGIcon( "com/formdev/flatlaf/themeeditor/icons/close.svg" ) );
registerKeyboardAction( e -> close(),
KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0, false ),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
SearchContext context = new SearchContext(); SearchContext context = new SearchContext();
context.setSearchWrap( true ); context.setSearchWrap( true );
setSearchContext( context ); setSearchContext( context );
@@ -93,29 +100,32 @@ class FlatFindReplaceBar
void setSearchContext( SearchContext context ) { void setSearchContext( SearchContext context ) {
this.context = context; this.context = context;
inSetContext = true;
try {
findField.setText( context.getSearchFor() ); findField.setText( context.getSearchFor() );
replaceField.setText( context.getReplaceWith() ); replaceField.setText( context.getReplaceWith() );
matchCaseToggleButton.setSelected( context.getMatchCase() ); matchCaseToggleButton.setSelected( context.getMatchCase() );
matchWholeWordToggleButton.setSelected( context.getWholeWord() ); matchWholeWordToggleButton.setSelected( context.getWholeWord() );
regexToggleButton.setSelected( context.isRegularExpression() ); regexToggleButton.setSelected( context.isRegularExpression() );
} finally {
inSetContext = false;
}
} }
@Override void activate( boolean findEditorSelection ) {
public boolean requestFocusInWindow() { // use selected text of editor for searching
// invoked from CollapsibleSectionPanel if( findEditorSelection ) {
// use selected text in editor for searching
String selectedText = textArea.getSelectedText(); String selectedText = textArea.getSelectedText();
if( !StringUtils.isEmpty( selectedText ) && selectedText.indexOf( '\n' ) < 0 ) if( !StringUtils.isEmpty( selectedText ) && selectedText.indexOf( '\n' ) < 0 )
findField.setText( selectedText ); findField.setText( selectedText );
else else
findField.selectAll(); findField.selectAll();
}
// if showing bar, highlight matches in editor // if showing bar, highlight matches in editor
// (not invoking this from addNotify() because this would break the slide-in animation)
markAll(); markAll();
return findField.requestFocusInWindow(); findField.requestFocusInWindow();
} }
@Override @Override
@@ -142,7 +152,20 @@ class FlatFindReplaceBar
} }
void markAll() { void markAll() {
if( inSetContext )
return;
// do mark all only once
if( markAllPending )
return;
markAllPending = true;
EventQueue.invokeLater( () -> {
markAllPending = false;
findOrMarkAll( false ); findOrMarkAll( false );
} );
} }
private void findOrMarkAll( boolean find ) { private void findOrMarkAll( boolean find ) {
@@ -254,11 +277,8 @@ class FlatFindReplaceBar
} }
private void close() { private void close() {
Container parent = getParent(); setVisible( false );
if( parent instanceof CollapsibleSectionPanel ) firePropertyChange( PROP_CLOSED, false, true );
((CollapsibleSectionPanel)parent).hideBottomComponent();
else if( parent != null )
parent.remove( this );
} }
private void initComponents() { private void initComponents() {

View File

@@ -34,7 +34,6 @@ import javax.swing.JPanel;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import org.fife.rsta.ui.CollapsibleSectionPanel;
import org.fife.ui.autocomplete.AutoCompletion; import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.CompletionProvider; import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
@@ -48,6 +47,7 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.fife.ui.rtextarea.Gutter; import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea; import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane; import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextarea.SearchContext;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
/** /**
@@ -62,7 +62,10 @@ class FlatThemeEditorPane
private static final String FLATLAF_STYLE = "text/flatlaf"; private static final String FLATLAF_STYLE = "text/flatlaf";
private final CollapsibleSectionPanel collapsiblePanel; private static boolean findReplaceVisible;
private static SearchContext findReplaceContext;
private final JPanel editorPanel;
private final RTextScrollPane scrollPane; private final RTextScrollPane scrollPane;
private final FlatSyntaxTextArea textArea; private final FlatSyntaxTextArea textArea;
private final ErrorStrip errorStrip; private final ErrorStrip errorStrip;
@@ -115,11 +118,11 @@ class FlatThemeEditorPane
// create error strip // create error strip
errorStrip = new ErrorStrip( textArea ); errorStrip = new ErrorStrip( textArea );
// create collapsible panel // create editor panel
collapsiblePanel = new CollapsibleSectionPanel(); editorPanel = new JPanel( new BorderLayout() );
collapsiblePanel.add( scrollPane ); editorPanel.add( scrollPane );
collapsiblePanel.add( errorStrip, BorderLayout.LINE_END ); editorPanel.add( errorStrip, BorderLayout.LINE_END );
add( collapsiblePanel, BorderLayout.CENTER ); add( editorPanel, BorderLayout.CENTER );
updateTheme(); updateTheme();
} }
@@ -166,6 +169,13 @@ class FlatThemeEditorPane
scrollPane.getGutter().setLineNumberFont( font ); scrollPane.getGutter().setLineNumberFont( font );
} }
void selected() {
if( findReplaceVisible )
showFindReplaceBar( false );
else
hideFindReplaceBar();
}
void windowActivated() { void windowActivated() {
if( preview != null ) if( preview != null )
preview.repaint(); preview.repaint();
@@ -261,13 +271,30 @@ class FlatThemeEditorPane
return (window instanceof JFrame) ? ((JFrame)window).getTitle() : null; return (window instanceof JFrame) ? ((JFrame)window).getTitle() : null;
} }
void showFindReplaceBar() { void showFindReplaceBar( boolean findEditorSelection ) {
if( findReplaceBar == null ) { if( findReplaceBar == null ) {
findReplaceBar = new FlatFindReplaceBar( textArea ); findReplaceBar = new FlatFindReplaceBar( textArea );
collapsiblePanel.addBottomComponent( findReplaceBar ); findReplaceBar.addPropertyChangeListener( FlatFindReplaceBar.PROP_CLOSED, e -> {
findReplaceVisible = false;
textArea.requestFocusInWindow();
} );
editorPanel.add( findReplaceBar, BorderLayout.SOUTH );
editorPanel.revalidate();
} }
collapsiblePanel.showBottomComponent( findReplaceBar ); findReplaceVisible = true;
if( findReplaceContext == null )
findReplaceContext = findReplaceBar.getSearchContext();
else
findReplaceBar.setSearchContext( findReplaceContext );
findReplaceBar.setVisible( true );
findReplaceBar.activate( findEditorSelection );
}
void hideFindReplaceBar() {
if( findReplaceBar != null )
findReplaceBar.setVisible( false );
} }
void showPreview( boolean show ) { void showPreview( boolean show ) {

View File

@@ -336,6 +336,7 @@ class FlatThemeFileEditor
SwingUtilities.invokeLater( () -> { SwingUtilities.invokeLater( () -> {
activateEditor(); activateEditor();
notifyEditorSelected();
} ); } );
saveState(); saveState();
enableDisableActions(); enableDisableActions();
@@ -448,11 +449,13 @@ class FlatThemeFileEditor
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent(); FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
String filename = (themeEditorPane != null) ? themeEditorPane.getFile().getName() : null; String filename = (themeEditorPane != null) ? themeEditorPane.getFile().getName() : null;
putPrefsString( state, KEY_RECENT_FILE, filename ); putPrefsString( state, KEY_RECENT_FILE, filename );
notifyEditorSelected();
} }
private void enableDisableActions() { private void enableDisableActions() {
boolean dirOpen = (directoryField.getSelectedItem() != null); boolean dirOpen = (directoryField.getSelectedItem() != null);
boolean editorOpen = (dirOpen &&tabbedPane.getSelectedIndex() >= 0); boolean editorOpen = (dirOpen && tabbedPane.getSelectedIndex() >= 0);
// enable/disable buttons // enable/disable buttons
newButton.setEnabled( dirOpen ); newButton.setEnabled( dirOpen );
@@ -687,6 +690,12 @@ class FlatThemeFileEditor
return result; return result;
} }
private void notifyEditorSelected() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null )
themeEditorPane.selected();
}
private void activateEditor() { private void activateEditor() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent(); FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null ) if( themeEditorPane != null )
@@ -709,7 +718,7 @@ class FlatThemeFileEditor
private void find() { private void find() {
FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent(); FlatThemeEditorPane themeEditorPane = (FlatThemeEditorPane) tabbedPane.getSelectedComponent();
if( themeEditorPane != null ) if( themeEditorPane != null )
themeEditorPane.showFindReplaceBar(); themeEditorPane.showFindReplaceBar( true );
} }
private void insertColor() { private void insertColor() {

View File

@@ -202,6 +202,11 @@ public class FlatThemeTokenMaker
} }
} }
@Override
public boolean isIdentifierChar( int languageIndex, char ch ) {
return super.isIdentifierChar( languageIndex, ch ) || ch == '@';
}
/*debug /*debug
private java.util.HashMap<Integer, String> tokenTypeStrMap; private java.util.HashMap<Integer, String> tokenTypeStrMap;

View File

@@ -980,6 +980,7 @@ TitlePane.background
TitlePane.borderColor TitlePane.borderColor
TitlePane.buttonHoverBackground TitlePane.buttonHoverBackground
TitlePane.buttonMaximizedHeight TitlePane.buttonMaximizedHeight
TitlePane.buttonMinimumWidth
TitlePane.buttonPressedBackground TitlePane.buttonPressedBackground
TitlePane.buttonSize TitlePane.buttonSize
TitlePane.centerTitle TitlePane.centerTitle
@@ -999,11 +1000,15 @@ TitlePane.inactiveBackground
TitlePane.inactiveForeground TitlePane.inactiveForeground
TitlePane.maximizeIcon TitlePane.maximizeIcon
TitlePane.menuBarEmbedded TitlePane.menuBarEmbedded
TitlePane.menuBarResizeHeight
TitlePane.menuBarTitleGap TitlePane.menuBarTitleGap
TitlePane.menuBarTitleMinimumGap
TitlePane.noIconLeftGap TitlePane.noIconLeftGap
TitlePane.restoreIcon TitlePane.restoreIcon
TitlePane.showIcon TitlePane.showIcon
TitlePane.showIconBesideTitle
TitlePane.titleMargins TitlePane.titleMargins
TitlePane.titleMinimumWidth
TitlePane.unifiedBackground TitlePane.unifiedBackground
TitlePane.useWindowDecorations TitlePane.useWindowDecorations
TitledBorder.border TitledBorder.border