本页内容

自定义Qt Quick Controls

Qt Quick Controls 由一组项目构成的层次结构(树)。为了实现自定义的外观和风格,可以将每个项目的默认 QML 实现替换为自定义实现。

自定义控件

有时,您可能希望为用户界面的特定部分创建“独一无二”的外观,而在其他地方则使用统一的样式。也许您对当前使用的样式很满意,但某个按钮具有特殊的意义。

创建此按钮的第一种方法是直接在需要的位置进行定义。例如,也许你不满意“基本”样式中按钮的方形角。要将其改为圆角,你可以重写background 项,并设置Rectangle的radius属性:

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

    Button {
        id: button
        text: "A Special Button"
        background: Rectangle {
            implicitWidth: 100
            implicitHeight: 40
            color: button.down ? "#d6d6d6" : "#f6f6f6"
            border.color: "#26282a"
            border.width: 1
            radius: 4
        }
    }
}

注意:由于 任何给定样式中构成控件的各个项目都是为了协同工作而设计的,因此可能需要覆盖其他项目才能获得你想要的外观。此外,并非所有样式都可以自定义。有关更多信息,请参阅《自定义参考》中的注释。

如果计划在多个位置使用圆角按钮,第二种创建按钮的方法比较合适。该方法需要将代码移至项目中的独立 QML 文件中。

采用此方法时,我们将从“Basic”样式的Button.qml 文件中复制背景代码。该文件位于 Qt 安装目录下的以下路径中:

$QTDIR/qml/QtQuick/Controls/Basic/Button.qml

完成上述操作后,只需添加以下这一行代码:

radius: 4

为避免与模块本身中的控件混淆,我们将该文件命名为MyButton.qml 。要在您的应用程序中使用该控件,请通过其文件名进行引用:

import QtQuick.Controls.Basic

ApplicationWindow {
    MyButton {
        text: qsTr("A Special Button")
    }
}

创建按钮的第三种方法在结构上更为严谨,无论是在文件系统中的位置,还是在 QML 中的使用方式上都是如此。 首先,像上面那样复制一个现有文件,但这次将其放入项目中的一个子文件夹中,该文件夹命名为(例如)controls 。要使用该控件,首先将该文件夹导入到一个命名空间中:

import QtQuick.Controls.Basic
import "controls" as MyControls

ApplicationWindow {
    MyControls.Button {
        text: qsTr("A Special Button")
    }
}

现在您已经拥有了MyControls 命名空间,可以将控件命名为与Qt Quick Controls 模块中实际对应控件相同的名称。对于任何想要添加的控件,您都可以重复此过程。

这三种方法的另一个好处是,无需从头开始实现模板。

注意: 此处提到的三种方法 不适用于自定义附加的ToolTip ,因为该控件是内部创建的共享项。若要对ToolTip 进行一次性自定义,请参阅Custom Tool Tips 。若要自定义附加的ToolTip ,则必须将其作为您自己的样式的一部分提供。

创建自定义样式

创建自定义样式有多种方法。下面,我们将介绍各种方法。

样式的定义

在Qt Quick Controls 中,样式本质上是一个目录内的一组QML文件。要使样式可被使用,必须满足以下四个条件:

  • 必须存在至少一个名称与控件匹配的 QML 文件(例如,Button.qml )。
  • 每个 QML 文件必须包含从QtQuick.Templates导入的相关类型作为根项。例如,Button.qml 必须包含一个 Button 模板作为其根项。

    如果像上一节那样,改用从QtQuick.Controls导入的对应类型,则无法正常工作:因为我们定义的控件会尝试从自身派生。

  • QML 文件旁边必须存在一个qmldir文件。下面是一个简单的qmldir 文件示例,该文件定义了一种提供按钮的样式:
    module MyStyle
    Button 2.15 Button.qml

    如果您使用编译时样式选择,qmldir 还应导入备用样式:

    # ...
    import QtQuick.Controls.Basic auto

    对于运行时样式选择,也可以采用这种做法,而无需使用例如QQuickStyle::setFallbackStyle() 之类的函数。

    此类样式的目录结构如下所示:

    MyStyle
    ├─── Button.qml
    └─── qmldir
  • 这些文件必须位于可通过QML 导入路径找到的目录中。

    例如,如果上述MyStyle目录的路径为/home/user/MyApp/MyStyle ,则必须将/home/user/MyApp 添加到 QML 导入路径中。

    要在MyApp 中使用 MyStyle,请按名称引用它:

    • ./MyApp -style MyStyle

    样式名称必须与样式目录的大小写完全一致;不支持使用mystyle或MYSTYLE作为引用。

默认情况下,样式系统会将“Basic”样式作为未实现控件的备用样式。若要自定义或扩展任何其他内置样式,可以使用QQuickStyle 指定不同的备用样式。

这意味着您可以为自定义样式实现任意数量的控件,并将其放置在几乎任何位置。它还允许用户为您的应用程序创建自己的样式。

在Qt Quick Designer中预览自定义样式

使用上述方法,可以在Qt Quick 设计器中预览自定义样式。要做到这一点,请确保项目中包含一个qtquickcontrols2.conf文件,并且其中存在以下条目:

[Controls]
Style=MyStyle

如需了解更多信息,请参阅“扁平化样式”示例。

特定于样式的 C++ 扩展

有时您可能需要使用 C++ 来扩展自定义样式。

  • 如果应用程序仅使用包含该类型的样式,请通过添加QML_ELEMENT 宏并将该文件纳入您的QML模块,向QML引擎注册该类型:

    qt_add_qml_module(ACoolItem
        URI MyItems
        VERSION 1.0
        SOURCES
            acoolcppitem.cpp acoolcppitem.h
    )
    CONFIG += qmltypes
    QML_IMPORT_NAME = MyItems
    QML_IMPORT_MAJOR_VERSION = 1

    如果声明该类的头文件无法从项目的包含路径中访问,您可能需要修改包含路径,以便生成的注册代码能够被编译。

    INCLUDEPATH += MyItems

    有关详细信息,请参阅《从 C++ 定义 QML 类型》和《构建 QML 应用程序》。

  • 如果使用该类型的样式是应用程序所用多种样式中的一种,请考虑将每种样式放入一个单独的模块中。这样,这些模块将按需加载。

自定义样式的注意事项

在实现自己的样式和自定义控件时,有几点需要注意,以确保应用程序尽可能高效。

避免为样式中实现的项委托分配 ID

正如《样式的定义》中所述,当您为某个控件实现自定义样式时,通常会基于该控件的相关模板开始。例如,一个样式的 `Button.qml ` 结构通常如下所示:

T.Button {
    // ...

    background: Rectangle {
        // ...
    }

    contentItem: Text {
        // ...
    }

    // ...
}

当您在应用程序中使用 Button 控件时,系统会创建background 和contentItem 这两个项,并将它们作为子项添加到根项Button 之下:

// Creates the Button root item, the Rectangle background,
// and the Text contentItem.
Button {
    text: qsTr("Confirm")
}

