I'm currently writing a custom style module for my QML application (using Qt 6.6.1). I don't want to reinvent the wheel and rewrite the whole backend of basic controls, so I'm using the QtQuick Control template method, as decribed here.
My problem is that I want to create a SpinBox with rounded corner, like on the following design :
I achieve to round the corners of the text field by applying a radius to the background property's Rectangle. But I find hard to round the corner of the +
and -
buttons.
My first idea was to have a kind of parent rounded Rectangle
with clip: true
, but I can't do this with the Quick Control Template method. The root Item must be T.SpinBox
, and I cannot set another parent to sub-elements properties like background
, up.indicator
, down.indicator
and contentItem
Here is my current code :
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Templates as T
T.SpinBox {
id: control
implicitWidth: background.implicitWidth
+ up.indicator.width
+ leftPadding
+ rightPadding
implicitHeight: background.implicitHeight
+ topPadding
+ bottomPadding
leftPadding: padding
+ (control.mirrored ?
(up.indicator ?
up.indicator.width
: 0
)
: (down.indicator ?
down.indicator.width
: 0
)
)
rightPadding: padding
+ (control.mirrored ?
(down.indicator ?
down.indicator.width
: 0)
: (up.indicator ?
up.indicator.width
: 0
)
)
wheelEnabled: true
editable: true
font: Theme.fonts.controlText
validator: IntValidator {
locale: control.locale.name
bottom: Math.min(control.from, control.to)
top: Math.max(control.from, control.to)
}
contentItem: TextInput {
id: textField
z: 2
text: control.displayText
clip: width < implicitWidth
font: control.font
color: Theme.colors.textDisabled
selectionColor: control.palette.highlight
selectedTextColor: control.palette.highlightedText
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
anchors.left: parent.left
anchors.right: up.indicator.left
anchors.top: parent.top
anchors.bottom: parent.bottom
readOnly: !control.editable
validator: control.validator
inputMethodHints: control.inputMethodHints
}
up.indicator: Rectangle {
id: upIndicator
implicitWidth: 12
implicitHeight: 9
height: parent.height / 2
anchors.right: parent.mirrored ? undefined : parent.right
anchors.left: parent.mirrored ? parent.left : undefined
anchors.top: parent.top
color: "red"
}
down.indicator: Rectangle {
id: downIndicator
implicitWidth: 12
implicitHeight: 9
height: parent.height / 2
color: "blue"
anchors.right: parent.mirrored ? undefined : parent.right
anchors.left: parent.mirrored ? parent.left : undefined
anchors.bottom: parent.bottom
}
background: Rectangle {
id: backgroundRectangle
implicitWidth: 30
implicitHeight: 18
color: "green"
border.width: 0
radius: 2
}
}
I've also tried to set Items to SpinBox fields properties, from Items instancied elsewhere (in a clipping Rectangle for example, but it didn't cropped the items :
//imports
T.SpinBox {
//... some size management
contentItem: textField
up.indicator: upIndicator
down.indicator: downIndicator
background: backgroundRectangle
Rectangle {
id: clippingRectangle
anchors.fill: parent
radius: 2
clip: true
TextInput {
id: textField
//...
}
Rectangle {
id: upIndicator
//...
}
Rectangle {
id: downIndicator
//...
}
Rectangle {
id: backgroundRectangle
//...
}
}
}
I've also tried to work with Layers and MultiEffect to put a mask on different items, without success again
import QtQuick.Effects
T.SpinBox {
//... Size management + assign different fields as in first exemple
Rectangle {
id: maskRectangle
color: "transparent"
radius: 2
anchors.fill: control
border.width: 1
border.color: Theme.colors.widgetBorder
z: 3
}
layer.enabled: true
MultiEffect { // Mask whole control -> doesn't works !
source: control
anchors.fill: control
maskEnabled: true
maskSource: maskRectangle
}
MultiEffect { // Mask up and down indicator items -> doesn't works !
source: upIndicator
anchors.fill: upIndicator
maskEnabled: true
maskSource: maskRectangle
}
}
I'm out of ideas to achive this design. I would prefer to don't rewrite a full custom spinBox from scratch. I like the way that the custom style works, and can adapt controls depending on application stye. Does anyone have any idea or clues to help me ?
I achieved to do this according to @JarmMan's suggestion.
I preffered use several Rectangles because it's easier to maintain and understand than Shapes.
Here is the resulting code :
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Shapes
import QtQuick.Templates as T
import MyTheme
T.SpinBox {
id: control
implicitWidth: background.implicitWidth + up.indicator.width + leftPadding + rightPadding
implicitHeight: background.implicitHeight + topPadding + bottomPadding
wheelEnabled: true
editable: true
font: Theme.fonts.controlText
clip: true
validator: IntValidator {
locale: control.locale.name
bottom: Math.min(control.from, control.to)
top: Math.max(control.from, control.to)
}
contentItem: TextInput {
id: textField
font: control.font
color: Theme.colors.textDisabled
text: control.displayText
selectionColor: control.palette.highlight
selectedTextColor: control.palette.highlightedText
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
anchors.left: parent.left
anchors.right: upIndicator.left
anchors.top: parent.top
anchors.bottom: parent.bottom
readOnly: !control.editable
validator: control.validator
inputMethodHints: control.inputMethodHints
}
up.indicator: Rectangle {
id: upIndicator
implicitWidth: 12
implicitHeight: 9
height: control.height / 2
anchors.right: control.right
anchors.top: control.top
color: "transparent"
radius: 2
Rectangle {
height: parent.radius
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: parent.color
border.width: 0
}
Rectangle {
width: parent.radius
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
color: parent.color
border.width: 0
}
Image {
source: "qrc:/qt/qml/MyTheme/Images/SpinBox-Up.svg"
sourceSize.height: 3
sourceSize.width: 6
fillMode: Image.PreserveAspectFit
anchors.centerIn: upIndicator
}
}
down.indicator: Rectangle {
id: downIndicator
implicitWidth: 12
implicitHeight: 9
height: control.height / 2
color: "transparent"
radius: 2
anchors.right: control.right
anchors.bottom: control.bottom
Rectangle {
height: parent.radius
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: parent.color
border.width: 0
}
Rectangle {
width: parent.radius
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
color: parent.color
border.width: 0
}
Image {
source: "qrc:/qt/qml/MyTheme/Images/SpinBox-Down.svg"
sourceSize.height: 3
sourceSize.width: 6
fillMode: Image.PreserveAspectFit
anchors.centerIn: downIndicator
}
}
background: Rectangle {
id: backgroundRectangle
implicitWidth: 30
implicitHeight: 18
color: "transparent"
border {
width: 1
color: Theme.colors.widgetBorder
}
radius: 2
Shape {
anchors.fill: parent
ShapePath {
startX: upIndicator.x
startY: 0
PathLine {
relativeX: 0
relativeY: backgroundRectangle.height
}
strokeColor: Theme.colors.widgetBorder
strokeWidth: 1
fillColor: "transparent"
}
ShapePath {
startX: upIndicator.x
startY: control.height / 2
PathLine {
relativeX : upIndicator.width
relativeY: 0
}
strokeColor: Theme.colors.widgetBorder
strokeWidth: 1
fillColor: "transparent"
}
}
}
}