mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
Linux: hide popups when window is moved, resized, maximized, restored, iconified or switched to another window (issue #962)
This commit is contained in:
@@ -30,6 +30,9 @@ FlatLaf Change Log
|
||||
- FlatLaf window decorations: Minimize and maximize icons were not shown for
|
||||
custom scale factors less than 100% (e.g. `-Dflatlaf.uiScale=75%`). (issue
|
||||
#951)
|
||||
- Linux: Popups (menus and combobox lists) were not hidden when window is moved,
|
||||
resized, maximized, restored, iconified or switched to another window. (issue
|
||||
#962)
|
||||
|
||||
|
||||
## 3.5.4
|
||||
|
||||
@@ -111,6 +111,7 @@ public abstract class FlatLaf
|
||||
private PopupFactory oldPopupFactory;
|
||||
private MnemonicHandler mnemonicHandler;
|
||||
private boolean subMenuUsabilityHelperInstalled;
|
||||
private LinuxPopupMenuCanceler linuxPopupMenuCanceler;
|
||||
|
||||
private Consumer<UIDefaults> postInitialization;
|
||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||
@@ -305,6 +306,10 @@ public abstract class FlatLaf
|
||||
// install submenu usability helper
|
||||
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
||||
|
||||
// install Linux popup menu canceler
|
||||
if( SystemInfo.isLinux )
|
||||
linuxPopupMenuCanceler = new LinuxPopupMenuCanceler();
|
||||
|
||||
// listen to desktop property changes to update UI if system font or scaling changes
|
||||
if( SystemInfo.isWindows ) {
|
||||
// Windows 10 allows increasing font size independent of scaling:
|
||||
@@ -397,6 +402,12 @@ public abstract class FlatLaf
|
||||
subMenuUsabilityHelperInstalled = false;
|
||||
}
|
||||
|
||||
// uninstall Linux popup menu canceler
|
||||
if( linuxPopupMenuCanceler != null ) {
|
||||
linuxPopupMenuCanceler.uninstall();
|
||||
linuxPopupMenuCanceler = null;
|
||||
}
|
||||
|
||||
// restore default link color
|
||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||
postInitialization = null;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2025 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;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.MenuElement;
|
||||
import javax.swing.MenuSelectionManager;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
/**
|
||||
* Cancels (hides) popup menus on Linux.
|
||||
* <p>
|
||||
* On Linux, popups are not hidden under following conditions, which results in
|
||||
* misplaced popups:
|
||||
* <ul>
|
||||
* <li>window moved or resized
|
||||
* <li>window maximized or restored
|
||||
* <li>window iconified
|
||||
* <li>window deactivated (e.g. activated other application)
|
||||
* </ul>
|
||||
*
|
||||
* On Windows and macOS, popups are automatically hidden.
|
||||
* <p>
|
||||
* The implementation is similar to what's done in
|
||||
* {@code javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber},
|
||||
* but only hides popup in some conditions.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
*/
|
||||
class LinuxPopupMenuCanceler
|
||||
extends WindowAdapter
|
||||
implements ChangeListener, ComponentListener
|
||||
{
|
||||
private MenuElement[] lastPathSelectedPath;
|
||||
private Window window;
|
||||
|
||||
LinuxPopupMenuCanceler() {
|
||||
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
|
||||
msm.addChangeListener( this );
|
||||
|
||||
lastPathSelectedPath = msm.getSelectedPath();
|
||||
if( lastPathSelectedPath.length > 0 )
|
||||
addWindowListeners( lastPathSelectedPath[0] );
|
||||
}
|
||||
|
||||
void uninstall() {
|
||||
MenuSelectionManager.defaultManager().removeChangeListener( this );
|
||||
}
|
||||
|
||||
private void addWindowListeners( MenuElement selected ) {
|
||||
// see BasicPopupMenuUI.MouseGrabber.grabWindow()
|
||||
Component invoker = selected.getComponent();
|
||||
if( invoker instanceof JPopupMenu )
|
||||
invoker = ((JPopupMenu)invoker).getInvoker();
|
||||
window = (invoker instanceof Window)
|
||||
? (Window) invoker
|
||||
: SwingUtilities.windowForComponent( invoker );
|
||||
|
||||
if( window != null ) {
|
||||
window.addWindowListener( this );
|
||||
window.addComponentListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
private void removeWindowListeners() {
|
||||
if( window != null ) {
|
||||
window.removeWindowListener( this );
|
||||
window.removeComponentListener( this );
|
||||
window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelPopupMenu() {
|
||||
try {
|
||||
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
|
||||
MenuElement[] selectedPath = msm.getSelectedPath();
|
||||
for( MenuElement e : selectedPath ) {
|
||||
if( e instanceof JPopupMenu )
|
||||
((JPopupMenu)e).putClientProperty( "JPopupMenu.firePopupMenuCanceled", true );
|
||||
}
|
||||
msm.clearSelectedPath();
|
||||
} catch( RuntimeException ex ) {
|
||||
removeWindowListeners();
|
||||
throw ex;
|
||||
} catch( Error ex ) {
|
||||
removeWindowListeners();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
//---- ChangeListener ----
|
||||
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e ) {
|
||||
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||
|
||||
if( selectedPath.length == 0 )
|
||||
removeWindowListeners();
|
||||
else if( lastPathSelectedPath.length == 0 )
|
||||
addWindowListeners( selectedPath[0] );
|
||||
|
||||
lastPathSelectedPath = selectedPath;
|
||||
}
|
||||
|
||||
//---- WindowListener ----
|
||||
|
||||
@Override
|
||||
public void windowIconified( WindowEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated( WindowEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing( WindowEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
|
||||
//---- ComponentListener ----
|
||||
|
||||
@Override
|
||||
public void componentResized( ComponentEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentMoved( ComponentEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown( ComponentEvent e ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden( ComponentEvent e ) {
|
||||
cancelPopupMenu();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user