用户单击菜单项后如何保持SWT菜单打开? [英] How to keep SWT menu open after user clicks menu item?

查看:60
本文介绍了用户单击菜单项后如何保持SWT菜单打开?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有复选框菜单项菜单的Eclipse RCP / SWT应用程序。

I have an Eclipse RCP / SWT application with a Menu of Check Box Menu Items.

我希望能够在单击其他位置之前先检查/取消选中多个项目。关闭菜单。但是,默认的SWT行为是单击后关闭菜单。

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.

我已经实现了以下非常hacked的解决方案,该解决方案有效,但肯定不是很优雅,并且可能赢得了在所有平台上或任何情况下均无法正常工作。因此,我对一种更简单的技术(如果存在)很感兴趣。

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.

下面的代码应立即在eclipse内编译并运行(对于长度,我可以创建的最短的自包含示例):

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 );
                    }
                }
            } );
        }
    }
}


推荐答案

我在Win7 32bit和eclipse 4.2上尝试了您的代码。不幸的是,它给了问题并且正在闪烁。无论如何,这是另一种变化。我认为您必须至少使用两个侦听器,一个用于菜单项,无论如何都需要,另一个用于获取菜单的坐标:

I tried your code on Win7 32bit and with eclipse 4.2. Unfortunately it was giving problem and was flickering. Anyway, here is another variation. In my opinion you have to use at least two listeners, one for your menu item(s), which in any case are needed, and the other to get the co-ordinates of the menu:

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(e.x, e.y);
            }
        });

        shell.setMenu(menu);

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}



Update


Update

感谢@Baz,请参见下面的评论。

Thanks to @Baz, please see the comment below.

在Linux 32位上使用eclipse 3.6.2进行了尝试,不幸的是,
似乎不起作用。

Tried this on Linux 32bit with eclipse 3.6.2 and unfortunately it doesn't seem to work.

更新2(通过ulmangt)

Update 2 (by ulmangt)

下面是对您的解决方案的修改,该修改有效在64位Windows 7和64位Ubuntu Linux上对我来说都是如此。

Below is a modification of your solution which works for me on both 64-bit Windows 7 and 64-bit Ubuntu Linux.

org.eclipse.swt.widgets.Menu 类具有程序包保护字段,该字段确定是否已设置菜单位置。如果不是这样,则至少在Linux上,该菜单会出现在鼠标单击下。

The org.eclipse.swt.widgets.Menu class has a package protected field which determines whether the menu location has been set. If not, on Linux at least, the menu appears under the mouse click.

因此,要获得正确的行为,需要使用反射将此布尔值字段重置为false。另外,菜单可能也可以被处置和重新创建。

So getting the right behavior requires using reflection to reset this boolean field to false. Alternatively, the menu could probably be disposed and recreated.

最后,Linux看起来像 menu.setLocation(point) menu.setVisible(true) asyncExec 块制成。

Finally, Linux appears to like menu.setLocation( point ) and menu.setVisible( true ) to 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( e.x, e.y );
            }
        } );

        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( );
    }
}

这篇关于用户单击菜单项后如何保持SWT菜单打开?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