Sur cette page

Personnalisation Qt Quick Controls

Qt Quick Controls sont organisés selon une hiérarchie (arborescence) d’éléments. Afin de personnaliser l’apparence et le comportement, l’implémentation QML par défaut de chaque élément peut être remplacée par une implémentation personnalisée.

Personnalisation d'un contrôle

Il vous arrivera parfois de vouloir créer un style « unique » pour une partie spécifique de votre interface utilisateur, tout en conservant un style identique partout ailleurs. Peut-être êtes-vous satisfait du style que vous utilisez, mais un bouton particulier revêt une importance particulière.

La première façon de créer ce bouton consiste simplement à le définir directement à l’endroit où vous en avez besoin. Par exemple, vous n’êtes peut-être pas satisfait des coins carrés du bouton du style « Basic ». Pour les arrondir, vous pouvez redéfinir l’élément « background » et définir la propriété « radius » du rectangle :

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
        }
    }
}

Remarque : les différents éléments qui composent un contrôle dans un style donnéétant conçus pour fonctionner ensemble, il peut s’avérer nécessaire de redéfinir d’autres éléments pour obtenir l’apparence souhaitée. De plus, tous les styles ne peuvent pas être personnalisés. Consultez la remarque dans la Référence de personnalisation pour plus d’informations.

La deuxième méthode de création du bouton est recommandée si vous prévoyez d’utiliser votre bouton aux coins arrondis à plusieurs endroits. Elle consiste à déplacer le code dans son propre fichier QML au sein de votre projet.

Pour cette approche, nous allons copier le code d'arrière-plan à partir du fichier ` Button.qml` du style « Basic ». Ce fichier se trouve à l'emplacement suivant dans votre installation Qt :

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

Une fois cela fait, il suffit d'ajouter la ligne suivante :

radius: 4

Pour éviter toute confusion avec les contrôles du module lui-même, nous nommerons le fichier « MyButton.qml ». Pour utiliser ce contrôle dans votre application, faites-y référence par son nom de fichier :

import QtQuick.Controls.Basic

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

La troisième méthode pour créer le bouton est un peu plus structurée, tant au niveau de l’emplacement du fichier dans le système de fichiers que de son utilisation dans QML. Commencez par copier un fichier existant comme vous l'avez fait ci-dessus, mais cette fois-ci, placez-le dans un sous-dossier de votre projet nommé (par exemple) controls. Pour utiliser le contrôle, importez d'abord le dossier dans un espace de noms :

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

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

Comme vous disposez désormais de l'espace de noms MyControls, vous pouvez nommer les contrôles d'après leurs équivalents réels dans le module Qt Quick Controls. Vous pouvez répéter ce processus pour tout contrôle que vous souhaitez ajouter.

L'un des avantages supplémentaires de ces trois méthodes est qu'il n'est pas nécessaire d'implémenter le modèle à partir de zéro.

Remarque : les trois approches mentionnées ici ne fonctionnent pas pour personnaliser l’ ToolTip joint, car il s’agit d’un élément partagé créé en interne. Pour effectuer une personnalisation ponctuelle d’un ToolTip, consultez Custom Tool Tips. Pour personnaliser l’ ToolTip joint, celui-ci doit être fourni dans le cadre de votre propre style.

Création d’un style personnalisé

Il existe plusieurs façons de créer vos propres styles. Nous vous expliquons ci-dessous les différentes approches.

Définition d'un style

