Window decorations on Linux: fixed behavior of maximize/restore button when tiling window to left or right half of screen (issue #647)

This commit is contained in:
Karl Tauber
2023-02-23 15:26:11 +01:00
parent 546382e471
commit 425f3acced
4 changed files with 101 additions and 13 deletions

View File

@@ -28,6 +28,8 @@ FlatLaf Change Log
decorations are used (e.g. Windows 10/11) or not (e.g. macOS). Now the glass decorations are used (e.g. Windows 10/11) or not (e.g. macOS). Now the glass
pane no longer overlaps the FlatLaf window title bar. (issue #630) pane no longer overlaps the FlatLaf window title bar. (issue #630)
- Linux: Fixed broken window resizing on multi-screen setups. (issue #632) - Linux: Fixed broken window resizing on multi-screen setups. (issue #632)
- Linux: Fixed behavior of maximize/restore button when tiling window to left
or right half of screen. (issue #647)
- IntelliJ Themes: - IntelliJ Themes:
- Fixed default button hover background in "Solarized Light" theme. (issue - Fixed default button hover background in "Solarized Light" theme. (issue
#628) #628)

View File

@@ -551,7 +551,7 @@ public class FlatRootPaneUI
protected boolean isWindowMaximized( Component c ) { protected boolean isWindowMaximized( Component c ) {
Container parent = c.getParent(); Container parent = c.getParent();
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
} }
} }

View File

@@ -360,9 +360,8 @@ public class FlatTitlePane
if( window instanceof Frame ) { if( window instanceof Frame ) {
Frame frame = (Frame) window; Frame frame = (Frame) window;
boolean maximized = ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0);
if( maximized && if( isWindowMaximized() &&
!(SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window )) && !(SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window )) &&
rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null ) rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null )
{ {
@@ -393,7 +392,7 @@ public class FlatTitlePane
if( window instanceof Frame ) { if( window instanceof Frame ) {
Frame frame = (Frame) window; Frame frame = (Frame) window;
boolean maximizable = frame.isResizable() && clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_MAXIMIZE, true ); boolean maximizable = frame.isResizable() && clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_MAXIMIZE, true );
boolean maximized = ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0); boolean maximized = isWindowMaximized();
iconifyButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICONIFFY, true ) ); iconifyButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICONIFFY, true ) );
maximizeButton.setVisible( maximizable && !maximized ); maximizeButton.setVisible( maximizable && !maximized );
@@ -643,7 +642,10 @@ public class FlatTitlePane
/** @since 2.4 */ /** @since 2.4 */
protected boolean isWindowMaximized() { protected boolean isWindowMaximized() {
return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; // Windows and macOS use always MAXIMIZED_BOTH.
// Only Linux uses MAXIMIZED_VERT and MAXIMIZED_HORIZ (when dragging window to left or right edge).
// (searched jdk source code)
return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
} }
/** /**
@@ -661,8 +663,30 @@ public class FlatTitlePane
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true ); rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", true );
// maximize window // maximize window
if( !FlatNativeWindowBorder.showWindow( frame, FlatNativeWindowBorder.Provider.SW_MAXIMIZE ) ) if( !FlatNativeWindowBorder.showWindow( frame, FlatNativeWindowBorder.Provider.SW_MAXIMIZE ) ) {
frame.setExtendedState( frame.getExtendedState() | Frame.MAXIMIZED_BOTH ); int oldState = frame.getExtendedState();
int newState = oldState | Frame.MAXIMIZED_BOTH;
if( SystemInfo.isLinux ) {
// Linux supports vertical and horizontal maximization:
// - dragging a window to left or right edge of screen vertically maximizes
// the window to the left or right half of the screen
// - don't know whether user can do horizontal maximization
// (Windows and macOS use only MAXIMIZED_BOTH)
//
// If a window is maximized vertically or horizontally (but not both),
// then Frame.setExtendedState() behaves not as expected on Linux.
// E.g. if window state is MAXIMIZED_VERT, calling setExtendedState(MAXIMIZED_BOTH)
// changes state to MAXIMIZED_HORIZ. But calling setExtendedState(MAXIMIZED_HORIZ)
// changes state from MAXIMIZED_VERT to MAXIMIZED_BOTH.
// Seems to be a bug in sun.awt.X11.XNETProtocol.requestState(),
// which does some strange state XOR-ing...
if( (oldState & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_VERT )
newState = oldState & ~Frame.MAXIMIZED_BOTH | Frame.MAXIMIZED_HORIZ;
}
frame.setExtendedState( newState );
}
} }
protected void updateMaximizedBounds() { protected void updateMaximizedBounds() {
@@ -766,8 +790,7 @@ public class FlatTitlePane
if( !(window instanceof Frame) || !((Frame)window).isResizable() ) if( !(window instanceof Frame) || !((Frame)window).isResizable() )
return; return;
Frame frame = (Frame) window; if( isWindowMaximized() )
if( (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
restore(); restore();
else else
maximize(); maximize();
@@ -1188,6 +1211,13 @@ public class FlatTitlePane
@Override @Override
public void windowStateChanged( WindowEvent e ) { public void windowStateChanged( WindowEvent e ) {
/*debug
System.out.println( "state " + e.getOldState() + " -> " + e.getNewState() + " "
+ ((e.getNewState() & Frame.MAXIMIZED_HORIZ) != 0 ? " HORIZ" : "")
+ ((e.getNewState() & Frame.MAXIMIZED_VERT) != 0 ? " VERT" : "")
);
debug*/
frameStateChanged(); frameStateChanged();
updateNativeTitleBarHeightAndHitTestSpots(); updateNativeTitleBarHeightAndHitTestSpots();
} }
@@ -1195,7 +1225,7 @@ public class FlatTitlePane
//---- interface MouseListener ---- //---- interface MouseListener ----
private Point dragOffset; private Point dragOffset;
private boolean nativeMove; private boolean linuxNativeMove;
private long lastSingleClickWhen; private long lastSingleClickWhen;
@Override @Override
@@ -1241,7 +1271,7 @@ public class FlatTitlePane
return; return;
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window ); dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
nativeMove = false; linuxNativeMove = false;
// on Linux, move or maximize/restore window // on Linux, move or maximize/restore window
if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) { if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
@@ -1261,7 +1291,7 @@ public class FlatTitlePane
case 1: case 1:
// move window via _NET_WM_MOVERESIZE message // move window via _NET_WM_MOVERESIZE message
e.consume(); e.consume();
nativeMove = FlatNativeLinuxLibrary.moveOrResizeWindow( window, e, FlatNativeLinuxLibrary.MOVE ); linuxNativeMove = FlatNativeLinuxLibrary.moveOrResizeWindow( window, e, FlatNativeLinuxLibrary.MOVE );
lastSingleClickWhen = e.getWhen(); lastSingleClickWhen = e.getWhen();
break; break;
@@ -1291,7 +1321,7 @@ public class FlatTitlePane
if( window == null || dragOffset == null ) if( window == null || dragOffset == null )
return; // should newer occur return; // should newer occur
if( nativeMove ) if( linuxNativeMove )
return; return;
if( !SwingUtilities.isLeftMouseButton( e ) ) if( !SwingUtilities.isLeftMouseButton( e ) )

View File

@@ -0,0 +1,56 @@
package com.formdev.flatlaf.testing;
import java.awt.FlowLayout;
import java.awt.Frame;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.util.SystemInfo;
public class SwingFrameStateTest
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatLightLaf.setup();
if( SystemInfo.isLinux )
JFrame.setDefaultLookAndFeelDecorated( true );
JFrame frame = new JFrame( "SwingFrameStateTest" );
JButton restoreButton = new JButton( "Restore" );
JButton vertButton = new JButton( "Max. Vert." );
JButton horizButton = new JButton( "Max. Horiz." );
JButton bothButton = new JButton( "Max. Both" );
restoreButton.addActionListener( e -> frame.setExtendedState( 0 ) );
vertButton.addActionListener( e -> frame.setExtendedState( Frame.MAXIMIZED_VERT ) );
horizButton.addActionListener( e -> frame.setExtendedState( Frame.MAXIMIZED_HORIZ ) );
bothButton.addActionListener( e -> frame.setExtendedState( Frame.MAXIMIZED_BOTH ) );
JPanel panel = new JPanel( new FlowLayout() );
panel.add( restoreButton );
panel.add( vertButton );
panel.add( horizButton );
panel.add( bothButton );
JLabel stateInfo = new JLabel();
frame.addWindowStateListener( e -> {
int state = frame.getExtendedState();
stateInfo.setText( " state "
+ ((state & Frame.MAXIMIZED_VERT) != 0 ? "VERT " : "")
+ ((state & Frame.MAXIMIZED_HORIZ) != 0 ? "HORIZ " : "")
);
} );
panel.add( stateInfo );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add( panel );
frame.setSize( 700, 300 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
} );
}
}