假设您随后需要对该 Button 进行一次性的自定义(如《自定义控件》中所述):

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

    Button {
        id: button
        text: "A Special Button"
        background: Rectangle {
            implicitWidth: 100
            implicitHeight: 40
            color: button.down ? "#d6d6d6" : "#f6f6f6"
            border.color: "#26282a"
            border.width: 1
            radius: 4
        }
    }
}

在 QML 中,这通常会导致默认的background 实现和一次性自定义的background 项同时被创建。Qt Quick Controls 采用了一种技术,避免同时创建这两个项,而仅创建自定义的background ,从而大大提高了控件的创建性能。

该技术依赖于样式对该项的实现中不存在ID。如果分配了 ID,该技术将无法生效,两个项都会被创建。例如,人们可能会想给background 或contentItem 分配一个 ID,以便文件中的其他对象可以引用这些项:

T.Button {
    // ...

    background: Rectangle {
        id: backgroundRect
        // ...
    }

    contentItem: Text {
        // Use backgroundRect in some way...
    }

    // ...
}

使用此代码时,每次创建具有自定义背景的 Button 实例,两个背景都会被创建,从而导致创建性能不佳。

在 Qt 5.15 之前,旧的、未使用的背景会被删除以释放与其关联的资源。但是,由于控件并不拥有这些项,因此不应删除它们。 从 Qt 5.15 开始,旧项不再被删除,因此 `backgroundRect ` 项的存活时间会比实际需要的更长——通常会一直存在直到应用程序退出。 尽管旧项会被隐藏、在视觉上脱离控件的父子关系,并从辅助功能树中移除,但在这种情况下为项分配 ID 时,务必注意这些未使用项的创建时间和内存占用情况。

避免对自定义项进行命令式赋值

上文提到的技术仅在首次以声明式方式分配项时有效,因此命令式赋值会导致孤儿项。在可能的情况下,请始终使用声明式绑定来分配自定义项。

在 QML 实现中不要导入 QtQuick.Controls

在编写样式控件实现的 QML 代码时,切勿导入QtQuick.Controls 。否则将导致 QML 编译器无法编译该 QML 文件。

实现被其他类型使用的类型

假设您在应用程序中使用了 ScrollView,并决定对其滚动条进行自定义。您可能会想直接实现一个自定义的ScrollBar.qml,并让ScrollView 自动识别该自定义的ScrollBar 。然而,这种做法并不可行。您必须同时实现ScrollBar.qml和 ScrollView.qml。

关联属性

样式通常会包含某些适用于所有控件的属性或特征。附加属性是扩展 QML 项的绝佳方式,无需修改该项所属的任何现有 C++ 代码。例如,“Material”和“Universal”两种样式都具有一个“theme”附加属性,用于控制项及其子项是在浅色主题还是深色主题下渲染。

举个例子,让我们添加一个控制高度的附加属性。我们的样式将通过投影来体现高度;高度越高,阴影越大。

第一步是在Qt Creator或 Qt Extension for Visual Studio Code 中创建一个新的Qt Quick Controls 应用程序。之后,我们在 Qt Creator或 Qt Extension for Visual Studio Code 中添加一个用于存储海拔值的 C++ 类型。由于该类型将用于我们样式支持的每个控件,并且我们可能希望在以后添加其他附加属性,因此我们将它命名为 MyStyle。以下是MyStyle.h 的内容:

#ifndef MYSTYLE_H
#define MYSTYLE_H

#include <QObject>
#include <QtQml>

class MyStyle : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int elevation READ elevation WRITE setElevation NOTIFY elevationChanged)

public:
    explicit MyStyle(QObject *parent = nullptr);

    static MyStyle *qmlAttachedProperties(QObject *object);

    int elevation() const;
    void setElevation(int elevation);

signals:
    void elevationChanged();

private:
    int m_elevation;
};

QML_DECLARE_TYPEINFO(MyStyle, QML_HAS_ATTACHED_PROPERTIES)

#endif // MYSTYLE_H

MyStyle.cpp:

#include "mystyle.h"

MyStyle::MyStyle(QObject *parent) :
    QObject(parent),
    m_elevation(0)
{
}

MyStyle *MyStyle::qmlAttachedProperties(QObject *object)
{
    return new MyStyle(object);
}

int MyStyle::elevation() const
{
    return m_elevation;
}

void MyStyle::setElevation(int elevation)
{
    if (elevation == m_elevation)
        return;

    m_elevation = elevation;
    emit elevationChanged();
}

MyStyle 类型的特殊之处在于,它不应被实例化,而应仅用于其关联属性。因此,我们以如下方式在main.cpp 中对其进行注册:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "mystyle.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterUncreatableType<MyStyle>("MyStyle", 1, 0, "MyStyle", "MyStyle is an attached property");

    QQmlApplicationEngine engine;
    // Make the directory containing our style known to the QML engine.
    engine.addImportPath(":/");
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));

    return app.exec();
}

随后,我们将$QTDIR/qml/QtQuick/Controls/Basic/ 中 Basic 样式中的Button.qml 复制到项目目录下新建的myproject 文件夹中。将新复制的Button.qml 添加到qml.qrc 中,该资源文件包含我们的 QML 文件。

接下来,我们为 Button 的background 委托添加一个阴影效果:

// ...
import QtQuick.Effects
import MyStyle
// ...

background: Rectangle {
    // ...

    layer.enabled: control.enabled && control.MyStyle.elevation > 0
    layer.effect: MultiEffect {
        shadowEnabled: true
        shadowHorizontalOffset: 3
        shadowVerticalOffset: 3
        shadowColor: control.visualFocus ? "#330066ff" : "#aaaaaa"
        shadowBlur: control.pressed ? 0.8 : 0.4
    }
}

请注意:

  • 当 Button 的 elevation 为0
  • 根据按钮是否获得焦点来改变阴影的颜色
  • 让阴影的大小取决于凸起效果

为了测试该附加属性,我们在main.qml 中创建了一个包含两个Button的Row :

import QtQuick
import QtQuick.Controls

import MyStyle 1.0

ApplicationWindow {
    id: window
    width: 400
    height: 400
    visible: true

    Row {
        spacing: 20
        anchors.centerIn: parent

        Button {
            text: "Button 1"
        }
        Button {
            text: "Button 2"
            MyStyle.elevation: 10
        }
    }
}

其中一个按钮没有高度,另一个按钮的高度为10 。

配置完成后,我们可以运行示例。为了让应用程序使用新样式,我们将-style MyStyle 作为应用程序参数传递,但指定要使用的样式有多种方式。

最终结果:

自定义样式的按钮

请注意,import MyStyle 1.0 语句仅因我们使用了属于MyStyle 的附加属性才必需。即使移除该导入语句,两个按钮仍将使用我们的自定义样式。

自定义参考

以下代码片段展示了如何采用与“自定义控件”一节中相同的方法,对“基本样式”中的控件进行自定义。这些代码可作为实现自定义外观和感觉的起点。

