Mouse click outside of control

I have a custom control, that combines a flat button(like a hypertext link) and a text control combined in a panel.
When the user clicks on the flat button, the flat button is hidden, and the text control is shown. I would like to hide the text control after the user clicks anywhere outside of it, but I can’t find a right way.

My current half working solution is to call CaptureMouse() on the text control when it is shown, and ReleaseMouse() when I handle the click event, but this also hides the text control when clicking inside of it. Also, using
the textcontrol’s HitTest method returns something weird like a (-2, -129914200, 53246976) with a wx.Point(43, 83) input from the event.Position. I’m currently thinking to check a collision between a mouse click position and the control but I’m not sure what is the best way (was hoping for hittest to do this).

Another problem with the current solution is that I can have multiple such controls, and I change the mouse cursor to hand when hovering over the link like button, but if I capture the mouse once one of the buttons is clicked, hovering over other buttons will not change the cursor to hand.

Is there an easier way to detect mouse clicks outside of a control ?

I’ve attached a small sample to illustrate the above.

example.py (3.47 KB)

Well, I managed to solve my primary issue by using a LEFT_UP event to reacquire MouseCapture if the LEFT_DOWN event happens inside the textctrl. Now I am facing an issue similar to this one: Redirecting to Google Groups

The textCtrl now eats all mouse events while it is shown, is there a way to have the current window respond to mouse events that should not only hide the textctrl, but also trigger other buttons? I notice that the ComboCtrl behaves like I want in that it closes the popup list when an outside click is made, but other buttons also get triggered if the click was made on them. Is there a way to resend the mouse event ?

···

On Monday, 7 October 2013 12:27:30 UTC+3, Alexandru Popa wrote:

I have a custom control, that combines a flat button(like a hypertext link) and a text control combined in a panel.
When the user clicks on the flat button, the flat button is hidden, and the text control is shown. I would like to hide the text control after the user clicks anywhere outside of it, but I can’t find a right way.

My current half working solution is to call CaptureMouse() on the text control when it is shown, and ReleaseMouse() when I handle the click event, but this also hides the text control when clicking inside of it. Also, using
the textcontrol’s HitTest method returns something weird like a (-2, -129914200, 53246976) with a wx.Point(43, 83) input from the event.Position. I’m currently thinking to check a collision between a mouse click position and the control but I’m not sure what is the best way (was hoping for hittest to do this).

Another problem with the current solution is that I can have multiple such controls, and I change the mouse cursor to hand when hovering over the link like button, but if I capture the mouse once one of the buttons is clicked, hovering over other buttons will not change the cursor to hand.

Is there an easier way to detect mouse clicks outside of a control ?

I’ve attached a small sample to illustrate the above.

Did you tried event.Skip()?

From the docs:

void wxEvent::Skip
(
bool
skip = true
)

This method can be used inside an event handler to control whether further event handlers bound to this event will be called after the current one returns.

Without Skip() (or equivalently if Skip(false) is used), the event will not be processed any more. If Skip(true) is called, the event processing system continues searching for a further handler function for this event, even though it has been processed already in the current handler.

In general, it is recommended to skip all non-command events to allow the default handling to take place. The command events are, however, normally not skipped as usually a single command such as a button click or menu item selection must only be processed by one handler.

Alexandru Popa wrote:

Well, I managed to solve my primary issue by using a LEFT_UP event to
reacquire MouseCapture if the LEFT_DOWN event happens inside the
textctrl. Now I am facing an issue similar to this one:
Redirecting to Google Groups

The textCtrl now eats all mouse events while it is shown, is there a way
to have the current window respond to mouse events that should not only
hide the textctrl, but also trigger other buttons? I notice that the
ComboCtrl behaves like I want in that it closes the popup list when an
outside click is made, but other buttons also get triggered if the click
was made on them. Is there a way to resend the mouse event ?

