setting wrapMode property of Text in ScrollView - qt

I wrote QML like this:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import App 0.1
ApplicationWindow {
id: appWindow
visible: true
width: 300
height: 500
ColumnLayout {
ScrollView {
ColumnLayout {
Text {
width: 250
text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
wrapMode: Text.WrapAnywhere
}
}
}
}
AppController {
id: controller
}
}
and this is the visual outcome:
I want to wrap Text inside ScrollView. I've tried many patterns but I failed to achieve the desired result. How can I do that?

Just remove ColumnLayout, that's not necessary here. This works for me (if that's what you're looking for):
import QtQuick 2.2
import QtQuick.Controls 1.3
ApplicationWindow {
id: appWindow
visible: true
width: 300
height: 500
ScrollView {
anchors.fill: parent
Column {
Repeater {
model: 10
delegate: Text {
width: appWindow.width - 50
text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
wrapMode: Text.WrapAnywhere
}
}
}
}
}
Try to avoid fixed sizes where possible. In your case the Text doesn't react to window resizing. Also when providing code snippets, just remove specific fragments like your AppController for example (even if it's no big deal in this case but it's easier for anyone to run and test your code) :)
Hope this helps!

Related

How to have Text that wraps, has a maximum height, and becomes scrollable if the text goes over that maxiumum height?

I'm trying to set up a Text component that has a maximum height of the window's height divided by three. The source of the text gives it as a single line, so it also needs to wrap once its width exceeds that of the window. Finally, I'd like the text to become scrollable if its height exceeds 1/3 of the window's.
I've also got a ListView below it, and I want the top of the ListView to be right at the bottom of the Text.
So far, I've come up with this:
ColumnLayout {
anchors.fill: parent
// anchors.margins: 10
ScrollView {
id: descriptionBox
clip: true
Layout.fillWidth: true
Layout.preferredHeight: parent.height / 3
contentWidth: parent.width
Text {
id: descriptionBoxLabel
text: "Eum id neque possimus inventore similique. Et dolores exercitationem vel dignissimos. Voluptatibus assumenda veniam consequuntur. Harum reprehenderit tempora nostrum. Assumenda unde omnis non sit minima voluptas eligendi eum."
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap
}
}
ListView {
id: useListView
model: ["Apple", "Banana", "Pear", "Watermelon"]
delegate: CheckDelegate {
text: modelData
width: Window.width - 20
}
Layout.fillWidth: true
Layout.fillHeight: true
}
}
This kind of works, but the Text doesn't become scrollable when I shrink the height of the window, and the top of the ListView isn't lined up with the bottom of the Text.
I am not sure what the original issue is, but, I reworked it to use a Flickable instead of a ScrollView. The difference being you have to be meticulous in setting contentWidth and contentHeight correctly for the ScrollBar to work. I also tweaked the width calculation of your ListView delegate to follow the width of the ListView:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ColumnLayout {
anchors.fill: parent
Flickable {
id: descriptionBox
Layout.fillWidth: true
Layout.preferredHeight: Math.min(parent.height / 3, frame.height)
clip: true
contentWidth: frame.width
contentHeight: frame.height
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
Frame {
id: frame
width: descriptionBox.width - 20
Text {
id: descriptionBoxLabel
text: "Eum id neque possimus inventore similique. Et dolores exercitationem vel dignissimos. Voluptatibus assumenda veniam consequuntur. Harum reprehenderit tempora nostrum. Assumenda unde omnis non sit minima voluptas eligendi eum."
width: parent.width
wrapMode: Text.WordWrap
}
}
}
ListView {
id: useListView
Layout.fillWidth: true
Layout.fillHeight: true
model: ["Apple", "Banana", "Pear", "Watermelon"]
delegate: CheckDelegate {
text: modelData
width: ListView.view.width - 20
}
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
}
}
}
You can Try it Online!
[EDIT]
To eliminate the white space, modified the Layout height with:
Layout.preferredHeight: Math.min(parent.height / 3, frame.height)

Dialog standardButtons position

