이 페이지에서

사용자 지정 Qt Quick Controls

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

컨트롤 사용자 정의

때로는 UI의 특정 부분에 대해 “단 한 번만 적용되는” 디자인을 만들고, 나머지 부분에는 동일한 스타일을 적용하고 싶을 때가 있습니다. 현재 사용 중인 스타일에는 만족하지만, 특별한 의미를 지닌 특정 버튼이 있을 수도 있습니다.

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

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

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

참고: 특정 스타일에서 컨트롤을 구성하는 여러 항목은 서로 연동되도록설계되었으므로 , 원하는 모양을 얻기 위해 다른 항목들도 재정의해야 할 수 있습니다. 또한, 모든 스타일을 사용자 정의할 수 있는 것은 아닙니다. 자세한 내용은 사용자 정의 참조 문서의 참고 사항을 참조하십시오.

버튼을 만드는 두 번째 방법은 둥근 모서리 버튼을 여러 곳에서 사용할 계획일 때 유용합니다. 이 방법은 코드를 프로젝트 내의 별도의 QML 파일로 옮기는 과정을 포함합니다.

이 방법을 사용하려면 Basic 스타일의 ` Button.qml` 파일에서 배경 코드를 복사해야 합니다. 이 파일은 Qt 설치 디렉터리의 다음 경로에서 찾을 수 있습니다:

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

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

radius: 4

모듈 자체에 포함된 컨트롤과 혼동을 피하기 위해, 이 파일의 이름을 ` MyButton.qml`로 지정하겠습니다. 애플리케이션에서 이 컨트롤을 사용하려면 파일 이름을 참조하십시오:

import QtQuick.Controls.Basic

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

버튼을 생성하는 세 번째 방법은 파일 시스템 내 파일의 위치와 QML에서 사용하는 방식 모두에서 좀 더 체계적입니다. 먼저, 위와 같이 기존 파일을 복사하되, 이번에는 프로젝트 내의 (예를 들어) ` controls`라는 하위 폴더에 저장합니다. 이 컨트롤을 사용하려면 먼저 해당 폴더를 네임스페이스로 임포트해야 합니다:

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

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

이제 MyControls 네임스페이스가 생성되었으므로, Qt Quick Controls 모듈에 있는 실제 컨트롤과 동일한 이름으로 컨트롤을 지정할 수 있습니다. 추가하고자 하는 모든 컨트롤에 대해 이 과정을 반복할 수 있습니다.

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

참고: 여기서 언급한 세가지 방법은 첨부된 ToolTip 을 사용자 정의하는 데는 적용되지 않습니다. 이는 내부적으로 생성된 공유 항목이기 때문입니다. ToolTip 을 일회성으로 사용자 정의하려면 Custom Tool Tips 를 참조하십시오. 첨부된 ToolTip 을 사용자 정의하려면 이를 사용자 정의 스타일의 일부로 제공해야 합니다.

사용자 정의 스타일 만들기

사용자 정의 스타일을 생성하는 방법에는 여러 가지가 있습니다. 아래에서는 다양한 접근 방식을 설명하겠습니다.

스타일 정의

