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
==================
## 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
#### New features and improvements

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 2.2
#Version 2.3
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
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_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_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_LEADING_COMPONENT = "JTextField.leadingComponent"
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()
fld public final static boolean isAARCH64
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_17_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 isKDE
fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_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 MnemonicHandler mnemonicHandler;
private SubMenuUsabilityHelper subMenuUsabilityHelper;
private boolean subMenuUsabilityHelperInstalled;
private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters;
@@ -232,6 +232,15 @@ public abstract class FlatLaf
@Override
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 )
initializeAqua();
@@ -246,8 +255,7 @@ public abstract class FlatLaf
mnemonicHandler.install();
// install submenu usability helper
subMenuUsabilityHelper = new SubMenuUsabilityHelper();
subMenuUsabilityHelper.install();
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) {
@@ -304,6 +312,10 @@ public abstract class FlatLaf
@Override
public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener
if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -329,9 +341,9 @@ public abstract class FlatLaf
}
// uninstall submenu usability helper
if( subMenuUsabilityHelper != null ) {
subMenuUsabilityHelper.uninstall();
subMenuUsabilityHelper = null;
if( subMenuUsabilityHelperInstalled ) {
SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelperInstalled = false;
}
// 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_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 SafeTrianglePainter safeTrianglePainter;
private boolean changePending;
@@ -74,13 +79,22 @@ class SubMenuUsabilityHelper
private Rectangle invokerBounds;
void install() {
MenuSelectionManager.defaultManager().addChangeListener( this );
static synchronized boolean install() {
if( instance != null )
return false;
instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance );
return true;
}
void uninstall() {
MenuSelectionManager.defaultManager().removeChangeListener( this );
uninstallEventQueue();
static synchronized void uninstall() {
if( instance == null )
return;
MenuSelectionManager.defaultManager().removeChangeListener( instance );
instance.uninstallEventQueue();
instance = null;
}
@Override

View File

@@ -608,7 +608,7 @@ public class FlatComboBoxUI
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 );
paddingBorder.uninstall();
@@ -682,7 +682,7 @@ public class FlatComboBoxUI
@Override
protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp );
paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall();
return size;
@@ -900,7 +900,7 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
paddingBorder.install( c );
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
return c;
}
@@ -923,6 +923,7 @@ public class FlatComboBoxUI
private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) {
this.padding = padding;
@@ -930,10 +931,12 @@ public class FlatComboBoxUI
// using synchronized to avoid problems with code that modifies combo box
// (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) )
return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder();
if( oldBorder == this )
@@ -987,6 +990,12 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom;
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;
}

View File

