Qt QML Put element above a Drawer - qt

I'm struggling with a very basic question..
Using QT 5.15.2:
We have a simple application with one main window and 2-3 sub-window (1 level down from main). The main window consists of a content item, a header and some menu-flaps distributed across the main window. So Far the sub-pages were opened with a drawer element.
However, the drawer overlays the flaps and header once opened and we need to re-instanciate the flaps and header within the drawer to have it visible. This is not really nice. Is there any way to define the z-level on which the drawer is opened? (apparently setting z doesn't work).
Item{
id: id_mainWindow
z: 0
Drawer{
id: id_subMenu1
anchors.fill: parent
z: 1
/* Not so nice workaround */
Button{
id: id_subClose
z: 100
onClicked{
id_subMenu1.close()
}
}
}
/* Unfortunately, this one gets hidden once, the drawer is open */
Button{
id: id_subOpenClose
z: 100
onClicked{
if( id_subMenu1.open ){
id_subMenu1.close()
} else {
id_subMenu1.open()
}
}
}
}

I would suggest that a Drawer is not the right component for this job as it is technically a Popup. It might be worth checking out the TabBar component instead.
Nevertheless here's a re-write of your code so that your Drawer opens without covering your id_subOpenClose button.
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
Rectangle {
id: id_mainWindow
anchors.fill: parent
Drawer {
id: id_subMenu1
/*
Set the Drawer's height and y position so that it does not cover your button
*/
y: id_subOpenClose.height
height: id_mainWindow.height - id_subOpenClose.height
width: id_mainWindow.width
// Do not dim background
dim: false
// Set this to zero if you want no shadow
Material.elevation: 2
edge: Qt.RightEdge
Label {
text: 'Hello World'
anchors.centerIn: parent
}
}
/*
This is your header button that was getting hidden
Here it stays as if it were part of a global header and does not get hidden by
the Drawer.
*/
Button{
id: id_subOpenClose
text: id_subMenu1.visible? 'close': 'open'
onClicked: id_subMenu1.visible? id_subMenu1.close(): id_subMenu1.open()
}
}
For an interactive WASM example of the above see here.

Related

How to avoid binding loop when setting padding?

I want to update the padding of a ScrollView if there is a scrollbar visible, but on the other hand, the visibility of the scrollbar is dependent on the height/width of the content inside the scrollbar, which changes when the padding changes. The following causes a binding loop:
ScrollView {
id: control
rightPadding: Scrollbar.vertical.visible ? Scrollbar.vertical.width : 0
....
ScrollBar.vertical: ScrollBar {
parent: control
visible: control.height < height
...
}
}
How can I achieve this without a binding loop? Thanks
I was unable to get your code frag to work - it seems like your code should depend on the contents of your ScrollView, but this is not included in your code frag, and it may be missing some other references.
Anyway, I suggest approaching this a little differently - change the ScrollView's content's width based on whether or not the ScrollBar is visible. I also set the ScrollBar policy instead of visibility. I have created a full example where you can add or remove content using a slider for demonstration:
import QtQuick 2.15
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: root
visible: true
height: 500
width: 500
ColumnLayout {
anchors {
fill: parent
}
Slider {
// use slider to add delegates to the ScrollView to toggle the scroll bar visibility
id: slider
to: 20
}
ScrollView {
id: scroll
Layout.fillHeight: true
Layout.fillWidth: true
ScrollBar.vertical.policy: scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
property bool scrollBarVisible: scroll.contentHeight > scroll.height
ColumnLayout {
width: scroll.scrollBarVisible ? scroll.width - scroll.ScrollBar.vertical.width : scroll.width // change the width of the
Repeater {
model: slider.value
delegate: Rectangle {
color: "tomato"
Layout.fillWidth: true
Layout.preferredHeight: 150
}
}
}
}
}
}
One thing to note though - your ScrollView content cannot have its height depend on its width, for example, if the content had some Text that wraps if there is not enough width, causing it to get taller when the width decreases. This would get back into infinite-loop territory.

Setting header on ListView prevents scrolling when the delegate has MouseArea

