En esta página

Personalización Qt Quick Controls

Qt Quick Controls consisten en una jerarquía (árbol) de elementos. Para proporcionar un aspecto personalizado, la implementación QML predeterminada de cada elemento puede sustituirse por una personalizada.

Personalizar un control

A veces querrás crear un aspecto "único" para una parte específica de tu interfaz de usuario, y utilizar un estilo completo en todas las demás partes. Tal vez esté satisfecho con el estilo que utiliza, pero hay un botón que tiene un significado especial.

La primera forma de crear este botón es simplemente definirlo in situ, allí donde sea necesario. Por ejemplo, tal vez no esté satisfecho con que el botón del estilo Básico tenga las esquinas cuadradas. Para hacerlas redondeadas, puede anular el elemento background y establecer la propiedad radio de Rectángulo:

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

Nota: como los diferentes elementos que componen un control en cualquier estilo están diseñados para trabajar juntos, puede ser necesario anular otros elementos para obtener el aspecto que buscas. Además, no todos los estilos pueden personalizarse. Consulte la nota en Referencia de personalización para obtener más información.

La segunda forma de crear el botón es buena si planea utilizar su botón redondeado en varios lugares. Implica mover el código a su propio archivo QML dentro de su proyecto.

Para ello, copiaremos el código de fondo del estilo Basic en Button.qml. Este archivo se encuentra en la siguiente ruta en tu instalación de Qt:

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

Una vez hecho esto, simplemente añadiremos la siguiente línea:

radius: 4

Para evitar confusiones con los controles del propio módulo, llamaremos al fichero MyButton.qml. Para utilizar el control en su aplicación, refiérase a él por su nombre de archivo:

import QtQuick.Controls.Basic

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

La tercera forma de crear el botón es un poco más estructurada, tanto en términos de dónde se sitúa el archivo en el sistema de archivos como de cómo se utiliza en QML. En primer lugar, copie un archivo existente como hizo anteriormente, pero esta vez, póngalo en una subcarpeta de su proyecto llamada (por ejemplo) controls. Para utilizar el control, primero importa la carpeta a un espacio de nombres:

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

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

Como ahora tiene el espacio de nombres MyControls, puede nombrar los controles como sus homólogos reales en el módulo Qt Quick Controls. Puede repetir este proceso para cualquier control que desee añadir.

Una ventaja añadida de estos tres métodos es que no es necesario implementar la plantilla desde cero.

Nota: los tres enfoques mencionados aquí no funcionan para personalizar el ToolTip adjunto, ya que se trata de un elemento compartido creado internamente. Para realizar una personalización puntual de un ToolTip, consulte Custom Tool Tips. Para personalizar el ToolTip adjunto , debe proporcionarlo como parte de su propio estilo.

Creación de un estilo personalizado

Hay varias formas de crear estilos propios. A continuación, explicaremos los distintos enfoques.

Definición de un estilo

En Qt Quick Controls, un estilo es esencialmente un conjunto de archivos QML dentro de un mismo directorio. Hay cuatro requisitos para que un estilo sea utilizable:

  • Debe existir al menos un archivo QML cuyo nombre coincida con un control (por ejemplo, Button.qml).
  • Cada archivo QML debe contener el tipo correspondiente de la importación QtQuick.Templates como elemento raíz. Por ejemplo, Button.qml debe contener una plantilla Button como elemento raíz.

    Si en su lugar utilizáramos el tipo correspondiente de la importación QtQuick.Controls como hicimos en la sección anterior, no funcionaría: el control que estuviéramos definiendo intentaría derivar de sí mismo.

  • Debe existir un archivo qmldir junto al archivo o archivos QML. A continuación se muestra un ejemplo de un simple archivo qmldir para un estilo que proporciona un botón:
    module MyStyle
    Button 2.15 Button.qml

    Si está utilizando la selección de estilo en tiempo de compilación, el qmldir también debe importar el estilo de reserva:

    # ...
    import QtQuick.Controls.Basic auto

    Esto también se puede hacer para la selección de estilo en tiempo de ejecución en lugar de utilizar, por ejemplo, QQuickStyle::setFallbackStyle().

    La estructura de directorios para un estilo de este tipo es la siguiente:

    MyStyle
    ├─── Button.qml
    └─── qmldir
  • Los archivos deben estar en un directorio que se pueda encontrar a través de la ruta de importación de QML.

    Por ejemplo, si la ruta al directorio MyStyle mencionado anteriormente era /home/user/MyApp/MyStyle, entonces /home/user/MyApp debe añadirse a la ruta de importación QML.

    Para utilizar MyStyle en MyApp, haga referencia a él por su nombre:

    • ./MyApp -style MyStyle

    El nombre del estilo debe coincidir con el del directorio de estilos; no se admite el uso de mystyle o MYSTYLE.

