사용자 지정 Qt Quick Controls

Qt Quick Controls 는 항목의 계층 구조(트리)로 구성됩니다. 사용자 지정 모양과 느낌을 제공하기 위해 각 항목의 기본 QML 구현을 사용자 지정으로 바꿀 수 있습니다.

컨트롤 사용자 지정하기

때로는 UI의 특정 부분에 대해 "일회성" 모양을 만들고 다른 모든 곳에 완전한 스타일을 사용하고 싶을 때가 있습니다. 현재 사용 중인 스타일에 만족하지만 특별한 의미가 있는 특정 버튼이 있을 수도 있습니다.

이 버튼을 만드는 첫 번째 방법은 필요한 위치에 간단히 정의하는 것입니다. 예를 들어 기본 스타일의 버튼 모서리가 사각형인 것이 마음에 들지 않을 수 있습니다. 모서리를 둥글게 만들려면 background 항목을 재정의하고 직사각형의 반지름 속성을 설정하면 됩니다:

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

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

참고: 특정 스타일의 컨트롤을 구성하는 여러 항목이 함께 작동하도록 설계되었으므로 원하는 모양을 얻으려면 다른 항목을 재정의해야 할 수도 있습니다. 또한 모든 스타일을 사용자 지정할 수 있는 것은 아닙니다. 자세한 내용은 사용자 지정 참조의 메모를 참조하세요.

둥근 버튼을 여러 곳에서 사용하려는 경우 버튼을 만드는 두 번째 방법이 좋습니다. 이 방법은 코드를 프로젝트 내의 자체 QML 파일로 이동하는 것입니다.

이 접근 방식에서는 기본 스타일의 Button.qml 에서 배경 코드를 복사합니다. 이 파일은 Qt 설치의 다음 경로에서 찾을 수 있습니다:

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

그런 다음 다음 줄을 추가하기만 하면 됩니다:

radius: 4

모듈 자체의 컨트롤과 혼동을 피하기 위해 파일을 MyButton.qml 이라고 부를 것입니다. 애플리케이션에서 컨트롤을 사용하려면 파일 이름으로 해당 컨트롤을 참조하세요:

import QtQuick.Controls.Basic

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

세 번째 버튼 생성 방법은 파일이 파일 시스템에서 어디에 위치하는지, 그리고 QML에서 어떻게 사용되는지 측면에서 좀 더 구조화되어 있습니다. 먼저 위에서 한 것처럼 기존 파일을 복사하되 이번에는 프로젝트의 하위 폴더(예: controls)에 파일을 넣습니다. 컨트롤을 사용하려면 먼저 폴더를 네임스페이스로 가져옵니다:

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

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

이제 MyControls 네임스페이스가 생겼으므로 Qt Quick Controls 모듈에 있는 실제 컨트롤의 이름을 따서 컨트롤의 이름을 지정할 수 있습니다. 추가하려는 모든 컨트롤에 대해 이 과정을 반복할 수 있습니다.

이 세 가지 방법의 또 다른 장점은 템플릿을 처음부터 구현할 필요가 없다는 것입니다.

참고: 여기에 언급된 세 가지 방법은 내부적으로 만든 공유 항목이므로 첨부된 ToolTip 을 사용자 지정하는 데는 사용할 수 없습니다. ToolTip 을 일회성으로 사용자 지정하려면 Custom Tool Tips 을 참조하세요. 첨부된 ToolTip 을 사용자 지정하려면 고유한 스타일의 일부로 제공해야 합니다.

사용자 정의 스타일 만들기

나만의 스타일을 만드는 방법에는 여러 가지가 있습니다. 아래에서 다양한 접근 방식에 대해 설명합니다.

스타일의 정의