@@ -56,7 +56,7 @@ public class FlatEmptyBorder
protected static Insets scaleInsets( Component c, Insets insets,
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.top = scale( top );
insets.right = scale( leftToRight ? right : left );

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
@@ -27,6 +29,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -100,6 +105,10 @@ public class FlatMenuBarUI
super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
}
@Override
@@ -221,6 +230,130 @@ public class FlatMenuBarUI
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 ----------------------------------------------------
/**

View File

@@ -446,6 +446,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
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 )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );

View File

@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatNativeWindowBorder
{
// 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 disabled via system property
private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater &&
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
!SystemInfo.isProjector &&
!SystemInfo.isWebswing &&
!SystemInfo.isWinPE &&

View File

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

View File

@@ -364,6 +364,12 @@ public class FlatRootPaneUI
((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 -----------------------------------------------
protected class FlatRootLayout

View File

@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -195,6 +197,8 @@ public class FlatTableHeaderUI
@Override
public void paint( Graphics g, JComponent c ) {
fixDraggedAndResizingColumns( header );
TableColumnModel columnModel = header.getColumnModel();
if( columnModel.getColumnCount() <= 0 )
return;
@@ -269,6 +273,32 @@ public class FlatTableHeaderUI
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 ----------------------------------
/**

View File

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

View File

@@ -24,6 +24,7 @@ import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
@@ -50,13 +51,13 @@ import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
@@ -83,10 +84,14 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.titleMinimumWidth int
* @uiDefault TitlePane.buttonMinimumWidth int
* @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon 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 int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
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 boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
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;
@@ -142,6 +152,8 @@ public class FlatTitlePane
// necessary for closing window with double-click on icon
iconLabel.addMouseListener( handler );
applyComponentOrientation( rootPane.getComponentOrientation() );
}
protected FlatTitlePaneBorder createTitlePaneBorder() {
@@ -163,7 +175,6 @@ public class FlatTitlePane
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -183,18 +194,52 @@ public class FlatTitlePane
setLayout( new BorderLayout() {
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// make left panel (with embedded menu bar) smaller if horizontal space is rare
// to avoid that embedded menu bar overlaps button bar
// compute available bounds
Insets insets = target.getInsets();
int width = target.getWidth() - insets.left - insets.right;
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) {
int oldWidth = leftPanel.getWidth();
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 );
leftPanel.setSize( newWidth, leftPanel.getHeight() );
if( !getComponentOrientation().isLeftToRight() )
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
int x = insets.left;
int y = insets.top;
int w = target.getWidth() - insets.left - insets.right;
int h = target.getHeight() - insets.top - insets.bottom;
// compute widths
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,
@@ -233,10 +278,7 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if( buttonMaximizedHeight > 0 &&
window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
// make title pane height smaller when frame is maximized
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 ) {
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.setContentAreaFilled( false );
button.setBorder( BorderFactory.createEmptyBorder() );
@@ -356,11 +404,12 @@ public class FlatTitlePane
boolean hasIcon = (images != null && !images.isEmpty());
// 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
iconLabel.setVisible( hasIcon );
leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
iconLabel.setVisible( hasIcon && !showIconBesideTitle );
leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@@ -567,6 +616,11 @@ debug*/
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.
*/
@@ -729,7 +783,7 @@ debug*/
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) {
if( !showIconBesideTitle && iconLabel.isVisible() ) {
// compute real icon size (without insets; 1px larger for easier hitting)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets();
@@ -741,9 +795,7 @@ debug*/
// if frame is maximized, increase icon bounds to upper-left corner
// of window to allow closing window via double-click in upper-left corner
if( window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( isWindowMaximized() ) {
iconBounds.height += iconBounds.y;
iconBounds.y = 0;
@@ -758,6 +810,38 @@ debug*/
hitTestSpots.add( iconBounds );
else
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 );
@@ -768,6 +852,15 @@ debug*/
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar );
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 );
if( horizontalGlue != null ) {
// 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()) )
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() );
return insets;
@@ -873,7 +966,7 @@ debug*/
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 );
}
@@ -881,10 +974,6 @@ debug*/
JMenuBar menuBar = rootPane.getJMenuBar();
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
}
protected boolean isWindowMaximized( Component c ) {
return window instanceof Frame && (((Frame) window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
}
//---- class FlatTitleLabelUI ---------------------------------------------
@@ -898,32 +987,101 @@ debug*/
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
int labelWidth = l.getWidth();
int textWidth = labelWidth - (textX * 2);
int gap = UIScale.scale( menuBarTitleGap );
protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon,
Rectangle viewR, Rectangle iconR, Rectangle textR )
{
JMenuBar menuBar = rootPane.getJMenuBar();
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar );
boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar );
boolean leftToRight = getComponentOrientation().isLeftToRight();
// 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();
Insets insets = l.getInsets();
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
textX = leadingTextX;
if( hasEmbeddedMenuBar ) {
int minGap = UIScale.scale( menuBarTitleMinimumGap );
// apply minimum leading gap (between embedded menu bar and title)
if( hasEmbeddedLeadingMenus ) {
if( leftToRight )
viewR.x += minGap;
viewR.width -= minGap;
}
// 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;
}
}
super.paintEnabledText( l, g, s, textX, textY );
// 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 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 inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor = defaultActiveBorder;
private Color activeColor;
static JBRWindowTopBorder getInstance() {
if( instance == null )
@@ -250,7 +252,7 @@ public class JBRCustomDecorations
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return defaultActiveBorder;
return null;
Color colorizationColor = getColorizationColor();
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 ) {
Window window = SwingUtilities.windowForComponent( c );
boolean active = window != null && window.isActive();
boolean dark = FlatLaf.isLafDark();
// paint top border
// - in light themes
// - in dark themes only for active windows if colorization affects borders
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
if( !paintTopBorder )
return;
g.setColor( active ? activeColor : inactiveLightColor );
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}