Por defecto, el sistema de estilos utiliza el estilo Basic como alternativa para los controles que no están implementados. Para personalizar o ampliar cualquier otro estilo incorporado, es posible especificar un estilo alternativo diferente utilizando QQuickStyle.

Lo que esto significa es que puedes implementar tantos controles como quieras para tu estilo personalizado, y colocarlos casi en cualquier lugar. También permite a los usuarios crear sus propios estilos para tu aplicación.

Vista previa de los estilos personalizados en Qt Quick Designer

Usando el enfoque anterior, es posible previsualizar un estilo personalizado en Qt Quick Designer. Para ello, asegúrese de que el proyecto tiene un archivo qtquickcontrols2.conf, y que existe la siguiente entrada:

[Controls]
Style=MyStyle

Para obtener más información, eche un vistazo al ejemplo de estilo plano.

Extensiones C++ específicas del estilo

A veces puede que necesite utilizar C++ para ampliar su estilo personalizado.

  • Si el estilo que utiliza el tipo es el único utilizado por una aplicación, registre el tipo con el motor QML añadiendo la macro QML_ELEMENT y haciendo que el archivo forme parte de su módulo 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 la cabecera en la que se declara la clase no es accesible desde la ruta de inclusión de su proyecto, es posible que tenga que modificar la ruta de inclusión para que se pueda compilar el código de registro generado.

    INCLUDEPATH += MyItems

    Consulte Definición de tipos QML desde C++ y Construcción de una aplicación QML para obtener más información.

  • Si el estilo que utiliza el tipo es uno de los muchos estilos que utiliza una aplicación, considere la posibilidad de colocar cada estilo en un módulo independiente. Los módulos se cargarán bajo demanda.

Consideraciones para los estilos personalizados

Cuando implementes tu propio estilo y personalices los controles, hay algunos puntos a tener en cuenta para asegurar que tu aplicación tenga el mayor rendimiento posible.

Evita asignar un id a las implementaciones de delegados de elementos de los estilos

Como se explica en Definición de un Estilo, cuando implementas tu propio estilo para un control, comienzas con la plantilla relevante para ese control. Por ejemplo, un estilo Button.qml se estructurará de forma similar a esta:

T.Button {
    // ...

    background: Rectangle {
        // ...
    }

    contentItem: Text {
        // ...
    }

    // ...
}

Cuando utilice un Botón en su aplicación, los elementos background y contentItem serán creados y emparentados con el elemento raíz Button:

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

Supongamos que necesita personalizar el botón (como se explica en Personalizar un control):

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, esto normalmente daría lugar a la creación tanto de la implementación por defecto background como de los elementos personalizados background. Qt Quick Controls utiliza una técnica que evita la creación de ambos elementos, y en su lugar sólo crea el elemento personalizado background, mejorando enormemente el rendimiento de creación de los controles.

Esta técnica se basa en la ausencia de un id en la implementación del estilo de ese elemento. Si se asigna un id, la técnica no puede funcionar, y se crearán ambos elementos. Por ejemplo, puede ser tentador asignar un id a background o contentItem para que otros objetos dentro del archivo puedan referirse a esos elementos:

T.Button {
    // ...

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

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

    // ...
}

Con este código, cada vez que se cree una instancia de Button con un fondo personalizado, se crearán ambos fondos, lo que resultará en un rendimiento de creación subóptimo.

Antes de Qt 5.15, el fondo antiguo no utilizado se eliminaba para liberar los recursos asociados a él. Sin embargo, como el control no es propietario de los elementos, no debería borrarlos. A partir de Qt 5.15, los elementos antiguos ya no se borran, por lo que el elemento backgroundRect vivirá más tiempo del necesario, normalmente hasta que se cierre la aplicación. Aunque el elemento antiguo se ocultará, visualmente no tendrá parentesco con el control y se eliminará del árbol de accesibilidad, es importante tener en cuenta el tiempo de creación y el uso de memoria de estos elementos no utilizados al asignar un id en este contexto.

