Why does my Custom QML Tooltip have no background? - qt

I have created a custom tooltip style using this guide: https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#creating-a-custom-style
here is ToolTip.qml within my style:
import QtQuick.Templates 2.0 as T
import QtQuick
T.ToolTip {
id: ctrl
contentItem: Text{
color: "red"
text: ctrl.text
}
background: Rectangle {
color: "blue"
border.color: "yellow"
}
}
And here is how I use it:
import QtQuick.Controls 2.15
import QtQml 2.15
import QtQuick.Layouts 2.15
import QtQuick
ApplicationWindow {
id: mainWindow
visible: true
width: 800
height: 600
visibility: Window.Maximized
Button {
anchors.centerIn: parent
id: button
text: "Click me"
onClicked: {
console.log("Clicked")
}
ToolTip.visible: hovered
ToolTip.text: "hello world"
}
}
The text colour works, but the background doesn't.
Why does it not show the blue background rectangle?
I am using Qt6 with PySide6.
*edit:
I have tried using TextMetrics to give the background a width and height. Is this the idiomatic way to do it? It feels like I shouldn't need text metrics. This also leaves the Label uncentered in the background, which looks bad.
import QtQuick.Templates 2.0 as T
import QtQuick
import QtQuick.Controls
T.ToolTip {
id: ctrl
contentItem: Label {
color: "red"
text: ctrl.text
}
background: Rectangle {
id: bg
color: "yellow"
width: tmet.width + 5
height: tmet.height + 5
}
TextMetrics {
id: tmet
font: ctrl.font
text: ctrl.text
}
}

Everytime I need to create custom controls I'll look into the QtQuick Basic style. On my machine I can find it here Qt/6.4.0/gcc_64/qml/QtQuick/Controls/Basic/ToolTip.qml
Templates are non-visual implementations of controls' logic and
behavior.
You need to set a size on the ToolTip. Look how they are setting the implicitWidth and implicitHeight of the ToolTip.
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
T.ToolTip {
id: control
x: parent ? (parent.width - implicitWidth) / 2 : 0
y: -implicitHeight - 3
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
margins: 6
padding: 6
closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
contentItem: Text {
text: control.text
font: control.font
wrapMode: Text.Wrap
color: control.palette.toolTipText
}
background: Rectangle {
border.color: control.palette.dark
color: control.palette.toolTipBase
}
}

To set the toolTip text color, it is not necessary to define a custom contentItem Label component. You can change the tooltip color by setting palette.toolTipText.
I can see your use case is to try to apply a custom ToolTip style so that all your components will inherit.
[EDIT: Change the direction of my answer to a custom Button instead of changing the default ToolTip style]
Alternatively, consider implementing custom components that have a custom ToolTip style. e.g. instead of Button, consider having MyButton. This way, we leave the default Button with the default style.
import QtQuick
import QtQuick.Controls
Page {
MyButton {
anchors.centerIn: parent
id: button
text: "Click me"
onClicked: {
console.log("Clicked")
}
ToolTip.visible: hovered
ToolTip.text: "hello world"
}
}
// MyButton.qml
import QtQuick
import QtQuick.Controls
Button {
ToolTip {
visible: parent.hovered
text: parent.ToolTip.text
palette.toolTipText: "red"
background: Rectangle {
color: "lightsteelblue"
border.color: "yellow"
}
}
}
You can Try it Online!

Related

Change color of SwipeDelegate when pressed

