Popup: on Windows 10, update drop shadow of heavy-weight popup if popup moved/resized (issue #942)
Some checks failed
CI / build (11) (push) Has been cancelled
CI / build-on (17, ) (push) Has been cancelled
CI / build-on (21, ) (push) Has been cancelled
CI / build-on (23, ) (push) Has been cancelled
CI / build-on (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
Karl Tauber
2025-01-25 06:55:37 +01:00
parent f30dd876e4
commit cca9707f6b
5 changed files with 218 additions and 22 deletions

View File

@@ -25,6 +25,8 @@ FlatLaf Change Log
- FileChooser: Improved performance when navigating to large directories with - FileChooser: Improved performance when navigating to large directories with
thousands of files. (issue #953) thousands of files. (issue #953)
- PopupFactory: Fixed NPE on Windows 10 when `owner` is `null`. (issue #952) - PopupFactory: Fixed NPE on Windows 10 when `owner` is `null`. (issue #952)
- Popup: On Windows 10, drop shadow of heavy-weight popup was not updated if
popup moved/resized. (issue #942)
- FlatLaf window decorations: Minimize and maximize icons were not shown for - FlatLaf window decorations: Minimize and maximize icons were not shown for
custom scale factors less than 100% (e.g. `-Dflatlaf.uiScale=75%`). (issue custom scale factors less than 100% (e.g. `-Dflatlaf.uiScale=75%`). (issue
#951) #951)

View File

@@ -74,7 +74,7 @@ public class FlatDropShadowBorder
this.shadowColor = shadowColor; this.shadowColor = shadowColor;
this.shadowInsets = shadowInsets; this.shadowInsets = shadowInsets;
this.shadowOpacity = shadowOpacity; this.shadowOpacity = Math.min( Math.max( shadowOpacity, 0f ), 1f );
shadowSize = maxInset( shadowInsets ); shadowSize = maxInset( shadowInsets );
} }

View File

@@ -709,6 +709,7 @@ public class FlatPopupFactory
private class DropShadowPopup private class DropShadowPopup
extends NonFlashingPopup extends NonFlashingPopup
implements ComponentListener
{ {
// light weight // light weight
private JComponent lightComp; private JComponent lightComp;
@@ -768,7 +769,7 @@ public class FlatPopupFactory
} }
// Windows 11: reset corner preference on reused heavy weight popups // Windows 11: reset corner preference on reused heavy weight popups
if( isWindows11BorderSupported() ) { if( SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded() ) {
resetWindows11Border( popupWindow ); resetWindows11Border( popupWindow );
if( dropShadowWindow != null ) if( dropShadowWindow != null )
resetWindows11Border( dropShadowWindow ); resetWindows11Border( dropShadowWindow );
@@ -838,10 +839,18 @@ public class FlatPopupFactory
if( insets.left != 0 || insets.top != 0 ) if( insets.left != 0 || insets.top != 0 )
lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top ); lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top );
} }
if( popupWindow != null ) {
removeAllPopupWindowComponentListeners();
popupWindow.addComponentListener( this );
}
} }
@Override @Override
void hideImpl() { void hideImpl() {
if( popupWindow != null )
removeAllPopupWindowComponentListeners();
if( dropShadowDelegate != null ) { if( dropShadowDelegate != null ) {
dropShadowDelegate.hide(); dropShadowDelegate.hide();
dropShadowDelegate = null; dropShadowDelegate = null;
@@ -941,23 +950,55 @@ public class FlatPopupFactory
@Override @Override
void reset( Component contents, int ownerX, int ownerY ) { void reset( Component contents, int ownerX, int ownerY ) {
if( popupWindow != null )
removeAllPopupWindowComponentListeners();
super.reset( contents, ownerX, ownerY ); super.reset( contents, ownerX, ownerY );
if( dropShadowWindow != null ) { updateDropShadowWindowBounds();
// 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 ) );
dropShadowPanel2.invalidate();
dropShadowWindow.pack();
// update drop shadow popup window location private void updateDropShadowWindowBounds() {
int x = popupWindow.getX() - insets.left; if( dropShadowWindow == null )
int y = popupWindow.getY() - insets.top; return;
dropShadowWindow.setLocation( x, y );
// calculate size of drop shadow window
Dimension size = popupWindow.getSize();
Insets insets = dropShadowPanel2.getInsets();
int w = size.width + insets.left + insets.right;
int h = size.height + insets.top + insets.bottom;
// update drop shadow popup window bounds
int x = popupWindow.getX() - insets.left;
int y = popupWindow.getY() - insets.top;
dropShadowWindow.setBounds( x, y, w, h );
dropShadowWindow.validate();
}
private void removeAllPopupWindowComponentListeners() {
// make sure that there is no old component listener
// necessary because this class is cloned if reusing popup windows
for( ComponentListener l : popupWindow.getComponentListeners() ) {
if( l instanceof DropShadowPopup )
popupWindow.removeComponentListener( l );
} }
} }
//---- interface ComponentListener ----
@Override
public void componentResized( ComponentEvent e ) {
if( e.getSource() == popupWindow )
updateDropShadowWindowBounds();
}
@Override
public void componentMoved( ComponentEvent e ) {
if( e.getSource() == popupWindow )
updateDropShadowWindowBounds();
}
@Override public void componentShown( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {}
} }
} }

View File

@@ -21,6 +21,10 @@ import java.awt.event.MouseEvent;
import java.util.Random; import java.util.Random;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.Animator; import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
@@ -43,6 +47,8 @@ public class FlatPopupTest
FlatPopupTest() { FlatPopupTest() {
initComponents(); initComponents();
addPopupMenuListener( popupMenu1, "popupMenu1" );
addPopupMenuListener( popupMenu2, "popupMenu2" );
} }
private void showPopupMenu() { private void showPopupMenu() {
@@ -114,6 +120,46 @@ public class FlatPopupTest
} }
} }
private void showDirectPopup() {
DirectPopupContent content = new DirectPopupContent();
content.putClientProperty( FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, true );
Point pt = showDirectPopupButton.getLocationOnScreen();
System.setProperty( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, "false" );
UIManager.put( "Popup.dropShadowColor", Color.red );
UIManager.put( "Popup.dropShadowInsets", new Insets( 5, 5, 5, 5 ) );
UIManager.put( "Popup.dropShadowOpacity", 1f );
Popup popup = PopupFactory.getSharedInstance().getPopup( showDirectPopupButton,
content, pt.x, pt.y + showDirectPopupButton.getHeight() + 10 );
content.popup = popup;
popup.show();
System.clearProperty( FlatSystemProperties.USE_ROUNDED_POPUP_BORDER );
UIManager.put( "Popup.dropShadowColor", null );
UIManager.put( "Popup.dropShadowInsets", null );
UIManager.put( "Popup.dropShadowOpacity", null );
}
private void addPopupMenuListener( JPopupMenu popupMenu, String name ) {
popupMenu.addPopupMenuListener( new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
System.out.println( "popupMenuWillBecomeVisible " + name );
}
@Override
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
System.out.println( "popupMenuWillBecomeInvisible " + name );
}
@Override
public void popupMenuCanceled( PopupMenuEvent e ) {
System.out.println( "popupMenuCanceled " + name );
}
} );
}
@Override @Override
public void updateUI() { public void updateUI() {
super.updateUI(); super.updateUI();
@@ -128,15 +174,12 @@ public class FlatPopupTest
} }
} }
private void countChanged() {
// TODO add your code here
}
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
label1 = new JLabel(); label1 = new JLabel();
label2 = new JLabel(); label2 = new JLabel();
showPopupMenuButton = new JButton(); showPopupMenuButton = new JButton();
showDirectPopupButton = new JButton();
showLargePopupMenuButton = new JButton(); showLargePopupMenuButton = new JButton();
showPopupButton = new JButton(); showPopupButton = new JButton();
hidePopupButton = new JButton(); hidePopupButton = new JButton();
@@ -209,6 +252,11 @@ public class FlatPopupTest
showPopupMenuButton.addActionListener(e -> showPopupMenu()); showPopupMenuButton.addActionListener(e -> showPopupMenu());
add(showPopupMenuButton, "cell 0 2"); add(showPopupMenuButton, "cell 0 2");
//---- showDirectPopupButton ----
showDirectPopupButton.setText("show direct move/resize popup");
showDirectPopupButton.addActionListener(e -> showDirectPopup());
add(showDirectPopupButton, "cell 2 2 2 1");
//---- showLargePopupMenuButton ---- //---- showLargePopupMenuButton ----
showLargePopupMenuButton.setText("show heavy-weight JPopupMenu"); showLargePopupMenuButton.setText("show heavy-weight JPopupMenu");
showLargePopupMenuButton.addActionListener(e -> showLargePopupMenu()); showLargePopupMenuButton.addActionListener(e -> showLargePopupMenu());
@@ -240,7 +288,6 @@ public class FlatPopupTest
//---- countField ---- //---- countField ----
countField.setModel(new SpinnerNumberModel(1, 1, null, 1)); countField.setModel(new SpinnerNumberModel(1, 1, null, 1));
countField.addChangeListener(e -> countChanged());
add(countField, "cell 5 4"); add(countField, "cell 5 4");
//---- label4 ---- //---- label4 ----
@@ -366,6 +413,7 @@ public class FlatPopupTest
private JLabel label1; private JLabel label1;
private JLabel label2; private JLabel label2;
private JButton showPopupMenuButton; private JButton showPopupMenuButton;
private JButton showDirectPopupButton;
private JButton showLargePopupMenuButton; private JButton showLargePopupMenuButton;
private JButton showPopupButton; private JButton showPopupButton;
private JButton hidePopupButton; private JButton hidePopupButton;
@@ -444,4 +492,69 @@ public class FlatPopupTest
private JLabel label6; private JLabel label6;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on // JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
} }
//---- class MyPopupContent -----------------------------------------------
private static class DirectPopupContent
extends JPanel
{
Popup popup;
DirectPopupContent() {
initComponents();
}
private void resizePopup() {
Window popupWindow = SwingUtilities.windowForComponent( this );
popupWindow.setSize( popupWindow.getWidth() + 20, popupWindow.getHeight() + 50 );
}
private void movePopup() {
Window popupWindow = SwingUtilities.windowForComponent( this );
popupWindow.setLocation( popupWindow.getX() + 20, popupWindow.getY() + 50 );
}
private void hidePopup() {
popup.hide();
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
resizeButton = new JButton();
moveButton = new JButton();
hideButton = new JButton();
//======== this ========
setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]" +
"[fill]",
// rows
"[]"));
//---- resizeButton ----
resizeButton.setText("Resize");
resizeButton.addActionListener(e -> resizePopup());
add(resizeButton, "cell 0 0");
//---- moveButton ----
moveButton.setText("Move");
moveButton.addActionListener(e -> movePopup());
add(moveButton, "cell 1 0");
//---- hideButton ----
hideButton.setText("Hide");
hideButton.addActionListener(e -> hidePopup());
add(hideButton, "cell 2 0");
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
private JButton resizeButton;
private JButton moveButton;
private JButton hideButton;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
}
} }

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.2.3.0.386" Java: "21" encoding: "UTF-8" JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -30,6 +30,13 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2" "value": "cell 0 2"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) {
name: "showDirectPopupButton"
"text": "show direct move/resize popup"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showDirectPopup", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 2 2 1"
} )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "showLargePopupMenuButton" name: "showLargePopupMenuButton"
"text": "show heavy-weight JPopupMenu" "text": "show heavy-weight JPopupMenu"
@@ -77,7 +84,6 @@ new FormModel {
minimum: 1 minimum: 1
value: 1 value: 1
} }
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "countChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 4" "value": "cell 5 4"
} ) } )
@@ -215,5 +221,39 @@ new FormModel {
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 5, 505 ) "location": new java.awt.Point( 5, 505 )
} ) } )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill][fill]"
"$rowConstraints": "[]"
} ) {
name: "panel1"
auxiliary() {
"JavaCodeGenerator.className": "DirectPopupContent"
}
add( new FormComponent( "javax.swing.JButton" ) {
name: "resizeButton"
"text": "Resize"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "resizePopup", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "moveButton"
"text": "Move"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "movePopup", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "hideButton"
"text": "Hide"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hidePopup", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 180, 395 )
"size": new java.awt.Dimension( 270, 100 )
} )
} }
} }