Evitar asignaciones imperativas de elementos personalizados

La técnica mencionada en la sección anterior sólo funciona cuando un elemento se asigna de forma declarativa por primera vez, por lo que las asignaciones imperativas darán lugar a elementos huérfanos. Siempre que sea posible, utilice vínculos declarativos para asignar elementos personalizados.

No importar QtQuick.Controls en implementaciones QML

Cuando escriba el QML para la implementación de un control en su estilo, es importante no importar QtQuick.Controls. Hacerlo impedirá que el compilador QML compile el QML.

Implementar tipos utilizados por otros tipos

Supongamos que estás utilizando ScrollViews en tu aplicación, y decides que quieres personalizar sus barras de desplazamiento. Es tentador simplemente implementar un ScrollBar.qml personalizado y hacer que ScrollView recoja el ScrollBar personalizado automáticamente. Sin embargo, esto no funcionará. Debe implementar tanto ScrollBar.qml como ScrollView.qml.

Propiedades adjuntas

Es habitual que un estilo tenga ciertas propiedades o atributos que se aplican a todos los controles. Las propiedades adjuntas son una buena forma de ampliar un elemento en QML sin tener que modificar ningún C++ existente perteneciente a ese elemento. Por ejemplo, tanto el estilo Material como el Universal tienen una propiedad adjunta theme que controla si un ítem y sus hijos serán renderizados en un tema claro u oscuro.

Como ejemplo, vamos a añadir una propiedad adjunta que controla la elevación. Nuestro estilo ilustrará la elevación con una sombra; cuanto mayor sea la elevación, mayor será la sombra.

El primer paso es crear una nueva aplicación Qt Quick Controls en Qt Creator. Después, añadimos un tipo C++ que almacena la elevación. Dado que el tipo se utilizará para cada control soportado por nuestro estilo, y porque es posible que deseemos añadir otras propiedades adjuntas más adelante, lo llamaremos MyStyle. Aquí está 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();
}

El tipo MyStyle es especial en el sentido de que no debe ser instanciado, sino utilizado para sus propiedades adjuntas. Por esa razón, lo registramos de la siguiente manera en 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();
}

A continuación, copiamos Button.qml del estilo Basic en $QTDIR/qml/QtQuick/Controls/Basic/ en una nueva carpeta myproject en el directorio de nuestro proyecto. Añadimos el Button.qml recién copiado a qml.qrc, que es el archivo de recursos que contiene nuestros archivos QML.

A continuación, añadimos una sombra paralela al delegado background del Botón:

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

Fíjate que nosotros:

  • No nos molestamos en usar la sombra cuando la elevación es 0
  • Cambiamos el color de la sombra dependiendo de si el botón tiene o no foco
  • Hacemos que el tamaño de la sombra dependa de la elevación

Para probar la propiedad adjunta, creamos un Row con dos Botones en 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
        }
    }
}

Un botón no tiene elevación, y el otro tiene una elevación de 10.

Con eso en su lugar, podemos ejecutar nuestro ejemplo. Para decirle a la aplicación que utilice nuestro nuevo estilo, pasamos -style MyStyle como argumento de la aplicación, pero hay muchas formas de especificar el estilo a utilizar.

El resultado final:

Botones personalizados

Observe que la declaración import MyStyle 1.0 sólo es necesaria porque estamos utilizando la propiedad adjunta perteneciente a MyStyle. Ambos botones utilizarán nuestro estilo personalizado, incluso si elimináramos la importación.

Referencia de personalización

Los siguientes fragmentos de código presentan ejemplos en los que los controles del estilo Básico se han personalizado utilizando el mismo enfoque que en la sección Personalización de un control. El código puede utilizarse como punto de partida para implementar un aspecto personalizado.

Nota: Los estilos macOS y Windows no se pueden personalizar. En su lugar, se recomienda basar siempre un control personalizado en un único estilo que esté disponible en todas las plataformas, por ejemplo, Basic Style, Fusion Style, Imagine Style, Material Style, Universal Style. De este modo, se garantiza que siempre tendrá el mismo aspecto, independientemente del estilo con el que se ejecute la aplicación. Para aprender a utilizar un estilo diferente, consulte Uso de estilos en Qt Quick Controls. Alternativamente, puede crear su propio estilo.