注意: macOS和Windows样式 不适合用于自定义。相反,建议始终基于一种在所有平台上都可用的单一样式来构建自定义控件,例如Basic Style、Fusion Style、Imagine Style、Material Style 或Universal Style。 这样做可以确保无论应用程序使用哪种样式运行,其外观始终保持一致。要了解如何使用其他样式,请参阅《Qt Quick Controls 》中的“使用样式”部分。此外,您还可以创建自己的样式。

注意:修改 控件大小时, 实际内容大小通常保持不变。例如,某些样式可能会让按钮填满控件的边界,但会将CheckBox 的复选标记和文本在水平和/或垂直方向上居中。如果不需要默认的调整大小行为,请重写相应的委托。

自定义 ApplicationWindow

ApplicationWindow 由一个视觉项组成:background 。

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    visible: true

    background: Rectangle {
        gradient: Gradient {
            GradientStop { position: 0; color: "#ffffff" }
            GradientStop { position: 1; color: "#c1bbf9" }
        }
    }
}

自定义 BusyIndicator

BusyIndicator 包含两个视觉项:background 和contentItem 。

自定义样式的忙线指示器

import QtQuick
import QtQuick.Controls.Basic

BusyIndicator {
    id: control

    contentItem: Item {
        implicitWidth: 64
        implicitHeight: 64

        Item {
            id: item
            x: parent.width / 2 - 32
            y: parent.height / 2 - 32
            width: 64
            height: 64
            opacity: control.running ? 1 : 0

            Behavior on opacity {
                OpacityAnimator {
                    duration: 250
                }
            }

            RotationAnimator {
                target: item
                running: control.visible && control.running
                from: 0
                to: 360
                loops: Animation.Infinite
                duration: 1250
            }

            Repeater {
                id: repeater
                model: 6

                Rectangle {
                    id: delegate
                    x: item.width / 2 - width / 2
                    y: item.height / 2 - height / 2
                    implicitWidth: 10
                    implicitHeight: 10
                    radius: 5
                    color: "#21be2b"

                    required property int index

                    transform: [
                        Translate {
                            y: -Math.min(item.width, item.height) * 0.5 + 5
                        },
                        Rotation {
                            angle: delegate.index / repeater.count * 360
                            origin.x: 5
                            origin.y: 5
                        }
                    ]
                }
            }
        }
    }
}

自定义按钮

Button 包含两个视觉项:background 和contentItem 。

自定义样式按钮

import QtQuick
import QtQuick.Controls.Basic

Button {
    id: control
    text: qsTr("Button")

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        elide: Text.ElideRight
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 40
        opacity: enabled ? 1 : 0.3
        border.color: control.down ? "#17a81a" : "#21be2b"
        border.width: 1
        radius: 2
    }
}

自定义复选框

CheckBox 由三个视觉控件组成:background 、contentItem 和indicator 。

自定义样式的复选框

import QtQuick
import QtQuick.Controls.Basic

CheckBox {
    id: control
    text: qsTr("CheckBox")
    checked: true

    indicator: Rectangle {
        implicitWidth: 26
        implicitHeight: 26
        x: control.leftPadding
        y: parent.height / 2 - height / 2
        radius: 3
        border.color: control.down ? "#17a81a" : "#21be2b"

        Rectangle {
            width: 14
            height: 14
            x: 6
            y: 6
            radius: 2
            color: control.down ? "#17a81a" : "#21be2b"
            visible: control.checked
        }
    }

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        verticalAlignment: Text.AlignVCenter
        leftPadding: control.indicator.width + control.spacing
    }
}

自定义 CheckDelegate

CheckDelegate 由三个可视化控件组成:background 、contentItem 和indicator 。

自定义样式的检查委托

import QtQuick
import QtQuick.Controls.Basic

CheckDelegate {
    id: control
    text: qsTr("CheckDelegate")
    checked: true

    contentItem: Text {
        rightPadding: control.indicator.width + control.spacing
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        elide: Text.ElideRight
        verticalAlignment: Text.AlignVCenter
    }

    indicator: Rectangle {
        implicitWidth: 26
        implicitHeight: 26
        x: control.width - width - control.rightPadding
        y: control.topPadding + control.availableHeight / 2 - height / 2
        radius: 3
        color: "transparent"
        border.color: control.down ? "#17a81a" : "#21be2b"

        Rectangle {
            width: 14
            height: 14
            x: 6
            y: 6
            radius: 2
            color: control.down ? "#17a81a" : "#21be2b"
            visible: control.checked
        }
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 40
        visible: control.down || control.highlighted
        color: control.down ? "#bdbebf" : "#eeeeee"
    }
}

自定义 ComboBox

ComboBox 包括background 、contentItem 、popup 、indicator 以及delegate 。

自定义样式的下拉列表框

pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Controls.Basic

ComboBox {
    id: control
    model: ["First", "Second", "Third"]

    delegate: ItemDelegate {
        id: delegate

        required property var model
        required property int index

        width: control.width
        contentItem: Text {
            text: delegate.model[control.textRole]
            color: "#21be2b"
            font: control.font
            elide: Text.ElideRight
            verticalAlignment: Text.AlignVCenter
        }
        highlighted: control.highlightedIndex === index
    }

    indicator: Canvas {
        id: canvas
        x: control.width - width - control.rightPadding
        y: control.topPadding + (control.availableHeight - height) / 2
        width: 12
        height: 8
        contextType: "2d"

        Connections {
            target: control
            function onPressedChanged() { canvas.requestPaint(); }
        }

        onPaint: {
            context.reset();
            context.moveTo(0, 0);
            context.lineTo(width, 0);
            context.lineTo(width / 2, height);
            context.closePath();
            context.fillStyle = control.pressed ? "#17a81a" : "#21be2b";
            context.fill();
        }
    }

    contentItem: Text {
        leftPadding: 0
        rightPadding: control.indicator.width + control.spacing

        text: control.displayText
        font: control.font
        color: control.pressed ? "#17a81a" : "#21be2b"
        verticalAlignment: Text.AlignVCenter
        elide: Text.ElideRight
    }

    background: Rectangle {
        implicitWidth: 120
        implicitHeight: 40
        border.color: control.pressed ? "#17a81a" : "#21be2b"
        border.width: control.visualFocus ? 2 : 1
        radius: 2
    }

    popup: Popup {
        y: control.height - 1
        width: control.width
        height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin)
        padding: 1

        contentItem: ListView {
            clip: true
            implicitHeight: contentHeight
            model: control.popup.visible ? control.delegateModel : null
            currentIndex: control.highlightedIndex

            ScrollIndicator.vertical: ScrollIndicator { }
        }

        background: Rectangle {
            border.color: "#21be2b"
            radius: 2
        }
    }
}

如ComboBox Model Roles 中所述,ComboBox 支持多种类型的模型。

由于所有模型都提供了一个名为modelData的匿名属性,因此以下表达式在所有情况下都能检索到正确的文本:

text: model[control.textRole]

当您提供特定的textRole ,且模型包含提供所选角色的结构化数据时,该表达式即为常规属性查找。当您提供包含单一生成的数据(如字符串列表)的模型,且textRole 为空时,该表达式将检索modelData 。

自定义 DelayButton

DelayButton 由两个视觉项组成:background 和contentItem 。

自定义样式的延迟按钮

import QtQuick
import QtQuick.Controls.Basic

DelayButton {
    id: control
    checked: true
    text: qsTr("Delay\nButton")

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: "white"
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        elide: Text.ElideRight
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 100
        opacity: enabled ? 1 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        radius: size / 2

        readonly property real size: Math.min(control.width, control.height)
        width: size
        height: size
        anchors.centerIn: parent

        Canvas {
            id: canvas
            anchors.fill: parent

            Connections {
                target: control
                function onProgressChanged() { canvas.requestPaint(); }
            }

            onPaint: {
                var ctx = getContext("2d")
                ctx.clearRect(0, 0, width, height)
                ctx.strokeStyle = "white"
                ctx.lineWidth = parent.size / 20
                ctx.beginPath()
                var startAngle = Math.PI / 5 * 3
                var endAngle = startAngle + control.progress * Math.PI / 5 * 9
                ctx.arc(width / 2, height / 2, width / 2 - ctx.lineWidth / 2 - 2, startAngle, endAngle)
                ctx.stroke()
            }
        }
    }
}

自定义“Dial”

“旋钮”由两个可视项组成:background 和handle 。

定制风格表盘

import QtQuick
import QtQuick.Controls.Basic

Dial {
    id: control
    background: Rectangle {
        x: control.width / 2 - width / 2
        y: control.height / 2 - height / 2
        implicitWidth: 140
        implicitHeight: 140
        width: Math.max(64, Math.min(control.width, control.height))
        height: width
        color: "transparent"
        radius: width / 2
        border.color: control.pressed ? "#17a81a" : "#21be2b"
        opacity: control.enabled ? 1 : 0.3
    }

    handle: Rectangle {
        id: handleItem
        x: control.background.x + control.background.width / 2 - width / 2
        y: control.background.y + control.background.height / 2 - height / 2
        width: 16
        height: 16
        color: control.pressed ? "#17a81a" : "#21be2b"
        radius: 8
        antialiasing: true
        opacity: control.enabled ? 1 : 0.3
        transform: [
            Translate {
                y: -Math.min(control.background.width, control.background.height) * 0.4 + handleItem.height / 2
            },
            Rotation {
                angle: control.angle
                origin.x: handleItem.width / 2
                origin.y: handleItem.height / 2
            }
        ]
    }
}

自定义 DoubleSpinBox

DoubleSpinBox 其自定义方式与按钮相同。

自定义抽屉

Drawer 可以包含一个名为background 的可视化控件。

background: Rectangle {
    Rectangle {
        x: parent.width - 1
        width: 1
        height: parent.height
        color: "#21be2b"
    }
}

自定义 Frame

Frame 由一个可视化项组成:background 。

自定义样式的框架

import QtQuick
import QtQuick.Controls.Basic

Frame {
    background: Rectangle {
        color: "transparent"
        border.color: "#21be2b"
        radius: 2
    }

    Label {
        text: qsTr("Content goes here!")
    }
}

自定义 GroupBox

GroupBox 由两个可视化项组成:background 和label 。

自定义样式的组框

import QtQuick
import QtQuick.Controls.Basic

GroupBox {
    id: control
    title: qsTr("GroupBox")

    background: Rectangle {
        y: control.topPadding - control.bottomPadding
        width: parent.width
        height: parent.height - control.topPadding + control.bottomPadding
        color: "transparent"
        border.color: "#21be2b"
        radius: 2
    }

    label: Label {
        x: control.leftPadding
        width: control.availableWidth
        text: control.title
        color: "#21be2b"
        elide: Text.ElideRight
    }

    Label {
        text: qsTr("Content goes here!")
    }
}

自定义 ItemDelegate

ItemDelegate 包含两个可视化项:background 和contentItem 。

自定义样式项委托

import QtQuick
import QtQuick.Controls.Basic

ItemDelegate {
    id: control
    text: qsTr("ItemDelegate")

    contentItem: Text {
        rightPadding: control.spacing
        text: control.text
        font: control.font
        color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf"
        elide: Text.ElideRight
        verticalAlignment: Text.AlignVCenter
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 40
        opacity: enabled ? 1 : 0.3
        color: control.down ? "#dddedf" : "#eeeeee"

        Rectangle {
            width: parent.width
            height: 1
            color: control.down ? "#17a81a" : "#21be2b"
            anchors.bottom: parent.bottom
        }
    }
}

自定义标签

标签可以包含一个可视化background 项。

自定义样式标签

import QtQuick
import QtQuick.Controls.Basic

Label {
    text: qsTr("Label")
    color: "#21be2b"
}

自定义菜单

自定义样式菜单

import QtQuick
import QtQuick.Controls.Basic

Menu {
    id: menu

    Action { text: qsTr("Tool Bar"); checkable: true }
    Action { text: qsTr("Side Bar"); checkable: true; checked: true }
    Action { text: qsTr("Status Bar"); checkable: true; checked: true }

    MenuSeparator {
        contentItem: Rectangle {
            implicitWidth: 200
            implicitHeight: 1
            color: "#21be2b"
        }
    }

    Menu {
        title: qsTr("Advanced")
        // ...
    }

    topPadding: 2
    bottomPadding: 2

    delegate: MenuItem {
        id: menuItem
        implicitWidth: 200
        implicitHeight: 40

        arrow: Canvas {
            x: parent.width - width
            implicitWidth: 40
            implicitHeight: 40
            visible: menuItem.subMenu
            onPaint: {
                var ctx = getContext("2d")
                ctx.fillStyle = menuItem.highlighted ? "#ffffff" : "#21be2b"
                ctx.moveTo(15, 15)
                ctx.lineTo(width - 15, height / 2)
                ctx.lineTo(15, height - 15)
                ctx.closePath()
                ctx.fill()
            }
        }

        indicator: Item {
            implicitWidth: 40
            implicitHeight: 40
            Rectangle {
                width: 26
                height: 26
                anchors.centerIn: parent
                visible: menuItem.checkable
                border.color: "#21be2b"
                radius: 3
                Rectangle {
                    width: 14
                    height: 14
                    anchors.centerIn: parent
                    visible: menuItem.checked
                    color: "#21be2b"
                    radius: 2
                }
            }
        }

        contentItem: Text {
            leftPadding: menuItem.indicator.width
            rightPadding: menuItem.arrow.width
            text: menuItem.text
            font: menuItem.font
            opacity: enabled ? 1.0 : 0.3
            color: menuItem.highlighted ? "#ffffff" : "#21be2b"
            horizontalAlignment: Text.AlignLeft
            verticalAlignment: Text.AlignVCenter
            elide: Text.ElideRight
        }

        background: Rectangle {
            implicitWidth: 200
            implicitHeight: 40
            opacity: enabled ? 1 : 0.3
            color: menuItem.highlighted ? "#21be2b" : "transparent"
        }
    }

    background: Rectangle {
        implicitWidth: 200
        implicitHeight: 40
        color: "#ffffff"
        border.color: "#21be2b"
        radius: 2
    }
}