Dans Qt Quick Controls, un style est essentiellement un ensemble de fichiers QML situés dans un même répertoire. Pour qu’un style soit utilisable, il doit répondre à quatre conditions :

  • Il doit exister au moins un fichier QML dont le nom correspond à un contrôle (par exemple, Button.qml).
  • Chaque fichier QML doit contenir, en tant qu’élément racine, le type correspondant issu de l’importation QtQuick.Templates. Par exemple, le fichier Button.qml doit contenir un modèle Button comme élément racine.

    Si nous utilisions à la place le type correspondant issu de l’importation QtQuick.Controls, comme nous l’avons fait dans la section précédente, cela ne fonctionnerait pas : le contrôle que nous définissons tenterait de dériver de lui-même.

  • Un fichier qmldir doit exister aux côtés du ou des fichiers QML. Voici un exemple de fichier qmldir simple pour un style fournissant un bouton :
    module MyStyle
    Button 2.15 Button.qml

    Si vous utilisez la sélection de style à la compilation, le fichier `qmldir` doit également importer le style de secours :

    # ...
    import QtQuick.Controls.Basic auto

    Cela peut également être fait pour la sélection de style à l'exécution au lieu d'utiliser, par exemple, ` QQuickStyle::setFallbackStyle()`.

    La structure de répertoires pour un tel style se présente comme suit :

    MyStyle
    ├─── Button.qml
    └─── qmldir
  • Les fichiers doivent se trouver dans un répertoire accessible via le chemin d'importation QML.

    Par exemple, si le chemin d'accès au répertoire MyStyle mentionné ci-dessus est /home/user/MyApp/MyStyle, alors /home/user/MyApp doit être ajouté au chemin d'importation QML.

    Pour utiliser MyStyle dans MyApp, faites-y référence par son nom :

    • ./MyApp -style MyStyle

    Le nom du style doit respecter la casse du répertoire du style ; l'utilisation de « mystyle » ou « MYSTYLE » n'est pas prise en charge.

Par défaut, le système de styles utilise le style « Basic » comme style de secours pour les contrôles qui ne sont pas implémentés. Pour personnaliser ou étendre tout autre style intégré, il est possible de spécifier un style de secours différent à l’aide de ` QQuickStyle`.

Cela signifie que vous pouvez implémenter autant de contrôles que vous le souhaitez pour votre style personnalisé, et les placer pratiquement n'importe où. Cela permet également aux utilisateurs de créer leurs propres styles pour votre application.

Aperçu des styles personnalisés dans le concepteur d’ Qt Quick

En suivant la méthode décrite ci-dessus, il est possible de prévisualiser un style personnalisé dans le concepteur d'Qt Quick. Pour ce faire, assurez-vous que le projet contient un fichier qtquickcontrols2.conf et que l'entrée suivante y figure :

[Controls]
Style=MyStyle

Pour plus d’informations, consultez l’ exemple « Flat Style ».

Extensions C++ spécifiques aux styles

Il peut parfois s'avérer nécessaire d'utiliser le C++ pour étendre votre style personnalisé.

  • Si le style utilisant ce type est le seul utilisé par une application, enregistrez ce type auprès du moteur QML en ajoutant la macro ` QML_ELEMENT ` et en intégrant le fichier à votre module 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

    Si l'en-tête dans lequel la classe est déclarée n'est pas accessible depuis le chemin d'inclusion de votre projet, vous devrez peut-être modifier ce chemin afin que le code d'enregistrement généré puisse être compilé.

    INCLUDEPATH += MyItems

    Pour plus d’informations, consultez les sections « Définition de types QML depuis C++ » et « Création d’une application QML ».

  • Si le style utilisant ce type fait partie des nombreux styles utilisés par une application, envisagez de placer chaque style dans un module distinct. Les modules seront alors chargés à la demande.

Considérations relatives aux styles personnalisés

Lorsque vous implémentez votre propre style et personnalisez des contrôles, certains points doivent être pris en compte pour garantir que votre application soit aussi performante que possible.

Évitez d'attribuer un identifiant aux implémentations des délégués d'élément dans les styles

Comme expliqué dans la section « Définition d'un style », lorsque vous implémentez votre propre style pour un contrôle, vous partez du modèle correspondant à ce contrôle. Par exemple, l'Button.qml e d'un style sera structurée de la manière suivante :

T.Button {
    // ...

    background: Rectangle {
        // ...
    }

    contentItem: Text {
        // ...
    }

    // ...
}

Lorsque vous utilisez un bouton (Button) dans votre application, les éléments « background » et « contentItem » seront créés et rattachés à l’élément racine « Button » :

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

Supposons que vous deviez ensuite effectuer une personnalisation ponctuelle du bouton (comme expliqué dans la section « Personnalisation d’un contrôle ») :

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
        }
    }
}

En QML, cela entraînerait normalement la création à la fois de l’implémentation par défaut background et de l’élément personnalisé ponctuel background. Qt Quick Controls utilise une technique qui évite de créer ces deux éléments et ne crée à la place que l’élément personnalisé background, ce qui améliore considérablement les performances de création des contrôles.