I have a simple Qt Quick application containing a ListView with a few SwipeDelegates.
import QtQuick 2.12
import QtQuick.Controls 2.15
import QtQuick.Window 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ListView {
id: list
anchors.fill: parent
model: ListModel {
id: listView
ListElement { name: "Element 1" }
ListElement { name: "Element 2" }
ListElement { name: "Element 3" }
ListElement { name: "Element 4" }
}
delegate: SwipeDelegate {
contentItem: Text {
width: parent.width
text: name
}
swipe.right: Label {
id: deleteLabel
text: qsTr("Delete")
color: "white"
verticalAlignment: Label.AlignVCenter
padding: 12
height: parent.height
anchors.right: parent.right
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
}
}
}
}
Without setting any background color they show up with a grey background which gets darker if I press (or press and hold) an item:
I want to change that 'highlighted' color to something else, for example green. I was hoping setting the background color based on the onPressed event would help me, but adding background: Rectangle { color: "green" } to the SwipeDelegate as a test removes the darker 'highlight' on pressing entirely.
Is there a way to customize the highlight color?
If it's a style that supports palettes, you can set the relevant palette role. For the Default style ("Basic" in Qt 6), it assigns its background colour like so:
color: Color.blend(control.down ? control.palette.midlight : control.palette.light,
control.palette.highlight, control.visualFocus ? 0.15 : 0.0)
So if you're using that style, you can set that property on the delegate:
palette.midlight: "green"
If the style doesn't support palettes, you have a few options:
Create a Binding to the relevant property.
Assign a colour in Component.onCompleted.
Write your own background.

how to read custom property of parent ListView from external delegate?

I have a ListView and its delegate defined in other qml-file.
main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
ListModel {
id: listModel
ListElement {
name: "Bill Smith"
}
ListElement {
name: "John Brown"
}
ListElement {
name: "Sam Wise"
}
}
ListView {
width: 180; height: 200
property color delegateColor: "green"
model: listModel
delegate: ExternalDelegate {}
}
}
ExternalDelegate.qml:
import QtQuick 2.12
import QtQuick.Controls 2.12
ItemDelegate {
background: Rectangle {
color: ListView.view.delegateColor
}
Text {
text: model.name
}
}
Parent ListView has a custom property delegateColor. I need to read this property from the delegate. But if I try to access it by attached property ListView.view it does not work. And I see messages in console:
qrc:/ExternalDelegate.qml:7: TypeError: Cannot read property 'delegateColor' of null
How to read custom property of ListView from an external delegate?
I need to set this property in ListView (not in delegate) because I also want to access this property from header, footer and section delegates.
In this case it is better that the components do not know what they are used for but rather to expose through properties of the root element, for example an alias of the color of the rectangle can be created, and in the case of the text the ItemDelegate component already has that property.
import QtQuick 2.12
import QtQuick.Controls 2.12
ItemDelegate {
id: root
property alias color: bg.color
background: Rectangle {
id: bg
}
contentItem: Text {
text: root.text
}
}
delegate: ExternalDelegate {
text: model.name
color: ListView.view.delegateColor
}
Another solution is to only change the alias for a new property:
import QtQuick 2.12
import QtQuick.Controls 2.12
ItemDelegate {
id: root
property color color : "white"
background: Rectangle {
color: root.color
}
contentItem: Text {
text: root.text
}
}

Dynamically change Material theme in QML

What is the best way to handle theme changes in QML?
I noticed some controls like Switch And ApplicationWindow do this automatically, but others like Text and Rectangle just don't!
Is it at all possible to avoid having to check which theme is currently set and then each time set the color accordingly? (color: theme.position < 1 ? "black" : "white")
main.qml
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Controls.Material 2.13
//ok: the background color changes automatically when the theme changes
ApplicationWindow {
id: root
visible: true
width: 1366
height: 768
title: qsTr("Theme")
Material.theme: theme.position < 1 ? Material.Light : Material.Dark
//ok: the text color changes automatically when the theme changes
Switch {
id: theme
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 10
text: "Dark theme"
checked: false
}
//not ok: the background is always white
Rectangle {
anchors.centerIn: parent
width: 200
height: width
//not ok: the color is always black
Text {
anchors.centerIn: parent
text: "some text"
font.pixelSize: 40
}
}
}
qtquickcontrols2.conf
[Controls]
Style=Material
[Material]
Theme=Dark
Accent=Orange
Primary=BlueGrey
Text and Rectangle are primitives from Qt Quick, which means that they don't understand Qt Quick Controls' Material style colour propagation. You can use Label and Frame instead:
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Controls.Material 2.13
ApplicationWindow {
id: root
visible: true
width: 1366
height: 768
title: qsTr("Theme")
Material.theme: theme.position < 1 ? Material.Light : Material.Dark
Switch {
id: theme
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 10
text: "Dark theme"
checked: false
}
Frame {
anchors.centerIn: parent
width: 200
height: width
Label {
anchors.centerIn: parent
text: "some text"
font.pixelSize: 40
}
}
}
Note that Frame will consume mouse events, so if you don't want that, you'll need to use e.g. Control, and handle the colours yourself using the Material style's attached properties:
Control {
anchors.centerIn: parent
width: 200
height: width
background: Rectangle {
color: parent.Material.background
border.color: parent.Material.foreground
}
Label {
anchors.centerIn: parent
text: "some text"
font.pixelSize: 40
}
}

