mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 22:10:54 +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
|
- 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)
|
||||||
|
- 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
|
## 3.5.4
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ public abstract class FlatLaf
|
|||||||
private PopupFactory oldPopupFactory;
|
private PopupFactory oldPopupFactory;
|
||||||
private MnemonicHandler mnemonicHandler;
|
private MnemonicHandler mnemonicHandler;
|
||||||
private boolean subMenuUsabilityHelperInstalled;
|
private boolean subMenuUsabilityHelperInstalled;
|
||||||
|
private LinuxPopupMenuCanceler linuxPopupMenuCanceler;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||||
@@ -305,6 +306,10 @@ public abstract class FlatLaf
|
|||||||
// install submenu usability helper
|
// install submenu usability helper
|
||||||
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
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
|
// listen to desktop property changes to update UI if system font or scaling changes
|
||||||
if( SystemInfo.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
// Windows 10 allows increasing font size independent of scaling:
|
// Windows 10 allows increasing font size independent of scaling:
|
||||||
@@ -397,6 +402,12 @@ public abstract class FlatLaf
|
|||||||
subMenuUsabilityHelperInstalled = false;
|
subMenuUsabilityHelperInstalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstall Linux popup menu canceler
|
||||||
|
if( linuxPopupMenuCanceler != null ) {
|
||||||
|
linuxPopupMenuCanceler.uninstall();
|
||||||
|
linuxPopupMenuCanceler = null;
|
||||||
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
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