La personalización de ApplicationWindow

ApplicationWindow consta de un elemento visual: background.

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    visible: true

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

La personalización de BusyIndicator

BusyIndicator consta de dos elementos visuales: background y contentItem.

Indicador de ocupado personalizado

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

Personalización de Button

Button consta de dos elementos visuales: background y contentItem.

Botón personalizado

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

Personalización de CheckBox

CheckBox consta de tres elementos visuales: background contentItem y indicator.

Casilla de verificación con estilo personalizado

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

La personalización de CheckDelegate

CheckDelegate consta de tres elementos visuales: background, contentItem y indicator.

Delegado de comprobación con estilo personalizado

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

Personalización de ComboBox

ComboBox consta de background, contentItem, popup, indicator, y delegate.

Cuadro combinado personalizado

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

Como se explica en ComboBox Model Roles, ComboBox soporta múltiples tipos de modelos.

Dado que todos los modelos proporcionan una propiedad anónima con modelData, la siguiente expresión recupera el texto correcto en todos los casos:

text: model[control.textRole]

Cuando se proporciona un textRole específico y un modelo con datos estructurados que proporciona el rol seleccionado, esta expresión es una búsqueda regular de propiedades. Cuando se proporciona un modelo con datos singulares, como una lista de cadenas, y un textRole vacío, esta expresión recupera el modelData.

Personalización de DelayButton

DelayButton consta de dos elementos visuales: background y contentItem.

Botón de retardo personalizado

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

Personalización de Dial

Dial consta de dos elementos visuales: background y handle.

Esfera personalizada

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

Personalización de DoubleSpinBox

DoubleSpinBox puede personalizarse de la misma manera que Button.

Personalización de Drawer

El cajón puede tener un elemento visual background.

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

Personalización de Frame

El marco consta de un elemento visual: background.

Marco personalizado

import QtQuick
import QtQuick.Controls.Basic

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

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

Personalización de GroupBox

GroupBox consta de dos elementos visuales: background y label.

Cuadro de grupo con estilo personalizado

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

Personalización de ItemDelegate

ItemDelegate consta de dos elementos visuales: background y contentItem.

Delegado de elementos con estilo personalizado

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

Personalización de la etiqueta

La etiqueta puede tener un elemento visual background.

Etiqueta personalizada

import QtQuick
import QtQuick.Controls.Basic

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

Menú de personalización

Menú personalizado

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

Personalización de MenuBar

MenuBar puede tener un elemento visual background y MenuBarItem consta de dos elementos visuales: background y contentItem.

Barra de menús personalizada con los menús Archivo y Edición

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

Personalización de PageIndicator

PageIndicator consta de un background, contentItem, y delegate.

Indicador de página con estilo personalizado que muestra varias páginas

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

Personalización de Pane

Pane consta de background.

Panel personalizado con fondo decorativo

import QtQuick
import QtQuick.Controls.Basic

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

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

Personalización de Popup

Popup consta de background y contentItem.

Ventana emergente personalizada con borde y sombra

import QtQuick
import QtQuick.Controls.Basic

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

Personalización de ProgressBar

ProgressBar consta de dos elementos visuales: background y contentItem.

Barra de progreso personalizada que muestra la finalización parcial

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

Arriba, el contentItem también se anima para representar un estado de la barra de progreso indeterminate.

Personalización de RadioButton

RadioButton consta de tres elementos visuales: background contentItem y indicator.

Botón de radio con estilo personalizado en estado seleccionado

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

Personalización de RadioDelegate

RadioDelegate consta de tres elementos visuales: background, contentItem y indicator.

Delegado de radio con estilo personalizado en la lista

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

La personalización de RangeSlider

RangeSlider consta de tres elementos visuales: background, first.handle y second.handle.

Deslizador de gamas personalizado

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

Personalización de RoundButton

RoundButton puede personalizarse de la misma manera que Button.

Personalización de ScrollBar

ScrollBar consta de dos elementos visuales: background y contentItem.

Barra de desplazamiento personalizada

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

Personalización de ScrollIndicator

ScrollIndicator consta de dos elementos visuales: background y contentItem.