I have a ListView and its delegate has a MouseArea. If the ListView doesn't have a header, everything works well. However, if I set a header, I can only click on the delegates but can't scroll.
What's is more interesting is that when I set the header's width to half the width of the window, I can scroll normally on the right side of the screen (where there's no header), but can't scroll on the left side under the header.
EDIT: Experimented with this a bit and found another thing. The delegate's height is 80 and if I set the header's height to, say, 30, then I can't swipe when the mouse lands on the top 30 pixels of a delegate as if the header is attached to each item in the list?
[edited code] This the full code that recreates the problem for me (can't scroll on the left side but can on the right). I'm using qt 2.15
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: root
width: 640
height: 600
visible: true
ListView {
id: list
anchors.fill: parent
model: 20
delegate: Rectangle {
signal clicked();
implicitHeight: 70
width: root.width
MouseArea {
anchors.fill: parent
z: 2
onClicked: console.log("clicked");
}
Text {
anchors.centerIn: parent
text: "text"
}
}
// Works fine when I comment this out
headerPositioning: ListView.OverlayHeader
header: Rectangle {
z: 2
implicitHeight: 100
implicitWidth: root.width / 2
color: "blue"
}
}
}
Example (the header is blue):
https://bugreports.qt.io/browse/QTBUG-101727 was written up about this; probably the same as QTBUG-89409. And we fixed it already in 5.15.7. It's also OK in Qt 6.
Note that if you press on the header itself and try to flick, you won't be able to, in any version; that's intentional: https://bugreports.qt.io/browse/QTBUG-74046
The problem turns out to be too specific and can't be recreated so I guess I should close the question.
(SO asks me to wait for 18 hours...)

Why does my QML background transparency break depending on width setting?

I'm running into some strange QML behavior. Basically, I have a TabBar header with several tabs running across it. I'd like the background element to be mostly the same for each of them, but some of them I want to be able to dynamically change the color of. So I have a component:
Component {
id: standardBackground
Rectangle {
opacity: parent.parent.checked ? 0 : (parent.parent.pressed ? 0.8 : 1)
color: tabColor
}
}
And for each TabButton, I'm doing:
TabButton {
text: qsTr("Tab 1")
background: Loader { sourceComponent: standardBackground }
height: 60
}
This works perfectly, but I'm running into some really strange errors. First off, running it this way gives me the following QML warning:
QML TabButton: Binding loop detected for property "implicitWidth"
So I figured I could fix this by adding: width: parent.width to the Rectangle in my component. This does silence the warning, but for some reason, it makes it so that the first tab will always be transparent regardless of whether or not it's clicked. This only affects the first tab. I have no clue why this would happen.
However, when I set width: <anything>, then this fixes both problems: No warnings and correct transparency. Playing around with different settings for the width causes no noticeable changes, as long as it's positive. So I have it set to 1. If I set it to 0, I get the same "implicit width" warnings.
So a couple different questions:
Why does the transparency of the component break when I set width: parent.width?
Why can I set width to any constant value without it affecting the GUI at all?
Is there a better way of silencing the warning about implicit width?
Here is my full code (simplified to less tabs):
import QtQuick 2.6
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Universal 2.0
import Qt.labs.settings 1.0
import QtQuick.VirtualKeyboard 2.1
import QtQuick.VirtualKeyboard.Settings 2.1
import "DataEntry"
ApplicationWindow {
id: window
width: 1280
height: 1024
visible: true
title: "Hello World"
property var tabColor: "#353637"
property var dummy: InputContext.focus
Settings {
id: settings
property string style: "Universal"
}
Component {
id: standardBackground
Rectangle {
opacity: parent.parent.checked ? 0 : (parent.parent.pressed ? 0.8 : 1)
color: tabColor
width: 1
}
}
header: TabBar {
id: bar
width: parent.width
height: 60
TabButton {
text: qsTr("Tab 1")
background: Loader { sourceComponent: standardBackground }
height: 60
}
TabButton {
text: qsTr("Tab 2")
background: Loader {
sourceComponent: standardBackground
function getTabColor(error){
if (error)
return '#cccc00'
return window.tabColor
}
property var tabColor: getTabColor(hasError)
}
height: 60
}
}
StackLayout {
id: viewStack
width: parent.width
anchors.fill: parent
currentIndex: bar.currentIndex
tab1 {
}
tab2 {
}
}
}
As we are on SO I tend to answer only one question. For you, I choos the question for the binding loop.
The reason for that binding loop is documented here.
You do not specify a size for the Loader so the implicit width of the Loader is set to the width specified by the loaded Item. Here you set the size to be the same as the Loader's size. Now this would not be a problem, and the result would just be 0
Now we stir in the Button which also has an implicitSize set to its styling items. Here the Loader is instantiated widht width 0 and then resized to fill the implicitWidth of the Button which is (without a sized background) depending on the text and the paddings.
And now we update the round. So, the implicitWidth of the Rectangle is depending on the width of the Loader whose implicitWidth is depending on the Rectangles width. Further the Loaders width is depending on the Buttons width, which is depending on its implicitWidth and which is in turn depending on its childrenRect.width...
A binding loop is easily detected even if there are no direct problems, as the system is stabilizing in the first iteration.

QML: referencing root window by parent reference is unreliable

Qt/QML question. Using Qt 5.7.
Take the following simple QML program that displays a red rectangle and a blue rectangle aligned vertically. Click handlers for both rectangles attempt to change the color of the parent host window. But with a subtle difference. The red rectangle references the host window directly by it's id (rootWindow). The blue click handler changes color via a parent reference.
The former case works fine. The latter case does not work. It seems like the root window is treated specially and isn't directly part of the parent/child hierarchy, even if the Rectangles are logically nested in the code that way.
Can someone explain the rule around this?
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: rootWindow
color: "#ffffee"
Rectangle {
id: rect1; width: 50; height: 50; color:"red"
MouseArea {
anchors.fill: parent;
onClicked: {
print("rect1 clicked");
rootWindow.color = "green"; // works fine
}
}
}
Rectangle {
id: rect2; width: 50; height: 50; color:"blue"
anchors.top: rect1.bottom
MouseArea {
anchors.fill: parent;
onClicked: {
print("rect2 clicked");
rect2.parent.color = "pink"; // does not work
}
}
}
}
If you add the following line to the onClicked handler, you'll see that its parent isn't the Window:
print(rect2.parent)
Output:
qml: QQuickRootItem(0x18b18147bc0)
This is explained not-so-visibly in the documentation for Window:
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. The item's parent will be the window's contentItem, which is the root of the Item ownership tree within that Window.
The window itself isn't an item, so it uses contentItem instead so that child items can have a parent.
However, in Qt 5.7, Window got an attached property that can be used to access the window of an item:
rect2.Window.window.color = "pink";
Whichever item comes before the Window.window part will be the item that the attached property is used on. You could use it on any item in this scene (e.g. the MouseArea), as they all belong to the same window.
Note that attached properties create a QObject-derived object for each unique item they're used on, so be mindful of how you use them, especially in items that are created in very large numbers.

