StyleKit Features Overview

This page gives a brief introduction to the main features of StyleKit. For a complete reference of all available types and properties, see the QML Types page.

Creating a Style

A Style is a QML object that describes the visual appearance of all Qt Quick Controls in your application — button, slider, checkBox, and so on. Each has its own group in the style where you can set properties such as colors, sizes, radii, and shadows for the visual parts that make up the control.

The control group is special, since it acts as a fallback for all the other control groups. If you leave out some of the properties for a slider, for example, they will be read from control instead. Properties not set in slider or control fall back further to the fallback style which is a complete style similar to the Basic Style. This means you don't necessarily need to style all the available controls, only the ones you want to customize — the fallback system takes care of styling the rest:

 // PlainStyle.qml

 Style {
     control {
         // control does not map to an actual Qt Quick Control, but is a shared
         // fallback for all other controls. Use it to define styling that is
         // common to all of them. Unset properties fall back to Style.fallbackStyle.
         padding: 6
         text.color: "white"
         background {
             radius: 4
             border.color: "gray"
         }
         indicator {
             implicitWidth: 20
             implicitHeight: 20
             border.width: 1
             radius: 3
         }
         handle {
             implicitWidth: 20
             implicitHeight: 20
             radius: 10
         }
     }

     slider {
         // slider defines the styling for a Qt Quick Slider.
         // Unset properties fall back to control.
         handle.color: "white"
         indicator {
             implicitWidth: Style.Stretch
             implicitHeight: 6
             color: "steelblue"
             foreground.color: "skyblue"
         }
     }

     abstractButton {
         // abstractButton does not map to an actual Qt Quick Control, but
         // is a shared fallback for button-like controls, such as button,
         // radioButton, checkBox). Use it to define styling that is common
         // to all of them. Unset properties fall back to control.
         background.shadow {
             opacity: 0.6
             verticalOffset: 2
             horizontalOffset: 2
             color: "gray"
         }
     }

     button {
         // button defines the styling for a Qt Quick Button.
         // Unset properties fall back to abstractButton.
         background {
             implicitWidth: 120
             color: "lightsteelblue"
             gradient: Gradient {
                 GradientStop { position: 0.0; color: Qt.alpha("black", 0.0)}
                 GradientStop { position: 1.0; color: Qt.alpha("black", 0.2)}
             }
         }
     }

     // Controls left undefined — such as radioButton, checkBox, or
     // switchControl — fall back to their immediate base type, which in
     // this style will be either abstractButton or control directly.
 }

Activating a Style

To activate the style, assign it to StyleKit.style on the root ApplicationWindow. All controls in the application then pick it up automatically:

 // Main.qml

 import QtQuick
 import Qt.labs.StyleKit

 ApplicationWindow {
     id: app
     width: 1024
     height: 800
     visible: true

     // Assign the style to be used
     StyleKit.style: PlainStyle {}

     // Controls are used as normal
     Column {
         anchors.fill: parent
         anchors.margins: 10
         spacing: 10

         Button {
             text: "Button"
         }

         Slider {
             width: 200
         }
     }
 }

Control States

Controls change appearance depending on user interaction — a button looks different when hovered, pressed, or disabled. StyleKit lets you express this in the style by placing the state name in front of the affected properties, to give them alternative values when the control is in that state.

States can be nested, and a more specific combination (e.g. hovered.checked) takes precedence over its individual components:

 button {
     text.color: "aliceblue"
     background.color: "cornflowerblue"
     pressed.background.color: "deepskyblue"
     hovered.background.color: "lightskyblue"
     focused.background.color: "lightsteelblue"
     checked.background.color: "royalblue"
     highlighted.background.color: "lightblue"
     disabled {
         background.color: "lightgray"
         background.border.color: "darkgray"
     }

     // Nested states, such as hovered.checked in this case, takes
     // precedence over both hovered and checked:
     hovered.checked.background.color: "steelblue"
 }

State Transitions

State changes can be animated by setting the transition property on a control style. A StyleAnimation provides a convenient way to animate groups of related style properties, such as all background or indicator colors, but you can use standard QML animations such as ColorAnimation and NumberAnimation as well:

 comboBox {
     background.color: "lightgray"
     hovered.background.color: "plum"

     indicator.color: "white"
     hovered.indicator.color: "pink"
     hovered.indicator.border.width: 4

     transition: Transition {
         StyleAnimation {
             animateBackgroundColors: true
             animateIndicatorColors: true
             animateIndicatorBorder: true
             easing.type: Easing.OutQuad
             duration: 500
         }
     }
 }

Theming

