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.
Related
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!
I have a custom button that shows a white overlay over the content and background area when pressed. The user of the button can specify how to align the content (label + text) by defining the contentItem.
The current implementation works, but I would like to control the press state inside the button. How can the button display the white overlay on top of the contentItem from within the button?
I prefer not to add a bunch of properties to the button to define the layout of the text/icon. Is there another way to accomplish this?
Working code so far:
ButtonOverlay {
id: btnOverlay
width: 100
height: 50
state: ""
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.state === "pressed"
}
}
onPressed: {
btnOverlay.state = "pressed"
}
onReleased: {
btnOverlay.state = ""
}
}
ButtonOverlay.qml
Button {
id: root
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: root.state === "pressed"
color: "white"
opacity: 0.5
}
}
}
I've removed the overlay in the contentItem and moved the overlay from the background into the Button. Furthermore I've used the z property to change the drawing order.
component ButtonOverlay: Button {
id: btn
background: Rectangle {
radius: 10
color: "gray"
}
Rectangle {
id: overlay
anchors.fill: parent
radius: btn.background.radius
color: "white"
opacity: 0.5
visible: btn.pressed
z: 1
}
}
ButtonOverlay {
width: 100
height: 50
contentItem: Rectangle {
color: "red"
Label { text: "button" }
}
}
Perhaps you could use Frame for your ButtonOverlay. Frame works by automatically sizing to your inner contents but allows you to easily replace the borders via the background property. To use it, you will have to move the entire Button out:
import QtQuick
import QtQuick.Controls
Page {
ButtonOverlay {
id: btnOverlay
pressed: btn.pressed
Button {
id: btn
width: 100
height: 50
implicitWidth: width
implicitHeight: height
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.pressed
}
}
}
}
}
// ButtonOverlay.qml
import QtQuick
import QtQuick.Controls
Frame {
id: root
property bool pressed: false
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: pressed
color: "white"
opacity: 0.5
}
}
}
You can Try it Online!
This code does produce checkboxes in a tableview but when I click on the checkbox it becomes big. I want it to remain of a constant size.
Please guide.
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1
Rectangle
{
id: rightside
anchors.fill: parent
height: parent.height
width: 1500
TableView
{
anchors.fill: parent
TableViewColumn
{
role: "selectall"
title: "Select All"
width: 100
delegate: CheckBox
{
anchors.fill: parent
checked: false
}
}
TableViewColumn {
role: "size"
title: "Size"
width: 100
}
TableViewColumn
{
role: "last_updated"
title: "Last Updated"
width: 100
delegate: Component
{
Rectangle
{
height: 100
width: 120
id: head
RowLayout
{
height: parent.height
width: parent.width
Rectangle
{
height: 20
width: 20
color: "red"
border.color: "black"
radius: 100
MouseArea
{
anchors.fill: parent
onClicked: parent.color = "grey"
}
}
}
}
}
}
model: ListModel
{
id: mymodel
ListElement { text: "Banana" }
ListElement { text: "Apple" }
ListElement { text: "Coconut" }
}
}
}
There are lots of way to solve your problem. But first, let's do proper distinction between Qt Qtuick Controls versions. To do it, use this import statement:
import QtQuick.Controls 1.4 as QC1
And respectively use all components that requires QC1, e.g.: QC1.TableView, QC1.TableViewColumn.
In your example you are getting overlapping of components. To avoid it in terms of QC1 you can define a higher row delegate for your TableView. But this discards the default style. Simple example of its usage with style goes here:
rowDelegate: Rectangle {
height: 30
SystemPalette {
id: myPalette
colorGroup: SystemPalette.Active
}
color: {
var baseColor = styleData.alternate ? myPalette.alternateBase : myPalette.base
return styleData.selected ? myPalette.highlight : baseColor
}
}
As result you'll get this:
Another option in terms of QC2 is to redefine indicator style of CheckBox. Below you'll find an example that could possibly fit your app, based on Customizing CheckBox documentation; so your CheckBox delegate will look like this:
delegate: CheckBox {
id: control
anchors.fill: parent
checked: false
indicator: Rectangle {
id: outer
readonly property int size: 18
implicitWidth: size
implicitHeight: size
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 4
border.color: control.down ? "orangered" : "orange"
Rectangle {
id: inner
anchors.centerIn: parent
width: outer.size/2
height: width
radius: 3
color: control.down ? "orangered" : "orange"
visible: control.checked
}
}
}
As result you'll get this:
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
}
}
How can I change the text color of the menu items of a QML MenuBar?
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3 as QtQuickControlStyle
ApplicationWindow {
title: qsTr("Test")
width: 640
height: 480
visible: true
property color menuBackgroundColor: "#3C3C3C"
property color menuBorderColor: "#282828"
menuBar: MenuBar {
style: QtQuickControlStyle.MenuBarStyle {
padding {
left: 8
right: 8
top: 3
bottom: 3
}
background: Rectangle {
border.color: menuBorderColor
color: menuBackgroundColor
}
// font: // how to set font color to red?
// textColor: "red" /* does not work - results in Cannot assign to non-existent property "textColor" */
TextField { // does also not work
style: TextFieldStyle {
textColor: "red"
}
}
}
}
}
A similar question has been asked here but it seems not to work with menu items.
You have to redefine itemDelegate and itemDelegate.label for menuStyle. The former defines the style of the MenuBar text whereas the latter defines the style of menu items text.
In the following example I defined a full style for MenuBar and Menus, not only for their text. scrollIndicator is the only missing piece here. It can be represented as a Text/Label or an Image.
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ApplicationWindow {
title: qsTr("Test")
width: 640
height: 480
visible: true
property color menuBackgroundColor: "#3C3C3C"
property color menuBorderColor: "#282828"
menuBar: MenuBar {
Menu {
title: "File"
MenuItem { text: "Open..." }
MenuItem { text: "Close" }
}
Menu {
title: "Edit"
MenuItem { text: "Cut"; checkable: true}
MenuItem { text: "Copy" }
MenuItem { text: "Paste" }
MenuSeparator {visible: true }
Menu {
title: "submenu"
}
}
style: MenuBarStyle {
padding {
left: 8
right: 8
top: 3
bottom: 3
}
background: Rectangle {
id: rect
border.color: menuBorderColor
color: menuBackgroundColor
}
itemDelegate: Rectangle { // the menus
implicitWidth: lab.contentWidth * 1.4 // adjust width the way you prefer it
implicitHeight: lab.contentHeight // adjust height the way you prefer it
color: styleData.selected || styleData.open ? "red" : "transparent"
Label {
id: lab
anchors.horizontalCenter: parent.horizontalCenter
color: styleData.selected || styleData.open ? "white" : "red"
font.wordSpacing: 10
text: styleData.text
}
}
menuStyle: MenuStyle { // the menus items
id: goreStyle
frame: Rectangle {
color: menuBackgroundColor
}
itemDelegate {
background: Rectangle {
color: styleData.selected || styleData.open ? "red" : menuBackgroundColor
radius: styleData.selected ? 3 : 0
}
label: Label {
color: styleData.selected ? "white" : "red"
text: styleData.text
}
submenuIndicator: Text {
text: "\u25ba"
font: goreStyle.font
color: styleData.selected || styleData.open ? "white" : "red"
styleColor: Qt.lighter(color, 4)
}
shortcut: Label {
color: styleData.selected ? "white" : "red"
text: styleData.shortcut
}
checkmarkIndicator: CheckBox { // not strinctly a Checkbox. A Rectangle is fine too
checked: styleData.checked
style: CheckBoxStyle {
indicator: Rectangle {
implicitWidth: goreStyle.font.pixelSize
implicitHeight: implicitWidth
radius: 2
color: control.checked ? "red" : menuBackgroundColor
border.color: control.activeFocus ? menuBackgroundColor : "red"
border.width: 2
Rectangle {
visible: control.checked
color: "red"
border.color: menuBackgroundColor
border.width: 2
radius: 2
anchors.fill: parent
}
}
spacing: 10
}
}
}
// scrollIndicator: // <--- could be an image
separator: Rectangle {
width: parent.width
implicitHeight: 2
color: "white"
}
}
}
}
}
And here is the resulting MenuBar and Menus:
You can also choose to set a MenuStyle directly inside a Menu, in the style property. Something like this:
Menu {
title: "File"
MenuItem { text: "Open..." }
MenuItem { text: "Close" }
style: MenuStyle {
itemDelegate.label: Label {
color: "blue"
text: styleData.text
// stuff above here
}
}
In this last example only the "File" Menu items are styled with a blue color for text. One can argue how much ugly that would be, though.