I want to make Dialog with only 1 button ("OK"). But when I use "standardButtons: Dialog.Ok" it positions it to the right. How it may be positioned in the middle? I would like to keep current button dimensions.
I've tried to use DialogButtonBox, and also Rectangle and Buttons in footer, but every time it not worked, or look like a mess
Code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Button {
id: button
text: qsTr("Button")
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
onClicked: dialog.open()
}
Dialog {
id: dialog
modal: true
font.bold: true
title: "WARNING!!!"
Text {
id: dialogMessage
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
horizontalAlignment: Text.AlignHCenter
}
parent: Overlay.overlay
x: parent.width/2 - width/2
y: parent.height/2 - height/2
standardButtons: Dialog.Ok
}
}
There are a few options:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: window
width: 640
height: 480
visible: true
Dialog {
id: dialog
x: parent.width/2 - width/2
y: parent.height/2 - height/2
parent: Overlay.overlay
modal: true
font.bold: true
title: "WARNING!!!"
standardButtons: Dialog.Ok
visible: true
Text {
id: dialogMessage
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
horizontalAlignment: Text.AlignHCenter
}
// Declare your own DialogButtonBox.
footer: DialogButtonBox {
alignment: Qt.AlignHCenter
}
// Assign it declaratively.
// Binding {
// target: dialog.footer
// property: "alignment"
// value: Qt.AlignHCenter
// }
// Assign it imperatively.
// Component.onCompleted: dialog.footer.alignment = Qt.AlignHCenter
}
}
I've left the other ones commented out to illustrate that you only need one of these approaches.
Note that you can't just do:
footer.alignment: Qt.AlignHCenter
because the type of the footer property is Item, not DialogButtonBox, and Item doesn't have an alignment property.
Consider using Column Layout, it will keep the dialog in shape. Also use both Layout.alignment and alignment property in the DialogButtonBox - the dialog will look just as You wish:
Dialog {
id: dialog
modal: true
font.bold: true
visible: true
title: "WARNING!!!"
ColumnLayout {
Text {
id: dialogMessage
Layout.alignment: Qt.AlignHCenter
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
}
DialogButtonBox{
standardButtons: DialogButtonBox.Ok
Layout.alignment: Qt.AlignHCenter
alignment: Qt.AlignHCenter
onAccepted: dialog.close()
}
}
}

ColumnLayout with images occupying only the minimum space in QML

I have a ListView with ColumnLayout delegates containing images that could be of any size. I want each delegate to occupy the least possible width and height they can, only defining a certain maximum width, but I can't get images to size right instead of being centered in a box (shown by the gray rectangles here):
My code is this:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow {
id: window
width: 640
height: 800
visible: true
color: "black"
ListView {
id: listView
anchors.fill: parent
model: 3
delegate: ColumnLayout {
width: listView.width
Label {
text:
"Lorem ipsum dolor sit amet, consectetuer adipiscing " +
"Aenean commodo ligula eget dolor. Aenean massa."
color: "white"
wrapMode: Text.Wrap
font.pixelSize: 22
Layout.maximumWidth: listView.width / 2
}
Image {
asynchronous: true
source: "https://picsum.photos/1024/256"
fillMode: Image.PreserveAspectFit
Layout.maximumWidth: listView.width / 2
Rectangle { anchors.fill: parent; color: "#222"; z: -1 }
}
}
}
}
I can get the right behavior when using Column instead of ColumnLayout and replacing the Layout.maximumWidth lines by width: Math.min(implicitWidth, listView.width / 2), as shown below, but I need to use layouts in my project:
The blank space before after image is because of fillMode: Image.PreserveAspectFit, it tries to keep the aspect ratio of image (i.e image width/ image height). You should use fillmode: Stretch to fill the space completely.

How to make qml TableView row height dynamically adapt to content

The problem is the following:
If the length of the text is longer than the width of the cell, the text is wrapped, but the height of the row is not increased. which displays the rest of the text chopped. My other question is, how to adapt the height of each cell to the text contained in it?
Here's the QML part:
Window {
id: window
visible: true
width: 440
height: 400
title: qsTr("Table test")
ListModel {
id: stringsModel
ListElement {
ID: 0
String: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam'
}
ListElement {
ID: 1
String: 'This is a test string'
}ListElement {
ID: 1
String: 'This is another test string'
}
}
TableView {
anchors.fill: parent
frameVisible: false
model: stringsModel
TableViewColumn { role: "ID"; title: "ID"; width: window.width / 2 }
TableViewColumn { role: "String"; title: "String"; width: window.width / 2; delegate: stringDelegate;}
Component {
id: stringDelegate
Item {
id: stringItem
Text {
id: stringTxt
width: parent.width
text: styleData.value
wrapMode: TextEdit.WordWrap
}
}
}
}
}
Your delegate's root item should define its implicitHeight property.
Something like this:
Component {
id: stringDelegate
Item {
id: stringItem
implicitHeight: stringTxt.paintedHeight
Text {
id: stringTxt
width: parent.width
text: styleData.value
wrapMode: TextEdit.WordWrap
}
}
}

Qml text wrap (max width)