自定义 MenuBar

MenuBar 可以包含一个可视化项background ,而MenuBarItem 由两个可视化项组成:background 和contentItem 。

自定义样式的菜单栏,包含“文件”和“编辑”菜单

import QtQuick
import QtQuick.Controls.Basic

MenuBar {
    id: menuBar

    Menu { title: qsTr("File") }
    Menu { title: qsTr("Edit") }
    Menu { title: qsTr("View") }
    Menu { title: qsTr("Help") }

    delegate: MenuBarItem {
        id: menuBarItem

        contentItem: Text {
            text: menuBarItem.text
            font: menuBarItem.font
            opacity: enabled ? 1.0 : 0.3
            color: menuBarItem.highlighted ? "#ffffff" : "#21be2b"
            horizontalAlignment: Text.AlignLeft
            verticalAlignment: Text.AlignVCenter
            elide: Text.ElideRight
        }

        background: Rectangle {
            implicitWidth: 40
            implicitHeight: 40
            opacity: enabled ? 1 : 0.3
            color: menuBarItem.highlighted ? "#21be2b" : "transparent"
        }
    }

    background: Rectangle {
        implicitWidth: 40
        implicitHeight: 40
        color: "#ffffff"

        Rectangle {
            color: "#21be2b"
            width: parent.width
            height: 1
            anchors.bottom: parent.bottom
        }
    }
}

自定义 PageIndicator

PageIndicator 由background 、contentItem 和delegate 组成。

自定义样式的页面指示器,用于显示多页内容

import QtQuick
import QtQuick.Controls.Basic

PageIndicator {
    id: control
    count: 5
    currentIndex: 2

    delegate: Rectangle {
        implicitWidth: 8
        implicitHeight: 8

        radius: width / 2
        color: "#21be2b"

        opacity: index === control.currentIndex ? 0.95 : pressed ? 0.7 : 0.45

        required property int index

        Behavior on opacity {
            OpacityAnimator {
                duration: 100
            }
        }
    }
}

自定义 Pane

Pane 由background 组成。

带有装饰性背景的自定义样式窗格

import QtQuick
import QtQuick.Controls.Basic

Pane {
    background: Rectangle {
        color: "#eeeeee"
    }

    Label {
        text: qsTr("Content goes here!")
    }
}

自定义弹出窗口

弹出窗口由background 和contentItem 组成。

带有边框和阴影的自定义样式弹出窗口

import QtQuick
import QtQuick.Controls.Basic

Popup {
    id: popup
    background: Rectangle {
        implicitWidth: 200
        implicitHeight: 200
        border.color: "#444"
    }
    contentItem: Column {}
}

自定义进度条

ProgressBar 由两个视觉控件组成:background 和contentItem 。

自定义样式的进度条,显示部分完成状态

import QtQuick
import QtQuick.Controls.Basic

ProgressBar {
    id: control
    value: 0.5
    padding: 2

    background: Rectangle {
        implicitWidth: 200
        implicitHeight: 6
        color: "#e6e6e6"
        radius: 3
    }

    contentItem: Item {
        implicitWidth: 200
        implicitHeight: 4

        // Progress indicator for determinate state.
        Rectangle {
            width: control.visualPosition * parent.width
            height: parent.height
            radius: 2
            color: "#17a81a"
            visible: !control.indeterminate
        }

        // Scrolling animation for indeterminate state.
        Item {
            anchors.fill: parent
            visible: control.indeterminate
            clip: true

            Row {
                spacing: 20

                Repeater {
                    model: control.width / 40 + 1

                    Rectangle {
                        color: "#17a81a"
                        width: 20
                        height: control.height
                    }
                }
                XAnimator on x {
                    from: 0
                    to: -40
                    loops: Animation.Infinite
                    running: control.indeterminate
                }
            }
        }
    }
}

如上所示,contentItem 也会进行动画效果,以呈现indeterminate 的进度条状态。

自定义单选按钮

RadioButton 由三个视觉控件组成:background 、contentItem 和indicator 。

选中状态下的自定义样式单选按钮

import QtQuick
import QtQuick.Controls.Basic

RadioButton {
    id: control
    text: qsTr("RadioButton")
    checked: true

    indicator: Rectangle {
        implicitWidth: 26
        implicitHeight: 26
        x: control.leftPadding
        y: parent.height / 2 - height / 2
        radius: 13
        border.color: control.down ? "#17a81a" : "#21be2b"

        Rectangle {
            width: 14
            height: 14
            x: 6
            y: 6
            radius: 7
            color: control.down ? "#17a81a" : "#21be2b"
            visible: control.checked
        }
    }

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        verticalAlignment: Text.AlignVCenter
        leftPadding: control.indicator.width + control.spacing
    }
}

自定义 RadioDelegate

RadioDelegate 由三个视觉项组成:background 、contentItem 和indicator 。

列表中自定义样式的单选按钮委托

import QtQuick
import QtQuick.Controls.Basic

RadioDelegate {
    id: control
    text: qsTr("RadioDelegate")
    checked: true

    contentItem: Text {
        rightPadding: control.indicator.width + control.spacing
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        elide: Text.ElideRight
        verticalAlignment: Text.AlignVCenter
    }

    indicator: Rectangle {
        implicitWidth: 26
        implicitHeight: 26
        x: control.width - width - control.rightPadding
        y: parent.height / 2 - height / 2
        radius: 13
        color: "transparent"
        border.color: control.down ? "#17a81a" : "#21be2b"

        Rectangle {
            width: 14
            height: 14
            x: 6
            y: 6
            radius: 7
            color: control.down ? "#17a81a" : "#21be2b"
            visible: control.checked
        }
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 40
        visible: control.down || control.highlighted
        color: control.down ? "#bdbebf" : "#eeeeee"
    }
}

自定义 RangeSlider

RangeSlider 由三个可视化组件组成:background 、first.handle 和second.handle 。

自定义样式的范围滑块

import QtQuick
import QtQuick.Controls.Basic