Qt Quick Controls 에서 스타일은 기본적으로 단일 디렉토리 내에 있는 QML 파일들의 집합입니다. 스타일을 사용할 수 있으려면 다음 네 가지 조건이 충족되어야 합니다:

  • 컨트롤 이름(예: 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 인 경우, QML 임포트 경로에 /home/user/MyApp 를 추가해야 합니다.

    MyApp에서 MyStyle을 사용 하려면 이름으로 참조하십시오:

    • ./MyApp -style MyStyle

    스타일 이름은 스타일 디렉터리의 대소문자 구분을 정확히 따라야 하며, mystyle 또는 MYSTYLE과 같이 대소문자를 달리 표기하는 것은 지원되지 않습니다.

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

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

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

위의 방법을 사용하면 Qt Quick Designer에서 사용자 정의 스타일을 미리 볼 수 있습니다. 이를 위해서는 프로젝트에 qtquickcontrols2.conf 파일이 있고, 다음 항목이 포함되어 있는지 확인하십시오:

[Controls]
Style=MyStyle

자세한 내용은 Flat Style 예제를 참고하십시오.

스타일별 C++ 확장 기능

때로는 사용자 정의 스타일을 확장하기 위해 C++을 사용해야 할 수도 있습니다.

  • 해당 타입을 사용하는 스타일이 애플리케이션에서 유일하게 사용되는 스타일인 경우, ` QML_ELEMENT ` 매크로를 추가하고 해당 파일을 QML 모듈의 일부로 포함시켜 QML 엔진에 타입을 등록하십시오:

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

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

    INCLUDEPATH += MyItems

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

  • 해당 유형을 사용하는 스타일이 애플리케이션에서 사용하는 여러 스타일 중 하나인 경우, 각 스타일을 별도의 모듈에 배치하는 것을 고려해 보십시오. 그러면 모듈은 필요에 따라 로드됩니다.

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

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

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

'스타일 정의'에서 설명한 바와 같이, 컨트롤에 대한 사용자 정의 스타일을 구현할 때는 해당 컨트롤에 대한 관련 템플릿을 기반으로 시작합니다. 예를 들어, 스타일의 ` Button.qml `은 다음과 같이 구성됩니다.

T.Button {
    // ...

    background: Rectangle {
        // ...
    }

    contentItem: Text {
        // ...
    }

    // ...
}

애플리케이션에서 Button을 사용할 때, ` background ` 및 ` contentItem ` 항목이 생성되어 루트 ` Button ` 항목의 자식으로 설정됩니다:

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

이때 '컨트롤 사용자 정의( Customizing a Control)'에서 설명한 대로 Button을 한 번만 사용자 정의해야 한다고 가정해 보겠습니다:

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    width: 400
    height: 400
    visible: true

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

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

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

T.Button {
    // ...

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

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

    // ...
}

이 코드를 사용하면 사용자 정의 배경을 가진 Button 인스턴스가 생성될 때마다 두 배경 모두 생성되어 생성 성능이 저하됩니다.

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

사용자 정의 항목에 대한 명령형 할당 피하기

위 섹션에서 언급한 기법은 항목이 선언적으로 처음 할당될 때만 작동하므로, 명령형 할당은 고아 항목을 생성하게 됩니다. 가능한 경우 항상 선언적 바인딩을 사용하여 사용자 정의 항목을 할당하십시오.

QML 구현에서 QtQuick.Controls를 임포트하지 마십시오

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

다른 유형에서 사용하는 유형을 구현하십시오

애플리케이션에서 ScrollView를 사용하고 있으며, 해당 스크롤바를 사용자 정의하기로 결정했다고 가정해 보겠습니다. 단순히 사용자 정의 ScrollBar.qml을 구현하고, ScrollView 가 사용자 정의된 ScrollBar 를 자동으로 인식하도록 하는 것이 유혹적일 수 있습니다. 그러나 이 방법은 작동하지 않습니다. ScrollBar. qml과 ScrollView.qml 을 모두 구현해야 합니다.

부착 속성

스타일에는 모든 컨트롤에 적용되는 특정 속성이나 속성이 포함되는 경우가 흔합니다. 부착 속성은 해당 항목에 속한 기존 C++ 코드를 수정하지 않고도 QML에서 항목을 확장할 수 있는 훌륭한 방법입니다. 예를 들어, Material 및 Universal 스타일 모두 항목과 그 자식 요소가 밝은 테마로 렌더링될지 어두운 테마로 렌더링될지를 제어하는 ‘theme’ 부착 속성을 가지고 있습니다.

예를 들어, 높이를 제어하는 첨부 속성을 추가해 보겠습니다. 이 스타일은 드롭 섀도우를 통해 높이를 표현하며, 높이가 높을수록 그림자가 커집니다.

첫 번째 단계는 Qt Creator 또는 Qt Extension for Visual Studio Code 에서 새로운 Qt Quick Controls 애플리케이션을 생성하는 것입니다. 그 후, Qt Creator 또는 Qt Extension for Visual Studio Code 에서 고도를 저장하는 C++ 타입을 추가합니다. 이 타입은 우리 스타일이 지원하는 모든 컨트롤에 사용될 것이며, 나중에 다른 부착 속성을 추가할 수도 있으므로, 이를 MyStyle이라고 명명하겠습니다. 다음은 MyStyle.h 입니다:

#ifndef MYSTYLE_H
#define MYSTYLE_H

#include <QObject>
#include <QtQml>

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

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

    static MyStyle *qmlAttachedProperties(QObject *object);

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

signals:
    void elevationChanged();

private:
    int m_elevation;
};