I would like to put text inside a bubble, and I want that my bubble be equal to the text width, but if the text length is too long, I would like the text to wrap automatically and be equal to the parent width.
This code works but the text is not wrapping if text is too long:
Rectangle {
id:messageBoxCadre
width: (modelData.messageLength>25)? (wrapper.width - 20): messageBox.width+10
height: messageBox.height+5
color: modelData.myMessage ? "#aa84b2":"#380c47"
radius: 10
Text {
id:messageBox
text: '<b><font color=purple>'+modelData.message+'</font></b> '
wrapMode: "WordWrap"
}
}
and I tried this, text wrap, but if the text is too small the bubble width is not equal to the text size:
Rectangle {
id:messageBoxCadre
width: (modelData.messageLength>25)? (wrapper.width - 20): messageBox.width+10
height: messageBox.height+5
color: modelData.myMessage ? "#aa84b2":"#380c47"
radius: 10
Text {
id:messageBox
width: (modelData.messageLength>25)? (wrapper.width - 20): messageBox.width
text: '<b><font color=purple>'+modelData.message+'</font></b> '
wrapMode: "WordWrap"
}
}
You can almost do this neatly with states. The problem is that attempting to set the width of the parent by assigning it to the paintedWidth of the text box means it then sets the width of the text box, which QML detects as influencing paintedWidth. It wouldn't recurse further than this, but QML still kicks out warnings. One way around the problem is to do as follows, and have a dummy invisible text box that just works out how wide the text is/should be. Its a bit of a hack, but it works nicely.
You could change the "when" property of the state to be dependent on the size of the dummy text box (rather than the length of the string) if you preferred a pixel limit on the width of the box.
import QtQuick 1.0
Rectangle {
id: containing_rect
property string text
text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat"
//text: "a short string"
Text {
id: text_field
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width
text: parent.text
wrapMode: Text.WordWrap
}
Text {
id: dummy_text
text: parent.text
visible: false
}
states: [
State {
name: "wide text"
when: containing_rect.text.length > 20
PropertyChanges {
target: containing_rect
width: 200
height: text_field.paintedHeight
}
},
State {
name: "not wide text"
when: containing_rect.text.length <= 20
PropertyChanges {
target: containing_rect
width: dummy_text.paintedWidth
height: text_field.paintedHeight
}
}
]
}
Here's another way, which uses the Component.onCompleted script. It's more static than my other method, so I guess it depends on what you want to do with it.
import QtQuick 1.0
Rectangle {
id: containing_rect
property string text
height: text_field.paintedHeight
text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat"
//text: "a short string"
Text {
id: text_field
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width
text: parent.text
wrapMode: Text.WordWrap
}
Component.onCompleted: {
if (text_field.paintedWidth > 200) {
width = 200
} else {
width = text_field.paintedWidth
}
}
}
Very late to the party but the clean solution is to use an embedded TextMetrics object. Like this:
...
Text {
id: textObj
width: Math.min(textWidth, myThreshold)
// access to binding-loop-free width and height:
readonly property alias textWidth: textMetrics.boundingRect.width
readonly property alias textHeight: textMetrics.boundingRect.height
TextMetrics {
id: textMetrics
font: textObj.font
text: textObj.text
elide: textObj.elide
}
}
You can also try something like this, using the dummy text box mentioned above:
width: Math.min(dummy_text.paintedWidth, 250)
This will use the painted size of the text unless it is greater than your specified pixel width.
Try this:
Text {
property int MAX_WIDTH: 400
width: MAX_WIDTH
onTextChanged: width = Math.min(MAX_WIDTH, paintedWidth)
}
i did not use state, but i use the idea of dummy text to have width. thanks
my code :
Rectangle{
id:messageBoxCadre
width: (modelData.messageLength>25)? (wrapper.width - 20): messageBox.width+10
height: messageBox.height+5
color: modelData.myMessage ? "#aa84b2":"#380c47"
radius: 10
Text {
id:messageBox
width: (modelData.messageLength>25)? (wrapper.width - 20): dummy_text.dummy_text
text: '<b><font color=purple>'+modelData.message+'</font></b> '
wrapMode: "WordWrap"
}
Text {
id: dummy_text
text: '<b><font color=purple>'+modelData.message+'</font></b> '
visible: false
}
}
Obviously a couple years late, but I just ran into a similar issue (though I am using elide instead of wrap but the basics are the same). I ended up with what seems like a simple and clean solution so I figured if anyone else runs into this problem it can maybe help. Using the original code as an example:
property int maxWidth: 100 // however you want to define the max width
Rectangle{
id:messageBoxCadre
width: messageBox.paintedWidth+10 // width of the actual text, so your bubble will change to match the text width
height: messageBox.height+5
color: modelData.myMessage ? "#aa84b2":"#380c47"
radius: 10
Text {
id:messageBox
text: '<b><font color=purple>'+modelData.message+'</font></b> '
width: maxWidth // max width that your text can reach before wrapping
wrapMode: "WordWrap"
}
}
The only problem for this example is that with WordWrap, if a word is too long to fit the entire width of the Text item it will exceed whatever maxWidth you have set.

Resources