I tried my code on Win7 32bit and with eclipse 4.2. Unfortunately, this gave problems and flickered. Anyway, here is another variation. In my opinion, you should use at least two listeners, one for your menu item, which is necessary in any case, and the other to get the menu coordinates:
import org.eclipse.swt.SWT; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; public class TestMenu { private static Point point; public static void main(String[] args) { Display display = new Display(); Shell shell = new Shell(display); shell.setLayout(new GridLayout(1, false)); final Menu menu = new Menu(shell); MenuItem item = new MenuItem(menu, SWT.CHECK); item.setText("Check 1"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if(point == null) return; menu.setLocation(point); menu.setVisible(true); } }); shell.addMenuDetectListener(new MenuDetectListener() { public void menuDetected(MenuDetectEvent e) { point = new Point(ex, ey); } }); shell.setMenu(menu); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } }
UpdateThanks @Baz, see the comment below.
Tried this on Linux 32bit with eclipse 3.6.2 and unfortunately this doesn't seem to work.
Update 2 (by ulmangt)
The following is a modification of your solution, which works for me on both 64-bit Windows 7 and 64-bit Ubuntu Linux.
The org.eclipse.swt.widgets.Menu class has a package protection field that determines whether a menu location has been set. If not, at least on Linux, a menu appears under the click of a mouse.
Therefore, to obtain the correct behavior, you need to use reflection to reset this logical field to false. As an alternative, the menus could probably be positioned and recreated.
Finally, Linux seems that menu.setLocation( point ) and menu.setVisible( true ) should be made from an asyncExec block.
import java.lang.reflect.Field; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; public class MenuTest { private static Point point; public static void main( String[] args ) { Display display = new Display( ); final Shell shell = new Shell( display ); shell.setLayout( new GridLayout( 1, false ) ); final Menu menu = new Menu( shell ); MenuItem item = new MenuItem( menu, SWT.CHECK ); item.setText( "Check 1" ); item.addSelectionListener( new SelectionAdapter( ) { public void widgetSelected( final SelectionEvent e ) { if ( point == null ) return; Display.getDefault( ).asyncExec( new Runnable( ) { @Override public void run( ) { menu.setLocation( point ); menu.setVisible( true ); } } ); } } ); shell.addMenuDetectListener( new MenuDetectListener( ) { public void menuDetected( MenuDetectEvent e ) { point = new Point( ex, ey ); } } ); menu.addMenuListener( new MenuListener( ) { @Override public void menuHidden( MenuEvent event ) { try { Field field = Menu.class.getDeclaredField( "hasLocation" ); field.setAccessible( true ); field.set( menu, false ); } catch ( Exception e ) { e.printStackTrace(); } } @Override public void menuShown( MenuEvent event ) { } }); shell.setMenu( menu ); shell.open( ); while ( !shell.isDisposed( ) ) { if ( !display.readAndDispatch( ) ) display.sleep( ); } display.dispose( ); } }