RangeSlider {
    id: control
    first.value: 0.25
    second.value: 0.75

    background: Rectangle {
        x: control.leftPadding
        y: control.topPadding + control.availableHeight / 2 - height / 2
        implicitWidth: 200
        implicitHeight: 4
        width: control.availableWidth
        height: implicitHeight
        radius: 2
        color: "#bdbebf"

        Rectangle {
            x: control.first.visualPosition * parent.width
            width: control.second.visualPosition * parent.width - x
            height: parent.height
            color: "#21be2b"
            radius: 2
        }
    }

    first.handle: Rectangle {
        x: control.leftPadding + control.first.visualPosition * (control.availableWidth - width)
        y: control.topPadding + control.availableHeight / 2 - height / 2
        implicitWidth: 26
        implicitHeight: 26
        radius: 13
        color: control.first.pressed ? "#f0f0f0" : "#f6f6f6"
        border.color: "#bdbebf"
    }

    second.handle: Rectangle {
        x: control.leftPadding + control.second.visualPosition * (control.availableWidth - width)
        y: control.topPadding + control.availableHeight / 2 - height / 2
        implicitWidth: 26
        implicitHeight: 26
        radius: 13
        color: control.second.pressed ? "#f0f0f0" : "#f6f6f6"
        border.color: "#bdbebf"
    }
}

自定义 RoundButton

RoundButton 其自定义方式与Button 相同。

自定义 ScrollBar

ScrollBar 由两个可视化控件组成:background 和contentItem 。

自定义样式的滚动条

import QtQuick
import QtQuick.Controls.Basic

ScrollBar {
    id: control
    size: 0.3
    position: 0.2
    active: true
    orientation: Qt.Vertical

    contentItem: Rectangle {
        implicitWidth: 6
        implicitHeight: 100
        radius: width / 2
        color: control.pressed ? "#81e889" : "#c2f4c6"
        // Hide the ScrollBar when it's not needed.
        opacity: control.policy === ScrollBar.AlwaysOn || (control.active && control.size < 1.0) ? 0.75 : 0

        // Animate the changes in opacity (default duration is 250 ms).
        Behavior on opacity {
            NumberAnimation {}
        }
    }
}

自定义 ScrollIndicator

ScrollIndicator 由两个视觉项组成:background 和contentItem 。

自定义样式的滚动条指示器

import QtQuick
import QtQuick.Controls.Basic

ScrollIndicator {
    id: control
    size: 0.3
    position: 0.2
    active: true
    orientation: Qt.Vertical

    contentItem: Rectangle {
        implicitWidth: 2
        implicitHeight: 100
        color: "#c2f4c6"
    }
}

自定义 ScrollView

ScrollView 由一个background 控件以及水平和垂直滚动条组成。

自定义样式的滚动视图

ScrollView {
    id: control

    width: 200
    height: 200
    focus: true

    Label {
        text: "ABC"
        font.pixelSize: 224
    }

    ScrollBar.vertical: ScrollBar {
        parent: control
        x: control.mirrored ? 0 : control.width - width
        y: control.topPadding
        height: control.availableHeight
        active: control.ScrollBar.horizontal.active
    }

    ScrollBar.horizontal: ScrollBar {
        parent: control
        x: control.leftPadding
        y: control.height - height
        width: control.availableWidth
        active: control.ScrollBar.vertical.active
    }

    background: Rectangle {
        border.color: control.activeFocus ? "#21be2b" : "#bdbebf"
    }
}

自定义 Slider

Slider 由两个可视化控件组成:background 和handle 。

自定义样式滑块

import QtQuick
import QtQuick.Controls.Basic

Slider {
    id: control
    value: 0.5

    background: Rectangle {
        x: control.leftPadding
        y: control.topPadding + control.availableHeight / 2 - height / 2
        implicitWidth: 200
        implicitHeight: 4
        width: control.availableWidth
        height: implicitHeight
        radius: 2
        color: "#bdbebf"

        Rectangle {
            width: control.visualPosition * parent.width
            height: parent.height
            color: "#21be2b"
            radius: 2
        }
    }

    handle: Rectangle {
        x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
        y: control.topPadding + control.availableHeight / 2 - height / 2
        implicitWidth: 26
        implicitHeight: 26
        radius: 13
        color: control.pressed ? "#f0f0f0" : "#f6f6f6"
        border.color: "#bdbebf"
    }
}

自定义 SpinBox

SpinBox 由四个可视化控件组成:background 、contentItem 、up indicator 和down indicator 。

自定义样式的下拉框

import QtQuick
import QtQuick.Controls.Basic

