I have to perform a very simple task: I want to display a piece of text inside a rectangle and the size of that rectangle should precisely be the width of the text.
In C++, it's fairly easy to do. Just define the QString and apply the QFontMetrics to get its width. Then define the rectangle graphics element to have that size. It's done within five minutes.
I have heard that QML is easier to use. Therefore, I was expecting to solve that problem in less than five minutes. I didn't, and I'm still stuck at it. Here's what I have tried:
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
This doesn't work for some reason I don't understand. I have found a way to do it in a way that doesn't exactly suits my needs:
Rectangle {
width: 100
height: 100
MouseArea {
id: myMouseArea
anchors.fill: parent
onClicked: parent.width=myText.contentWidth
hoverEnabled: true
}
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
In this case, when I click the rectangle, it gets the correct width. Nevertheless, I am not interested in this solution, because I don't want to have to click to get a rectangle with the correct size.
I want that the rectangle's size gets the correct size whenever myText changes text. The use of onTextChanged in the Text item doesn't work either.
What am I missing here?
As far as I know, Font metrics were made available to developers in Qt 5.4, so they are relatively new, in QML. You got mainly FontMetrics and TextMetrics. A simple usage example:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 280; height: 150
TextMetrics {
id: textMetrics
font.family: "Arial"
font.pixelSize: 50
text: "Hello World"
}
Rectangle {
width: textMetrics.width
height: textMetrics.height
color: "steelblue"
Text {
text: textMetrics.text
font: textMetrics.font
}
}
}
As noted by Phrogz in the comment below, the TextMetrics type does not support measuring wrapped text.
EDIT
For what is worth I've never ever had the need to use metrics in QML. For me content* or painted* properties served the purpose and, as of Qt 5.12, they seem to work fine. Aka the following two solutions generate the correct visual behaviour:
// solution 1
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
// solution 2
Rectangle {
width: myText.paintedWidth
height: myText.paintedHeight
Text {
anchors.fill:parent
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
I would prefer those solutions to the usage of metrics for such a simple use case as the one proposed by the OP. For the opposite case - fitting a text in a specific size - a combination of properties can do the trick, e.g.:
Rectangle {
anchors.centerIn: parent
width: 200
height: 30
Text {
anchors.fill: parent
text: "Wonderful Text"
minimumPixelSize: 2
fontSizeMode: Text.Fit
font.pixelSize: 200
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Here the pixel size is simply over the top but the text still fits because a minimum size of 2 is set and the text has a clear fitting policy and clear boundaries, defined by the anchoring.
I'm sure Label component will do the job:
import QtQuick 2.1
import QtQuick.Controls 2.4
ApplicationWindow {
visible: true
Column {
Repeater {
model: [
{"color": "red", "radius": 1},
{"color": "green", "radius": 2},
{"color": "blue", "radius": 3}
]
Label {
padding: 0
text: modelData.color
font.family: "Helvetica"
font.pointSize: 50
background: Rectangle {
color: modelData.color
radius: modelData.radius
}
}
}
}
}
You don't need to use anchors.fill: parent for Text item because size of Text's parent depends on size of Text itself. It's cause binding loop.
This must works fine.
Rectangle {
width: myText.contentWidth
height: myText.contentHeight
Text {
id: myText
font.family: "Helvetica"
font.pointSize: 50
text: qsTr("The string I want to display")
}
}
Related
When I resize a Qt Quick ApplicationWindow that contains a Frame with an InnerShadow, I see flickering and visual artifacts. The same is not true when I either do not replace the default border or if I use a simple rectangle for the Frame object.
I tested this on my laptop that runs a 64-bit Arch Linux. It has an Nvidia GTX 1060 Max Q graphics card and an integrated Intel graphics card. I ran the code both with and without bumblebee.
Any way to work around or eliminate this flickering? It is pretty bad. My code and some screen-grabs are as below
EDIT: I have tried setting AA_ShareOpenGLContexts and AA_UseOpenGLES (and its software/desktop variants) attributes with no luck.
UPDATE: I have created an issue here: https://bugreports.qt.io/browse/QTBUG-81519, but I am still hoping someone can devise a workaround.
test.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.14
ApplicationWindow{
id: main
width: 2*screen.width/3
height: 2*screen.height/3
title: "Test ApplicationWindow"
color: activeColorPalette.window
visible:true
SystemPalette {
id: activeColorPalette
colorGroup: SystemPalette.Active
}
Frame{
anchors.fill: parent
anchors.margins: 10
background: Item{
id: root
anchors.fill: parent
Rectangle{
anchors.fill: parent
anchors.margins: 1
radius: 16
color: activeColorPalette.window
}
InnerShadow {
anchors.fill: root
horizontalOffset: 0
verticalOffset: 0
source: root
radius: 16
color: activeColorPalette.shadow
spread: 0.6
samples: 32
cached: true
fast:true
}
}
}
}
Window without flickering or artifacts
Window with flickering/visual artifacts while resizing
I found a workaround to eliminate the visual artifacts during resizing.
In my problem code, the InnerShadow used an Item QML type as the source, which is transparent by default and contained a grey Rectangle that I added within it. The visual distinction between the transparent source Item and the smaller child Rectangle inside it is what the InnerShadow uses to compute the shadow gradient within. The end result was a decorative shadow border. However, resizing the application resulted in ugly visual artifacts that would sometimes stay. Note: Changing the outer Item into a transparent Rectangle had no discernible effect.
But when I encapsulated the grey innermost rectangle into another transparent component like
Item {transparent Rectangle {grey inner Rectangle} }
or like
Rectangle{transparent Rectangle{grey inner Rectangle}}
in addition to setting the middle transparent Rectangle as the source for the InnerShadow, the visual artifacts are eliminated. Below is the working code for test_workaround.qml that you can compare to the test.qml above.
test_workaround.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtGraphicalEffects 1.14
ApplicationWindow{
id: main
width: 2*screen.width/3
height: 2*screen.height/3
title: "Test ApplicationWindow"
color: activeColorPalette.window
visible:true
SystemPalette {
id: activeColorPalette
colorGroup: SystemPalette.Active
}
Frame{
anchors.fill: parent
anchors.margins: 10
background: Item{
id: root
anchors.fill: parent
Rectangle{
id: middleRect
anchors.fill: parent
color: "transparent"
Rectangle{
id: innerRect
anchors.fill: parent
anchors.margins: 1
radius: 16
color: activeColorPalette.window
}
}
InnerShadow {
anchors.fill: root
horizontalOffset: 0
verticalOffset: 0
source: middleRect
radius: 16
color: activeColorPalette.shadow
spread: 0.6
samples: 32
cached: true
fast:true
smooth:true
}
}
}
}
Hi I wanted to add some Arabic text to my Phaser game. I have the following in the update() function:
this.scoreText = this.add.text( this.world.centerX, this.world.height/5,
"",{nsize: "32px", fill: "#FFF", align: "center"});
this.scoreText.setText("تُفاحة");
This produces strange letters on the screen which are not Arabic. Any ideas?
First off, you shouldn't be adding text in the update() method - this would cause it to be added multiple times (once for each frame, ideally 60 times per second). Move it to the create() method so that it's only added once. You also have a typo in the parameters: nsize should be just size.
function create() {
this.scoreText = this.add.text( this.world.centerX, this.world.height / 5, "", { size: "32px", fill: "#FFF", align: "center" });
this.scoreText.setText("تُفاحة");
}
You can try something like
Import the font in the CSS part in index.html
#import url(https://fonts.googleapis.com/earlyaccess/amiri.css);
Then just declare the style as a variable
var style = { font: "32px Amiri", fill: "#333", align: "center" };
Then in the create function add the text like in this jsfiddle https://jsfiddle.net/albator2018/r2zLtoqd/
There is another manner to do it but like what i've just explained it works fine
I'm trying to understand how anchors work in QML (Qt Quick 2.0). I've got a simple Item like this:
AddButton.qml:
Item {
Button {
text: "ADD"
width: 100
height: 50
}
}
Which I add to the main QML file like this:
main.qml:
Item {
id: root
width: 800
height: 600
AddButton {
id: addButton
}
}
This works fine. However, as soon as I try to put the button in the bottom right corner using anchors, the button disappears:
main.qml:
Item {
.....
AddButton {
id: addButton
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
It only comes back if I set a width and height at the main QML file level:
main.qml:
Item {
.....
AddButton {
id: addButton
width: 100
height: 50
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
So I'm wondering, why does the button disappear when I set the anchors? And is there any way to make it work without setting the width and height in the main QML file (basically to make it use whatever size is set in AddButton.qml?)
The problem is that the encapsulating Item has not an explicit width height. In this case the engine refers to the "natural" witdh/height, i.e. the implicitWidth/implicitHeight properties. Such properties happen to be zero in most cases, even in this specific case. Hence, your custom type has zero dimension.
Therefore the AddButton.anchors.bottom is in fact at the very top of the encapsulated Button, which in turn protrudes the encapsulating Item
There are two things about this:
You don't need to encapsulate the Button with an Item unless you want to hide the internals of the Button.
If the latter is your desire, try this:
Item {
width: 100 //give the object a dimension!
height: 50
Button {
text: "ADD"
anchors.fill: parent
}
}
Now you can anchor it, and it won't be positionated somewhere else.
I am doing a diagram in jointjs and I have a rect with a text inside, but that text is so long that does not fit inside the rectangle. How can I cut lines inside it so it all fits at the same size of the rectangle ??
((escape by itself does not work))
You can use a utility that joint js has called breaktext, assigning the result to a var :
var wraptext = joint.util.breakText('yourtext|escapejs', {
width: 300
});
Then, set that var as text attr of the rect:
var rectname = new joint.shapes.custom.Rect({
size: { width: 680, height: 75 },
attrs: {
text: {
text: wraptext,
}
}
});
Width in this case would be the parameter that determines the long of each text break
Hope it helps !!
Imagine I have Rect element and I wish to decorate it with a small (say 16x16) PNG image in the upper left. I am unable to determine how to achieve that task. I have studied the docs but have (so far) been unable to find a sample or reference on how to achieve that task. Does anyone have a recipe or a sample pointer that they would be willing to share to help me achieve my goal?
Better is to create your own custom shape that has a rectangle, image and text. This gives you much more flexibility and you don't have to have two elements in order to express one shape. Your shape decorated with a little image in the top left corner may look like:
joint.shapes.basic.DecoratedRect = joint.shapes.basic.Generic.extend({
markup: '<g class="rotatable"><g class="scalable"><rect/></g><image/><text/></g>',
defaults: joint.util.deepSupplement({
type: 'basic.DecoratedRect',
size: { width: 100, height: 60 },
attrs: {
'rect': { fill: '#FFFFFF', stroke: 'black', width: 100, height: 60 },
'text': { 'font-size': 14, text: '', 'ref-x': .5, 'ref-y': .5, ref: 'rect', 'y-alignment': 'middle', 'x-alignment': 'middle', fill: 'black' },
'image': { 'ref-x': 2, 'ref-y': 2, ref: 'rect', width: 16, height: 16 }
}
}, joint.shapes.basic.Generic.prototype.defaults)
});
And you can use it like this in your diagrams:
var decoratedRect = new joint.shapes.basic.DecoratedRect({
position: { x: 150, y: 80 },
size: { width: 100, height: 60 },
attrs: {
text: { text: 'My Element' },
image: { 'xlink:href': 'http://placehold.it/16x16' }
}
});
graph.addCell(decoratedRect);
Note how is the shape specified, the important bits are the markup, type and the attrs object that references the SVG elements in the markup by normal CSS selectors (here just tag selectors but you can use classes if you want). For the image tag, we take advantage of the JointJS special attributes for relative positioning (ref, ref-x and ref-y). With these attributes, we position the image relatively to the top left corner of the rect element and we offset it by 2px from the top edge (ref-y) and 2px from the left edge (ref-x).
One note: It is important that the type attribute ('basic.DecoratedRect') matches the namespace the shape is defined in (joint.shapes.basic.DecoratedRect). This is because when JointJS re-constructs graphs from JSON, it looks at the type attribute and makes a simple lookup to the joint.shapes namespace to see if there is a shape defined for this type.
We can create an element type for an image using the following recipe:
var image = new joint.shapes.basic.Image({
position : {
x : 100,
y : 100
},
size : {
width : 16,
height : 16
},
attrs : {
image : {
"xlink:href" : "images/myImage.png",
width : 16,
height : 16
}
}
});
graph.addCell(image);
This will position the image at x=100,y=100. It is important to make the size width/height match the attrs/image width/height and be the width/height of the image itself.
Although this doesn't decorate a previous element, it can be positioned over a previous element achieving the desired effect.