Qt Quick Controls 에서 스타일은 기본적으로 단일 디렉터리 내의 QML 파일 집합입니다. 스타일을 사용할 수 있으려면 네 가지 요구 사항이 있습니다:

  • 이름이 컨트롤(예: Button.qml)과 일치하는 QML 파일이 하나 이상 존재해야 합니다.
  • 각 QML 파일에는 QtQuick.Templates 가져오기의 관련 유형이 루트 항목으로 포함되어 있어야 합니다. 예를 들어 Button.qml에는 Button 템플릿이 루트 항목으로 포함되어야 합니다.

    이전 섹션에서와 같이 QtQuick.Controls 임포트에서 해당 유형을 대신 사용하면 정의한 컨트롤이 자체에서 파생하려고 시도하므로 작동하지 않습니다.

  • QML 파일과 함께 qmldir 파일이 존재해야 합니다. 아래는 버튼을 제공하는 스타일에 대한 간단한 qmldir 파일의 예시입니다:
    module MyStyle
    Button 2.15 Button.qml

    컴파일 시 스타일 선택을 사용하는 경우 qmldir은 폴백 스타일도 가져와야 합니다:

    # ...
    import QtQuick.Controls.Basic auto

    예를 들어 QQuickStyle::setFallbackStyle()를 사용하는 대신 런타임 스타일 선택에 대해서도 이 작업을 수행할 수 있습니다.

    이러한 스타일의 디렉토리 구조는 다음과 같습니다:

    MyStyle
    ├─── Button.qml
    └─── qmldir
  • 파일은 QML 가져오기 경로를 통해 찾을 수 있는 디렉토리에 있어야 합니다.

    예를 들어 위에서 언급한 MyStyle 디렉터리 경로가 /home/user/MyApp/MyStyle 인 경우 /home/user/MyApp 을 QML 가져오기 경로에 추가해야 합니다.

    MyApp에서 MyStyle을 사용하려면 이름으로 참조합니다:

    • ./MyApp -style MyStyle

    스타일 이름은 스타일 디렉터리의 대소문자와 일치해야 하며, mystyle 또는 MYSTYLE을 전달하는 것은 지원되지 않습니다.

기본적으로 스타일링 시스템은 구현되지 않은 컨트롤에 대해 기본 스타일을 폴백으로 사용합니다. 다른 기본 제공 스타일을 사용자 정의하거나 확장하려면 QQuickStyle 을 사용하여 다른 대체 스타일을 지정할 수 있습니다.

즉, 사용자 지정 스타일에 원하는 만큼의 컨트롤을 구현하고 거의 모든 곳에 배치할 수 있습니다. 또한 사용자가 애플리케이션에 대한 자신만의 스타일을 만들 수도 있습니다.

Qt Quick 디자이너에서 사용자 정의 스타일 미리보기

위의 방법을 사용하여 Qt Quick 디자이너에서 사용자 정의 스타일을 미리 볼 수 있습니다. 이렇게 하려면 프로젝트에 qtquickcontrols2.conf 파일이 있고 다음 항목이 있는지 확인합니다:

[Controls]
Style=MyStyle

자세한 내용은 플랫 스타일 예제를 참조하세요.

스타일별 C++ 확장

사용자 정의 스타일을 확장하기 위해 C++를 사용해야 하는 경우가 있습니다.

  • 해당 스타일을 사용하는 스타일이 애플리케이션에서 사용하는 유일한 스타일인 경우 QML_ELEMENT 매크로를 추가하고 파일을 QML 모듈의 일부로 만들어 QML 엔진에 해당 스타일을 등록합니다:

    qmake#tab-qmake
    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

    클래스가 선언된 헤더가 프로젝트의 include 경로에서 액세스할 수 없는 경우, 생성된 등록 코드를 컴파일할 수 있도록 include 경로를 수정해야 할 수 있습니다.

    INCLUDEPATH += MyItems

    자세한 내용은 C++에서 QML 유형 정의하기QML 애플리케이션 빌드하기를 참조하세요.

  • 해당 유형을 사용하는 스타일이 애플리케이션에서 사용하는 여러 스타일 중 하나인 경우 각 스타일을 별도의 모듈에 넣는 것이 좋습니다. 그러면 모듈이 필요할 때 로드됩니다.

사용자 정의 스타일에 대한 고려 사항

자신만의 스타일을 구현하고 컨트롤을 사용자 지정할 때 애플리케이션의 성능을 최대한 발휘할 수 있도록 몇 가지 유의해야 할 사항이 있습니다.

스타일의 항목 델리게이트 구현에 ID를 할당하지 마세요.

스타일 정의에서 설명한 대로 컨트롤에 대한 고유한 스타일을 구현할 때는 해당 컨트롤에 대한 관련 템플릿으로 시작합니다. 예를 들어 스타일의 Button.qml 구조는 이와 유사하게 구성됩니다:

T.Button {
    // ...

    background: Rectangle {
        // ...
    }

    contentItem: Text {
        // ...
    }

    // ...
}

애플리케이션에서 버튼을 사용하면 backgroundcontentItem 항목이 만들어지고 루트 Button 항목에 부모가 됩니다:

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

그런 다음 버튼의 일회성 사용자 지정( 컨트롤 사용자 지정에 설명된 대로)을 수행해야 한다고 가정해 보겠습니다:

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

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

QML에서는 일반적으로 기본 background 구현과 일회성 사용자 지정 background 항목이 모두 생성됩니다. Qt Quick Controls 는 두 항목을 모두 생성하지 않고 대신 사용자 지정 background 만 생성하는 기술을 사용하여 컨트롤의 생성 성능을 크게 향상시킵니다.

이 기법은 해당 항목의 스타일 구현에 ID가 없는 경우에 의존합니다. ID가 할당되어 있으면 이 기법이 작동하지 않으며 두 항목이 모두 생성됩니다. 예를 들어 파일 내의 다른 객체가 해당 항목을 참조할 수 있도록 background 또는 contentItem 에 ID를 할당하고 싶을 수 있습니다:

T.Button {
    // ...

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

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

    // ...
}

이 코드를 사용하면 사용자 정의 배경이 있는 버튼 인스턴스가 생성될 때마다 두 배경이 모두 생성되므로 생성 성능이 최적화되지 않습니다.

Qt 5.15 이전에는 사용하지 않는 오래된 배경을 삭제하여 관련 리소스를 해제했습니다. 그러나 컨트롤이 해당 항목을 소유하지 않으므로 삭제해서는 안 됩니다. Qt 5.15부터 이전 항목은 더 이상 삭제되지 않으므로 backgroundRect 항목은 일반적으로 애플리케이션이 종료될 때까지 필요 이상으로 오래 유지됩니다. 이전 항목은 숨겨지고 컨트롤에서 시각적으로 부모가 없으며 접근성 트리에서 제거되지만, 이러한 상황에서 ID를 할당할 때는 사용하지 않는 항목의 생성 시간과 메모리 사용량을 염두에 두는 것이 중요합니다.

사용자 지정 항목의 필수 할당 피하기

위 섹션에서 언급한 기술은 항목이 처음으로 선언적으로 할당될 때만 작동하므로 명령형 할당은 고아가 된 항목을 만들게 됩니다. 가능하면 항상 선언적 바인딩을 사용하여 사용자 지정 항목을 할당하세요.

QML 구현에서 QtQuick.Controls 임포트하지 않기

스타일의 컨트롤 구현을 위한 QML을 작성할 때 QtQuick.Controls 을 임포트하지 않는 것이 중요합니다. 그렇게 하면 QML 컴파일러가 QML을 컴파일하지 못하게 됩니다.

다른 유형에서 사용하는 유형 구현하기

애플리케이션에서 스크롤 뷰를 사용하고 있는데 스크롤 막대를 사용자 정의하고 싶다고 가정해 보겠습니다. 사용자 지정 ScrollBar.qml을 구현하고 ScrollView 이 사용자 지정 ScrollBar 을 자동으로 선택하도록 하고 싶을 수도 있습니다. 하지만 이렇게 하면 작동하지 않습니다. ScrollBar . qml과 ScrollView.qml을 모두 구현해야 합니다.

첨부된 속성

스타일에는 모든 컨트롤에 적용되는 특정 속성이나 속성이 있는 것이 일반적입니다. 첨부 프로퍼티는 해당 항목에 속하는 기존 C++를 수정하지 않고도 QML에서 항목을 확장할 수 있는 좋은 방법입니다. 예를 들어 MaterialUniversal 스타일에는 항목과 그 자식을 밝은 테마로 렌더링할지 어두운 테마로 렌더링할지를 제어하는 첨부 테마 속성이 있습니다.

예를 들어 높낮이를 제어하는 프로퍼티를 추가해 보겠습니다. 이 스타일은 그림자를 사용하여 고도를 나타내며, 고도가 높을수록 그림자가 커집니다.