Cette technique repose sur l’absence d’identifiant (id) dans l’implémentation du style de cet élément. Si un identifiant est attribué, la technique ne peut pas fonctionner et les deux éléments seront créés. Par exemple, il peut être tentant d’attribuer un identifiant à background ou à contentItem afin que d’autres objets du fichier puissent faire référence à ces éléments :

T.Button {
    // ...

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

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

    // ...
}

Avec ce code, chaque fois qu’une instance de Button avec un arrière-plan personnalisé est créée, les deux arrière-plans seront créés, ce qui se traduira par des performances de création sous-optimales.

Avant Qt 5.15, l’ancien arrière-plan inutilisé était supprimé afin de libérer les ressources qui lui étaient associées. Cependant, comme le contrôle n’est pas propriétaire de ces éléments, il ne doit pas les supprimer. À partir de Qt 5.15, les anciens éléments ne sont plus supprimés ; l’élément ` backgroundRect ` subsiste donc plus longtemps que nécessaire — généralement jusqu’à la fermeture de l’application. Bien que l’ancien élément soit masqué, visuellement dissocié du contrôle et supprimé de l’arborescence d’accessibilité, il est important de garder à l’esprit le temps de création et l’utilisation de mémoire de ces éléments inutilisés lors de l’attribution d’un identifiant dans ce contexte.

Évitez les affectations impératives d’éléments personnalisés

La technique mentionnée dans la section ci-dessus ne fonctionne que lorsqu’un élément est attribué de manière déclarative pour la première fois ; les attributions impératives entraîneront donc la création d’éléments orphelins. Utilisez toujours des liaisons déclaratives pour attribuer des éléments personnalisés lorsque cela est possible.

N'importez pas QtQuick.Controls dans les implémentations QML

Lorsque vous écrivez le code QML pour l'implémentation d'un contrôle dans votre style, il est important de ne pas importer QtQuick.Controls. Cela empêcherait le code QML d'être compilé par le compilateur QML.

Implémentez les types utilisés par d'autres types

Supposons que vous utilisiez des ScrollViews dans votre application et que vous décidiez de personnaliser leurs barres de défilement. Il serait tentant de simplement implémenter un fichier personnalisé ScrollBar.qml et de laisser ScrollView récupérer automatiquement le fichier ScrollBar personnalisé. Cependant, cela ne fonctionnera pas. Vous devez implémenter à la fois ScrollBar.qml et ScrollView.qml.

Propriétés associées

Il est courant qu’un style dispose de certaines propriétés ou d’attributs qui s’appliquent à tous les contrôles. Les propriétés associées constituent un excellent moyen d’étendre un élément en QML sans avoir à modifier le code C++ existant associé à cet élément. Par exemple, les styles « Material » et « Universal » disposent tous deux d’une propriété « theme » associée qui détermine si un élément et ses enfants seront affichés avec un thème clair ou sombre.

À titre d’exemple, ajoutons une propriété attachée qui contrôle l’élévation. Notre style illustrera l’élévation à l’aide d’une ombre portée ; plus l’élévation est élevée, plus l’ombre est grande.

La première étape consiste à créer une nouvelle application « Qt Quick Controls » sur Qt Creator ou sur Qt Extension for Visual Studio Code. Ensuite, nous ajoutons un type C++ (sur Qt Creator ou sur Qt Extension for Visual Studio Code) qui stocke l’élévation. Étant donné que ce type sera utilisé pour tous les contrôles pris en charge par notre style, et que nous pourrions souhaiter ajouter d’autres propriétés attachées ultérieurement, nous l’appellerons «MyStyle». Voici le fichier « 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();
}