StyleKit has built-in support for light and dark themes through the light and dark properties on a Style. Similar to a Style, a Theme lets you define the style each control should have when that theme is active. Properties that are not set in a theme will fall back to be read from the style:

 Style {
     light: Theme {
         applicationWindow.background.color: "gainsboro"
         control.text.color: "#202020"
         control.background.color: "#f0f0f0"
         control.background.border.color: "#d0d0d0"
         button.hovered.background.color: "#4a90d9"
         radioButton.indicator.foreground.color: "#d0d0d0"
     }

     dark: Theme {
         applicationWindow.background.color: "#2b2b2b"
         control.text.color: "#e0e0e0"
         control.background.color: "#404040"
         control.background.border.color: "#606060"
         button.hovered.background.color: "#6ab0f9"
         radioButton.indicator.foreground.color: "#606060"
     }
 }

Custom Themes

Beyond light and dark, you can define any number of additional themes using CustomTheme. Each CustomTheme has a name and holds a Theme object with the same structure as the built-in themes:

 Style {
     CustomTheme {
         name: "HighContrast"
         theme: Theme {
             control.background.color: "white"
             control.background.border.color: "black"
             control.background.border.width: 2
         }
     }

     CustomTheme {
         name: "Sepia"
         theme: Theme {
             control.text.color: "#5b4636"
             control.background.color: "#f4ecd8"
             control.background.border.color: "#c8b99a"
             applicationWindow.background.color: "#efe6d0"
         }
     }
 }

To switch themes at runtime, set Style.themeName from a QML file in your application to the name of the desired theme. The Style.themeNames property lists all available theme names, which makes it straightforward to populate a selector control:

 ComboBox {
     model: StyleKit.style.themeNames
     onCurrentTextChanged: StyleKit.style.themeName = currentText
 }

To activate a theme at application start-up, set themeName when assigning the style:

 ApplicationWindow {
     width: 1024
     height: 800
     visible: true

     StyleKit.style: MyStyleKitStyle {
         themeName: "HighContrast"
     }
 }

Style Variations

A StyleVariation lets you define alternative styling for parts of the application. This is useful when you need to style controls differently when they are children of, for example, a ToolBar or a GroupBox, or if you want to implement style hints that the application can optionally apply to some of the controls.

There are two types of style variations: type variations and instance variations.

Type Variations

A type variation contains alternative styling for controls that are children (or descendants) of another control type. This means that if a StyleVariation contains styling for a button, and it's added to the variations property of a frame, StyleKit will style all Buttons that are children of Frames in the application accordingly:

 Style {
     frame {
         variations: StyleVariation {
             button {
                 text.color: "ghostwhite"
                 background.border.width: 0
                 background.color: "slategrey"
             }
         }
     }

     groupBox {
         // groupBox falls back to frame. Therefore, if the varations set on a
         // frame is not wanted on a groupBox, just override it and set it back to [].
         variations: []
     }
 }

Instance Variations

Named StyleVariations can be applied to individual controls in the application using the StyleVariation.variations attached property. When applied, the control itself, and all its descendants, will receive the alternative styling. This is different from a type variation, which affects all controls of a certain type, such as all Frames. Instance variations will only affect the control instance (and the descendants) on which they're attached:

 Style {
     StyleVariation {
         name: "mini"
         control {
             padding: 2
             background.implicitHeight: 15
             indicator.implicitWidth: 15
             indicator.implicitHeight: 15
             handle.implicitWidth: 15
             handle.implicitHeight: 15
         }
     }

     StyleVariation {
         name: "alert"
         abstractButton.background.color: "red"
     }
 }

Apply them to controls in your application:

 GroupBox {
     title: "Mini controls"
     StyleVariation.variations: ["mini"]

     Row {
         spacing: 10
         Button { text: "Save" }
         CheckBox { text: "Option" }
         // This button also has the "alert" variation, in addition to "mini"
         Button {
             text: "Delete"
             StyleVariation.variations: ["alert"]
         }
     }
 }

Custom Controls

If your application includes custom controls that are not part of Qt Quick Controls, you can still integrate them with StyleKit. Just add a CustomControl for each of them and style them the same way you style the built-in controls:

 // MyStyle.qml

 Style {
     id: style
     readonly property int myControlType: 0
     CustomControl {
         controlType: style.myControlType
         background {
             implicitWidth: 120
             implicitHeight: 30
             radius: 0
         }
         hovered.background.color: "lightslategray"
         pressed.background.color: "skyblue"
     }
 }

In the control's implementation, use a StyleReader with the matching controlType to read back the style properties. The correct property values are resolved by taking Themes, StyleVariations, fallback types, and property propagation into account. For a style reader to do this, it needs to know the state of your control. Therefore bind your control's state to the relevant StyleReader properties such as hovered and pressed. Each time there is a state change, any affected style properties will be updated, causing your control to repaint:

 // Main.qml

 component MyControl : Rectangle {
     StyleReader {
         id: styleReader
         controlType: StyleKit.style.myControlType
         hovered: hoverHandler.hovered
         pressed: tapHandler.pressed
         palette: app.palette
     }

     HoverHandler { id: hoverHandler }
     TapHandler { id: tapHandler }

     implicitWidth: styleReader.background.implicitWidth
     implicitHeight: styleReader.background.implicitHeight
     color: styleReader.background.color
     radius: styleReader.background.radius

     Text {
         font: styleReader.font
         anchors.centerIn: parent
         text: "ok"
     }
 }

