Solved Row of Text items in ListView
-
I currently have a
ListView
with its delegate being a simpleText
item. The list is meant to display a set of messages generated by the application. The messages are generally of unpredictable length. However, by setting the text width to theListView
width, and setting wrapping on, each list element adjusts its height to the text without me having to do any more work. It is essential that I preserve this behaviour.I would now like to introduce another
Text
element into the list delegate. This is actually to hold one of a number of icons that are available to me in a custom font. As such, thisText
element just holds a single character and has a predictable size. The idea is that the icon text should be to the left of the main messageText
item. I implemented this using aRow
, setting its height to be the maximum of the content height of the iconText
element and the messageText
element.This seems to work, except that I now want to introduce a
MouseArea
to the list row, to allow the row to be selected. If I parent it to theRow
, the layout of theText
items is screwed up (the icon overlaps the message). If I parent it to the message element, it works, but then clicking on the icon to select the row does not work.I can sort of solve it by adding a separate
MouseArea
to the iconText
but not only does this seem a little clunky but I also have a dead spot in the spacing between the row elements. There is probably a way around it by eliminating the spacing and embedding the space into one of the text items instead but in my quest to understand QML better I am wondering if there is a better way to do things that allows me to specify a singleMouseArea
but still gets the sizing and layout right etc.The most relevant code is below:
ListView { id: messageList anchors.fill: parent anchors.margins: 5 model: messageModel // has fields 'message' and 'level' spacing: 5 delegate: Row { width: root.width height: Math.max(iconText.contentHeight, msgText.contentHeight) spacing: 5 Text { id: iconText text: iconText(level) color: iconColor(level) font.family: msgIconsFont.name font.pointSize: 15 MouseArea { anchors.fill: parent onClicked: errorList.currentIndex = index } } Text { id: msgTxt text: message width: root.width - iconText.width - parent.spacing wrapMode: Text.Wrap MouseArea { anchors.fill: parent onClicked: messageList.currentIndex = index } } } ...
-
@Bob64 said in Row of Text items in ListView:
@GrecKo Thank you so much for your help but it seems
implicitContentHeight
is not available in my version of Qt.Use
implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
then. -
OK, so I have realised that the problem with parenting the
MouseArea
to theRow
is that it is not possible to use the fill anchor in that context.So I guess the question simplifies to: how does one specify a
MouseArea
for aRow
? -
@Bob64 said in Row of Text items in ListView:
fill anchor in that context.
you can simply set height and width to fill the row
Row{ id:row anchors.fill: parent MouseArea{ //anchors.fill: parent height: row.height width: row.width onClicked: console.log("click") } }
-
@LeLev Thanks - that does seem like an obvious solution! Unfortunately, something isn't right, as selection is no longer working.
-
You could put the MouseArea as the parent of the Row, it would be less ugly than making the width of an Item (your MouseArea) in a Row depending on the row's width, while the Row's width depends on its children's width...
Alternatively you could use a
TapHandler
which is not anItem
.Your manual handling of width in a container could also be avoided if you were to use a Layout
I would write your delegate like that:
RowLayout { width: root.width spacing: 5 Text { text: iconText(level) color: iconColor(level) font { family: msgIconsFont.name pointSize: 15 } } Text { text: message Layout.fillWidth: true wrapMode: Text.Wrap } TapHandler{ onTapped: messageList.currentIndex = index } }
-
The best way is to use
ItemDelegate
as delegate for ListView from QtQuick.Controls 2 withRow
orRowLayout
ascontentItem
and listenonClicked
signal. -
Thanks - some ideas to try there.
Couple of notes:
TapHandler
doesn't appear to be available in the version of Qt I am currently constrained to use (5.9.6).- Although I have ended up using
RowLayout
(andSplitView
and such like) in some other places I always try to avoid QTQuick 1 dependencies if possible. Am I being unreasonable? Are layouts one of those things that are completely OK to mix with QtQuick 2?
-
While Layouts are importing looks like 1.x this doesn't means that they are from Quick 1, it is because of weird versioning in QML. The one thing you should avoid is QuickControls 1.x since they are deprecated at least (and also ugly and slow).
-
@IntruderExcluder Understood. Unfortunately I do have to use
SplitView
andTreeView
from Controls 1 as there are no alternatives. -
@Bob64 There is a SplitView in Qt Quick Controls 2 since Qt 5.13
-
@GrecKo unfortunately I am stuck on 5.9.6 for the time being. Also, judging by some of the questions in this forum, it looks like the new
SplitView
isn't quite ready for production use yet anyway. -
I think I have tried out all of the possible ideas in this thread but they all fail in one way or another. Either they are not available to me in Qt 5.9.6, or the text stops wrapping, or the mouse area stops capturing the list item selection, or the list row heights become a uniform height rather than each one adjusting itself according to its text. This last behaviour is something that is essential for me to preserve. The best I have been able to come up with is a variation on my original code with two separate mouse areas. To avoid the small dead spot in the space between row items, I have set the
spacing
to 0 and instead added aleftMargin
to theText
item. -
@Bob64 said in Row of Text items in ListView:
I think I have tried out all of the possible ideas in this thread but they all fail in one way or another. Either they are not available to me in Qt 5.9.6, or the text stops wrapping, or the mouse area stops capturing the list item selection, or the list row heights become a uniform height rather than each one adjusting itself according to its text.
IntruderExcluder's solution should work:
import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 Window { id: root visible: true width: 640 height: 480 title: qsTr("Hello World") ListModel { id: messageModel ListElement { message: "sadasdasdad" level: 1 } ListElement { message: "sadasdaasdasdasdasdasdad" level: 2 } ListElement { message: "sadasdaasdasdqweqweqwesdad" level: 3 } ListElement { message: "sadasdarwerwerwerwerwerwerwerwerwesdadsadasdarwerwerwerwerwerwerwerwerwesdad" level: 4 } } ListView { anchors.fill: parent model: messageModel delegate: ItemDelegate { width: root.width padding: 5 contentItem: RowLayout { spacing: 5 Text { text: model.level } Text { text: model.message Layout.fillWidth: true wrapMode: Text.Wrap } } onClicked: print(model.index) } } }
-
@GrecKo Thank you. That is almost there. For longer text items, the rows will adjust their height as required. However the default height of each row makes the list too spaced out for brief text items (seems to default to 40 for me). This seems like it should be a simple tweak but I haven't been able to find it yet!
-
That's because the implicitHeight of the ItemDelegate is based on the max of the one from its background and its contentItem.
You can ignore the background implicitHeight by adding:
implicitHeight: implicitContentHeight + topPadding + bottomPadding
-
@GrecKo Thank you so much for your help but it seems
implicitContentHeight
is not available in my version of Qt. -
@Bob64 said in Row of Text items in ListView:
@GrecKo Thank you so much for your help but it seems
implicitContentHeight
is not available in my version of Qt.Use
implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
then. -
@IntruderExcluder Thank you! I thought I had tried that one but obviously not. Anyway, it works well. Thank you for this and for your original suggestion.