Indicador de desplazamiento personalizado

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 personalización de ScrollView

ScrollView consta de un elemento background y barras de desplazamiento horizontal y vertical.

Vista de desplazamiento con estilo personalizado

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

Personalización de Slider

Slider consta de dos elementos visuales: background, y handle.

Deslizador personalizado

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

Personalización de SpinBox

SpinBox consta de cuatro elementos visuales: background, contentItem, up indicator, y down indicator.

Caja giratoria personalizada

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

Personalización de SplitView

SplitView consta de un delegado visual handle.

Vista dividida personalizada

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

Personalizar StackView

StackView consta de un elemento visual background, y permite personalizar las transiciones que se utilizan para las operaciones push, pop y 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
        }
    }
}

La personalización de SwipeDelegate

SwipeDelegate consta de seis elementos visuales: background, contentItem, indicator, swipe.left, swipe.right, y swipe.behind.

Delegado de deslizamiento con estilo personalizado

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

Personalizar SwipeView

SwipeView puede tener un elemento visual background. La navegación es implementada por el contentItem.

import QtQuick
import QtQuick.Controls.Basic

SwipeView {
    id: control

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

Personalización de Switch

Switch consta de tres elementos visuales: background contentItem y indicator.

Interruptor personalizado

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

Personalización de SwitchDelegate

SwitchDelegate consta de tres elementos visuales: background, contentItem y indicator.

Delegado de conmutación con estilo personalizado

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

Personalización de TabBar

TabBar consta de dos elementos visuales: background, y contentItem.

Barra de pestañas personalizada

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

Personalización de TabButton

TabButton puede personalizarse de la misma manera que Button.

Personalizar TextArea

TextArea consta de un elemento background.

Área de texto con estilo personalizado

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

La personalización de TextField

TextField consiste en un elemento background.

Campo de texto con estilo personalizado

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

Personalización de ToolBar

ToolBar consta de un elemento visual: background.

Barra de herramientas personalizada

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

La personalización de ToolButton

ToolButton consta de dos elementos visuales: background y contentItem.

Botón de herramienta con estilo personalizado

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

La personalización de ToolSeparator

ToolSeparator consta de dos elementos visuales: background y contentItem.

Separador de herramientas personalizado

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

Personalización de ToolTip

ToolTip consta de dos elementos visuales: background y 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"
    }
}

Nota: para personalizar attached ToolTip, debe proporcionarse como parte de su propio estilo. Para realizar una personalización puntual de un ToolTip, consulte Custom Tool Tips.

Personalización de Tumbler

Tumbler consta de tres elementos visuales: background, contentItem, y delegate.

Vaso personalizado

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 desea definir su propio elemento de contenido, utilice ListView o PathView como elemento raíz. Para un Tumbler envolvente, utilice 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
    }
}

Para un Tumbler sin envoltorio, utilice 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
    }
}

Personalización TableViewDelegate

TableViewDelegate hereda de ItemDelegate, lo que significa que se compone de dos elementos visuales: background y contentItem.

Siempre puede asignar su propio delegado de edición personalizado a editDelegate si tiene necesidades fuera de lo que ofrece el delegado de edición predeterminado.

Delegado de vista de tabla con estilo personalizado

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

Personalización de HeaderViewDelegate

HeaderViewDelegate hereda de TableViewDelegate, lo que significa que está compuesto por dos elementos: background y contentItem. Siempre puedes personalizarlos con cualquier elemento arbitrario.

Delegado de vista de cabecera con estilo personalizado

He aquí un ejemplo de personalización del delegado de vista de cabecera 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
        }
    }
}

Aquí tienes un ejemplo de personalización de la vista delegada de cabecera 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
        }
    }
}

Estilizando Controles usando StyleKit

El módulo StyleKit de Qt Labs proporciona un conjunto de tipos QML para estilizar Qt Quick Controls, construido sobre Qt Quick Templates. Permite definir un estilo visual completo para todos los controles a partir de un único objeto Style, incluyendo soporte para temas, estilos basados en estados y transiciones. StyleKit gestiona automáticamente la implementación de la plantilla subyacente, permitiéndole centrarse únicamente en aspectos visuales como colores, dimensiones, bordes y sombras.

El módulo Qt Labs StyleKit es un módulo de Technology Preview en 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.