I think I would approach this a little differently. Have you tried using the EVT_KILL_FOCUS event to trigger hiding the textctrl? That would leave the mouse uncaptured so it continues to behave as you wish over the other controls, and as soon as the active textctrl loses the focus (by clicking elsewhere, or using Tab) then your handler can hide it. Be sure to call event.Skip in the EVT_KILL_FOCUS handler so the default handler will still be called.

···

--
Robin Dunn
Software Craftsman

Torsten wrote:

Did you tried event.Skip()?

From the docs:

void wxEvent::Skip ( bool /skip/ = |true| )

This method can be used inside an event handler to control whether
further event handlers bound to this event will be called after the
current one returns.

That won't help when the mouse is captured. The events will always go to the widget with the capture, and only that widget, even if the cursor is located above another widget.

···

--
Robin Dunn
Software Craftsman

Yes, I already handle the EVT_KILL_FOCUS , what I don’t like about it is that a click on an empty portion of one of the panels will not trigger this event, this is what made me try the capture mouse solution as well.

Right now I’m actually thinking of a few other options:

  • adding a button next to the text ctrl(only when visible), so that after writing smth there, the user would be forced to click the button in order to accept the new value.
  • using a mix of CallLater() and EVT_LEAVE_WINDOW, so that when the mouse exits the textctrl, EVT_LEAVE_WINDOW would trigger and activate CallLater() which should check the mouse position after x milliseconds and hide the textctrl if the mouse position is still outside the textctrl rectangle. This would be a little bit more complicated, since I have to consider multiple EVT_LEAVE_WINDOW triggers.
  • a mix of the above
    I’m open to further suggestions btw :slight_smile: !

Thanks !

···

On Wednesday, 9 October 2013 04:21:11 UTC+3, Robin Dunn wrote:

Alexandru Popa wrote:

Well, I managed to solve my primary issue by using a LEFT_UP event to

reacquire MouseCapture if the LEFT_DOWN event happens inside the

textctrl. Now I am facing an issue similar to this one:

https://groups.google.com/forum/#!searchin/wxpython-users/transient/wxpython-users/ufJj7qfIwQc/0rM6kJdb_lgJ

The textCtrl now eats all mouse events while it is shown, is there a way

to have the current window respond to mouse events that should not only

hide the textctrl, but also trigger other buttons? I notice that the

ComboCtrl behaves like I want in that it closes the popup list when an

outside click is made, but other buttons also get triggered if the click

was made on them. Is there a way to resend the mouse event ?

I think I would approach this a little differently. Have you tried
using the EVT_KILL_FOCUS event to trigger hiding the textctrl? That
would leave the mouse uncaptured so it continues to behave as you wish
over the other controls, and as soon as the active textctrl loses the
focus (by clicking elsewhere, or using Tab) then your handler can hide
it. Be sure to call event.Skip in the EVT_KILL_FOCUS handler so the
default handler will still be called.


Robin Dunn

Software Craftsman

http://wxPython.org

EVT_KILL_FOCUS can be tiggered when clicking on an empty portion of the panel. You can bind EVT_LEFT_DOWN to the panel and invoke SetFocusIgnoringChildren() in the handler.

Mark

···

At 2013-10-09 14:46:49,“Alexandru Popa” a.ephesos@gmail.com wrote:

Yes, I already handle the EVT_KILL_FOCUS , what I don’t like about it is that a click on an empty portion of one of the panels will not trigger this event…

I thought the event would be swallowed by one of the panel children, but you are correct, it did get triggered and SetFocusIgnoringChildren() is a nice touch :).

The problem is now solved, thank you for your help, and sorry for my misguided presumptions !

···

On Wednesday, 9 October 2013 17:23:17 UTC+3, Mark Weng wrote:

EVT_KILL_FOCUS can be tiggered when clicking on an empty portion of the panel. You can bind EVT_LEFT_DOWN to the panel and invoke SetFocusIgnoringChildren() in the handler.

Mark

At 2013-10-09 14:46:49,“Alexandru Popa” a.ephesos@gmail.com wrote:

Yes, I already handle the EVT_KILL_FOCUS , what I don’t like about it is that a click on an empty portion of one of the panels will not trigger this event…