I have an Eclipse RCP / SWT application with a Menu of Check Box Menu Items.
I would like to be able to check/uncheck multiple items before clicking elsewhere to close the menu. However, the default SWT behavior is to close the menu after a single click.
I have implemented the following very hacked solution which works, but is certainly not elegant and probably won't work properly on all platforms or under all circumstances. So I'm very interested in a simpler technique if one exists.
The following code should compile and run inside eclipse right out of the box (apologies for the length, its the shortest self contained example I could create):
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 );
}
}
} );
}
}
}