QML mouseArea losing focus on releasing mouse button - qt

In my program you have polygons on screen, and you can drag their sides, the verticies, or the whole polygons around. I want to show the user which will happen with the mouse in its current position. For that I need the onPositionChanged event handler of the mouseArea this is happening in.
The dragging works fine, the problem is that the mouseArea loses focus the moment the mouse is released on it, so I'm not able to call my onPositionChanged function when the user is not pressing a mouse button.
HoverEnabled is set to true in my mouseArea. It seems some other object keeps stealing focus, but I set preventStealing to true, set focus to true, and looked for the word "focus" in the whole project, and removed every line that would steal focus There are also no focusScopes in the project.
example code:
MouseArea
{
hoverEnaled: true
focus: true
preventStealing: true
...
onPositionChanged:
{
doStuff();
//Works fine while mouse button is pressed. Doesn't get called when it's released
}
Component.onCompleted: forceActiveFocus();
...
}

I figured it out!
Before getting to my polygon screen, there was a popup in the program, that was closed improperly (instead of popup.close(), popup.visible = false; popup.destroy(); was used). Closing it the proper way solved my problem.

Related

How do I overcome Godot changing the button font to the default color?

I am using Godot 4. I have a lot of buttons created manually in a container. I have a color variable (tempcol) set in the main scene. When I click a button with the tempcol set say Color.Red- the button's font color changes to white (seems to the default font color) but when I click another button the original button changes to red( the correct button's text is set to the correct color but first set to the wrong color). No button prefab settings seem to make a difference. The Normal Style is set to StyleBoxFlat (which a white background). Is there anything I can do make the font's color immediately appear as the right color?
pressed.connect(c);
func c():
if (get_parent().tempcol!=Color.BLACK):
set("custom_colors/font_color", get_parent().tempcol);
get_parent().check(num,squx,squy,bsqu);
else:
var B1=B.instantiate();
B1._set_position(Vector2(centrex,centrey));
B1.num=num;
get_parent().call_deferred("add_child", B1);
Button States
I'm beginning this answer with a description of the states when using the mouse, as it is the more complex way to interact with the Button.
When you have the Buttons in the scene, unless you have them Disabled, they will be in their Normal state.
When you move the pointer over the Button, it goes from Normal to Hover. And if you move the pointer out, it should return from Hover to Normal.
If you click the Button it goes from Hover to Pressed. And now the button has focus.
When you release the click on top of the Button, it goes from Pressed to Hover (this can happen to fast to see the Pressed state). Then when you move the pointer out of the Button, it goes from Hover to Focus (not to Normal).
Alternatively, you can move the pointer out of the Button instead releasing the click, which also results in the Focus state.
And when you interact with another Control, it goes from Focus back to Normal.
State Graph using mouse
This is the graph of the states of the Button when you are using a mouse:
So we see the Button can take two 5paths from Normal to Normal:
Normal -> Hover -> Normal
Here you moved the mouse pointer over the button, and moved it out.
Normal -> Hover -> Pressed (hover + focus) -> Hover (focus) -> Focus -> Normal
Here you moved the mouse pointer over the Button, pressed and released click, then moved the mouse pointer out and clicked some other Control.
State Graph using touch
When you are using touch input, there is no Hover state, so the graph reduces to this:
And here we have one single path from Normal to Normal: Normal -> Pressed + Focus -> Focus -> Normal. Here you tap the Button, and then tap on some other Control.
Graph State when using keyboard input
When using keyboard it is similar to using touch in that there is no Hover. However, we control Focus independently from Pressed, which results in this:
And here we have three paths from Normal to Normal:
Normal -> Focus -> Normal.
Here you tab into the Button, and tab out of it.
Normal -> Focus -> Pressed + Focus -> Focus -> Normal.
Here you tab into the Button, press and release it, then tab out of it.
Note: You could try to tab out without releasing the Button, which results in the Button going directly from Pressed (Focus) to Normal (It is similar to moving the mouse out from the Button while holding the click). However, I have decided to not count it as separate transition nor path.
Button Color
Alright, now we know the states, what color do we see in each state?
Godot 3:
Normal: custom_colors/font_color.
Hover: custom_colors/font_color_hover.
Pressed: custom_colors/font_color_pressed.
Focus: custom_colors/font_color_focus.
Disabled: custom_colors/font_color_disabled.
Note: When you have Hover combined with Focus, you still get custom_colors/font_color_hover. And when you have Pressed combined with Hover or Focus, you still get custom_colors/font_color_pressed.
Godot 4:
Normal: theme_override_colors/font_color.
Hover: theme_override_colors/font_hover_color.
Pressed: theme_override_colors/font_pressed_color.
Focus: theme_override_colors/font_focus_color.
Disabled: theme_override_colors/font_disabled_color.
Note: Similar to what happens in Godot 3, when you have Hover combined with Focus, you still get theme_override_colors/font_hover_color. And when you have Pressed combined with Hover or Focus you still get theme_override_colors/font_pressed_color. In fact, I could not manage to get theme_override_colors/font_hover_pressed_color.
Your Questions
When I click a button (…) the button's font color changes to white
That is not the Normal color. While you have the Button pressed, you are looking at the Pressed color. Once released, if the mouse pointer is over the Button you are looking at the Hover color. Otherwise, this is the Focus color.
when I click another button the original button changes to red
That is the Normal color.
No button prefab settings seem to make a difference.
Prefab is not a thing. This is Godot.
Is there anything I can do make the font's color immediately appear as the right color?
It is not a matter of "immediately", but what state do you want to change the color for. I suspect you want to set colors for Focus, Hover and Pressed to the same as the color for Normal. And be aware that knowing the state is important for user experience. Which Control has focus is in particular important for keyboard only input.
I set every single theme_override font color.
set("theme_override_colors/font_pressed_color", get_parent().tempcol);
set("theme_override_colors/font_hover_color",get_parent().tempcol);
set("theme_override_colors/font_color", get_parent().tempcol);
set("theme_override_colors/font_focus_color",get_parent().tempcol);
and it was only when I set the font_focus_color did it set change to the correct color immediately.