TextArea problematic background QML QT

Trying different code combinations and partially solving my problem I came across a behavior that I can not quite explain. So to the point, When I create a simple TextArea without Scrollview it looks like this:
RowLayout {
id: rowLayout
Rectangle{
height: 50
width: 295
TextArea {
id: textArea
text: (" message...")
wrapMode: Text.WrapAnywhere
anchors.fill: parent
}
}
Text area creates a default background. And now I want to do TextArea with ScrollView ALSO with the default TextArea background but it comes out something like that :
RowLayout {
id: rowLayout
Rectangle{
height: 50
width: 295
ScrollView {
id: scrollView1
anchors.fill: parent
TextArea {
id: textArea
text: (" message...")
wrapMode: Text.WrapAnywhere
}
}
}
The only chance to set the default TextArea background is set implicitHeight,implicitWidth but then after entering the text into a TextArea until the scrollbar appears, the background extends over the entire length by going behind the other components like this :
RowLayout {
id: rowLayout
Rectangle{
//color: "#00000000"
height: 50
width: 295
ScrollView {
id: scrollView1
anchors.fill: parent
TextArea {
id: textArea
text: (" message...")
wrapMode: Text.WrapAnywhere
implicitHeight: 50
implicitWidth: 295
}
}
}
So the only thing I want is a scrollable textarea but with this black default background and NOT my background which I can do with rectangle.
Can anyone take a look?
Thank you :)
I tried do my best. Check the example below, hope it will help =)
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 400
height: 400
RowLayout {
width: 295
height: 50
anchors.centerIn: parent
ScrollView {
Layout.fillHeight: true
Layout.fillWidth: true
background: Rectangle { color: "black" }
TextArea {
id: messageField
placeholderText: qsTr("message...")
color: "white"
wrapMode: TextArea.WrapAnywhere
}
}
}
}
Result:

Align and size buttons to the textbox above it

I am using Qt qml to design a very simple user interface. The QML is as follows:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Rectangle {
width: 800; height: 600
ColumnLayout {
anchors.centerIn: parent
spacing: 25
TextField {
placeholderText: qsTr("User name")
width: 200
}
TextField {
placeholderText: qsTr("Password")
width: 200
echoMode: TextInput.Password
}
RowLayout {
Button {
text: "Log In"
}
Button {
text: "Cancel"
}
}
}
}
What I am trying to do is ensure that both the buttons take the same size and they occupy the same amount of space horizontally as the TextField components. Is it possible to achieve this using QML?
Just resize the Layout to the wanted width and use the attached property Layout.fillWidth: true to expand / reduce the size of the Item. Just updated your example to illustrate:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Rectangle {
width: 800; height: 600
ColumnLayout {
width: 200
anchors.centerIn: parent
spacing: 25
TextField {
placeholderText: qsTr("User name")
Layout.fillWidth: true
}
TextField {
placeholderText: qsTr("Password")
Layout.fillWidth: true
echoMode: TextInput.Password
}
RowLayout {
Button {
text: "Log In"
Layout.fillWidth: true
}
Button {
text: "Cancel"
Layout.fillWidth: true
}
}
}
}
PS: It's not needed in a child-layout where Layout.fillWidth and Layout.fillHeight is set by default.
If you have any questions on that feel free to ask...
Yes, you just need to give an id to your TextField
TextField {
id: myTextField
...
}
and reference the width of your TextField in your Button:
Button{
...
width: myTextField.width
}

Resources