Popup: fixed flicker of popups (e.g. tooltips) while they are moving (e.g. following mouse pointer) (issues #832 and #672)

This commit is contained in:
Karl Tauber
2024-05-22 14:02:40 +02:00
parent 029f273dd9
commit a311bac89b
4 changed files with 323 additions and 82 deletions

View File

@@ -6,6 +6,8 @@ FlatLaf Change Log
#### New features and improvements #### New features and improvements
- Label: Support painting background with rounded corners. (issue #842) - Label: Support painting background with rounded corners. (issue #842)
- Popup: Fixed flicker of popups (e.g. tooltips) while they are moving (e.g.
following mouse pointer). (issues #832 and #672)
#### Incompatibilities #### Incompatibilities

View File

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

View File

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

View File

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