How to disable clicking through item in qml?

The application I currently work on has a map as a background, and above it various other dialogs(views) with more than one view inside can be opened. When some of the dialogs is active, when dragging over it's background map is moving like there's nothing above it. Does someone know how to disable this? I don't want map to react on clicks or anything inside a dialog.
The project is organised so that each dialog is implemented in separate qml file:
I have each qml file for each dialog, and each component of application (map), so
when you click, for example on settings tab in scrollable horizontal list, settings tab is opened from qml that holds all dialogs, including bottom and top of the app
each dialog is above map and has a 50% transparent background, with related images and buttons in it
I want to disable dragging map while dragging over dialog's background. I tried with setting this to each dialog:
MouseArea {
anchors.fill: parent
onClicked: mouse.accepted = true
}
(parent is Item that holds all elements of a dialog), but this doesn't work.
If I'm understanding your question correctly, it should suffice to set the MouseArea's propagateComposedEvents to false.

Qt Quick: How to handle hover-type events in MouseArea, yet let lower-z MouseAreas handle them too?

If, on my MouseArea, I've enabled the property hoverEnabled, I'm notified of the entered signal, even if no mouse button is held at that time (just as I want).
But then a lower-z MouseArea will not receive positionChanged events on the first MouseArea's space, since they're eaten up by the first MouseArea.
The Qt docs on positionChanged specifically say this:
When handling this signal, changing the accepted property of the mouse parameter has no effect.
Example code:
Item {
width: 100
height: 100
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
console.log("MouseArea 1");
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
console.log("MouseArea 2");
}
}
}
"MouseArea 1" is never printed. I want both strings to be printed.
Is there a way to do it?
Related question note: This question is very similar but the asker there has 2 differently-sized overlapping MouseAreas, and he wants the smaller one (which is has higher z) to eat up events when the events fall within both. I want both overlapping MouseAreas to handle events that fall within both.
At a first glance, it doesn't look like this is possible, most likely because an implementation detail limitation. Normally, it is effortless to propagate events, what you need is to propagateComposedEvents: true and to mouse.accepted = false in the handler.
However, while that works as expected for clicks, it doesn't work for onEntered. My first thought was that it was the lack of the mouse signal parameter for the entered signal, however positionChanged has it but it still doesn't work as expected, as the documentation says, it has no effect. And that's actually true for positionChanged unlike for clicked, for which the documentation also says should have no effect but it most certainly does.
Furthermore, even if you make the top mouse area smaller, you will be able to register the cursor entering for both, but position changes will only register for the top-most mouse area that is under the cursor.
I wouldn't say that such functionality is an unreasonable feature, so you might want to file a bug report with a request to fix it so that you can get "common sense" behavior. However, such a fix will likely not come any time soon, if ever.
Depending on your actual usage scenario there might be some workarounds possible.
Worst case scenario, you can always have one single mouse area for the entire application window and manually propagate events to underlying objects, it is really not as tedious as it may sound, various similar limitations in the way things work in QML has driven me to do exactly that for both mouse and keyboard events.

Keep cursor shape while pressed as it moves outside its MouseArea