How do I get a Loader to no longer fill its parent?

I have a Loader object in my main QML file. I load different QML sources in the loader at run time depending on the current context.
My steps are like this:
Load a login.qml file and set anchors.centerIn: parent on the Loader.
After successfully logging in, I load task.qml and then set anchors.fill: parent on the Loader.
After the user logs out, I want to redirect back to login.qml, and I set anchors.centerIn: parent again on the loader.
I would expect this would cause the Loader to be centered in the parent and no longer fill it. However, this is not the case. The Loader still fills the parent.
How do I change the anchors on an Item so it is centered again and no longer fills its parent?
You don't need to set anchors.centerIn again. Instead, you need to set anchors.fill and width and height to undefined so it defaults to the implicit size and again uses the anchors.centerIn property.
Here's a working example:
import QtQuick 2.0
import QtQuick.Controls 1.0
Item {
width: 800
height: 600
Rectangle {
id: rect
anchors.centerIn: parent
implicitWidth: 500
implicitHeight: 200
color: "red"
Button {
text: "Toggle Full Size"
anchors.centerIn: parent
onClicked: {
if (rect.anchors.fill) {
rect.anchors.fill = undefined
rect.width = undefined
rect.height = undefined
} else {
rect.anchors.fill = rect.parent
}
}
}
}
}
Alternatively, a simpler solution might be to make your Loader always fill its parent and instead add an extra Item at the top level of your login.qml file so the login view is centered in this Item. That would remove the necessity to change the anchor on the Loader.

Resources