첫 번째 단계는 Qt Creator 에서 Qt Quick Controls 애플리케이션을 만드는 것입니다. 그런 다음 고도를 저장하는 C++ 타입을 추가합니다. 이 유형은 스타일에서 지원하는 모든 컨트롤에 사용되며 나중에 다른 첨부 프로퍼티를 추가할 수 있으므로 MyStyle이라고 부르겠습니다. 여기 MyStyle.h:

#ifndef MYSTYLE_H
#define MYSTYLE_H

#include <QObject>
#include <QtQml>

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

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

    static MyStyle *qmlAttachedProperties(QObject *object);

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

signals:
    void elevationChanged();

private:
    int m_elevation;
};

QML_DECLARE_TYPEINFO(MyStyle, QML_HAS_ATTACHED_PROPERTIES)

#endif // MYSTYLE_H

MyStyle.cpp:

#include "mystyle.h"

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

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

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

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

    m_elevation = elevation;
    emit elevationChanged();
}

MyStyle 유형은 인스턴스화되지 않고 첨부된 프로퍼티에 사용되어야 한다는 점에서 특별합니다. 따라서 main.cpp 에 다음과 같은 방식으로 등록합니다:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "mystyle.h"

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

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

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

    return app.exec();
}

그런 다음 $QTDIR/qml/QtQuick/Controls/Basic/ 의 기본 스타일에서 Button.qml 을 프로젝트 디렉토리의 새 myproject 폴더에 복사합니다. 새로 복사한 Button.qml 을 QML 파일이 들어 있는 리소스 파일인 qml.qrc 에 추가합니다.

다음으로 버튼의 background 델리게이트에 그림자를 추가합니다:

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