Le type ` MyStyle ` est particulier dans la mesure où il ne doit pas être instancié, mais plutôt utilisé pour ses propriétés associées. C'est pourquoi nous l'enregistrons de la manière suivante dans ` 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();
}

Nous copions ensuite ` Button.qml ` depuis le style « Basic » situé dans ` $QTDIR/qml/QtQuick/Controls/Basic/ ` vers un nouveau dossier ` myproject ` dans le répertoire de notre projet. Ajoutez le fichier ` Button.qml ` ainsi copié à ` qml.qrc`, qui est le fichier de ressources contenant nos fichiers QML.

Ensuite, nous ajoutons une ombre portée au délégué ` background ` du bouton :

// ...
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
    }
}

Notez que nous :

  • N'utilisons pas l'ombre portée lorsque l'élévation est 0
  • Modifions la couleur de l'ombre selon que le bouton a ou non le focus
  • Faisons en sorte que la taille de l'ombre dépende de l'élévation

Pour tester cette propriété associée, nous créons une classe « Row » contenant deux boutons dans « main.qml » :

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
        }
    }
}

L'un des boutons n'a pas de relief, tandis que l'autre présente un relief de 10.

Une fois cela en place, nous pouvons exécuter notre exemple. Pour indiquer à l’application d’utiliser notre nouveau style, nous passons -style MyStyle en tant qu’argument d’application, mais il existe de nombreuses façons de spécifier le style à utiliser.

Résultat final :

Boutons personnalisés

Notez que l'instruction import MyStyle 1.0 n'est nécessaire que parce que nous utilisons la propriété attachée appartenant à MyStyle. Les deux boutons utiliseront notre style personnalisé, même si nous supprimions l'importation.

Référence de personnalisation

Les extraits de code suivants présentent des exemples dans lesquels les contrôles du style « Basic » ont été personnalisés en utilisant la même approche que celle décrite dans la section « Personnalisation d’un contrôle ». Ce code peut servir de point de départ pour mettre en œuvre une apparence personnalisée.

Remarque : les styles macOS et Windows ne se prêtent pas à la personnalisation. Il est plutôt recommandé de toujours baser un contrôle personnalisé sur un style unique disponible sur toutes les plateformes, par exemple le style « Basic », « Fusion », « Imagine », « Material » ou « Universal ». Vous aurez ainsi la garantie que l’apparence restera toujours la même, quel que soit le style utilisé pour exécuter l’application. Pour savoir comment utiliser un style différent, consultez la section « Utilisation des styles » dans Qt Quick Controls. Vous pouvez également créer votre propre style.

Remarque : lorsque vous modifiez la taille des contrôles, la taille réelle du contenu reste généralement inchangée. Par exemple, certains styles peuvent faire en sorte que leur bouton occupe toute la surface du contrôle, tout en centrant horizontalement et/ou verticalement l’indicateur de sélection et le texte d’un CheckBox. Si le comportement par défaut en matière de redimensionnement ne vous convient pas, redéfinissez le ou les délégués concernés.

Personnalisation de ApplicationWindow

ApplicationWindow se compose d’un seul élément visuel : background.

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    visible: true

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

Personnalisation de BusyIndicator

BusyIndicator se compose de deux éléments visuels : background et contentItem.

Indicateur d'activité personnalisé

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
                        }
                    ]
                }
            }
        }
    }
}

Personnalisation du bouton

Le bouton se compose de deux éléments visuels : background et contentItem.

Bouton personnalisé

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
    }
}

Personnalisation de la case à cocher

CheckBox se compose de trois éléments visuels : background, contentItem et indicator.

Case à cocher personnalisée

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 {
            largeur: 14
            hauteur: 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
    }
}

Personnalisation de CheckDelegate

CheckDelegate se compose de trois éléments visuels : background, contentItem et indicator.

Délégué de vérification au style personnalisé

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"
    }
}

Personnalisation de ComboBox

ComboBox se compose de background, contentItem, popup, indicator et delegate.

Liste déroulante personnalisée

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
        }
    }
}

Comme expliqué dans ComboBox Model Roles, ComboBox prend en charge plusieurs types de modèles.

Étant donné que tous les modèles fournissent une propriété anonyme avec modelData, l'expression suivante permet d'extraire le texte approprié dans tous les cas :

text: model[control.textRole]

Lorsque vous fournissez un textRole spécifique et un modèle contenant des données structurées qui fournissent le rôle sélectionné, cette expression correspond à une recherche de propriété classique. Lorsque vous fournissez un modèle contenant des données au singulier, telles qu’une liste de chaînes de caractères, et un textRole vide, cette expression récupère l’ modelData.

Personnalisation de DelayButton

DelayButton se compose de deux éléments visuels : background et contentItem.

Bouton de délai personnalisé

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()
            }
        }
    }
}

Personnalisation de Dial

Le cadran se compose de deux éléments visuels : background et handle.

Cadran au design personnalisé

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
            }
        ]
    }
}

Personnalisation de la DoubleSpinBox

DoubleSpinBox peut être personnalisé de la même manière que le bouton.

Personnalisation de Drawer

Le tiroir peut comporter un élément d'background.

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

Personnalisation du cadre

Le cadre se compose d'un élément visuel : « background ».

Cadre au style personnalisé

import QtQuick
import QtQuick.Controls.Basic

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

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

Personnalisation de GroupBox

GroupBox se compose de deux éléments visuels : background et label.

Boîte de groupe personnalisée

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!")
    }
}

Personnalisation de ItemDelegate

ItemDelegate comprend deux éléments visuels : background et contentItem.

Délégué d'élément au style personnalisé

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
        }
    }
}

Personnalisation de Label

Label peut disposer d'un élément visuel de type « background ».

Étiquette personnalisée

import QtQuick
import QtQuick.Controls.Basic

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

Personnalisation du menu

Menu personnalisé

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
    }
}

Personnalisation de MenuBar

MenuBar peut comporter un élément d’ background, et MenuBarItem se compose de deux éléments d’ : background et contentItem.

Barre de menus personnalisée avec les menus « Fichier » et « Édition »

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
        }
    }
}

Personnalisation de PageIndicator

PageIndicator comprend un « background », un « contentItem » et un « delegate ».

Indicateur de page personnalisé affichant plusieurs pages

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
            }
        }
    }
}

Personnalisation du volet

Le volet se compose d’un élément background.

Panneau personnalisé avec un arrière-plan décoratif

import QtQuick
import QtQuick.Controls.Basic

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

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

Personnalisation de la fenêtre contextuelle

La fenêtre contextuelle se compose d’un élément « background » et d’un élément « contentItem ».

Fenêtre contextuelle personnalisée avec bordure et ombre

import QtQuick
import QtQuick.Controls.Basic

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

Personnalisation de la barre de progression

ProgressBar Elle se compose de deux éléments visuels : un « background » et un « contentItem ».

Barre de progression personnalisée indiquant le niveau d'avancement partiel

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
                }
            }
        }
    }
}

Ci-dessus, le « contentItem » est également animé pour représenter l'état d'une barre de progression « indeterminate ».

Personnalisation de RadioButton

RadioButton comprend trois éléments visuels : background, contentItem et indicator.

Bouton radio personnalisé à l'état « sélectionné »

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
    }
}

Personnalisation de RadioDelegate

RadioDelegate comprend trois éléments visuels : background, contentItem et indicator.

Délégué radio personnalisé dans une liste

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"
    }
}

Personnalisation de RangeSlider

RangeSlider comprend trois éléments visuels : background, first.handle et second.handle.

Curseur de plage personnalisé

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"
    }
}

La personnalisation de RoundButton

RoundButton peut être personnalisé de la même manière que Button.

Personnalisation de ScrollBar

ScrollBar comprend deux éléments visuels : background et contentItem.

Barre de défilement personnalisée

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 {}
        }
    }
}

Personnalisation de ScrollIndicator

ScrollIndicator comprend deux éléments visuels : background et contentItem.

Indicateur de défilement personnalisé

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"
    }
}

La personnalisation de ScrollView

ScrollView comprend un élément « background », ainsi que des barres de défilement horizontales et verticales.

Vue défilante personnalisée

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"
    }
}

Personnalisation de Slider

Le Slider se compose de deux éléments visuels : background et handle.

Curseur personnalisé

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"
    }
}

Personnalisation de la SpinBox

SpinBox se compose de quatre éléments visuels : background, contentItem, up indicator et down indicator.

Boîte de sélection personnalisée

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"
    }
}

La personnalisation de SplitView

SplitView consiste en un délégué visuel handle.

Affichage en deux volets personnalisé

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"
    }
}

La personnalisation de StackView

StackView peut comporter un élément d’ background visuelle et permet de personnaliser les transitions utilisées pour les opérations « push », « pop » et « replace ».

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
        }
    }
}

Personnalisation de SwipeDelegate

SwipeDelegate comprend six éléments visuels : background, contentItem, indicator, swipe.left, swipe.right et swipe.behind.

Délégué de balayage personnalisé

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
            }
        }
    }
}

Personnalisation de SwipeView

SwipeView peut comporter un élément d'background. La navigation est mise en œuvre par contentItem.

import QtQuick
import QtQuick.Controls.Basic

SwipeView {
    id: control

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

Personnalisation de Switch

Switch se compose de trois éléments visuels : background, contentItem et indicator.

Interrupteur au design personnalisé

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
    }
}

Personnalisation de SwitchDelegate

SwitchDelegate se compose de trois éléments visuels : background, contentItem et indicator.

Délégué de commutateur personnalisé

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"
    }
}

Personnalisation de TabBar

TabBar comprend deux éléments visuels : background et contentItem.

Barre d'onglets personnalisée

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")
    }
}

La personnalisation de TabButton

TabButton peut être personnalisé de la même manière que Button.

Personnalisation de TextArea

TextArea Elle se compose d'un élément « background ».

Zone de texte avec mise en forme personnalisée

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"
    }
}

Personnalisation de TextField

TextField consiste en un élément « background ».

Champ de texte avec mise en forme personnalisée

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"
    }
}

Personnalisation de la barre d'outils

ToolBar comprend un élément visuel : background.

Barre d'outils personnalisée

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")
        }
    }
}

Personnalisation de ToolButton

ToolButton comprend deux éléments visuels : « background » et « contentItem ».

Bouton d'outil personnalisé

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))
    }
}

Personnalisation de ToolSeparator

ToolSeparator comprend deux éléments visuels : background et contentItem.

Séparateur d'outils au style personnalisé

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
        }
    }
}

Personnalisation de ToolTip

ToolTip comprend deux éléments visuels : background et 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"
    }
}

Remarque : pour personnaliser l’ attached ToolTip, celui-ci doit être fourni dans le cadre de votre propre style. Pour effectuer une personnalisation ponctuelle d’un ToolTip, consultez Custom Tool Tips.

Personnalisation de Tumbler

Tumbler se compose de trois éléments visuels : background, contentItem et delegate.

Gobelet personnalisé

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"
    }
}

Si vous souhaitez définir votre propre `contentItem`, utilisez soit un ` ListView `, soit un ` PathView ` comme élément racine. Pour un Tumbler avec retour à la ligne, utilisez ` 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
    }
}