SpinBox {
    id: control
    value: 50
    editable: true

    contentItem: TextInput {
        z: 2
        text: control.textFromValue(control.value, control.locale)

        font: control.font
        color: "#21be2b"
        selectionColor: "#21be2b"
        selectedTextColor: "#ffffff"
        horizontalAlignment: Qt.AlignHCenter
        verticalAlignment: Qt.AlignVCenter

        readOnly: !control.editable
        validator: control.validator
        inputMethodHints: Qt.ImhFormattedNumbersOnly
    }

    up.indicator: Rectangle {
        x: control.mirrored ? 0 : parent.width - width
        height: parent.height
        implicitWidth: 40
        implicitHeight: 40
        color: control.up.pressed ? "#e4e4e4" : "#f6f6f6"
        border.color: enabled ? "#21be2b" : "#bdbebf"

        Text {
            text: "+"
            font.pixelSize: control.font.pixelSize * 2
            color: "#21be2b"
            anchors.fill: parent
            fontSizeMode: Text.Fit
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
    }

    down.indicator: Rectangle {
        x: control.mirrored ? parent.width - width : 0
        height: parent.height
        implicitWidth: 40
        implicitHeight: 40
        color: control.down.pressed ? "#e4e4e4" : "#f6f6f6"
        border.color: enabled ? "#21be2b" : "#bdbebf"

        Text {
            text: "-"
            font.pixelSize: control.font.pixelSize * 2
            color: "#21be2b"
            anchors.fill: parent
            fontSizeMode: Text.Fit
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
    }

    background: Rectangle {
        implicitWidth: 140
        border.color: "#bdbebf"
    }
}

自定义 SplitView

SplitView 包含一个视觉handle 委托。

自定义样式的分屏视图

SplitView {
    id: splitView
    anchors.fill: parent

    handle: Rectangle {
        implicitWidth: 4
        implicitHeight: 4
        color: SplitHandle.pressed ? "#81e889"
            : (SplitHandle.hovered ? Qt.lighter("#c2f4c6", 1.1) : "#c2f4c6")
    }

    Rectangle {
        implicitWidth: 150
        color: "#444"
    }
    Rectangle {
        implicitWidth: 50
        color: "#666"
    }
}

自定义 StackView

StackView 可以包含一个可视化background 项,并允许自定义用于“推送”、“弹出”和“替换”操作的过渡效果。

import QtQuick
import QtQuick.Controls.Basic

StackView {
    id: control

    popEnter: Transition {
        XAnimator {
            from: (control.mirrored ? -1 : 1) * -control.width
            to: 0
            duration: 400
            easing.type: Easing.OutCubic
        }
    }

    popExit: Transition {
        XAnimator {
            from: 0
            to: (control.mirrored ? -1 : 1) * control.width
            duration: 400
            easing.type: Easing.OutCubic
        }
    }
}

自定义 SwipeDelegate

SwipeDelegate 由六个可视化项组成:background 、contentItem 、indicator 、swipe.left 、swipe.right 以及swipe.behind 。

自定义样式的滑动委托

import QtQuick
import QtQuick.Controls.Basic

SwipeDelegate {
    id: control
    text: qsTr("SwipeDelegate")

    Component {
        id: component

        Rectangle {
            color: SwipeDelegate.pressed ? "#333" : "#444"
            width: parent.width
            height: parent.height
            clip: true

            Label {
                text: qsTr("Press me!")
                color: "#21be2b"
                anchors.centerIn: parent
            }
        }
    }

    swipe.left: component
    swipe.right: component

    contentItem: Text {
        text: control.text
        font: control.font
        color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf"
        elide: Text.ElideRight
        verticalAlignment: Text.AlignVCenter

        Behavior on x {
            enabled: !control.down
            NumberAnimation {
                easing.type: Easing.InOutCubic
                duration: 400
            }
        }
    }
}

自定义 SwipeView

SwipeView 可以包含一个可视化background 控件。导航功能由contentItem 实现。

import QtQuick
import QtQuick.Controls.Basic

SwipeView {
    id: control

    background: Rectangle {
        color: "#eeeeee"
    }
}

自定义 Switch

Switch 由三个视觉控件组成:background 、contentItem 和indicator 。

自定义样式开关

import QtQuick
import QtQuick.Controls.Basic

Switch {
    id: control
    text: qsTr("Switch")

    indicator: Rectangle {
        implicitWidth: 48
        implicitHeight: 26
        x: control.leftPadding
        y: parent.height / 2 - height / 2
        radius: 13
        color: control.checked ? "#17a81a" : "#ffffff"
        border.color: control.checked ? "#17a81a" : "#cccccc"

        Rectangle {
            x: control.checked ? parent.width - width : 0
            width: 26
            height: 26
            radius: 13
            color: control.down ? "#cccccc" : "#ffffff"
            border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999"
        }
    }

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        verticalAlignment: Text.AlignVCenter
        leftPadding: control.indicator.width + control.spacing
    }
}

自定义 SwitchDelegate

SwitchDelegate 包含三个可视化控件:background 、contentItem 和indicator 。

自定义样式的开关委托

import QtQuick
import QtQuick.Controls.Basic

SwitchDelegate {
    id: control
    text: qsTr("SwitchDelegate")
    checked: true

    contentItem: Text {
        rightPadding: control.indicator.width + control.spacing
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        elide: Text.ElideRight
        verticalAlignment: Text.AlignVCenter
    }

    indicator: Rectangle {
        implicitWidth: 48
        implicitHeight: 26
        x: control.width - width - control.rightPadding
        y: parent.height / 2 - height / 2
        radius: 13
        color: control.checked ? "#17a81a" : "transparent"
        border.color: control.checked ? "#17a81a" : "#cccccc"

        Rectangle {
            x: control.checked ? parent.width - width : 0
            width: 26
            height: 26
            radius: 13
            color: control.down ? "#cccccc" : "#ffffff"
            border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999"
        }
    }

    background: Rectangle {
        implicitWidth: 100
        implicitHeight: 40
        visible: control.down || control.highlighted
        color: control.down ? "#bdbebf" : "#eeeeee"
    }
}

自定义 TabBar

TabBar 包含两个视觉控件:background 和contentItem 。

自定义样式的标签栏

import QtQuick
import QtQuick.Controls.Basic

TabBar {
    id: control

    background: Rectangle {
        color: "#eeeeee"
    }

    TabButton {
        text: qsTr("Home")
    }
    TabButton {
        text: qsTr("Discover")
    }
    TabButton {
        text: qsTr("Activity")
    }
}

自定义 TabButton

TabButton 其自定义方式与Button 相同。

自定义 TextArea

TextArea 由一个background 控件组成。

自定义样式的文本区域

import QtQuick
import QtQuick.Controls.Basic

TextArea {
    id: control
    placeholderText: qsTr("Enter description")

    background: Rectangle {
        implicitWidth: 200
        implicitHeight: 40
        border.color: control.enabled ? "#21be2b" : "transparent"
    }
}

自定义 TextField

TextField 由一个background 控件组成。

自定义样式的文本输入框

import QtQuick
import QtQuick.Controls.Basic

TextField {
    id: control
    placeholderText: qsTr("Enter description")

    background: Rectangle {
        implicitWidth: 200
        implicitHeight: 40
        color: control.enabled ? "transparent" : "#353637"
        border.color: control.enabled ? "#21be2b" : "transparent"
    }
}

自定义 ToolBar

ToolBar 包含一个可视化项:background 。

自定义样式工具栏

ToolBar {
    id: control

    background: Rectangle {
        implicitHeight: 40
        color: "#eeeeee"

        Rectangle {
            width: parent.width
            height: 1
            anchors.bottom: parent.bottom
            color: "transparent"
            border.color: "#21be2b"
        }
    }

    RowLayout {
        anchors.fill: parent
        ToolButton {
            text: qsTr("Undo")
        }
        ToolButton {
            text: qsTr("Redo")
        }
    }
}

自定义工具按钮

ToolButton 包含两个可视化项:background 和contentItem 。

自定义样式的工具按钮

import QtQuick
import QtQuick.Controls.Basic

ToolButton {
    id: control
    text: qsTr("ToolButton")
    width: 120

    contentItem: Text {
        text: control.text
        font: control.font
        opacity: enabled ? 1.0 : 0.3
        color: control.down ? "#17a81a" : "#21be2b"
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        elide: Text.ElideRight
    }

    background: Rectangle {
        implicitWidth: 40
        implicitHeight: 40
        color: Qt.darker("#33333333", control.enabled && (control.checked || control.highlighted) ? 1.5 : 1.0)
        opacity: enabled ? 1 : 0.3
        visible: control.down || (control.enabled && (control.checked || control.highlighted))
    }
}

自定义工具栏分隔符

ToolSeparator 由两个可视化项组成:background 和contentItem 。

自定义样式的工具分隔符

ToolBar {
    RowLayout {
        anchors.fill: parent

        ToolButton {
            text: qsTr("Action 1")
        }
        ToolButton {
            text: qsTr("Action 2")
        }

        ToolSeparator {
            padding: vertical ? 10 : 2
            topPadding: vertical ? 2 : 10
            bottomPadding: vertical ? 2 : 10

            contentItem: Rectangle {
                implicitWidth: parent.vertical ? 1 : 24
                implicitHeight: parent.vertical ? 24 : 1
                color: "#c3c3c3"
            }
        }

        ToolButton {
            text: qsTr("Action 3")
        }
        ToolButton {
            text: qsTr("Action 4")
        }

        Item {
            Layout.fillWidth: true
        }
    }
}

自定义工具提示

ToolTip 由两个视觉项组成:background 和contentItem 。

import QtQuick
import QtQuick.Controls.Basic

ToolTip {
    id: control
    text: qsTr("A descriptive tool tip of what the button does")

    contentItem: Text {
        text: control.text
        font: control.font
        color: "#21be2b"
    }

    background: Rectangle {
        border.color: "#21be2b"
    }
}

注意:若要 自定义attached ToolTip ,必须将其作为您自定义样式的一部分提供。若要对ToolTip 进行一次性自定义,请参阅Custom Tool Tips 。

自定义 Tumbler

Tumbler 由三个视觉元素组成:background 、contentItem 和delegate 。

定制款马克杯

import QtQuick
import QtQuick.Controls.Basic

Tumbler {
    id: control
    model: 15

    background: Item {
        Rectangle {
            opacity: control.enabled ? 0.2 : 0.1
            border.color: "#000000"
            width: parent.width
            height: 1
            anchors.top: parent.top
        }

        Rectangle {
            opacity: control.enabled ? 0.2 : 0.1
            border.color: "#000000"
            width: parent.width
            height: 1
            anchors.bottom: parent.bottom
        }
    }

    delegate: Text {
        text: qsTr("Item %1").arg(modelData + 1)
        font: control.font
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)

        required property var modelData
        required property int index
    }

    Rectangle {
        anchors.horizontalCenter: control.horizontalCenter
        y: control.height * 0.4
        width: 40
        height: 1
        color: "#21be2b"
    }

    Rectangle {
        anchors.horizontalCenter: control.horizontalCenter
        y: control.height * 0.6
        width: 40
        height: 1
        color: "#21be2b"
    }
}