I am implementing narrow resize handles which give me annoying behavior. The cursor shape is as expected while the mouse is directly over the handle, but once dragging the handle is initiated, the cursor shape becomes inconsistent. There are two causes of this:
when the cursor moves fast and goes ahead of the handle until the handle "catches up" (or when "fluid qml" is too fluid) - this is particularly nasty as the cursor shape rapidly changes and blinks
when the cursor moves outside of the allowed degree of freedom for the handle
I looked up the doc but it doesn't seem to contain anything about locking the cursor until the press is released.
I did manage to find a hack to fix it - using a dummy overlay MouseArea with acceptedButtons: Qt.NoButton - this actually helps to fake cursor consistency, but comes with an issue of its own. Having that overlay mouse area doesn't allow the cursor to change to a resize shape when it is over the handle, since the handle is under the overlay mouse area it doesn't get to modify the cursor shape at all. So the resize shape kicks in only after the handle is clicked, which is far from ideal. Setting the overlay mouse area to enabled: false doesn't change that - it still keeps blocking cursor shape changes from underlying mouse areas. There is a workaround for that as well, for example setting the overlay mouse area size to 0x0, but it is kind of ugly.
Ideally, the cursor shape should persist until the mouse area is pressed, regardless of whether it is in or outside of its area - after all, the press is not released if you go outside of it, thus the mouse area is still in control and should persist its cursor shape. For example - the window resize handles remain resize shape even if it is moved to resize the window smaller than its minimal size, until the press is released.
To me it seems that there are flaws in the implementation of MouseArea - the cursor shape is not kept while pressed, and the cursor shape is changed even if the mouse area is disabled.
I didn't find a way to do this out-of-the-box, it is pretty easy to create a helper for this though. On the qml side you can e.g. have:
CursorChanger {
cursor: Qt.SizeHorCursor
active: dragArea.containsMouse || dragArea.drag.active
}
On the C++ side you'll need a helper class like this:
class CursorChanger : public QObject
{
Q_OBJECT
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(int cursor READ cursor WRITE setCursor NOTIFY cursorChanged)
// ...
}
In the implementation you can use QGuiApplication::setOverrideCursor and QGuiApplication::restoreOverrideCursor to actually set/reset the cursor. Don't forget to also reset in the CursorChanger destructor if active at that point. If you then register the type:
qmlRegisterType<CursorChanger>(uri, 1, 0, "CursorChanger");`
You can use this type from qml.
I think there are some use cases for the current behavior. For instance, the cursor could convey some relationship between the currently hovered object and the one where the mouse was pressed. Another example, a drag could have its speed intentionally limited, and when the user goes too fast there is a consequence that depends on the item behind.
dtech's demand is more common for sure, and I also would like to see that as an optional feature, but not as a change. The way it is now provides more versatile pieces for apps. I don't like polished components that can be used only exactly as the library writer imagined.
Another QML only solution for a persistent drag cursor is to have a MouseArea behind all itens to hold the persistent shape when needed:
Item
{
id: scene; width: 800; height: 600
MouseArea
{
id: mouse
anchors.fill: scene
}
Rectangle
{
id: draggable; width: 40; height: 30; color: "red"
MouseArea
{
anchors.fill: draggable
drag.target : draggable
//set and unset a persistent cursor
onPressed : mouse.cursorShape = Qt.DragMoveCursor;
onReleased: mouse.cursorShape = Qt.ArrowCursor; //QT default cursor
//let non default scene cursors prevail over the item's
cursorShape: mouse.cursorShape === Qt.ArrowCursor ?
Qt.OpenHandCursor : mouse.cursorShape;
}
}
}

Is there a CSS class or so for a pressed state of an INPUT submit?

When a INPUT submit element is pressed, then it looks "pressed". Is there a CSS class or so for a pressed state of an INPUT submit?
Unfortunately, the :active class like
input:active {
background-color: red;
}
does not do a proper job, since it stays active when "pressed" even though the pointer is no more over the submit.
Is there a way to get this done with pure CSS or do I need JavaScript?
[edit]
Look at a normal (unstyled) submit button (http://jsfiddle.net/j39nw/). If you press it, then it looks "lowered". Now, hold the mouse button pressed and move the mouse pointer outside of the button. As you can see, the "lowered" effect vanishes as soon as the pointer leaves the button even though it is still pressed. With the classes active or focus you cannot get such an effect, since it will still look pressed even though it is no more.
There is no way of doing this with css, but if you check the mouseenter/mouseleavestate with jquery you can add a class and apply the focus to that class.
$('.input').on('mouseenter', function(){
$(this).addClass('hover');
}).on('mouseleave', function() {
$(this).removeClass('hover');
$(document).on('mouseup', function() {
$('.input').blur();
});
});
EDIT:
When the mouse is moved out of the element the element loses focus. When it is moved back in while the mouse button is still held down, the element regains focus.
See: http://jsfiddle.net/RJvLE/4/
EDIT#2:
As #Daniel suggests in the comments the code gets a bit easier when using active instead of focus, cf. http://jsfiddle.net/Rw7F4/
input[type="submit"]:active{
background: red;
}
It works, check this demo
http://jsfiddle.net/amoljawale/RJvLE/

Resources