Component does not integrate into the flow of a ColumnLayout
-
Hi!
I build a TextInputField Component, which I would like to be able to simply integrate in the flow of a ColumnLayout. But a regular "static" Text above it is covered by the component, while a Button following the TextFieldComponent appears properly according to the spacing rules.
(EDIT: I just found out that the button only follows correctly, because it adjust itself in relation to the Text component, but not to my custom component!)
Can anybody help me find out, what am I doing wrong here? Thank you!
TextInputField Component:
import QtQuick 2.9 import QtQuick.Layouts 1.3 Item { id: rootItem property int w: 0 property int h: 0 property int xPos: 0 property int yPos: 0 property int padding: 6 property string primaryColor: "midnightblue" property string primaryColorInverse: "lightsteelblue" property int rectBorderRadius: 2 property int rectBorderWidth: 1 property string placeholderText: "e.g. Tempo & Rehearsal Marks" Rectangle { id: rect // x: rootItem.xPos // y: parent.y + parent.height + parent.spacing + parent.spacing/2 width: rootItem.w height: rootItem.h color: rootItem.primaryColorInverse radius: rootItem.rectBorderRadius border { width: rootItem.rectBorderWidth color: rootItem.primaryColor } // antialiasing: false TextInput { id: txtInput y: rect.y + 8 width: rect.w height: rect.h anchors { fill: rect } horizontalAlignment: rect.AlignHCenter // verticalAlignment: rect.AlignVCenter color: rootItem.primaryColor padding: rootItem.padding selectByMouse: true layer.enabled: true Text { text: rootItem.placeholderText color: "teal" y: txtInput.y anchors { fill: txtInput } padding: txtInput.padding visible: !txtInput.text && !txtInput.activeFocus } } } }
Sample App (Musescore Plugin):
import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 import MuseScore 3.0 MuseScore { menuPath: "Plugins.Staff List Creator" description: "Descr" version: "0.1.0" id: root pluginType: "dock" dockArea: "right" width: 300 height: 600 requiresScore: true ColumnLayout { id: sourceStaffSelector anchors { left: root.left leftMargin: 5 top: root.top topMargin: 5 rightMargin: 5 } spacing: 14 width: 300 Text { id: createStaffListSourceTitle text: qsTr("Enter a title for the new Staff List:") font { //pixelSize: 20 pointSize: 14 bold: true } anchors { fill: parent } color: "white" height: 28 } // My Custom Component that should appear UNDER the above TEXT component... TextInputField { id: staffListTitle w: 300 h: 28 } Button { id: btnCreateStaffList text: qsTr("Create Staff List") [...] } } }
Screenshot of the result (with remarks):
-
There are multiple issues here:
-
You newer set any
width
orheight
for your component.Item
has built-inwidth
andheight
properties. So addingw
andh
is pointless (and since Layout does not know about them, it does not work), remove that or at least tie it to the concrete dimensions fromItem
. -
You are using
anchors
in your Text element, which is inside a Layout. This is an error - items inside layouts should not have any anchors. Qt warns about it in the console. If you want to modify positioning inside a layout, use the attached properties (Layout.fillWidth
,Layout.margins
,Layout.alignment
, etc.). -
Components in Layout need to have some width or height set (anything other than
0
). In some cases, that means settingheight
, in othersimplicitHeight
properties. If you want a component to stretch and fill all available space, useLayout.fillWidth
orLayout.fillHeight
.
-
-
Thank you very much for your quick response!
First, I should mention that I cannot use Qt Creator, because there is currently no way to import Musescore. (If anybody does, that would be awesome!)
So I use VScode and the integrated IDE of Musescore, which is very, very limited. Also, when it comes to external files, Musescore has to be restarted as soon as there have been changes, since Musescore does not recognise that a dependency was changed (and deleting the .qmlc won't work either).
I am not sure, though, if this is the reason, why I do not see the errors in the console you are mentioning (about using anchors within Layouts). I have to investigate...You say that I do not set any width and height in my component. If I understood you well, then I have to explicitly set the "width" and "height" properties on "Item"? This is what I did and - voilà! - it works! (I also removed the anchors you mentioned.)
Item { id: rootItem property int w: 0 property int h: 0 width: w height: h [...]
However: what do you mean by "...or at least tie it to the concrete dimensions from Item"? What would that look like?
Thank you again! Much appreciated! :-)
-
@rowild said in Component does not integrate into the flow of a ColumnLayout:
@sierdzio
You say that I do not set any width and height in my component. If I understood you well, then I have to explicitly set the "width" and "height" properties on "Item"? This is what I did and - voilà! - it works! (I also removed the anchors you mentioned.)Nice!
Item { id: rootItem property int w: 0 property int h: 0 width: w height: h [...]
That's OK. But looking at the code, I'd say you don't need
property int w
at all. Just usewidth
.Then in child items (Rectangle, Text etc.) you don't need to do:
width: rootItem.w height: rootItem.h
It's enough to call
anchors.fill: parent
.However: what do you mean by "...or at least tie it to the concrete dimensions from Item"? What would that look like?
Exactly what you did :-)
Thank you again! Much appreciated! :-)
Happy coding :-)
-
Your custom component look unnecessarily complicated.
Why do you use a Item as the root object, and then fill it with a Rectangle?
You could have just used a Rectangle as root.Building on top of a TextField looks to be easier (it seems you are just customizing the background and placeholder label)
TextField { id: root property string primaryColor: "midnightblue" property string primaryColorInverse: "lightsteelblue" property int rectBorderRadius: 2 property int rectBorderWidth: 1 background: Rectangle { color: rootItem.primaryColorInverse radius: rootItem.rectBorderRadius border { width: rootItem.rectBorderWidth color: rootItem.primaryColor } } Text { text: root.placeholderText // other stuff for your placeholder (see here for inspiration : https://github.com/qt/qtquickcontrols2/blob/dev/src/imports/controls/TextField.qml ) } }
A lot of properties are missing from your handmade solution, for example you don't set any implicitWidth or implicitHeight (which are used by layouts). Even simple stuff like the text of the TextInput is not accessible from outside.
An alternative from subclassing TextField would be to provide your own QtQuick Controls 2 style : https://doc.qt.io/archives/qt-5.11/qtquickcontrols2-customize.html#creating-a-custom-style
-
@GrecKo Thank you so much for your valuable feedback!
I am totally new to QML and "learned" the creation of custom components from a Youtube tutorial. There a "MyTextField" and a "MyButton" component are created the same way, meaning that "Item" is used as the base. Therefore I thought custom components must all start with "Item". It seems I am wrong! So thank you for opening up my perspective!
I also read about the "implicit" attributes and played around with them. So far I didn't grasp the concept, my tries didn't result in anything that gave me the "aha!" experience - they did simply nothing... I assume these values can be compared somewhat to CSS's flexbox features, where - in case there are more objects taking up the space of their parent - a given width or height won't shrink any further, if "flex-shrink: 0" is set?
I will work through what you posted and will report back.
Again, this is a lot of help and an excellent forum, so thanks to all of you for helping me out here!
Have a good day! -
@sierdzio I have to ask something concerning the "anchors" attribute: In your second answer you say it is ok to "anchors.fill:parent" for the Rectangle, but in you first answer you mention that using "anchors" on a TextElement that lives within a Layout Component is a no-go. Do I understand you correctly on this or am I mixing up something? (I looked for docu talking about that, but haven't found anything yet - probably oversaw quite a bit. You don't happen to have a link to a place in the documentation that talks about that by any chance?)
Thank you!
-
@rowild said in Component does not integrate into the flow of a ColumnLayout:
@sierdzio I have to ask something concerning the "anchors" attribute: In your second answer you say it is ok to "anchors.fill:parent" for the Rectangle, but in you first answer you mention that using "anchors" on a TextElement that lives within a Layout Component is a no-go. Do I understand you correctly on this or am I mixing up something? (I looked for docu talking about that, but haven't found anything yet - probably oversaw quite a bit. You don't happen to have a link to a place in the documentation that talks about that by any chance?)
Thank you!
It may be confusing but it's not a mixup.
In first post I was talking about components which are direct children of a
Layout
- there you cannot useanchors
.In second post I was speaking of elements which were children of other items (not
Layout
) - there you can useanchors
as much as you like :-) -
When I define my custom Component, as suggested by @GrecKo, using "TextField {}" as root and not "Item {}", I get this error:
Creating component failed
line 47: Type CustomTextInputField unavailable// "CustomTextInputfield.qml"
import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 TextField { id: txtInput property int padding: 6 property string primaryColor: "midnightblue" property string primaryColorInverse: "lightsteelblue" property int rectBorderRadius: 2 property int rectBorderWidth: 1 property string placeholderText: "e.g. Tempo & Rehearsal Marks" // width: txtInput.width // height: txtInput.height color: txtInput.primaryColor padding: txtInput.padding selectByMouse: true layer.enabled: true background: Rectangle { color: txtInput.primaryColorInverse radius: txtInput.rectBorderRadius border { width: txtInput.rectBorderWidth color: txtInput.primaryColor } } Text { text: txtInput.placeholderText color: "#eeeeee" y: txtInput.y width: txtInput.width height: txtInput.height padding: txtInput.padding visible: !txtInput.text && !txtInput.activeFocus } }
It does work, though, when I use "Item" around everything (and move the properties and adjust ids, of course).
What am I misunderstanding here? Which mistake(s) do I make?