若要定义自己的 contentItem,请使用ListView 或PathView 作为根项。对于包裹式 Tumbler,请使用PathView :

Tumbler {
    id: tumbler

    contentItem: PathView {
        id: pathView
        model: tumbler.model
        delegate: tumbler.delegate
        clip: true
        pathItemCount: tumbler.visibleItemCount + 1
        preferredHighlightBegin: 0.5
        preferredHighlightEnd: 0.5
        dragMargin: width / 2

        path: Path {
            startX: pathView.width / 2
            startY: -pathView.delegateHeight / 2
            PathLine {
                x: pathView.width / 2
                y: pathView.pathItemCount * pathView.delegateHeight - pathView.delegateHeight / 2
            }
        }

        property real delegateHeight: tumbler.availableHeight / tumbler.visibleItemCount
    }
}

对于不自动换行的 Tumbler,请使用ListView :

Tumbler {
    id: tumbler

    contentItem: ListView {
        model: tumbler.model
        delegate: tumbler.delegate

        snapMode: ListView.SnapToItem
        highlightRangeMode: ListView.StrictlyEnforceRange
        preferredHighlightBegin: height / 2 - (height / tumbler.visibleItemCount / 2)
        preferredHighlightEnd: height / 2 + (height / tumbler.visibleItemCount / 2)
        clip: true
    }
}

自定义 TableViewDelegate

TableViewDelegate 继承自ItemDelegate ,这意味着它由两个视觉项组成:background 和contentItem 。

如果您的需求超出了默认编辑委托所提供的功能范围,您可以随时将自定义的编辑委托分配给editDelegate 。

自定义样式的表格视图委托

delegate: TableViewDelegate {
    id: tableCell

    checked: column === 0 ? checkBox.checked : tableView.itemAtIndex(tableView.index(row, 0)).checked
    selected: checked

    background: Item {
        Rectangle {
            anchors.fill: parent
            anchors.margins: tableCell.current ? 3 : 1
            color: tableCell.selected ? "blue" : "white"
        }

        Rectangle {
            anchors.fill: parent
            color: "transparent"
            border.color: "darkblue"
            border.width: tableCell.current ? 2 : 0
        }
    }

    contentItem: Item {
        implicitHeight: 40
        visible: !tableCell.editing

        RowLayout {
            anchors.fill: parent

            CheckBox {
                id: checkBox
                implicitWidth: height
                Layout.fillHeight: true
                checked: false
                visible: tableCell.column === 0
            }

            Text {
                Layout.leftMargin: 4
                Layout.fillWidth: true
                Layout.fillHeight: true
                verticalAlignment: Text.AlignVCenter
                color: tableCell.selected ? "white" : "black"
                text: tableCell.model.display
            }
        }
    }

    TableView.editDelegate: FocusScope {
        width: parent.width
        height: parent.height

        TableView.onCommit: {
            let qaim = tableCell.tableView.model
            if (!qaim)
                return
            const index = qaim.index(tableCell.row, tableCell.column)
            // instead of the edit role, any custom role supported by the model can be checked
            // e.g. if (!tableCell.checked || !tableCell.model.customRole)
            if (!tableCell.checked || !tableCell.model.edit)
                return
            // instead of the edit role, any custom role supported by the model can be set
            // e.g. tableCell.model.customRole = textField.text
            tableCell.model.edit = textField.text
            tableCell.model.display = textField.text
        }

        Component.onCompleted: textField.selectAll()

        TextField {
            id: textField
            anchors.fill: parent
            text: tableCell.model.edit ?? tableCell.model.display ?? ""
            focus: true
        }
    }
}

自定义 HeaderViewDelegate

HeaderViewDelegate 继承自TableViewDelegate ,这意味着它由两个视图项组成:background 和contentItem 。您可以随时使用任意视图项对它们进行自定义。

自定义样式的页眉视图委托

以下是一个自定义水平标题视图委托的示例:

delegate: HorizontalHeaderViewDelegate {
    id: horizontalDelegate

    required property int index
    required property string modelData

    background: Rectangle {
        height: horizontalDelegate.height
        color: columnCheckBox.checked ? palette.highlight : palette.base
        radius: 8
    }

    contentItem: Item {
        implicitWidth: columnCheckBox.implicitWidth * 2
        implicitHeight: 40

        CheckBox {
            id: columnCheckBox
            anchors.centerIn: parent
            text: horizontalDelegate.modelData
            Component.onCompleted: checked = horizontalDelegate.index === 1
        }
    }
}

以下是自定义垂直标题视图委托的示例:

delegate: VerticalHeaderViewDelegate {
    id: verticalDelegate

    required property int index

    background: Rectangle {
        height: verticalDelegate.height
        color: palette.base
        border.width: rowCheckBox.checked ? 2 : 0
        border.color: palette.highlight
        radius: 8
    }

    contentItem: Item {
        implicitWidth: rowCheckBox.implicitWidth * 2
        implicitHeight: 40

        CheckBox {
            id: rowCheckBox
            anchors.centerIn: parent
            text: verticalDelegate.index + 1
            Component.onCompleted: checked = verticalDelegate.index % 3 === 0
        }
    }
}

使用 StyleKit 设置控件样式

Qt Labs的StyleKit模块提供了一组用于为Qt Quick Controls 设置样式的QML类型,该模块基于Qt Quick Templates 2构建。它允许您通过单个Style对象为所有控件定义完整的视觉样式,包括对主题、基于状态的样式设置以及过渡效果的支持。 StyleKit 会自动处理底层的模板实现,让您能够完全专注于颜色、尺寸、边框和阴影等视觉方面。

Qt Labs的StyleKit模块是 Qt 6.11 中的技术预览模块。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.