Suzan Cioc
Suzan Cioc

Reputation: 30097

How to track mouse EXIT/ENTER/HOVER over composite?

The code below shows, that:

1) HOVER is totally ignored with composite. How to enable?

2) ENTER/EXIT are tracked, but when mouse entered child control region, parent composite receives EXIT event. How to make that entre area of a composite would serve as belonging to composite?

public class TryHover {

    public static void main(String[] args) {

        Display display = new Display();

        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());

        Composite composite = new Composite(shell, SWT.BORDER);
        composite.setLayout(new GridLayout(1, false));

        Label label1 = new Label(composite, SWT.BORDER);
        label1.setText("Label 1");

        Label label2 = new Label(composite, SWT.BORDER);
        label2.setText("Label 2");

        composite.addListener(SWT.MouseEnter, new Listener() {

            @Override
            public void handleEvent(Event event) {
                System.out.println("ENTER");

            }
        });

        composite.addListener(SWT.MouseExit, new Listener() {

            @Override
            public void handleEvent(Event event) {
                System.out.println("EXIT");

            }
        });

        composite.addListener(SWT.MouseHover, new Listener() {

            @Override
            public void handleEvent(Event event) {
                System.out.println("HOVER");

            }
        });

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

    }
}

UPDATE

Yes, agree, if mouse is slow, it produces HOVER too.

Then the question is about ENTER/EXIT only: how to turn EXITs of on child controls?

Upvotes: 5

Views: 2364

Answers (2)

Baz
Baz

Reputation: 36884

SWT.MouseHover is only triggered when the mouse is located over the Composite and doesn't move for a fraction of a second.

SWT.MouseMove is triggered on each movement of the mouse over the Composite.

So it really depends on what you want to achieve here.


As for the second part of your question: SWT does not propagate events up the widget hierarchy (with some exceptions).

You can however implement your own logic to determine when to listen to the events.

  • For SWT.MouseExit: Get the coordinates of the mouse and check if any of the children contains the mouse. If so, do nothing, if not, you left the Composite.
  • For SWT.MouseEnter: This is a bit more tricky. I came up with a solution that keeps track of the current Widget the mouse is moving over using a filter on the display. When you enter the Composite, you can check if the previous widget was a child. If so, do nothing.

private static Widget mouseControl = null;

public static void main(String[] args)
{
    Display display = new Display();

    /* Overall, keep track of the Widget the mouse is moving over */
    display.addFilter(SWT.MouseMove, new Listener()
    {
        @Override
        public void handleEvent(Event e)
        {
            mouseControl = e.widget;
        }
    });

    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());

    final Composite composite = new Composite(shell, SWT.BORDER);
    composite.setLayout(new GridLayout(1, false));

    Label label1 = new Label(composite, SWT.BORDER);
    label1.setText("Label 1");

    Label label2 = new Label(composite, SWT.BORDER);
    label2.setText("Label 2");

    composite.addListener(SWT.MouseEnter, new Listener()
    {
        @Override
        public void handleEvent(Event event)
        {
            /* Check if the mouse was previously moving over a child (you could
             * even do recursive search here) */
            for (Control child : composite.getChildren())
            {
                if (child.equals(mouseControl))
                    return;
            }
            System.out.println("ENTER");
        }
    });

    composite.addListener(SWT.MouseExit, new Listener()
    {
        @Override
        public void handleEvent(Event event)
        {
            /* Check if the mouse is now located over a child (you could
             * even do recursive search here) */
            for (Control child : composite.getChildren())
            {
                if (child.getBounds().contains(new Point(event.x, event.y)))
                    return;
            }
            System.out.println("EXIT");
        }
    });

    composite.addListener(SWT.MouseHover, new Listener()
    {

        @Override
        public void handleEvent(Event event)
        {
            System.out.println("HOVER");
        }
    });

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

Upvotes: 3

Alex K.
Alex K.

Reputation: 3304

Not sure, why Hover is ignored for you. I can see, that it is tracked (please see screenshot), and mouse stays hovered over composite for at least a second. Your code was used without any modifications.

enter image description here

May be it is somehow related to OS? Try to call composite.setEnabled(true); as disabled controls do not receive mouse events.

Upvotes: 1

Related Questions