Difference in QML between Window and Item in parent-children relationship - qt

I'm wondering why for Item this works:
Item {
id: root
width: 640
height: 480
MouseArea {
anchors.fill: (root or parent. It doesn't matter)
onClicked: console.log("clicked")
}
}
But for Window it doesn't. Only anchoring by parent will work, but for anchoring by id it will fail.

According to the documentation, anchors.fill requires the argument either to be or to identify an Item-derived object.
From here you can follow the inheritance chain of Window and see that it is not actually an Item.
Moreover, from here you can see that:
If you assign an Item to the data list, it becomes a child of the Window's contentItem, so that it appears inside the window.
where for the contentItem we have that:
This attached property holds the invisible root item of the scene or null if the item is not in a window. 
Because of that, it makes sense what you are observing:
the id of the Window does not identify an Item
→ anchoring by id results in an error
the parent is actually the hidden Item-derived contentItem to which each child of Window is automatically parented → anchoring by parent correctly works

Related

How to scroll with arrow keys in a Kirigami ScrollablePage?

The Qt QML based mobile/desktop convergent UI framework Kirigami provides a QML type ScrollablePage to support scrolling through content. Placing any visual QML item into it automatically makes it scrollable if it's larger than the ScrollablePage itself:
ScrollablePage is a Page that holds scrollable content, such as ListViews.
Scrolling and scrolling indicators will be automatically managed.
Kirigami.ScrollablePage {
id: root
//The rectangle will automatically be scrollable
Rectangle {
width: root.width
height: 99999
}
}
(source)
This provides scrollbars and allows scrolling with the mouse wheel, two-finger-scrolling with the touchpad and flicking ("click and throw") scrolling as we're used to from touchscreen devices.
However, it does not allow scrolling with any keyboard keys (Arrow Up / Down, Page Up / Down). How can I make that possible? The usual approach of doing Keys.onUpPressed: scrollBar.decrease() does not work because the ScrollablePage's scrollbar is not accessible as part of its public API.
Instructions
Use a Flickable to wrap the content items you put into your ScrollablePage. Then evaluate key press events in the Flickable and in response execute flick() to scroll the view. Example (combining examples from the Kirigami manual and from the Qt manual):
Kirigami.ScrollablePage {
id: root
Flickable {
focus: true
topMargin: 20; leftMargin: 20; bottomMargin: 20; rightMargin: 20
Keys.onUpPressed: flick(0, 800)
Keys.onDownPressed: flick(0, -800)
Rectangle {
width: root.width
height: 5000
}
}
}
Details and Explanation
While you can't access the scrollbar, you can access what the scrollbar uses to move the view: a Flickable instance. You just have to wrap it around the page's content. If you don't, ScrollablePage internally uses ScrollView to wrap your page's content in a Flickable anyway, but then you don't have a reference on it to execute flick().
Executing flick() does the same as when the user flicks the element, so the scrollbar position etc. will be updated alright.
If it still does not work, then (1) maybe you give too small pixel/second values to Flickable::flick() for scrolling to be visible or (2) maybe the initial Flickable::flickDeceleration values on your platform are messed up. These values are platform specific, so it can require some experimentation. On some platforms, setting them to zero during a flick() will help, while under Linux this is exactly the value preventing any scroll movement.
It is not necessary to enable ScrollablePage::keyboardNavigationEnabled for the above solution to work, since that is only for moving the currentItem of suitable content with the arrow keys (see below), and not for scrolling in general. It will even prevent ordinary scrolling in case your page content is an item view (ListView, GridView etc.).
Alternative solution for item views
If the content of your ScrollablePage is an item view (any QML object that has a currentItem property, such as ListView or GridView), then instead of wrapping that content in a Flickable just enable ScrollablePage::keyboardNavigationEnabled. It will allow you to move the currentItem with the Arrow Up and Arrow Down keys. That's what one usually wants for these views, even though it's not scrolling but rather keyboard navigation.

QML tab occasionally stretches off-screen

I have a QML-based GUI with a fixed width that uses the TabView type in several places.
On one page, I have something like this (leaving out most properties except for lateral anchors):
ColumnLayout {
MyTabsViewSubmenu { // defined below
Layout.fillWidth: true
Tab {
id: someId
title: someString
anchors.fill: parent
SomeCustomClass {
id: someId2
anchors.fill: parent
}
}
// three more tabs defined the same way, with the same anchors...
}
// another item below the tabs...
}
MyTabsViewSubmenu is something like this:
TabView {
Rectanble {
anchors.fill: parent
}
style: TabViewStyle {
// miscellaneous style stuff
}
}
One of my four tabs in the ColumnLayout above sometimes stretches off the screen when selected. As far as I can tell, there is nothing special about it compared to the other items used as tabs throughout the GUI. The layout of this tab is something like this:
Item {
MyTabsViewSubmenu {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: sibling.top
Tab {
// no anchors (see below)
SomeItem {
anchors.fill: parent
}
// ... other tabs....
}
Rectangle {
id: sibling
anchors.left: parent.left
anchors.right: parent.right
// ... stuff....
}
}
The entire page stretches off the screen: both the sub-tab content and the content in the Rectangle I've shown here as sibling.
I would suspect that possibly the missing anchors.fill: parent in the innermost Tabs might be the problem, except that sibling is not (as far as I can tell* missing any anchors, and I've never seen the tabs stretched offscreen without the sibling being stretched offscreen as well.
It seems entirely unpredictable whether the stretching occurs or the layout is done correctly. Once the layout has stretched off the screen, I can sometimes get it to correct itself by navigating away from that page and back.
I'm using Qt 5.6.1-1 on Debian 7.
EDIT: When I navigate to the "stretched" tab and the bug occurs causing the stretching, the tabs themselves at the top of the page also get "stretched" somewhat. Returning to a different tab un-stretches the tabs.
The fix
Setting Layout.maximumWidth (as per Velkan's comment) appears to resolve the issue. Additionally, it appears to make the page load faster.
Observations and testing
It's now about a week and a half since I introduced this change to the code, and the product has been heavily tested since then.
We have discovered a second component that needs Layout.maximumWidth set in order to keep from stretching off the screen, and indeed applying this fix to both the original problematic components has prevented the screen-stretching bug. So this is definitely a valid fix.
Possible root cause (i.e. groundless speculation)
I suspect that the QML engine attempts to size "Layout" objects by starting with the maximum width, then shrinking them to fit (or something like this). If the maximum width is unset, it's set to something like "infinity". Without a maximumWidth, it appears that the auto-shrinking operation sometimes fails, leaving the component stretched offscreen. I suspect that the automatic-resizing code may be impacted by some kind of nondeterminism in the order in which the sizes of different QML components are computed.

Change order of visible items

Is it possible to move up/down the visual items in order to change its overlapping? currently, the child hides its parent, but i'd like to have the reverse, i.e. the parent hides the child. Maybe some properties exists?
Yes it's possible. You will need to change the z property of the involved items. According to the documentation:
Sets the stacking order of sibling items. By default the stacking order is 0.
Items with a higher stacking value are drawn on top of siblings with a lower stacking order. Items with the same stacking value are drawn bottom up in the order they appear. Items with a negative stacking value are drawn under their parent's content.
Hence, you only need to set z property of children to a negative value:
import QtQuick 2.4
Rectangle {
width:800
height: 480
color: "yellow"
// opacity: 0.5 (1)
Rectangle{
width: 100
height: 100
color:"red"
z:-1
}
}
In this example the inner Rectangle is not visible since its z property has a negative value. Uncomment the opacity assignament of the outer Rectangle in (1) to see it.

QML Minimum drag/flick distance for changing item

I have a ListView in QML using theese properties :
ListView {
id : list
boundsBehaviour: Flickable.StopAtBounds
snapMode: PathView.SnapOneItem
highlightFollowsCurrentItem: true
highlightRangeMode: ListView.StrictlyEnforceRange
...
}
My problem is the following :
I'm trying to determine exactly when the drag/flick will made the list move to the next/previous item or staying on the same one when releasing the touch.
Is there a property to modify or something useful to know which behaviour will happen ?
Thanks.
you are looking for startDragDistance property in QApplication class, the default value of startDragDistance is 10 pixels for Windows (it depends on OS)
In order to set drag distance to 50 pixels, you can use the following line
QApplication::setStartDragDistance(50);

ListView positionViewAtIndex() not working

I have two ListViews in a QML project that are both running off of the same model. I am trying to get them to start out at different indices (the model starts with 2 ListElements in it). In order to do this, I call positionViewAtIndex when the component completes:
ListView {
model: mymodel
Component.onCompleted: positionViewAtIndex(1,ListView.Beginning)
//...
}
However, neither ListView actually is positioned at the desired index. Is there something I'm not doing? The only solution that I have seen for this problem is to ensure that you're not calling the method before the ListView completes, but I am doing that.
I am using Qt 5.2/QtQuick 2.0.
Edit: After playing around with the other positioner functions, I have found that none of them work. I have also found that changing currentIndex does not work either. Furthermore, I have found that currentIndex is not being changed with the view -- onCurrentIndexChanged is never being fired.
So, I figured it out. It turns out that a ListView instantiates its delegates before it worries about its own properties...so the delegate was only reading off of the ListView's width before the ListView set its own dimensions. When a delegate has a width/height property in the orientation of the view equal to zero, the view will not know where to scroll to when positionViewAtIndex() is called. So, in order to fix this, you have to use a conditional binding:
Component {
id: myDelegate
Item {
width: ListView.view.width == 0 ? 480 /*or some preset*/ : ListView.view.width
}
}
This will give the delegate a nonzero width and cause the positionViewAtIndex() function to work.
Of course, if your ListView is vertical, then you need to set the height property and not the width property.
Alternatively you can set currentIndex to 1

Resources