Custom Delegates

Each visual part of a control — background, handle, indicator, etc — is rendered by a delegate. By default StyleKit uses StyledItem for rendering, but you can replace it entirely with you own QML component.

The component needs to define two required properties that StyleKit populates automatically:

  • delegateStyle — the DelegateStyle that carries the resolved style properties (color, radius, implicit size, etc.)
  • control — the Qt Quick Control the delegate belongs to

You then assign the component to the delegate property of the visual part that you want to affect:

 // import QtQuick.Templates as T

 slider {
     handle.delegate: Rectangle {
         id: handle
         required property DelegateStyle delegateStyle
         required property T.Slider control
         implicitWidth: delegateStyle.implicitWidth
         implicitHeight: delegateStyle.implicitHeight
         radius: delegateStyle.radius
         color: delegateStyle.color
         Text {
             anchors.centerIn: parent
             text: handle.control.value.toFixed(0)
         }
     }
 }

Overlay and Underlay

If you only want to augment the default rendering rather than replace it completely, use StyledItem as the root of your delegate. Any children you add to StyledItem are drawn on top of (overlay) the default rendering:

 Style {
     component Star : Shape {
         id: star
         property color color
         ShapePath {
             fillColor: star.color
             scale: Qt.size(star.width, star.height)
             PathMove { x: 0.50; y: 0.00 }
             PathLine { x: 0.59; y: 0.35 }
             PathLine { x: 0.97; y: 0.35 }
             PathLine { x: 0.66; y: 0.57 }
             PathLine { x: 0.78; y: 0.91 }
             PathLine { x: 0.50; y: 0.70 }
             PathLine { x: 0.22; y: 0.91 }
             PathLine { x: 0.34; y: 0.57 }
             PathLine { x: 0.03; y: 0.35 }
             PathLine { x: 0.41; y: 0.35 }
             PathLine { x: 0.50; y: 0.00 }
         }
     }

     button {
         background.delegate: StyledItem {
             width: parent.width
             height: parent.height
             // Draw a star on top the default rendering
             Star {
                 anchors.fill: parent
                 color: "gold"
             }
         }
     }
 }

To draw something underneath the default rendering instead, make your delegate an Item, place the extra content first, and embed a StyledItem as a child to render the default appearance on top:

 Style {
     component Star : Shape {
         id: star
         property color color
         ShapePath {
             fillColor: star.color
             scale: Qt.size(star.width, star.height)
             PathMove { x: 0.50; y: 0.00 }
             PathLine { x: 0.59; y: 0.35 }
             PathLine { x: 0.97; y: 0.35 }
             PathLine { x: 0.66; y: 0.57 }
             PathLine { x: 0.78; y: 0.91 }
             PathLine { x: 0.50; y: 0.70 }
             PathLine { x: 0.22; y: 0.91 }
             PathLine { x: 0.34; y: 0.57 }
             PathLine { x: 0.03; y: 0.35 }
             PathLine { x: 0.41; y: 0.35 }
             PathLine { x: 0.50; y: 0.00 }
         }
     }

     slider.handle.delegate: Item {
         // Since the root item is not a StyledItem, the following
         // required properties must be defined explicitly:
         required property DelegateStyle delegateStyle
         required property QtObject control

         implicitWidth: delegateStyle.implicitWidth
         implicitHeight: delegateStyle.implicitHeight
         width: parent.width
         height: parent.height
         scale: delegateStyle.scale
         rotation: delegateStyle.rotation
         visible: delegateStyle.visible

         // Draw a star underneath the default handle delegate
         Star {
             width: parent.width * 2
             height: parent.height * 2
             anchors.centerIn: parent
             color: "gold"
         }

         StyledItem {
             // Forward delegateStyle into StyledItem. StyledItem doesn't
             // use 'control' for anything, so it can be omitted.
             delegateStyle: parent.delegateStyle
         }
     }
 }

Custom Data

Custom delegates sometimes need additional styling properties that go beyond the built-in ones — for example to style child items that StyleKit knows nothing about. The data property makes this possible. It can hold any QtObject, so it can carry whatever information your delegate needs.

Like all the other style properties, data participates in style property resolution — the delegate always receives the object that matches the current control state, active theme, and style variations. Unlike the built-in properties, data is propagated as a whole — individual properties inside the object do not propagate separately:

 component OverlayData : QtObject {
     property color overlayColor
 }

 toolButton {
     background.delegate: StyledItem {
         id: custom
         Text {
             color: custom.delegateStyle.data.overlayColor
             font.pixelSize: 30
             text: "シ"
         }
     }
     background.data: OverlayData {
         overlayColor: "sandybrown"
     }
     hovered.background.data: OverlayData {
         overlayColor: "magenta"
     }
 }