Pour un Tumbler sans mise en page, utilisez 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
    }
}

Personnalisation de TableViewDelegate

TableViewDelegate hérite de ItemDelegate, ce qui signifie qu’il est composé de deux éléments visuels : background et contentItem.

Vous pouvez à tout moment attribuer votre propre délégué d'édition personnalisé à ` editDelegate ` si vos besoins vont au-delà de ce qu'offre le délégué d'édition par défaut.

Délégué de vue de tableau avec mise en forme personnalisée

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
        }
    }
}

Personnalisation de HeaderViewDelegate

HeaderViewDelegate hérite de TableViewDelegate, ce qui signifie qu’il est composé de deux éléments : background et contentItem. Vous pouvez à tout moment les personnaliser avec n’importe quel élément de votre choix.

Délégué de vue d'en-tête personnalisé

Voici un exemple de personnalisation du délégué de vue d'en-tête horizontal :

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
        }
    }
}

Voici un exemple de personnalisation du délégué de vue d’en-tête vertical :

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
        }
    }
}

Personnalisation des contrôles à l’aide de StyleKit

Le module StyleKit de Qt Labs fournit un ensemble de types QML permettant de styliser Qt Quick Controls, s’appuyant sur Qt Quick Templates 2. Il vous permet de définir un style visuel complet pour tous vos contrôles à partir d’un seul objet Style, y compris la prise en charge des thèmes, du stylisme basé sur les états et des transitions. StyleKit gère automatiquement l’implémentation sous-jacente des modèles, ce qui vous permet de vous concentrer exclusivement sur les aspects visuels tels que les couleurs, les dimensions, les bordures et les ombres.

Le module StyleKit de Qt Labs est un module « Technology Preview » dans 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.