Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Standard Widget Toolkit (SWT) » workaround: display SWT popupmenu over Swing components on GTK
workaround: display SWT popupmenu over Swing components on GTK [message #667250] Wed, 27 April 2011 16:09 Go to next message
Samuel Thiriot is currently offline Samuel ThiriotFriend
Messages: 2
Registered: April 2011
Junior Member
Hi,

In short: I'm writing this first post to share a workaround for a frequent problem: SWT popup menu is not (always) displayed over Swing components. The workaround consists in creating an hidden shell, to open and close it, and to call the setActive() method from the shell of interest; after several calls, the popup menu should be displayed - that may be detected using the isVisible() method.


Context: many people do include SWING (more generally, AWT) components into SWT ones using the great AWT_SWT bridge.
Sometimes, it would be great to display SWT PopupMenus over these swing components.

This works without much problems for Windows or Apple plateforms. You simply have to detect the right button click, to position the SWT Popup Menu (a Menu object) at the right place, and to make it visible. However, on operating systems based on GTK - such like many linux distributions - a problem occurs when making the Menu visible: the menu is supposed to be visible, but it is never displayed.

The problem was notably discussed in these threads:
- http://www.eclipsezone.com/eclipse/forums/t96060.html
- http://dev.eclipse.org/newslists/news.eclipse.platform.swt/m sg34752.html
- http://www.eclipsezone.com/eclipse/forums/t95687.html
- http://dev.eclipse.org/newslists/news.eclipse.platform.swt/m sg33992.html

The idea of this workaround is inspired by the last post from Jason Wrang, who underlines that in case of problem, after calling Menu.setVisible(true):
- an immediate call to the Menu isVisible() method returns true
- a call to Menu IsVisible after tens of milliseconds (or more) returns false

In his post, Jason Wrang propose to create an invisible Shell displayed over the original one, which is used for displaying the SWT popupmenu. As long as the menu is not visible (with a safety threshold), this last shell is disposed and another one is created. However, I don't really like the principle of displaying the menu on another Shell - for instance, a right click on another swt component in the initial shell will not always lead to the hiding of the popup menu. Feel free to use this workaround or the Jason Wrang's one !

As far as I observed, switching the main shell to the background, then displaying it again on top, by the use of a classic ALT-TAB, appear to solve (manually) the problem. Nevertheless, neither the shell.setActive(), shell.forceActive(), shell.setFocus() nor shell.forceFocus() reproduces this effect - and we have no way to ask the window manager to "loose the focus" for a shell. As a consequence, the solution is to display another shell, to hide it, and to force the focus of the first shell. No need to use this other shell for displaying the popup menu, its role is solely to make the windowmanager to put our shell to front one more time.

The minimal code for this workaround is below (note that it is far more simple than previous workaround).
Feedback welcomed in order to check if this always solves the problem or not !

package client.gui.prefuse;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;

import prefuse.visual.VisualItem;

/**
 * Demonstrates the workaround for displaying a SWT popup menu 
 * over swing components under GTK (menu not displayed / visible bug)
 * 
 * @author Samuel Thiriot, INRIA
 *
 */
public class PopupOverAwt {

	private Display swtDisplay;
	private Shell swtShell ;
	
	private Menu swtPopupMenu;
	
	private final static int MAX_RETRIES = 10;
	
	public PopupOverAwt() {
		
				
	}
	

    /**
     * Workaround: due to a GTK problem (Linux and other Unix), 
     * popup menus are not always displayed. This tries several 
     * times to display it. 
     * 
     * see
     * http://dev.eclipse.org/newslists/news.eclipse.platform.swt/msg33992.html
     * http://www.eclipsezone.com/eclipse/forums/t95687.html
     * @param menu
     * @param retriesRemaining
     */
    protected void retryVisible(final int retriesRemaining) {
    	
	

    	swtDisplay.asyncExec(new Runnable() {
			
			@Override
			public void run() {
				
				if (swtPopupMenu.isVisible()) {
					System.out.println("made visible after "+(MAX_RETRIES-retriesRemaining)+" attempts");
					
				} else if (retriesRemaining > 0) {
					
					System.out.println("retrying (remains "+(retriesRemaining-1)+")");
					
					//swtHost.getShell().forceFocus();
					//swtHost.getShell().forceActive();
					//menu.setVisible(false);
					swtPopupMenu.setVisible(false);
					
					{
						Shell shell = new Shell(swtDisplay, 
								SWT.APPLICATION_MODAL | // should lead the window manager to switch another window to the front
								SWT.DIALOG_TRIM	// not displayed into taskbars nor in task managers 
								);
						shell.setSize(10, 10); // big enough to avoid errors from the gtk layer
						shell.setBackground(swtDisplay.getSystemColor(SWT.COLOR_RED));
						shell.setText("Not visible");
						shell.setVisible(false);
						shell.open();
						shell.dispose();
					}
					swtPopupMenu.getShell().forceActive();
					
					//forceFocus();
					//forceActive();
					swtPopupMenu.setVisible(true);
					
					retryVisible(retriesRemaining-1);
					
				} else {
					System.err.println("unable to display the menu, sorry :-(");
					
				}
					
			}
		});
    }
    
    protected void swtDirectShowMenu(int x, int y) {
    	
		if (swtDisplay.isDisposed())	 // possible quick exit
    		return;
		
		swtPopupMenu.setLocation(new Point(x, y));
    	
		System.out.println("Displaying the menu at coordinates "+x+","+y);
		swtPopupMenu.setVisible(true);
		
		// if GUI not based on GTK, the menu should already be displayed. 
		
		retryVisible(MAX_RETRIES); // but just in case, we ensure this is the case :-)
	
    }
    

	/**
	 * May be called from the AWT thread. Just called swtDirectShowMenu with the very same parameters, 
	 * but from the right thread.
	 * @param x
	 * @param y
	 * 
	 */
    protected void swtIndirectShowMenu(final int x, final int y) {
    	
    	swtDisplay.asyncExec(new Runnable() {
			
			@Override
			public void run() {
			
				swtDirectShowMenu(x, y);
			}
		});
    	
    }

    
	public void display() {
	
		// creates a SWT Shell
		swtDisplay = new Display();
		swtShell = new Shell(swtDisplay);
		swtShell.setText("click somewhere !");
		
		Composite swtComposite = new Composite(swtShell, SWT.BORDER|SWT.EMBEDDED);
		
		swtShell.setLayout(new FillLayout());
	
		
		// create AWT embedded components into the SWT shell
		Frame awtFrame = SWT_AWT.new_Frame(swtComposite);
		Panel awtPanel = new Panel(new BorderLayout());
		awtFrame.add(awtPanel);
		

		// create the popup menu to display
		swtPopupMenu = new Menu(swtComposite);
		
		MenuItem item1 = new MenuItem (swtPopupMenu, SWT.PUSH);
    	item1.setText ("useless item for test");
    	item1.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				System.out.println("The useless popup menu was clicked !");
			}
			@Override
			public void widgetDefaultSelected(SelectionEvent arg0) {
			}
		});
		
		swtPopupMenu.addMenuListener(new MenuListener() {
			
			@Override
			public void menuShown(MenuEvent arg0) {
				
			}
			
			@Override
			public void menuHidden(MenuEvent arg0) {
				
				System.out.println("the SWT menu was hidden (by itself)");
			}
		});
	    

		// management of events from awt to swt
		
		JPanel jPanel = new JPanel(new BorderLayout());
		awtPanel.add(jPanel);
		jPanel.addMouseListener(new MouseAdapter() {	// maps AWT mouse events to the display of the popup menu
			
			public void mousePressed(final MouseEvent e) {
				System.out.println("AWT click detected");
				swtDisplay.asyncExec(new Runnable() {
					public void run() {
						System.out.println("SWT calling menu");
						swtIndirectShowMenu(e.getXOnScreen(), e.getYOnScreen());					
					}
				});				
			}
			
		});

		
		// loop for SWT events
		swtShell.setBounds(10, 10, 300, 300);
		swtShell.open();
		while (!swtShell.isDisposed()) {
			if (!swtDisplay.readAndDispatch()) 
				swtDisplay.sleep();
		}
		
		swtDisplay.dispose();
		
	}
	
	public static void main(String [] args) {

		PopupOverAwt test = new PopupOverAwt();
		
		test.display();
		

		

	}
	
}



HTH,

SamT.
Re: workaround: display SWT popupmenu over Swing components on GTK [message #667949 is a reply to message #667250] Mon, 02 May 2011 22:55 Go to previous message
Samuel Thiriot is currently offline Samuel ThiriotFriend
Messages: 2
Registered: April 2011
Junior Member
Tested and working:
- linux ubuntu 10.10, gnome 2.32, opengl acceleration with compiz activated.
- windows XP pro SP3

A problem was detected with a previous release of ubuntu on a very slow computer. In this one, the supposely invisible shell *does* appear in the taskbar; also, the menu often don't appear, or appears after as much as several minutes. This computer may be a bad example, however; to be continued.
Previous Topic:Table ScrollBars
Next Topic:PDF printing
Goto Forum:
  


Current Time: Fri Apr 19 18:47:52 GMT 2024

Powered by FUDForum. Page generated in 0.04167 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top