QML_DECLARE_TYPEINFO(MyStyle, QML_HAS_ATTACHED_PROPERTIES)

#endif // MYSTYLE_H

MyStyle.cpp:

#include "mystyle.h"

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

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

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

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

    m_elevation = elevation;
    emit elevationChanged();
}

MyStyle 유형은 인스턴스화되어서는 안 되며, 대신 첨부 속성을 위해 사용되어야 한다는 점에서 특별합니다. 이러한 이유로, main.cpp 에서 다음과 같은 방식으로 이 유형을 등록합니다:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "mystyle.h"

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

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

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

    return app.exec();
}

그런 다음 $QTDIR/qml/QtQuick/Controls/Basic/ 의 Basic 스타일에서 Button.qml 를 복사하여 프로젝트 디렉토리의 새로운 myproject 폴더로 이동시킵니다. 방금 복사한 Button.qml 를 QML 파일이 포함된 리소스 파일인 qml.qrc 에 추가합니다.

다음으로, Button의 background 델리게이트에 드롭 섀도우를 추가합니다:

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

background: Rectangle {
    // ...

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

다음 사항에 유의하십시오:

  • 엘리베이션이 적용된 상태에서는 드롭 섀도우를 사용하지 않습니다. 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 에 속한 부착 속성을 사용하고 있기 때문에 필요한 것임을 유의하십시오. import 문을 제거하더라도 두 버튼 모두 사용자 정의 스타일을 사용합니다.

사용자 지정 참조

다음 코드 조각들은 ‘컨트롤 사용자 지정’ 섹션과 동일한 접근 방식을 사용하여 Basic 스타일의 컨트롤을 사용자 지정한 예시를 보여줍니다. 이 코드는 사용자 정의 모양과 느낌을 구현하기 위한 출발점으로 활용할 수 있습니다.

참고: macOS 및 Windows스타일은 사용자 정의에 적합하지 않습니다. 대신, 사용자 정의 컨트롤은 항상 모든 플랫폼에서 사용할 수 있는 단일 스타일(예: Basic Style, Fusion Style, Imagine Style, Material Style, Universal Style)을 기반으로 하는 것이 권장됩니다. 이렇게 하면 애플리케이션이 어떤 스타일로 실행되든 항상 동일한 모양을 유지할 수 있습니다. 다른 스타일을 사용하는 방법에 대해서는 Qt Quick Controls 의 ‘스타일 사용’을 참조하십시오. 또는 사용자 정의 스타일을 직접 만들 수도 있습니다.

참고: 컨트롤의 크기를 조정할때 , 실제 콘텐츠 크기는 일반적으로 동일하게 유지됩니다. 예를 들어, 일부 스타일은 버튼이 컨트롤의 경계를 채우도록 하면서도, CheckBox 의 확인 표시기와 텍스트를 가로 및/또는 세로로 중앙 정렬할 수 있습니다. 기본 크기 조정 동작이 바람직하지 않은 경우, 해당 델리게이트를 재정의하십시오.

ApplicationWindow 사용자 지정

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

import QtQuick
import QtQuick.Controls.Basic

ApplicationWindow {
    visible: true

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

BusyIndicator 사용자 지정

BusyIndicator 두 개의 시각적 항목, 즉 background 와 contentItem 로 구성됩니다.

사용자 정의 스타일의 작업 중 표시기

import QtQuick
import QtQuick.Controls.Basic

BusyIndicator {
    id: control

    contentItem: Item {
        implicitWidth: 64
        implicitHeight: 64

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

            Behavior on opacity {
                OpacityAnimator {
                    duration: 250
                }
            }

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

            Repeater {
                id: repeater
                model: 6

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

                    required property int index

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

Button 사용자 지정

Button은 두 개의 시각적 항목, 즉 background 와 contentItem 로 구성됩니다.

사용자 정의 스타일의 버튼

import QtQuick
import QtQuick.Controls.Basic

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

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

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

CheckBox 사용자 지정

CheckBox 세 가지 시각적 항목( background, contentItem 및 indicator)으로 구성됩니다.

사용자 정의 스타일의 체크박스

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

CheckDelegate 사용자 정의

CheckDelegate background, , 의 세 가지 시각적 항목으로 구성됩니다. contentItem indicator

사용자 정의 스타일의 체크 델리게이트

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

ComboBox 사용자 정의

ComboBox background, , , 및 로 구성됩니다. contentItem popup indicator delegate

사용자 정의 스타일의 콤보 박스

pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Controls.Basic

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

    delegate: ItemDelegate {
        id: delegate

        required property var model
        required property int index

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

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

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

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

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

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

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

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

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

            ScrollIndicator.vertical: ScrollIndicator { }
        }

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

ComboBox Model Roles 에서 설명한 바와 같이, ComboBox 는 여러 유형의 모델을 지원합니다.

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

text: model[control.textRole]

특정 ` textRole `와 선택한 역할을 제공하는 구조화된 데이터가 포함된 모델을 지정하면, 이 표현식은 일반적인 속성 조회로 작동합니다. 문자열 목록과 같은 단일 데이터가 포함된 모델과 빈 ` textRole`를 지정하면, 이 표현식은 ` modelData`를 반환합니다.

DelayButton 사용자 지정

DelayButton DelayButton은 ‘ background ’과 ‘ contentItem ’이라는 두 가지 시각적 항목으로 구성됩니다.

사용자 정의 스타일의 지연 버튼

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

        Canvas {
            id: canvas
            anchors.fill: parent

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

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

Dial 사용자 지정

Dial은 background 와 handle 라는 두 가지 시각적 항목으로 구성됩니다.

맞춤형 디자인의 다이얼

import QtQuick
import QtQuick.Controls.Basic

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

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

DoubleSpinBox 사용자 정의

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

Drawer 사용자 지정

Drawer에는 시각적 요소인 background 를 포함할 수 있습니다.

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

Frame 사용자 정의

Frame은 하나의 시각적 항목인 background 로 구성됩니다.

맞춤형 디자인 프레임

import QtQuick
import QtQuick.Controls.Basic

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

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

GroupBox 사용자 정의

GroupBox 두 개의 시각적 항목, 즉 background 와 label 로 구성됩니다.

사용자 정의 스타일의 그룹 박스

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

ItemDelegate 사용자 지정

ItemDelegate 두 개의 시각적 항목( background 및 contentItem)으로 구성됩니다.

사용자 정의 스타일 적용 항목 델리게이트

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

라벨 사용자 지정

라벨에는 시각적 background 항목이 포함될 수 있습니다.

맞춤형 디자인 라벨

import QtQuick
import QtQuick.Controls.Basic

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

메뉴 사용자 정의

사용자 정의 스타일의 메뉴

import QtQuick
import QtQuick.Controls.Basic

Menu {
    id: menu

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

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

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

    topPadding: 2
    bottomPadding: 2

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

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

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

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

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

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

메뉴 바 사용자 정의

MenuBar 에는 시각적 항목인 background 이 포함될 수 있으며, MenuBarItem 은 background 와 contentItem 라는 두 가지 시각적 항목으로 구성됩니다.

‘파일’ 및 ‘편집’ 메뉴가 포함된 사용자 정의 스타일의 메뉴 모음

import QtQuick
import QtQuick.Controls.Basic

MenuBar {
    id: menuBar

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

    delegate: MenuBarItem {
        id: menuBarItem

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

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

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

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

PageIndicator 사용자 지정

PageIndicator background, 및 로 구성됩니다. contentItem delegate

여러 페이지를 표시하는 사용자 정의 스타일의 페이지 표시기

import QtQuick
import QtQuick.Controls.Basic

PageIndicator {
    id: control
    count: 5
    currentIndex: 2

    delegate: Rectangle {
        implicitWidth: 8
        implicitHeight: 8

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

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

        required property int index

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

Pane 사용자 지정

Pane은 background 로 구성됩니다.

장식용 배경이 적용된 사용자 정의 스타일 패널

import QtQuick
import QtQuick.Controls.Basic

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

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

팝업 사용자 정의

팝업은 background 와 contentItem 로 구성됩니다.

테두리와 그림자가 적용된 사용자 정의 스타일의 팝업

import QtQuick
import QtQuick.Controls.Basic

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

진행률 표시줄 사용자 정의

ProgressBar 두 가지 시각적 요소, 즉 ' background '과 ' contentItem'으로 구성됩니다.

일부 완료 상태를 표시하는 사용자 정의 스타일의 진행률 표시줄

import QtQuick
import QtQuick.Controls.Basic

ProgressBar {
    id: control
    value: 0.5
    padding: 2

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

    contentItem: Item {
        implicitWidth: 200
        implicitHeight: 4

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

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

            Row {
                spacing: 20

                Repeater {
                    model: control.width / 40 + 1

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

위에서 contentItem은 indeterminate 진행률 표시줄 상태를 나타내기 위해 애니메이션 처리됩니다.

RadioButton 사용자 정의

RadioButton 세 가지 시각적 항목, 즉 background, contentItem 및 indicator 으로 구성됩니다.

선택된 상태의 사용자 정의 스타일 라디오 버튼

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

RadioDelegate 사용자 지정

RadioDelegate 는 세 가지 시각적 항목, 즉 background, contentItem 및 indicator 으로 구성됩니다.

목록 내 사용자 정의 스타일의 라디오 델리게이트

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

RangeSlider 사용자 지정

RangeSlider 는 세 가지 시각적 요소, 즉 background, first.handle 및 second.handle 로 구성됩니다.

사용자 정의 스타일의 범위 슬라이더

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

RoundButton 사용자 정의

RoundButton 는 Button과 동일한 방식으로 사용자 정의할 수 있습니다.

ScrollBar 사용자 지정

ScrollBar background 와 , 이렇게 두 가지 시각적 항목으로 구성됩니다. contentItem

사용자 정의 스타일의 스크롤 바

import QtQuick
import QtQuick.Controls.Basic

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

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

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

ScrollIndicator 사용자 지정

ScrollIndicator background 와 라는 두 가지 시각적 항목으로 구성됩니다. contentItem

사용자 정의 스타일의 스크롤 표시기

import QtQuick
import QtQuick.Controls.Basic

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

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

ScrollView 사용자 정의

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

사용자 정의 스타일의 스크롤 뷰

ScrollView {
    id: control

    width: 200
    height: 200
    focus: true

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

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

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

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

슬라이더 사용자 지정

슬라이더는 두 가지 시각적 항목, 즉 background 와 handle 로 구성됩니다.

사용자 정의 스타일의 슬라이더

import QtQuick
import QtQuick.Controls.Basic

Slider {
    id: control
    value: 0.5

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

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

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

SpinBox 사용자 지정

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

사용자 정의 스타일의 스핀 박스

import QtQuick
import QtQuick.Controls.Basic

SpinBox {
    id: control
    value: 50
    editable: true

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

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

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

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

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

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

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

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

SplitView 사용자 정의

SplitView handle 델리게이트로 구성됩니다.

사용자 정의 스타일의 분할 보기

SplitView {
    id: splitView
    anchors.fill: parent

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

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

StackView 사용자 정의

StackView 은 시각적 background 항목을 가질 수 있으며, push, pop 및 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
        }
    }
}

SwipeDelegate 사용자 정의

SwipeDelegate 는 background, contentItem, indicator, swipe.left, swipe.right, swipe.behind 등 6개의 시각적 항목으로 구성됩니다.

사용자 정의 스타일의 스와이프 델리게이트

import QtQuick
import QtQuick.Controls.Basic

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

    Component {
        id: component

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

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

    swipe.left: component
    swipe.right: component

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

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

SwipeView 사용자 정의

SwipeView 에는 시각적 background 항목이 포함될 수 있습니다. 탐색 기능은 contentItem 를 통해 구현됩니다.

import QtQuick
import QtQuick.Controls.Basic

SwipeView {
    id: control

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

Switch 사용자 정의

Switch는 background, contentItem, indicator 의 세 가지 시각적 항목으로 구성됩니다.

사용자 정의 스타일의 스위치

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

SwitchDelegate 사용자 정의

SwitchDelegate 다음 세 가지 시각적 항목으로 구성됩니다: background, contentItem 및 indicator.

사용자 정의 스타일의 스위치 델리게이트

import QtQuick
import QtQuick.Controls.Basic

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

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

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

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

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

TabBar 사용자 정의

TabBar 두 가지 시각적 항목, 즉 background 및 contentItem 으로 구성됩니다.

사용자 정의 스타일의 탭 바

import QtQuick
import QtQuick.Controls.Basic

TabBar {
    id: control

    background: Rectangle {
        color: "#eeeeee"
    }

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

TabButton 사용자 정의

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

TextArea 사용자 정의

TextArea background 항목으로 구성됩니다.

사용자 정의 스타일의 텍스트 영역

import QtQuick
import QtQuick.Controls.Basic

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

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

TextField 사용자 정의

TextField background 항목으로 구성됩니다.

사용자 정의 스타일의 텍스트 입력란

import QtQuick
import QtQuick.Controls.Basic

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

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

ToolBar 사용자 지정

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

사용자 정의 스타일의 도구 모음

ToolBar {
    id: control

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

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

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

ToolButton 사용자 지정

ToolButton 두 개의 시각적 항목( background 및 contentItem)으로 구성됩니다.

사용자 정의 스타일의 도구 버튼

import QtQuick
import QtQuick.Controls.Basic

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

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

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

ToolSeparator 사용자 지정

ToolSeparator 두 개의 시각적 항목( background 및 contentItem)으로 구성됩니다.

사용자 정의 스타일의 도구 구분선

ToolBar {
    RowLayout {
        anchors.fill: parent

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

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

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

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

        Item {
            Layout.fillWidth: true
        }
    }
}

툴팁 사용자 지정

ToolTip 두 가지 시각적 항목, 즉 background 와 contentItem 로 구성됩니다.

import QtQuick
import QtQuick.Controls.Basic

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

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

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

참고: attached ToolTip 를 사용자정의하려면 , 이를 사용자 정의 스타일의 일부로 제공해야 합니다. ToolTip 를 일회성으로 사용자 정의하려면 Custom Tool Tips 를 참조하십시오.

Tumbler 사용자 정의

Tumbler는 background, contentItem, delegate 의 세 가지 시각적 항목으로 구성됩니다.

맞춤 디자인 텀블러

import QtQuick
import QtQuick.Controls.Basic

Tumbler {
    id: control
    model: 15

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

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

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

        required property var modelData
        required property int index
    }

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

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

자신만의 contentItem을 정의하려면 ListView 또는 PathView 중 하나를 루트 항목으로 사용하십시오. 래핑형 Tumbler의 경우 PathView 를 사용하십시오:

Tumbler {
    id: tumbler

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

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

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

줄 바꿈이 없는 텀블러를 사용하려면 ListView 을 참조하세요:

Tumbler {
    id: tumbler

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

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

TableViewDelegate 사용자 정의

TableViewDelegate 는 ItemDelegate 를 상속받으므로, background 와 contentItem 라는 두 가지 시각적 요소로 구성됩니다.

기본 편집 델리게이트가 제공하는 기능 이외의 추가 기능이 필요한 경우, 언제든지 editDelegate 에 사용자 정의 편집 델리게이트를 할당할 수 있습니다.

사용자 정의 스타일의 테이블 뷰 델리게이트

delegate: TableViewDelegate {
    id: tableCell

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

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

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

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

        RowLayout {
            anchors.fill: parent

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

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

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

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

        Component.onCompleted: textField.selectAll()

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

HeaderViewDelegate 사용자 지정

HeaderViewDelegate는 TableViewDelegate 를 상속받으므로, background 와 contentItem 라는 두 가지 항목으로 구성됩니다. 이 항목들은 언제든지 임의의 항목으로 사용자 정의할 수 있습니다.

사용자 정의 스타일의 헤더 뷰 델리게이트

다음은 수평 헤더 뷰 델리게이트를 사용자 정의하는 예시입니다:

delegate: HorizontalHeaderViewDelegate {
    id: horizontalDelegate

    required property int index
    required property string modelData

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

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

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

다음은 수직 헤더 뷰 델리게이트를 사용자 정의하는 예시입니다:

delegate: VerticalHeaderViewDelegate {
    id: verticalDelegate

    required property int index

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

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

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

StyleKit을 사용한 컨트롤 스타일 지정

Qt Labs StyleKit 모듈은 Qt Quick Templates 2를 기반으로 하여 Qt Quick Controls 의 스타일을 지정하기 위한 일련의 QML 유형을 제공합니다. 이를 통해 테마, 상태 기반 스타일링 및 전환 효과를 포함하여 단일 Style 객체로 모든 컨트롤에 대한 완전한 시각적 스타일을 정의할 수 있습니다. StyleKit은 기본 템플릿 구현을 자동으로 처리하므로, 사용자는 색상, 크기, 테두리, 그림자 등 순수하게 시각적인 측면에만 집중할 수 있습니다.

Qt Labs StyleKit 모듈은 Qt 6.11의 기술 미리보기(Technology Preview) 모듈입니다.

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