background: Rectangle {
    // ...

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

참고하세요:

  • 고도가 다음과 같을 때 그림자를 사용하지 마십시오. 0
  • 버튼에 초점이 있는지 여부에 따라 그림자의 색상을 변경합니다.
  • 그림자의 크기를 높이에 따라 다르게 만들기

첨부된 프로퍼티를 사용해 보기 위해 main.qml 에 버튼 두 개가 있는 Row 을 만듭니다:

import QtQuick
import QtQuick.Controls

import MyStyle 1.0

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

    Row {
        spacing: 20
        anchors.centerIn: parent

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

한 버튼은 높이가 없고 다른 버튼은 10 의 높이를 갖습니다.

이제 예제를 실행할 수 있습니다. 애플리케이션에 새 스타일을 사용하도록 하기 위해 애플리케이션 인수로 -style MyStyle 을 전달하지만, 사용할 스타일을 지정하는 방법에는 여러 가지가 있습니다.

최종 결과물입니다:

import MyStyle 1.0 문은 MyStyle 에 속하는 첨부 프로퍼티를 사용하기 때문에 필요합니다. 가져오기를 제거하더라도 두 버튼 모두 사용자 정의 스타일을 사용합니다.

사용자 정의 참조

다음 코드 조각은 컨트롤 사용자 지정하기 섹션과 동일한 접근 방식을 사용하여 기본 스타일의 컨트롤을 사용자 지정한 예제입니다. 이 코드는 사용자 지정 모양과 느낌을 구현하기 위한 시작점으로 사용할 수 있습니다.

참고: macOSWindows 스타일은 사용자 지정에 적합하지 않습니다. 대신 항상 모든 플랫폼에서 사용할 수 있는 단일 스타일(예: 기본 스타일, 퓨전 스타일, 상상 스타일, 머티리얼 스타일, 유니버설 스타일)을 기반으로 사용자 지정 컨트롤을 만드는 것이 좋습니다. 이렇게 하면 애플리케이션이 어떤 스타일로 실행되든 항상 동일하게 보이도록 보장할 수 있습니다. 다른 스타일을 사용하는 방법을 알아보려면 Qt Quick Controls 에서 스타일 사용을 참조하세요. 또는 자신만의 스타일을 만들 수도 있습니다.

ApplicationWindow 사용자 지정

ApplicationWindow 는 하나의 시각적 항목으로 구성됩니다: background.

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    visible: true

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

바쁨 표시기 사용자 지정하기

BusyIndicatorbackgroundcontentItem 의 두 가지 시각적 항목으로 구성됩니다.

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

사용자 지정 버튼

버튼은 backgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

체크박스 사용자 지정하기

CheckBox 는 세 가지 시각적 항목으로 구성됩니다: background, contentItemindicator 입니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

체크 델리게이트 사용자 지정하기

CheckDelegate 는 세 가지 시각적 항목으로 구성됩니다: background, contentItemindicator.

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

콤보박스 사용자 지정하기

ComboBoxbackground, content item, popup, indicator, delegate 로 구성됩니다.

pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Controls.Basic

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

    delegate: ItemDelegate {
        id: delegate

        required property var model
        required property int index

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

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

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

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

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

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

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

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

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

            ScrollIndicator.vertical: ScrollIndicator { }
        }

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

ComboBox Model Roles, ComboBox 에서 설명한 대로 여러 유형의 모델을 지원합니다.

모든 모델은 modelData 으로 익명 속성을 제공하므로 다음 표현식은 모든 경우에 올바른 텍스트를 검색합니다:

text: model[control.textRole]

특정 textRole 과 선택한 역할을 제공하는 구조화된 데이터를 모델에 제공하는 경우 이 표현식은 일반 속성 조회입니다. 문자열 목록과 같은 단수 데이터와 빈 textRole 을 모델에 제공하면 이 표현식은 modelData 을 검색합니다.

DelayButton 사용자 지정하기

DelayButtonbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

다이얼 사용자 지정하기

다이얼은 backgroundhandle 의 두 가지 시각적 항목으로 구성됩니다.

QtQuick 임포트QtQuick.Controls.Basic 임포트Dial { id: 컨트롤 배경: 직사각형 { x: control.width / 2 - 너비 / 2 y: control.height / 2 - 높이 / 2 implicitWidth: 140 implicitHeight: 140 width: Math.max(64, Math.min(control.width, control.height)) height: width color: "투명" radius: width / 2 border.color: control.pressed? "#17a81a": "#21be2b" opacity: control.enabled? 1: 0.3} 핸들: 직사각형 { id: handleItemx : control.background.x + control.background.width / 2 - 너비 / 2y : control.background.y + control.background.height / 2 - 높이 / 2 너비: 16 높이: 16 color: control.pressed? "#17a81a": "#21be2b" radius: 8 앤티앨리어싱: true 불투명도: control.enabled? 1: 0.3 변형: [ 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} ] } }

서랍 커스터마이징하기

드로어에는 시각적 background 항목이 있을 수 있습니다.

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

프레임 사용자 지정하기

프레임은 하나의 시각적 항목으로 구성됩니다: background.

import QtQuick
import QtQuick.Controls.Basic

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

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

GroupBox 사용자 지정하기

GroupBoxbackgroundlabel 두 개의 시각적 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

ItemDelegate 커스터마이징하기

ItemDelegatebackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

레이블 사용자 지정하기

레이블에는 시각적 background 항목이 있을 수 있습니다.

import QtQuick
import QtQuick.Controls.Basic

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

메뉴 사용자 지정하기

import QtQuick
import QtQuick.Controls.Basic

Menu {
    id: menu

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

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

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

    topPadding: 2
    bottomPadding: 2

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

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

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

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

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

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

메뉴바 커스터마이징하기

MenuBar 는 시각적 background 항목을 가질 수 있으며 MenuBarItembackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

페이지 표시기 사용자 지정하기

PageIndicatorbackground, content item, delegate 로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

PageIndicator {
    id: control
    count: 5
    currentIndex: 2

    delegate: Rectangle {
        implicitWidth: 8
        implicitHeight: 8

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

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

        required property int index

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

창 사용자 지정하기

창은 background 으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

팝업 사용자 지정

팝업은 backgroundcontent item 으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

진행률 표시줄 사용자 지정하기

ProgressBarbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

위에서는 콘텐츠 항목에 애니메이션을 적용하여 indeterminate 진행률 표시줄 상태를 나타냅니다.

라디오 버튼 사용자 지정

RadioButton 은 세 가지 시각적 항목으로 구성됩니다: background, content itemindicator 입니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

라디오 델리게이트 커스터마이징

RadioDelegate 는 세 가지 시각적 항목으로 구성됩니다: background, contentItemindicator.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

범위 슬라이더 커스터마이징

RangeSlider 는 세 가지 시각적 항목으로 구성됩니다: background, first.handlesecond.handle.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

라운드 버튼 사용자 지정

RoundButton버튼과 같은 방식으로 사용자 정의할 수 있습니다.

스크롤바 사용자 지정

ScrollBarbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

스크롤 표시기 사용자 지정

ScrollIndicatorbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

스크롤뷰 사용자 지정

ScrollViewbackground 항목과 가로 및 세로 스크롤 막대로 구성됩니다.

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

슬라이더 사용자 지정하기

슬라이더는 두 개의 시각적 항목으로 구성됩니다: backgroundhandle.

import QtQuick
import QtQuick.Controls.Basic

Slider {
    id: control
    value: 0.5

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

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

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

스핀박스 사용자 지정

SpinBox 는 네 가지 시각적 항목으로 구성됩니다: background, contentItem, up indicator, down indicator.

import QtQuick
import QtQuick.Controls.Basic

SpinBox {
    id: control
    value: 50
    editable: true

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

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

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

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

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

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

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

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

SplitView 사용자 지정

SplitView 는 시각적 handle 델리게이트로 구성됩니다.

SplitView {
    id: splitView
    anchors.fill: parent

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

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

스택뷰 사용자 지정하기

StackView 는 시각적 background 항목을 가질 수 있으며 푸시, 팝, 바꾸기 작업에 사용되는 전환을 사용자 지정할 수 있습니다.

import QtQuick
import QtQuick.Controls.Basic

StackView {
    id: control

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

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

스와이프 델리게이트 사용자 지정

SwipeDelegate 는 6개의 시각적 항목으로 구성됩니다: background, content item, indicator, swipe.left, swipe.right, 그리고 swipe.behind 입니다.

import QtQuick
import QtQuick.Controls.Basic

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

    Component {
        id: component

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

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

    swipe.left: component
    swipe.right: component

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

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

스와이프뷰 사용자 지정

SwipeView 는 시각적 background 항목을 가질 수 있습니다. 탐색은 content item 으로 구현됩니다.

import QtQuick
import QtQuick.Controls.Basic

SwipeView {
    id: control

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

스위치 사용자 지정

스위치는 세 가지 시각적 항목으로 구성됩니다: background, content itemindicator 입니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

스위치 델리게이트 커스터마이징

SwitchDelegate 는 세 가지 시각적 항목으로 구성됩니다: background, contentItemindicator.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

탭바 커스터마이징

TabBar 는 두 개의 시각적 항목으로 구성됩니다: backgroundcontentItem 의 두 가지 시각적 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

TabBar {
    id: control

    background: Rectangle {
        color: "#eeeeee"
    }

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

탭 버튼 사용자 지정

TabButton버튼과 동일한 방식으로 사용자 정의할 수 있습니다.

텍스트 영역 사용자 지정

TextAreabackground 항목으로 구성됩니다.

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

텍스트 필드 사용자 지정

TextFieldbackground 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

도구 모음 사용자 지정

ToolBar 는 하나의 시각적 항목으로 구성됩니다: background.

ToolBar {
    id: control

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

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

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

도구 버튼 사용자 지정

ToolButtonbackgroundcontent item 두 개의 시각적 항목으로 구성됩니다.

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

도구 분리기 사용자 지정

ToolSeparatorbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

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

툴팁 커스터마이징

ToolTipbackgroundcontent item 의 두 가지 시각적 항목으로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

참고: attached ToolTip 을 사용자 지정하려면 자신만의 스타일로 제공해야 합니다. ToolTip 을 일회성으로 사용자 지정하려면 Custom Tool Tips 을 참조하세요.

텀블러 사용자 지정

텀블러는 세 가지 시각적 항목으로 구성됩니다: background, contentItem, delegate 입니다.

import QtQuick
import QtQuick.Controls.Basic

Tumbler {
    id: control
    model: 15

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

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

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

        required property var modelData
        required property int index
    }

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

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

고유한 콘텐츠 항목을 정의하려면 ListView 또는 PathView 을 루트 항목으로 사용합니다. 래핑 텀블러의 경우 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
    }
}

포장하지 않는 텀블러의 경우 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
    }
}

© 2025 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.