Community
Participate
Working Groups
On Motif, a Control will continue to get previously queued MouseMoves after a different control has requested mouse capture. This doesn't happen on Windows. We are doing something similar to what a Windows Tree does when the TreeItem is clipped. We popup a Shell(NO_TRIM) with the un-clipped version. In the attached example, you can mouse very quickly across the two Canvases that are used to popup a Shell. When a canvas pops up a shell, it sets mouse capture on that shell because you aren't guaranteed that the next mouse event will be inside the popup shell. But, even after setting capture on that popup shell it appears that the adjacent Canvas is also able to get a MouseEntered, causing a second popup shell to appear. You will see the popup count go to 2 at the console. Since the second popup steals mouse capture, the first popup never knows when to go away. import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class PopupTestOld { static int captureCount; public static void main(String[] args) { new PopupTestOld().run(); } public void run() { Shell shell = new Shell(); shell.setSize(300, 200); shell.setText("Popup Test"); // shell.setLayout(layout); final Canvas canvas = new Canvas(shell, SWT.BORDER); canvas.setBounds(40, 40, 100, 40); final Shell popup1 = new Shell(shell, SWT.NO_TRIM); canvas.addMouseTrackListener(new MouseTrackAdapter() { public void mouseEnter(MouseEvent event) { try { //Thread.currentThread().sleep(500); //Use on windows to slow down the UI and make the OS queue multiple MouseMoves } catch (Exception e){ e.printStackTrace(); } showPopup(canvas, popup1); } public void mouseExit(MouseEvent event) { //disposePopup(popup); } }); final Canvas canvas2 = new Canvas(shell, SWT.BORDER); canvas2.setBounds(40,100,100,40); final Shell popup2 = new Shell(shell, SWT.NO_TRIM); canvas2.addMouseTrackListener(new MouseTrackAdapter() { public void mouseEnter(MouseEvent event) { showPopup(canvas2, popup2); } public void mouseExit(MouseEvent event) { //disposePopup(popup); } }); shell.open(); Display display = Display.getDefault(); while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep(); shell.dispose(); } public void disposePopup(Shell popup) { if (!popup.isVisible()) return; popup.setCapture(false); captureCount --; System.out.println("Capture at " + captureCount); popup.setVisible(false); } public void showPopup(Canvas canvas, final Shell popup) { Point p = new Point(0,0); p = canvas.toDisplay(p); Point d = canvas.getSize(); Rectangle rect = new Rectangle(p.x, p.y, d.x+20, d.y); popup.setBounds(rect); popup.setBackground(new Color(null, 255,0,0)); popup.addMouseTrackListener(new MouseTrackAdapter() { public void mouseExit(MouseEvent event) { disposePopup(popup); } }); popup.addMouseMoveListener(new MouseMoveListener() { public void mouseMove(MouseEvent event) { Point p = popup.toDisplay(new Point(event.x, event.y)); if (!popup.getBounds().contains(p)) disposePopup(popup); } }); popup.setVisible(true); popup.setCapture(true); captureCount++; System.out.println("Capture at " + captureCount); } public class PopupHelper { protected Canvas canvas; public PopupHelper(Canvas canvas) { this.canvas = canvas; } } }
Assigned to wrong component. Please change "Assigned To:" for me, since I don't have authority.
Any updates on this?
Will be addressed post-2.0
I'm wasn't 100% sure that I was correct. So you are confirming that by calling setCapture(true) on a control, other controls should not receive mouse events until either setCapture(false) is called, or the mouse is clicked (which automatically releases capture)?
I just spoke with SN. Events that have been queued for other widgets can be delivered to them after a setCapture(true) has been invoked on a Control. One event that's guaranteed to be delivered is mouseExit to the control that loses focus when setCapture(true) is invoked. Steve's suggestion is to run through the event loop when invoking setCapture() and remove these undesirable events.
Running through the pending events is not possible. First, I don't think it is a good idea to run the event loop anywhere but in the main application loop. But, running these additional events will not solve my problem. If I were to run the loop locally, the second canvas would then get its queued mouse enter, it would popup *its* shell, and setCapture to that. Then, once there are no more pending events, the first canvas would popup its shell, and setCapture to it, leaving me with the original problem of having 2 shells popped up. There is no way of knowing what these pending mouse events caused. I understand that the original Canvas that received the mouseEnter, will therefore get a *single* mouseMove, and then the Exit when the popup Shell sets capture. This makes sense.
Will this be fixed for 2.0 or post 2.0 (or never)? I think it is pretty depressing to think that something like Canvas, which has no native function, will not be behave consistently across platforms. See also Bug 20569 for keyboard irregularities. I need to decide if I should disable GEF's rollover behavior in the next 24 hours.
This won't be addressed for 2.0.
>This won't be addressed for 2.0 Actually, that wasn't one of the choices. Please choose from: a) 2.0 b) Post 2.0 c) Never.
Based on discussions with SN and SQ I'd say never.
Listen. There have been several discussions about how this could be managed at the application end. Personally, I don't see why we can't implement exactly the same code (basically, a state machine for handling who has the capture in the event loop). Obviously, doing this would be extremely scary and we can't do something like that _right_now_. However, I want us to investigate that as soon as R2.0 ships, with an eye towards getting such a fix, or something equivalent, in for R2.1. In the mean time, I asked SN to send Randy an example of how to workaround the problem, and by golly I expect him to do that. Tomorrow. This PR is LATER, not WONTFIX!
This is the first time I have actually seen this PR although I have talked to GG about it and have discussed "capture" issues with RH as far back as the last OTI TC.
Proposed solution to rollover affect. Issues with setCapture will be reinvestigated after R2.0. Tested on Windows 2000, Linux Motif and Linux GTK. Note setCapture is not required. import org.eclipse.swt.*; import org.eclipse.swt.custom.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class HoverOnTop { public static void main(String[] args) { final Display display = new Display(); Shell shell = new Shell(display); shell.setLayout(new FillLayout()); // Create tooltip final Shell tooltip = new Shell(shell, SWT.ON_TOP); tooltip.setLayout(new FillLayout()); final Label l = new Label(tooltip, SWT.CENTER); Color tipForeground = display.getSystemColor (SWT.COLOR_INFO_FOREGROUND); Color tipBackground = display.getSystemColor (SWT.COLOR_INFO_BACKGROUND); l.setForeground(tipForeground); l.setBackground(tipBackground); final Runnable timer = new Runnable() { public void run() { if (tooltip.isDisposed()) return; Control c = display.getCursorControl(); if (c != l && c != tooltip) { tooltip.setVisible(false); return; } display.timerExec(400, this); } }; // create controls where tooltip will appear ScrolledComposite sc = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL); Composite comp = new Composite(sc, SWT.NONE); comp.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (!tooltip.isDisposed()) tooltip.dispose(); } }); sc.setContent(comp); comp.setLayout(new RowLayout(SWT.VERTICAL)); for (int i = 0; i < 50; i++) { final Button b = new Button(comp, SWT.PUSH); b.setLayoutData(new RowData(200, SWT.DEFAULT)); b.setText("Button "+i); b.addMouseTrackListener(new MouseTrackAdapter() { public void mouseEnter(MouseEvent e) { l.setText(b.getText()); Rectangle bounds = b.getBounds(); Point location = new Point(bounds.x, bounds.y); location = b.getParent().toDisplay(location); tooltip.setBounds(location.x, location.y, bounds.width, bounds.height); tooltip.setVisible(true); tooltip.layout(); // required due to bug on motif see 20792 display.timerExec(400, timer); } }); } Point preferredSize = comp.computeSize(SWT.DEFAULT, SWT.DEFAULT); comp.setSize(preferredSize); shell.setSize(200, 200); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } }
*** Bug 17509 has been marked as a duplicate of this bug. ***
>Control c = display.getCursorControl(); Wow, Display has so many cool utilities on it. Nice workaround. I'll have to change my code to have a singleton popup helper, which it probably should have had anyway. Otherwise, I'll get "popup trails". -------------Comments on post V2.0 fix-------------- SWT folks said: One event that's guaranteed to be delivered is mouseExit to the control that loses focus [you mean "mouse focus"?] when setCapture(true) is invoked. This would solve my problem *if* it were true. Part of the problem is that capture was requested by a second popup when the first popup thought that it had capture. Based on your statement, the first popup should get a MouseExit, but doesn't. Well, this would solve my problem but I don't think it would be enough to make the platforms consistent. Thanks again for the workaround.
Not sure who said what about setCapture() and mouse exit but we need to be suspicious of sending free events, that is doing something that the OS does not. I believe the "correct" fix will involve emulating the X behavior on Windows but this is premature.
This is causing me problems once again. Create a shell, add a mouse listener to it. Give the Shell a popup menu. On mouse down, setCapture to the Shell. On windows, this works fine, on Motif, the setCapture() seems to take effect asynchronously. Despite the mousedown coming first, the capture is set after the popup menu is shown. The popup menu is not functional as a result.
Randy, this is a different problem (you get an X error). Please enter a different PR for it. Thanks.
http://bugs.eclipse.org/bugs/show_bug.cgi?id=22737 This seems similar to me. Basically, setCapture() does not happen soon enough, or it hasn't happened when setCapture(true) returns. If I modify my test case to setCapture on MouseEnter instead of mousedown, the context menu works fine, because setCapture has more time to take effect.
The problem in 22737 was that the menu attempted to get a grab and failed but still popped up, causing bizzare behavoir. This PR is something different.
Closing for lack of activity. Please re-open if there is any intent to fix this at some point.