我有一个带有复选框菜单项菜单的 Eclipse RCP / SWT 应用程序。
我希望能够在单击其他地方关闭菜单之前选中/取消选中多个项目。但是,默认的 SWT 行为是单击后关闭菜单。
我已经实现了以下非常破解的解决方案,它可以工作,但肯定不优雅,可能无法在所有平台或所有情况下正常工作。因此,如果存在一种更简单的技术,我非常感兴趣。
以下代码应立即在 eclipse 中编译和运行(抱歉,它是我可以创建的最短的自包含示例):
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener2;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
public class MenuTest
{
public static void main( String[] args )
{
// create a SWT Display and Shell
final Display display = new Display( );
Shell shell = new Shell( display );
shell.setText( "Menu Example" );
// create a jface MenuManager and Menu
MenuManager popupMenu = new MenuManager( );
Menu menu = popupMenu.createContextMenu( shell );
shell.setMenu( menu );
// create a custom listener class
final PopupListener listener = new PopupListener( shell, menu );
// attach the listener to the Manager, Menu, and Shell (yuck!)
popupMenu.addMenuListener( listener );
menu.addListener( SWT.Show, listener );
shell.addListener( SWT.MouseDown, listener );
// add an item to the menu
popupMenu.add( new Action( "Test", Action.AS_CHECK_BOX )
{
@Override
public void run( )
{
System.out.println( "Test checked: " + isChecked( ) );
listener.keepMenuVisible( );
}
} );
// show the SWT shell
shell.setSize( 800, 800 );
shell.setLocation( 0, 0 );
shell.open( );
shell.moveAbove( null );
while ( !shell.isDisposed( ) )
if ( !display.readAndDispatch( ) ) display.sleep( );
return;
}
public static class PopupListener implements Listener, IMenuListener2
{
Menu menu;
Control control;
Point point;
public PopupListener( Control control, Menu menu )
{
this.control = control;
this.menu = menu;
}
@Override
public void handleEvent( Event event )
{
// when SWT.Show events are received, make the Menu visible
// (we'll programmatically create such events)
if ( event.type == SWT.Show )
{
menu.setVisible( true );
}
// when the mouse is clicked, map the position from Shell
// coordinates to Display coordinates and save the result
// this is necessary because there appears to be no way
// to ask the Menu what its current position is
else if ( event.type == SWT.MouseDown )
{
point = Display.getDefault( ).map( control, null, event.x, event.y );
}
}
@Override
public void menuAboutToShow( IMenuManager manager )
{
// if we have a saved point, use it to set the menu location
if ( point != null )
{
menu.setLocation( point.x, point.y );
}
}
@Override
public void menuAboutToHide( IMenuManager manager )
{
// do nothing
}
// whenever the checkbox action is pressed, the menu closes
// we run this to reopen the menu
public void keepMenuVisible( )
{
Display.getDefault( ).asyncExec( new Runnable( )
{
@Override
public void run( )
{
Event event = new Event( );
event.type = SWT.Show;
event.button = 3;
menu.notifyListeners( SWT.Show, event );
if ( point != null )
{
menu.setLocation( point.x, point.y );
}
}
} );
}
}
}