View File

@@ -143,8 +143,8 @@ public class MigLayoutVisualPadding
//---- class FlatMigInsets ------------------------------------------------
/**
* Marker class to identify our visual paddings and leaf paddings,
* which were set from outside, untouched.
* Marker class to identify our visual paddings and leave paddings
* set from outside untouched.
*/
private static class FlatMigInsets
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 ) {
// 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
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
if( scaleX == 1 && scaleY == 1 ) {
painter.paint( g, x, y, width, height, 1 );
return;
}
// scale rectangle
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
Rectangle2D.Double scaledRect = scale( scaleX, scaleY, t, x, y, width, height );
try {
// unscale to factor 1.0 and move origin (to whole numbers)
g.setTransform( new AffineTransform( 1, 0, 0, 1,
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );
// unscale to factor 1.0, keep rotation and move origin (to whole numbers)
AffineTransform t1x;
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 sheight = (int) scaledRect.height;
// paint
painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() );
painter.paint( g, 0, 0, swidth, sheight, scaleX );
} finally {
// restore original transform
g.setTransform( transform );
g.setTransform( t );
}
}
@@ -80,20 +105,16 @@ public class HiDPIUtils
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*/
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) {
double dx1 = transform.getScaleX();
double dy2 = transform.getScaleY();
double px = x * dx1 + transform.getTranslateX();
double py = y * dy2 + transform.getTranslateY();
dx1 *= width;
dy2 *= height;
private static Rectangle2D.Double scale( double scaleX, double scaleY, AffineTransform t, int x, int y, int width, int height ) {
double px = (x * scaleX) + t.getTranslateX();
double py = (y * scaleY) + t.getTranslateY();
double newx = normalize( px );
double newy = normalize( py );
dx1 = normalize( px + dx1 ) - newx;
dy2 = normalize( py + dy2 ) - newy;
double newX = normalize( px );
double newY = normalize( py );
double newWidth = normalize( px + (width * scaleX) ) - newX;
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 ) {

View File

@@ -64,7 +64,7 @@ light.font = +0
semibold.font = +0
# monospaced
[win]monospaced.font = Consolas, "Courier New", Monospaced
[win]monospaced.font = Monospaced
[mac]monospaced.font = Menlo, Monospaced
[linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced
monospaced.font = Monospaced
@@ -789,11 +789,16 @@ TitlePane.noIconLeftGap = 8
TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0
TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false
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.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

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

View File

@@ -65,6 +65,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.MigLayoutVisualPadding;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -113,6 +114,7 @@ public class FlatInspector
private int inspectParentLevel;
private boolean wasModifierKeyPressed;
private boolean showClassHierarchy;
private long lastWhen;
private JComponent highlightFigure;
private Popup popup;
@@ -131,8 +133,22 @@ public class FlatInspector
(((KeyEvent)e).getModifiersEx() & KEY_MODIFIERS_MASK) == (keyStroke.getModifiers() & KEY_MODIFIERS_MASK) )
{
Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if( activeWindow instanceof RootPaneContainer ) {
JRootPane rootPane = ((RootPaneContainer)activeWindow).getRootPane();
RootPaneContainer rootPaneContainer = null;
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 );
if( inspector == null ) {
inspector = new FlatInspector( rootPane );
@@ -172,6 +188,11 @@ public class FlatInspector
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT )
wasModifierKeyPressed = true;
} 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 ) {
inspectParentLevel++;
int parentLevel = inspect( lastX, lastY );
@@ -464,6 +485,15 @@ public class FlatInspector
if( margin != null )
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 minSize = c.getMinimumSize();
Dimension maxSize = c.getMaximumSize();

View File

@@ -129,7 +129,7 @@
#---- 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]

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.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse)
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
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.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.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1545,7 +1550,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- 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.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]

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.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse)
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
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.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.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground true
TitlePane.useWindowDecorations true
@@ -1550,7 +1555,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- 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.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
TitlePane.buttonMaximizedHeight 22
TitlePane.buttonMinimumWidth 30
TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI]
TitlePane.centerTitle false
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.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true
TitlePane.menuBarTitleGap 20
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8
TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI]
TitlePane.showIcon true
TitlePane.showIconBesideTitle false
TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI]
TitlePane.titleMinimumWidth 60
TitlePane.unifiedBackground 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.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.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.hoverForeground #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedBackground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedForeground #00aaff HSL 200 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.hoverForeground #0000ff HSL 240 100 50 javax.swing.plaf.ColorUIResource [UI]
ToggleButton.tab.selectedBackground #008800 HSL 120 100 27 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.underlineHeight 2
ToggleButton.textIconGap 4
@@ -1589,7 +1594,7 @@ mini.font [active] Segoe UI plain 9 javax.swing.plaf.Fon
#---- 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 );
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
initTableEditors( table1 );
initTableEditors( xTable1 );
@@ -1174,6 +1188,7 @@ public class FlatComponents2Test
{ "item 12", null, "December", null, null, null },
};
private int columnCount = columnNames.length;
private int rowCount = rows.length;
private final Map<Integer, Object[]> moreRowsMap = new HashMap<>();
@@ -1181,6 +1196,16 @@ public class FlatComponents2Test
setRowCount( rowCount );
}
void setColumnCount( int columnCount ) {
if( columnCount > columnNames.length )
columnCount = columnNames.length;
this.columnCount = columnCount;
// fire event
fireTableStructureChanged();
}
void setRowCount( int rowCount ) {
int oldRowCount = this.rowCount;
this.rowCount = rowCount;
@@ -1199,7 +1224,7 @@ public class FlatComponents2Test
@Override
public int getColumnCount() {
return columnNames.length;
return columnCount;
}
@Override

View File

@@ -181,6 +181,10 @@ public class FlatMenusTest
JMenuItem menuItem45 = new JMenuItem();
JMenuItem menuItem46 = 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();
JMenu menu8 = new JMenu();
FlatMenusTest.LargerMenuItem menuItem13 = new FlatMenusTest.LargerMenuItem();
@@ -442,6 +446,24 @@ public class FlatMenusTest
menu12.add(menuItem47);
}
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");

