使用 FillLayout 在 Composite 上记录 SWT MouseEnter/Exit 事件

Record SWT MouseEnter/Exit events on a Composite with a FillLayout

我有一个 Composite,我想在其中跟踪 SWT.MouseEnterSWT.MouseExit 事件。然而,由于 FillLayout.

Composite 内部还有另一个 Composite 消耗了整个区域

一些示例代码来说明我在做什么:

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
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.Shell;

public class EventListenerTest {

    public static void main(String[] args) {

        Display display = new Display();
        Shell shell = new Shell(display);

        // Set up the outer Composite
        Composite outerComp = new Composite(shell, SWT.BORDER);
        FillLayout fl = new FillLayout();
        // fl.marginHeight = 15;
        // fl.marginWidth = 15;
        outerComp.setLayout(fl);

        // Set up a nested Composite
        Composite innerComp = new Composite(outerComp, SWT.BORDER);

        // Set a listener on the outer Composite for a MouseEnter event
        outerComp.addListener(SWT.MouseEnter, new Listener() {
            @Override
            public void handleEvent(Event e) {
                System.out.println("Mouse ENTER event");
            }

        });

        // Similarly, for a MouseExit event
        outerComp.addListener(SWT.MouseExit, new Listener() {
            @Override
            public void handleEvent(Event e) {
                Composite comp = (Composite) e.widget;
                // Don't report if positioned over a child control
                for (Control child : comp.getChildren()) {
                    if (!child.getBounds().contains(new Point(e.x, e.y)))
                        System.out.println("Mouse EXIT event");
                }
            }

        });

        outerComp.pack();
        outerComp.layout();

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

        display.dispose();
        return;
      }
}

不幸的是,由于 innerComp 填满了它的整个父级,outerComp 永远不会记录鼠标 enter/exit 事件 除非 我稍微揭露一下它"area"。

我可以通过在其 FillLayout 上创建一些边距(注释掉第 21-22 行)来暴露一点 outerComp,但这确实不理想。出于审美原因,我不能在 outerComp 上有很大的边距,并且如果我将鼠标快速移到复合材料上(我必须将我的将鼠标非常 慢慢移过 1px 边距以触发它)。

从我的项目的设计角度来看,我实际上不应该知道 outerComp 包含什么(或控件嵌套的深度),因此在其子项上设置事件侦听器也不理想.

如果 FillLayout 占据了它的所有区域,我是否仍然可以在 outerComp 上跟踪这些鼠标事件?

您可以做的是向 Display 添加过滤器,并在处理程序中检查 Event 的来源 Widget 是否是您的 Composite.

这应该能让您了解如何操作:

public static void main(String[] args)
{
    Display display = new Display();
    Shell shell = new Shell();
    shell.setText("Whosebug");
    shell.setLayout(new GridLayout(2, true));

    final Composite left = new Composite(shell, SWT.NONE);
    Composite right = new Composite(shell, SWT.NONE);
    left.setLayout(new GridLayout(3, true));
    right.setLayout(new GridLayout(3, true));

    for (int i = 0; i < 6; i++)
    {
        new Label(left, SWT.NONE).setText("label-" + i);
        new Label(right, SWT.NONE).setText("label-" + i);
    }

    display.addFilter(SWT.MouseMove, new Listener()
    {
        @Override
        public void handleEvent(Event e)
        {
            if (isChildOrSelf(e.widget, left))
                System.out.println(e);
        }
    });

    shell.pack();
    shell.open();

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

    display.dispose();
}

private static boolean isChildOrSelf(Widget child, Composite parent)
{
    if(child == parent)
        return true;

    for (Control c : parent.getChildren())
    {
        if (c instanceof Composite)
        {
            boolean result = isChildOrSelf(child, (Composite)c);
            if (result)
                return true;
        }
        else if (c == child)
            return true;
    }

    return false;
}