Best way to deal with child to parent mouse and keyboard events in a gui? - c++

For my Gui I want to use the following system:
The way it works is that, if the widget under the mouse does not consume a mouse or kb event, it is passed to that widget's parent until it is consumed or the desktop is reached.
Just one thing puzzles me about it. Does that mean if a Button, for whatever reason has a Label as one of its children. If I click the label, would that not mean that my button, which is under the label would click (since a label does not consume the mouse), which is undesired in this case. Does that mean I'd have to do if(mouseEvent.source == this){do button stuff} ?
Thanks

If the label is a child widget, then yes, the label will attempt to eat the event, fail (as usually the label doesn't have a handler method) and thus pass the event back up to the button.
Chances are the easiest way to do this is to derive a subclass of the Label class, and override the handle function (assuming this is possible in your toolkit - which it should be in any decent toolkit). You can then use your handle function to capture mouse clicks, and pass any other event back up to the button.
Comparing pointers is probably a bad idea and is slightly dependent on the way the toolkit is implemented - for instance, it might deem the source as the button (because that's what it expects), not the label.
Though I find it highly strange that a Label is a widget in itself.....

It looks like you need a special kind of "Label" which consumes mouse events. You should be able to create a customized widget (derived from Label) that consumes mouse events for this specific case.
If that Label widget is from a third party that cannot be derived/subclassed, you should wrap the Label in another widget: A simple widget that consumes mouse events and has only one child, the original Label.

Related

Qt MouseMoveEvent only triggers with a mouse button press

I have an odd problem here.
I'm working on an application, and within one of my classes I'm monitoring my mouse events.
The weird thing is, my mouse move event will only get called if any mouse button is pressed.
I'm not even filtering for any button presses within the method; the method itself doesn't even get called unless I click on this object itself (the one that's monitoring it).
What generally causes this type of error to happen?
I'm not sure if it's relevant, but I have 2 different things monitoring my mouse inputs: 1) the main program monitoring the global mouse coordinates, and 2) an object within my program monitoring the mouse coordinates within itself.
Edit
So the problem has to be because mouse move event is generally used when people are dragging the cursor along the screen right?
My reason for not needing it like that is because I'm building a custom context menu of sorts, and I need to know when an item is hovered over.
It turns out that I didn't truly set everything within my class to enable mouse tracking.
I somehow thought if the class itself was set to have it enabled, I wouldn't need to set it to all the sub objects, but now I see how that wouldn't make any sense at all.
So just to clarify my solution:
The items that I needed to track my cursor's position needed to have
setMouseTracking(true);

Capture mouse clicks on text boxes in a Win32 game

Using C++, I am making a Tic-Tac-Toe game using the Win32 API. To mark a square (X or O) I want the player to click the square which then changes to an X or O.
What I am doing right now is having a button click event which turns a static text box to X or O. However, when I place the button on top of the text box and make it not visible, I can't click it.
What I really need is an invisible button that still functions. So it's not set WS_VISIBLE, but you can still click it.
Is this possible or is there another way around this problem?
I can see a couple of reasonable possibilities here.
The first and most obvious would be to skip using a button at all, and just have the underlying window process the WM_LBUTTONDOWN message, and set the "X" or "O" in the correct location. For this, you don't even need static controls -- you can just detect the mouse clicks directly on the parent window, and draw your "X" or "O" in the corresponding square.
Another possibility would be a button that's marked as "visible", but happens to be transparent. IMO, this is a fairly poor choice though. To do it, you'd need to either create a transparent button control on your own, or subclass a button control to disable its drawing.
At least IMO, the obvious route would be to skip using the static control at all. Instead, just use the buttons directly -- a button normally has a caption. Start with that caption as an empty string. When the button is clicked, change its caption to "X" or "O" as appropriate. It should probably also disable itself in response to the button click, so clicking it again won't have any further effect.
There's no way to make an invisible button that still functions. Imagine all of the ways that could be abused if it were possible! Not to mention how confusing to have invisible, yet functional, UI.
What Mark Ransom posted is exactly right: you need to get your existing control to respond to mouse click events, just like a button does. Then you can do whatever you want in response to clicks. You don't need a button just to be clickable.
You say that you have a "static text box", but I'm not really sure what that is. There are text boxes (which are not static), and then there are static controls (which can display text). I'm going to assume that you have the latter.
In that case, you don't need to handle the WM_LBUTTONDOWN and WM_LBUTTONUP messages directly, which would require that you subclass the control. Although that's probably the best approach design-wise (separation of responsibilities and all that), it's also a lot more trouble.
Instead, you can handle the click events from the parent's window procedure by setting the SS_NOTIFY style for your static control (you can do this either in the Dialog Editor or in your call to CreateWindow, depending on how you create the control). This causes the control to notify its parent in four cases: when it is clicked (STN_CLICKED), when it is double-clicked (STN_DBLCLK), when it is enabled (STN_ENABLE), and when it is disabled (STN_DISABLE).
So at the parent, you need to process WM_COMMAND messages. The message you're looking for will have a HIWORD(wParam) of STN_CLICKED (indicating that a static control with the SS_NOTIFY style has been clicked), a LOWORD(wParam) corresponding to your static control's ID (set either in the Dialog Editor or specified as the hMenu parameter in your call to CreateWindow), and an lParam containing a handle to your static control.
If you use SW_HIDE, it doesn't just make the window invisible but makes it behave like that too. What you really wanted is probably just make the button transparent. I never did that, you may find this or this helpful.
You may just scrap the textbox just use the button, i mean a button-looking checkbox with ownerdraw or bitmaps. Or scrapping the button and handle the mouse events Like Mark suggests.

Qt mouse leave event while button is pressed

I need to detect if the mouse pointer leaves my custom widget even if a mouse button is pressed.
According to this post, Qt does not cause a leaveEvent in case a button is pressed, at least not in version 4.4. I am working with 4.7.3, but still do not get a leaveEvent in the described case. I also tried with the various drag-related events, but no luck. Does anyone have an idea how to deal with this?
Actually there is an even better option than using the mouse events of the parent widget: You can implement the mouseMoveEvent function of the child widget and get the QMouseEvent::pos() position which is relative to the top left corner of the widget. That means, if you know the size of the widget (use for example QWidget::rect()), you can compute within the widget if the mouse pointer is still on the widget or not without having to change the parent widget.
Well, I did something similar to #gregseth.
Write your own MouseLeaveEvent that is invoked from MouseMoveEvent
when the mouse's position is outside of the widget's boundary.
As Qt's documentation says "Mouse move events will occur only when a mouse button is pressed down"
I got the same problem. The only workaround I found is to manage the mouseMove event of the parent widget, and to check if the position of the cursor is inside or outside the boundaries of the widget you want the event on.

Help with this issue

Here is the problem:
The way the GUI system I'm using works is as follows:
A widget can listen to mouse events. The listeners can consume an event they are listening to and as a result, the widget they were listening to will not receive the event, even if it was intended for them, only the listener will have received it.
The problem comes in here:
A scrollpane has a ListBox inside. The listbox can be scrolled. The Scrollpane can also be scrolled. What happens right now is when I mousewheel and the widget under the mouse is the listbox, both the listbox and the scrollpane move. The only solution I see is that the scrollpane consumes all mousewheel events, but then there is no way for the ListBox to ever be scrolled.
The behavior I desire is for the ListBox to scroll when it has focus, but if I did this, it would not work because for example if a radio button inside the scrollpane is focused, I still want the scrollpane to scroll, it is only for widgets with scrollbars such as textbox and listbox where this is not desired.
Thanks
Also, the gui system I'm using is my own which I'm developing.
The usual way to deal with this is that the ListBox only consumes the event if it actually scrolls. If you place the mouse in the listbox and use the scrollwheel the listbox scrolls until it reaches its scroll limit. After that further moves of the wheel cause the scrollpane to scroll.
Obviously there are variants on that - the important thing is to decide what you actually want to happen.
You also need another way of scrolling the scrollpane, so that if the user explicitly wants to scroll it they can, without having to scroll to the end of the ListBox. Scrollbars on the ScrollPane would be one way. You should also make sure there is a way of scrolling the ListBox without using the mouse wheel.

Qt popup grabMouse for all children

I'm trying to create a popup menu like control that contains custom widgets. I need to capture the mouse, but I need to have the children in the widget still get their mouse messages. It appears the grabMouse sends events only to the widget that grabbed the mouse, not its children.
The popup is simply a series of buttons (using a QGridLayout). The control should work that the user presses the right-mouse button, the popup appears, they move to an item and release the mouse button. Optimally it would work exactly like a QMenu popup but with custom widgets and a custom layout.
How can I achieve this?
It appears that simply specifying attribute Qt::Popup is enough to get the fundamental behaviour required.
Installing an event filter on all children is also necessary. All mouse events, enter/leave/hover events must be captured. QT has a defect with grabMouse so that won't work -- the filter must be used to get expected behaviour.