View File

@@ -194,6 +194,22 @@ new FormModel {
"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 ) {
"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.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.Arrays;
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.ui.FlatUIUtils;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
@@ -57,18 +59,60 @@ public class FlatPaintingStringTest
FlatPaintingStringTest() {
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( "none" ) );
add( new JLabel( "flatlaf" ) );
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel() );
if( SystemInfo.isJava_9_orLater ) {
add( new JLabel( "0.125" ) );
add( new JLabel( "0.25" ) );
add( new JLabel( "0.5" ) );
add( new JLabel( "0.625" ) );
add( new JLabel( "0.75" ) );
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 {
add( new JLabel( "0.25*scale" ) );
add( new JLabel( "0.5*scale" ) );
add( new JLabel( "0.625*scale" ) );
add( new JLabel( "0.75*scale" ) );
add( new JLabel( "0.875*scale" ) );
@@ -80,16 +124,14 @@ public class FlatPaintingStringTest
? HiDPIUtils.computeTextYCorrection( g )
: (scaleFactor > 1 ? -(0.625f * scaleFactor) : 0);
};
YCorrectionFunction oneQSysScale = (g, scaleFactor) -> -(0.25f * scaleFactor);
YCorrectionFunction halfSysScale = (g, scaleFactor) -> -(0.5f * scaleFactor);
YCorrectionFunction fiveEightsQSysScale = (g, scaleFactor) -> -(0.625f * scaleFactor);
YCorrectionFunction threeQSysScale = (g, scaleFactor) -> -(0.75f * scaleFactor);
YCorrectionFunction sevenEightsSysScale = (g, scaleFactor) -> -(0.875f * scaleFactor);
YCorrectionFunction oneQ = (g, scaleFactor) -> -0.25f;
YCorrectionFunction half = (g, scaleFactor) -> -0.5f;
YCorrectionFunction fiveEights = (g, scaleFactor) -> -0.625f;
YCorrectionFunction threeQ = (g, scaleFactor) -> -0.75f;
YCorrectionFunction sevenEights = (g, scaleFactor) -> -0.875f;
YCorrectionFunction ty = (g, scaleFactor) -> {
// Based on translateY, which is the scaled Y coordinate translation of the graphics context.
// When painting whole window, translateY is from top of window, and this works fine.
// But when repainting only parts of the window, then translateY starts somewhere
// else and the text if (re-)painted at the wrong Y location.
double y = g.getTransform().getTranslateY();
return (float) -(y - (int) y);
};
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, flatlaf );
add( scaleFactor, oneQSysScale );
add( scaleFactor, halfSysScale );
add( new JLabel( " " ) );
if( SystemInfo.isJava_9_orLater ) {
add( scaleFactor, oneQ );
add( scaleFactor, half );
add( scaleFactor, fiveEights );
add( scaleFactor, threeQ );
add( scaleFactor, sevenEights );
add( scaleFactor, (g, sf) -> -0.125f );
add( scaleFactor, (g, sf) -> -0.25f );
add( scaleFactor, (g, sf) -> -0.5f );
add( scaleFactor, (g, sf) -> -0.625f );
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 {
add( scaleFactor, fiveEightsQSysScale );
add( scaleFactor, threeQSysScale );
add( scaleFactor, sevenEightsSysScale );
add( scaleFactor, (g, sf) -> -(0.25f * sf) );
add( scaleFactor, (g, sf) -> -(0.5f * sf) );
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 ) {
if( SystemInfo.isJava_9_orLater ) {
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, 0.5f ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 0.75f ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 1 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 2 ), "gapx 0 0" );
add( new Painter( scaleFactor, correctionFunction, 3 ), "gapx 0 0" );
} else
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() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JPanel panel1 = new JPanel();
JLabel fontLabel = new JLabel();
fontField = new JComboBox<>();
fontMetricsLabel = new JLabel();
//======== this ========
setBorder(null);
@@ -134,11 +214,40 @@ public class FlatPaintingStringTest
// columns
"[fill]",
// rows
"[top]unrel" +
"[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 - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JComboBox<String> fontField;
private JLabel fontMetricsLabel;
// JFormDesigner - End of variables declaration //GEN-END:variables
private interface YCorrectionFunction {
@@ -152,17 +261,29 @@ public class FlatPaintingStringTest
{
private final float scaleFactor;
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" );
this.scaleFactor = scaleFactor;
this.correctionFunction = correctionFunction;
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 ) {
Font font = getFont();
Font font = UIManager.getFont( "defaultFont" );
setFont( font.deriveFont( (float) Math.round( font.getSize() * scaleFactor ) ) );
}
}
@@ -170,35 +291,36 @@ public class FlatPaintingStringTest
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
Insets insets = getInsets();
int leftRight = insets.left + insets.right;
return new Dimension(
scale( size.width -leftRight ) + leftRight,
scale( size.height ) );
if( SystemInfo.isJava_9_orLater ) {
// compute component size using JRE scaling
//
// The y offset is used to simulate different vertical component positions,
// 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
protected void paintComponent( Graphics 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 height = getHeight();
Insets insets = getInsets();
FontMetrics fm = getFontMetrics( getFont() );
// paint lines at 1x
HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height,
(g2d, x2, y2, width2, height2, scaleFactor2) -> {
// g.setColor( Color.blue );
// g.drawLine( 0, 0, width2, 0 );
// g.drawLine( 0, height2 - 1, width2, height2 - 1 );
g.setColor( Color.blue );
g.drawLine( 0, 0, width2, 0 );
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;
int topline = height2 - baseline - 1;
@@ -207,8 +329,15 @@ public class FlatPaintingStringTest
g.drawLine( 0, topline, width2, topline );
} );
// move x before scaling to have same left inset at all scale factors
g.translate( insets.left, 0 );
// simulate different vertical component positions
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
if( SystemInfo.isJava_9_orLater )
@@ -232,21 +361,36 @@ public class FlatPaintingStringTest
// draw string
g.setColor( getForeground() );
int y = insets.top + fm.getAscent();
int y = fm.getAscent();
JavaCompatibility.drawStringUnderlineCharAt( this, cg, "E", -1, 0, y );
// set tooltip text
if( getToolTipText() == null ) {
AffineTransform t = g2.getTransform();
double textY = t.getTranslateY() + (y * t.getScaleY());
setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) );
}
FlatUIUtils.resetRenderingHints( g2, oldRenderingHints );
AffineTransform t = g2.getTransform();
double textY = t.getTranslateY() + (y * t.getScaleY());
setToolTipText( textY + " + " + yCorrection + " = " + (textY + yCorrection) );
}
private int scale( int value ) {
return SystemInfo.isJava_9_orLater ? Math.round( value * scaleFactor ) : value;
/**
* 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 static double normalize( double 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 {
contentType: "form/swing"
@@ -9,10 +9,45 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[fill]"
"$rowConstraints": "[top]"
"$rowConstraints": "[top]unrel[top]"
} ) {
name: "this"
"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 ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 450, 300 )

View File

@@ -241,6 +241,23 @@ public class FlatWindowDecorationsTest
if( c instanceof Box.Filler )
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.revalidate();
}

View File

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

View File

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

View File

@@ -34,7 +34,6 @@ import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.fife.rsta.ui.CollapsibleSectionPanel;
import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.CompletionProvider;
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.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextarea.SearchContext;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -62,7 +62,10 @@ class FlatThemeEditorPane
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 FlatSyntaxTextArea textArea;
private final ErrorStrip errorStrip;
@@ -115,11 +118,11 @@ class FlatThemeEditorPane
// create error strip
errorStrip = new ErrorStrip( textArea );
// create collapsible panel
collapsiblePanel = new CollapsibleSectionPanel();
collapsiblePanel.add( scrollPane );
collapsiblePanel.add( errorStrip, BorderLayout.LINE_END );
add( collapsiblePanel, BorderLayout.CENTER );
// create editor panel
editorPanel = new JPanel( new BorderLayout() );
editorPanel.add( scrollPane );
editorPanel.add( errorStrip, BorderLayout.LINE_END );
add( editorPanel, BorderLayout.CENTER );
updateTheme();
}
@@ -166,6 +169,13 @@ class FlatThemeEditorPane
scrollPane.getGutter().setLineNumberFont( font );
}
void selected() {
if( findReplaceVisible )
showFindReplaceBar( false );
else
hideFindReplaceBar();
}
void windowActivated() {
if( preview != null )
preview.repaint();
@@ -261,13 +271,30 @@ class FlatThemeEditorPane
return (window instanceof JFrame) ? ((JFrame)window).getTitle() : null;
}
void showFindReplaceBar() {
void showFindReplaceBar( boolean findEditorSelection ) {
if( findReplaceBar == null ) {
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 ) {

View File

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

View File

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