// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqstylekitcontrol_p.h"
#include "qqstylekitstyle_p.h"
#include "qqstylekittheme_p.h"
#include "qqstylekitcontrolproperties_p.h"
#include "qqstylekitpropertyresolver_p.h"

QT_BEGIN_NAMESPACE

// ************* ControlStyleProperties ****************

/*!
    \qmltype ControlStyleProperties
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the stylable properties for a control.

    ControlStyleProperties defines the stylable properties for a
    control: the visual building blocks \l background, \l indicator, \l handle,
    and \l text, as well as layout properties like \l padding, \l spacing,
    and \l transition.

    The style properties are accessed through inherited types such as
    \l ControlStyle and \l StyleReader.

    \labs

    \sa ControlStyle, StyleReader, ControlStateStyle, DelegateStyle
*/

/*!
    \qmlproperty DelegateStyle ControlStyleProperties::background

    Grouped property for styling the background of a control.

    The background delegate is typically the main visual rectangle behind
    the control. Use it to set \l {DelegateStyle::color}{colors},
    \l {DelegateStyle::border}{borders},
    \l {DelegateStyle::radius}{radii},
    \l {DelegateStyle::shadow}{shadows},
    \l {DelegateStyle::gradient}{gradients},
    and \l {DelegateStyle::image}{images}.

    \note The default \l {Style::fallbackStyle}{fallback style} sets \c {background.visible}
    to \c false for controls that typically should not draw a background,
    such as \l [QtQuickControls]{CheckBox}, \l [QtQuickControls]{RadioButton},
    and \l [QtQuickControls]{Slider}. To show their background, set
    \c {background.visible} to \c true explicitly.
*/

/*!
    \qmlproperty real ControlStyleProperties::bottomPadding

    The \l [QtQuickControls]{Control Layout}{bottom padding}
    of the control. If not set, falls back to \l padding.

    \sa padding, topPadding, leftPadding, rightPadding
*/

/*!
    \qmlproperty HandleStyle ControlStyleProperties::handle

    Grouped property for styling the handle of a control.

    The handle is used by controls such as \l [QtQuickControls]{Switch},
    \l [QtQuickControls]{Slider}, and \l [QtQuickControls]{RangeSlider}.
    For a RangeSlider, the two handles can be styled individually through
    \l {HandleStyle::first}{handle.first} and \l {HandleStyle::second}{handle.second}.

    \sa HandleStyle, DelegateStyle
*/

/*!
    \qmlproperty IndicatorStyle ControlStyleProperties::indicator

    Grouped property for styling the indicator of a control. For a
    \l {AbstractStylableControls::}{checkBox}, the indicator is the
    frame, and its \l {IndicatorStyle::}{foreground} is the check mark.
    For a \l {AbstractStylableControls::}{slider}, the indicator is
    the groove, and the foreground is the fill.

    \sa DelegateStyle
*/

/*!
    \qmlproperty real ControlStyleProperties::leftPadding

    The \l [QtQuickControls]{Control Layout}{left padding} of the control. If not set, falls back to
    \l padding.

    \sa padding, rightPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real ControlStyleProperties::padding

    The uniform spacing between the
    \l [QtQuickControls]{Control Layout}{control's content area and the
    bounds of the control}. Setting this provides a default value for
    \l leftPadding, \l rightPadding, \l topPadding, and \l bottomPadding.
    Each side can be overridden individually.

    \sa leftPadding, rightPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real ControlStyleProperties::rightPadding

    The \l [QtQuickControls]{Control Layout}{right padding} of the control.
    If not set, falls back to \l padding.

    \sa padding, leftPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real ControlStyleProperties::spacing

    The spacing between visual elements inside the control, for example
    between an indicator and a label.
*/

/*!
    \qmlproperty TextStyle ControlStyleProperties::text

    Grouped property for styling the text label of a control.
*/

/*!
    \qmlproperty real ControlStyleProperties::topPadding

    The \l [QtQuickControls]{Control Layout}{top padding}
    of the control. If not set, falls back to \l padding.

    \sa padding, bottomPadding, leftPadding, rightPadding
*/

/*!
    \qmlproperty Transition ControlStyleProperties::transition

    A \l [QtQuick]{Transition} used to animate style properties when the
    control enters a new state, such as \l {ControlStateStyle::}{hovered}
    or \l {ControlStateStyle::}{pressed}. If set
    to \c null (the default), property changes are applied immediately
    without animation.

    \snippet DelegateStyle_transitions.qml custom transition

    To avoid repeating the same target properties for each delegate,
    StyleKit provides \l StyleAnimation for convenience, which can be
    used instead of, or in combination with, the
    \l {Animation and Transitions in Qt Quick}{standard animations:}

    \snippet DelegateStyle_transitions.qml transition

    Also note that \l ColorAnimation has a special feature that
    animates \e all color properties that changed during a
    state change if \l {PropertyAnimation::}{property} and
    \l {PropertyAnimation::}{properties} are left unset.
*/

// ************* DelegateStyle ****************

/*!
    \qmltype DelegateStyle
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the visual appearance of an element within a control.

    DelegateStyle describes the visual appearance of a visual element
    within a \l ControlStyle, such as its \l {ControlStyleProperties::}{background},
    \l {ControlStyleProperties::}{indicator}, or a sub-element like
    \l {IndicatorStyle::foreground}{indicator.foreground}.
    It provides properties for controlling
    \l {DelegateStyle::implicitWidth}{size},
    \l {DelegateStyle::}{color}, \l {DelegateStyle::}{border},
    \l {DelegateStyle::}{radius}, \l {DelegateStyle::}{shadow},
    \l {DelegateStyle::}{image}, \l {DelegateStyle::}{opacity}, and more.

    \labs

    \sa ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty Qt::Alignment DelegateStyle::alignment

    The alignment of the delegate within its parent. The default
    value is \c {Qt.AlignLeft | Qt.AlignVCenter}.
*/

/*!
    \qmlproperty BorderStyle DelegateStyle::border

    Grouped property for styling the border of this delegate.
*/

/*!
    \qmlproperty real DelegateStyle::bottomLeftRadius

    The bottom-left corner radius. If not set, falls back to \l radius.

    \sa radius, topLeftRadius, topRightRadius, bottomRightRadius
*/

/*!
    \qmlproperty real DelegateStyle::bottomMargin

    The bottom margin of this delegate. If not set, falls back to
    \l margins.

    \sa margins, topMargin, leftMargin, rightMargin
*/

/*!
    \qmlproperty real DelegateStyle::bottomRightRadius

    The bottom-right corner radius. If not set, falls back to \l radius.

    \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius
*/

/*!
    \qmlproperty bool DelegateStyle::clip

    Whether the delegate clips its contents. The default value is \c false.
*/

/*!
    \qmlproperty color DelegateStyle::color

    The fill color of this delegate. The default value is \c transparent.

    Unlike a Quick \l [QtQuick]{Rectangle}, where a gradient replaces
    the color, StyleKit draws the \l gradient on top of the color. This
    means you can use a semi-transparent gradient as an overlay (for
    example, a subtle shading effect) while the color shows through
    underneath.

    \snippet ControlStateStyle_properties.qml color and gradient

    \sa gradient, opacity
*/

/*!
    \qmlproperty QObject DelegateStyle::data

    An arbitrary object that can be used for passing
    custom data from the style to a custom \l delegate component.

    This allows you to define custom style properties beyond what the
    StyleKit API provides. The data object can vary between states and
    themes, making it possible to style elements in a custom \l delegate
    that are not covered by the built-in properties.

    The following snippet uses a custom delegate that draws an overlay
    \l Text whose color varies depending on the control's state. The delegate
    inherits \l StyledItem, which is optional but ensures the rest of
    the button background is rendered normally.

    \snippet DelegateStyle_delegates.qml data

    \note The \c data object is propagated as a whole. Unlike regular style
    properties, individual properties \e inside the data object do not
    propagate separately.

    \sa delegate
*/

/*!
    \qmlproperty Component DelegateStyle::delegate

    The delegate used to render the DelegateStyle in a
    \l {Qt Quick Controls}{Qt Quick Control}.

    The default value is \c null, in which case \l StyledItem is used
    for rendering instead. If you want to hide the delegate altogether
    (and prevent if from being created), set \l visible to \c false.

    The delegate needs to define the following required properties:
    \qml
    required property DelegateStyle delegateStyle
    required property QtObject control
    \endqml

    \c delegateStyle points to the DelegateStyle that describes how the
    delegate should be styled. \c control points to the
    \l {Qt Quick Controls}{Qt Quick Control} that owns the delegate. The latter
    can be used to resolve additional information about the control not
    available from the style.

    If you know the specific type of the owning control, you can use it
    instead of \l QtObject for the \c control property. For example, since
    a handle delegate in the snippet below is always used inside a Slider, the
    type can be set to \l {Qt Quick Templates 2 QML Types}{T.Slider}:

    \snippet DelegateStyle_delegates.qml delegate

    \note If a DelegateStyle has a \l {shadow}{drop shadow} defined, it will be
    drawn separately by a \l {ShadowStyle::delegate}{shadow delegate.}

    \sa data, visible, StyledItem
*/

/*!
    \qmlproperty Gradient DelegateStyle::gradient

    The gradient of this delegate. The default value is \c null (no gradient).

    Unlike a Quick \l [QtQuick]{Rectangle}, where a gradient replaces
    the color, StyleKit draws the \l gradient on top of the color. This
    means you can use a semi-transparent gradient as an overlay (for
    example, a subtle shading effect) while the color shows through
    underneath.

    \snippet ControlStateStyle_properties.qml color and gradient

    \sa color
*/

/*!
    \qmlproperty ImageStyle DelegateStyle::image

    Grouped property for placing an image inside this delegate.

    \sa ImageStyle
*/

/*!
    \qmlproperty real DelegateStyle::implicitHeight

    The implicit height of this delegate.

    \note This property is ignored if \l fillHeight is \c true, including when
    it is set higher up in the style hierarchy — for example in the
    \l {Style::fallbackStyle}{fallback style}.

    \sa fillHeight, {StyleKit Property Resolution}
*/

/*!
    \qmlproperty real DelegateStyle::implicitWidth

    The implicit width of this delegate.

    \note This property is ignored if \l fillWidth is \c true, including when
    it is set higher up in the style hierarchy — for example in the
    \l {Style::fallbackStyle}{fallback style}.

    \sa fillWidth, {StyleKit Property Resolution}
*/

/*!
    \qmlproperty real DelegateStyle::leftMargin

    The left margin of this delegate. If not set, falls back to
    \l margins.

    \sa margins, rightMargin, topMargin, bottomMargin
*/

/*!
    \qmlproperty real DelegateStyle::margins

    The uniform margin around this delegate. Setting this provides a
    default value for \l leftMargin, \l rightMargin, \l topMargin,
    and \l bottomMargin. Each side can be overridden individually.

    \sa leftMargin, rightMargin, topMargin, bottomMargin
*/

/*!
    \qmlproperty real DelegateStyle::minimumWidth

    The minimum width of this delegate. The delegate will not be sized
    smaller than this value.
*/

/*!
    \qmlproperty bool DelegateStyle::fillWidth

    Whether the delegate should fill the available width of the parent item.

    Some delegates, such as the groove of a \l [QtQuickControls]{Slider}, are
    often expected to fill the available width of the control, rather than
    having a fixed implicit width. This can be achieved by setting
    \c fillWidth to \c true. To add some space between the delegate and the
    edges of its parent, use the \l margins properties.

    If \c fillWidth is \c false, the delegate's width is determined by its \l implicitWidth.

    \sa fillHeight, implicitWidth
*/

/*!
    \qmlproperty bool DelegateStyle::fillHeight

    Whether the delegate should fill the available height of the parent item.

    Some delegates, such as the foreground of a checkbox indicator, are often expected
    to fill the available height (and width) of the indicator, rather than
    having a fixed implicit height. This can be achieved by setting
    \c fillHeight to \c true. To add some space between the delegate and the
    edges of its parent, use the \l margins properties.

    If \c fillHeight is \c false, the delegate's height is determined by its \l implicitHeight.

    \sa fillWidth, implicitHeight
*/

/*!
    \qmlproperty real DelegateStyle::opacity

    The opacity of this delegate, from \c 0.0 (fully transparent) to
    \c 1.0 (fully opaque). The default value is \c 1.0.

    \sa visible, color
*/

/*!
    \qmlproperty real DelegateStyle::radius

    The corner radius applied to all four corners of this delegate.
    Individual corners can be overridden with \l topLeftRadius,
    \l topRightRadius, \l bottomLeftRadius, and \l bottomRightRadius.

    \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius
*/

/*!
    \qmlproperty real DelegateStyle::rightMargin

    The right margin of this delegate. If not set, falls back to
    \l margins.

    \sa margins, leftMargin, topMargin, bottomMargin
*/

/*!
    \qmlproperty real DelegateStyle::rotation

    The rotation of this delegate, in degrees.
*/

/*!
    \qmlproperty real DelegateStyle::scale

    The scale factor of this delegate. The default value is \c 1.0.
*/

/*!
    \qmlproperty ShadowStyle DelegateStyle::shadow

    Grouped property for styling a drop shadow behind this delegate.

    \sa ShadowStyle
*/

/*!
    \qmlproperty real DelegateStyle::topLeftRadius

    The top-left corner radius. If not set, falls back to \l radius.

    \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius
*/

/*!
    \qmlproperty real DelegateStyle::topMargin

    The top margin of this delegate. If not set, falls back to
    \l margins.

    \sa margins, bottomMargin, leftMargin, rightMargin
*/

/*!
    \qmlproperty real DelegateStyle::topRightRadius

    The top-right corner radius. If not set, falls back to \l radius.

    \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius
*/

/*!
    \qmlproperty bool DelegateStyle::visible

    Whether this delegate is visible. The default value is \c true.

    \note Although the default value is \c true, the
    \l {Style::fallbackStyle}{fallback style} (which overrides many of the default
    values) sets \c {background.visible} to \c false for controls that typically
    should not draw a background, such as \l [QtQuickControls]{CheckBox},
    \l [QtQuickControls]{RadioButton}, and \l [QtQuickControls]{Slider}.

    When \c visible is \c false, the \l delegate will not be created. If you need
    a hidden delegate to be created, consider setting \l opacity to \c 0 instead.

    \sa opacity
*/

// ************* ImageStyle ****************

/*!
    \qmltype ImageStyle
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the image style for a delegate.

    ImageStyle provides properties for placing an image inside a
    \l DelegateStyle. It is available as the
    \l {DelegateStyle::image}{image} grouped property on any delegate.

    \labs

    \sa DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty color ImageStyle::color

    A color overlay applied to the image. This is typically used to tint
    monochrome icons to match the style's color scheme.
*/

/*!
    \qmlproperty enumeration ImageStyle::fillMode

    How the image should be scaled or tiled within the delegate. The
    value is one of the \l [QtQuick]{Image::fillMode}{Image.fillMode}
    enum values.
*/

/*!
    \qmlproperty url ImageStyle::source

    The URL of the image to display inside the delegate.
*/

// ************* BorderStyle ****************

/*!
    \qmltype BorderStyle
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the border style for a delegate.

    BorderStyle provides properties for the border drawn around a
    \l DelegateStyle. It is available as the
    \l {DelegateStyle::border}{border} grouped property on any delegate.

    \labs

    \sa DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty color BorderStyle::color

    The color of the border. The default value is \c transparent.
*/

/*!
    \qmlproperty real BorderStyle::width

    The width of the border in pixels.
*/

// ************* ShadowStyle ****************

/*!
    \qmltype ShadowStyle
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the drop shadow style for a delegate.

    ShadowStyle provides properties for rendering a drop shadow behind
    a \l DelegateStyle. It is available as the
    \l {DelegateStyle::shadow}{shadow} grouped property on any delegate.

    \snippet DelegateStyle_shadow.qml drop shadow

    By adjusting scale and color, shadows can also be used to create
    glow effects. The following snippet adds a diffuse white glow to
    the handle of a \l [QtQuickControls]{Switch} that intensifies
    when checked:

    \snippet DelegateStyle_shadow.qml glow

    \labs

    \sa DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty real ShadowStyle::blur

    The blur of the shadow. A value of \c 0.0 produces a sharp shadow, while
    larger values produce a softer, more diffuse shadow.
    The default value is \c 10.0.
*/

/*!
    \qmlproperty color ShadowStyle::color

    The color of the shadow. The default value is \c transparent, which
    means that no shadow will be drawn by default.

    \sa visible, opacity
*/

/*!
    \qmlproperty Component ShadowStyle::delegate

    A custom QML \l [QML]{Component} that replaces the default shadow
    rendering. When set, StyleKit instantiates this component instead
    of drawing the default shadow.
*/

/*!
    \qmlproperty real ShadowStyle::horizontalOffset

    The horizontal offset of the shadow from the delegate, in pixels.
*/

/*!
    \qmlproperty real ShadowStyle::opacity

    The opacity of the shadow, from \c 0.0 (fully transparent) to
    \c 1.0 (fully opaque). The default value is \c 1.0.

    \sa visible, color
*/

/*!
    \qmlproperty real ShadowStyle::scale

    The scale factor of the shadow relative to the delegate.
    The default value is \c 1.0.
*/

/*!
    \qmlproperty real ShadowStyle::verticalOffset

    The vertical offset of the shadow from the delegate, in pixels.
*/

/*!
    \qmlproperty bool ShadowStyle::visible

    Whether the shadow is visible. The default value is \c true.

    However, since the default shadow \l color is \c transparent,
    the shadow will not be visible unless a different color is set.

    \sa opacity
*/

// ************* HandleStyle ****************

/*!
    \qmltype HandleStyle
    \inqmlmodule Qt.labs.StyleKit
    \inherits DelegateStyle
    \brief Defines the handle style for controls such as \l [QtQuickControls]{Switch},
    \l [QtQuickControls]{Slider} and \l [QtQuickControls]{RangeSlider}.

    HandleStyle extends \l DelegateStyle with properties that are specific to
    styling a handle.

    Some controls have more than one handle. For example,
    \l [QtQuickControls]{RangeSlider} has both a \l [QtQuickControls]{RangeSlider::}{first} and a \l [QtQuickControls]{RangeSlider::}{second} handle.
    These can be styled independently using the \l first and \l second
    sub-handles, \c {slider.handle.first} and \c {slider.handle.second}.
    Properties not set on a sub-handle fall back to the handle itself.

    \labs

    \sa DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty DelegateStyle HandleStyle::first

    Grouped property for styling the first handle of for example a
    \l [QtQuickControls]{RangeSlider}. Unset properties fall back to
    the HandleStyle itself.
*/

/*!
    \qmlproperty DelegateStyle HandleStyle::second

    Grouped property for styling the second handle of for example a
    \l [QtQuickControls]{RangeSlider}. Unset properties fall back to
    the HandleStyle itself.
*/

// ************* SubIndicatorStyle ****************

/*!
    \qmltype SubIndicatorStyle
    \inqmlmodule Qt.labs.StyleKit
    \inherits DelegateStyle
    \brief Defines the style for a sub-indicator delegate.

    SubIndicatorStyle extends \l DelegateStyle with a \l foreground
    property. It is used for styling sub-indicator delegates such as
    \l {IndicatorStyle::first}{indicator.first} and
    \l {IndicatorStyle::second}{indicator.second} in controls
    with multiple indicators, like \l [QtQuickControls]{SpinBox}.
    For SpinBox, \c {indicator.first} styles the increment button, and
    \c {indicator.second} styles the decrement button.
    It is equivalent to \l IndicatorStyle, but does not itself
    contain sub-indicators.

    \labs

    \sa IndicatorStyle, DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty DelegateStyle SubIndicatorStyle::foreground

    Grouped property for styling the foreground element of this
    indicator. The foreground typically represents a checkmark, icon
    or arrow rendered inside the indicator.
*/

// ************* IndicatorStyle ****************

/*!
    \qmltype IndicatorStyle
    \inqmlmodule Qt.labs.StyleKit
    \inherits DelegateStyle
    \brief Defines the style for a control's indicator.

    IndicatorStyle extends \l DelegateStyle with properties that are specific to
    styling an indicator.

    The IndicatorStyle itself describes the indicator background, while the
    \l foreground describes the content drawn inside it. For example,
    \c {checkBox.indicator} styles the check box frame and
    \c {checkBox.indicator.foreground} styles the check mark. For
    \l {AbstractStylableControls::}{switchControl},
    \l {AbstractStylableControls::}{slider} and
    \l {AbstractStylableControls::}{progressBar}, the indicator styles
    the groove and the foreground styles the fill.

    \snippet DelegateStyle_indicator.qml checkBox

    Some controls have more than one indicator. For example,
    \l [QtQuickControls]{SpinBox} has both an increment and a decrement
    button. These can be styled independently using the \l first and \l second
    sub-indicators, \c {spinBox.indicator.first} and
    \c {spinBox.indicator.second}. For SpinBox, \c {indicator.first} styles
    the increment button, and \c {indicator.second} styles the decrement
    button. Properties not set on a sub-indicator fall back to the indicator
    itself.

    \note The \c up and \c down properties are kept as aliases for
    \c first and \c second for backward compatibility. New code
    should use \c first and \c second, as these names are generic and can be
    used by controls with sub-indicators that do not represent up/down actions.
    The compatibility aliases may be removed in a future version.

    \snippet DelegateStyle_indicator.qml up and down indicator

    \labs

    \sa SubIndicatorStyle, DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty DelegateStyle IndicatorStyle::foreground

    Grouped property for styling the foreground element of this
    indicator. For example, \c {checkBox.indicator} describes
    the check box frame, while \c {checkBox.indicator.foreground} is the check mark.

    For controls like \l {AbstractStylableControls::}{slider},
    \l {AbstractStylableControls::}{progressBar}, and
    \l {AbstractStylableControls::}{switchControl}, the \c {indicator.foreground}
    represents the progress track or fill within the groove, while the \c indicator
    itself represents the groove.
*/

/*!
    \qmlproperty SubIndicatorStyle IndicatorStyle::first

    Grouped property for styling the first sub-indicator of controls that
    expose multiple indicators. For \l [QtQuickControls]{SpinBox}, this
    styles the increment button.
    Unset properties fall back to the indicator itself.

    \note The \c up property is kept as an alias for \c first for backward
    compatibility. New code should use \c first, as this name is generic
    and can be used by controls with sub-indicators that do not represent
    up actions. The compatibility alias may be removed in a future version.

    \sa second
*/

/*!
    \qmlproperty SubIndicatorStyle IndicatorStyle::second

    Grouped property for styling the second sub-indicator of for example a
    \l [QtQuickControls]{SpinBox}.
    Unset properties fall back to the indicator itself.

    \note The \c down property is kept as an alias for \c second for backward
    compatibility. New code should use \c second, as this name is generic
    and can be used by controls with sub-indicators that do not represent
    down actions. The compatibility alias may be removed in a future version.

    \sa first
*/

// ************* TextStyle ****************

/*!
    \qmltype TextStyle
    \inqmlmodule Qt.labs.StyleKit
    \brief Defines the text style for a control's label.

    TextStyle provides properties for controlling the appearance of
    text labels within a control, such as
    \l color, \l alignment and \l padding,
    and certain font-related properties such as
    \l {TextStyle::}{bold},
    \l {TextStyle::}{italic}, and
    \l {TextStyle::pointSize}{font size}.

    It is available as the \l {ControlStyleProperties::text}{text}
    grouped property on every \l ControlStateStyle.

    \labs

    \sa DelegateStyle, ControlStyle, ControlStateStyle,
        {qtlabsstylekit-fallbackstyle.html}{FallbackStyle Reference}
*/

/*!
    \qmlproperty Qt::Alignment TextStyle::alignment

    The alignment of the text within its available area.
*/

/*!
    \qmlproperty bool TextStyle::bold

    Whether the text should be rendered in bold. The default value
    is \c false.

    \sa italic
*/

/*!
    \qmlproperty real TextStyle::bottomPadding

    The bottom padding around the text. If not set, falls back to
    \l padding.

    \sa padding, topPadding, leftPadding, rightPadding
*/

/*!
    \qmlproperty color TextStyle::color

    The color of the text.
*/

/*!
    \qmlproperty bool TextStyle::italic

    Whether the text should be rendered in italic. The default value
    is \c false.

    \sa bold
*/

/*!
    \qmlproperty real TextStyle::leftPadding

    The left padding around the text. If not set, falls back to
    \l padding.

    \sa padding, rightPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real TextStyle::padding

    The uniform padding around the text. Setting this provides a
    default value for \l leftPadding, \l rightPadding, \l topPadding,
    and \l bottomPadding. Each side can be overridden individually.

    \sa leftPadding, rightPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real TextStyle::pointSize

    The point size of the text font.
*/

/*!
    \qmlproperty real TextStyle::rightPadding

    The right padding around the text. If not set, falls back to
    \l padding.

    \sa padding, leftPadding, topPadding, bottomPadding
*/

/*!
    \qmlproperty real TextStyle::topPadding

    The top padding around the text. If not set, falls back to
    \l padding.

    \sa padding, bottomPadding, leftPadding, rightPadding
*/

// ************* QQStyleKitPropertyGroup ****************

QHash<PropertyPathId_t, QString> QQStyleKitPropertyGroup::s_pathStrings;

QQStyleKitPropertyGroup::QQStyleKitPropertyGroup(QQSK::PropertyGroup, QObject *parent)
    : QObject(parent)
{
}

PropertyPathId QQStyleKitPropertyGroup::propertyPathId(QQSK::Property property, PropertyPathId::Flag flag) const
{
    if (flag == PropertyPathId::Flag::IncludeSubtype) {
        if (m_pathFlags.testFlag(QQSK::PropertyPathFlag::DelegateSubtype1))
            return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype1);
        else if (m_pathFlags.testFlag(QQSK::PropertyPathFlag::DelegateSubtype2))
            return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype2);
    }
    return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype0);
}

QString QQStyleKitPropertyGroup::pathToString() const
{
    /* Start from the root of the path and build the path down to this group. This
     * mirrors how the groups were originally created and avoids rounding issues
     * that can arise if attempting to reconstruct the path “backwards”.
     * Note: For each group, m_groupSpace.start is stored relative to the root,
     * while m_groupSpace.size is relative to the parent group. However, when
     * calculating the group index, the group-space start must be computed
     * relative to the parent group.
     * We cache the requested paths, as the same paths are typically requested
     * repeatedly. The number of possible paths (and thus leaf groups) is well below
     * 100, and in practice the cache usually ends up with fewer than 20 entries. */
    if (s_pathStrings.contains(m_groupSpace.start))
        return s_pathStrings[m_groupSpace.start];

    constexpr PropertyPathId_t rootGroupsSize = nestedGroupsStartSize / nestedGroupCount;
    const auto metaEnum = QMetaEnum::fromType<QQSK::PropertyGroup>();

    PropertyPathId_t nestedGroupStart = m_groupSpace.start;
    PropertyPathId_t nestedGroupSize = rootGroupsSize;
    PropertyPathId_t nestedGroupIndex = nestedGroupStart / nestedGroupSize;
    auto groupType = QQSK::PropertyGroup(nestedGroupIndex);
    if (groupType == QQSK::PropertyGroup::Control)
        return {};

    QString groupName = QString::fromLatin1(metaEnum.valueToKey(static_cast<int>(groupType)));
    groupName[0] = groupName[0].toLower();
    QString pathString = groupName;

    while (true) {
        nestedGroupStart -= nestedGroupIndex * nestedGroupSize;
        nestedGroupSize /= nestedGroupCount;
        nestedGroupIndex = nestedGroupStart / nestedGroupSize;
        groupType = QQSK::PropertyGroup(nestedGroupIndex);
        if (groupType == QQSK::PropertyGroup::Control)
            break;

        QString groupName = QString::fromLatin1(metaEnum.valueToKey(static_cast<int>(groupType)));
        groupName[0] = groupName[0].toLower();
        pathString += '.'_L1 + groupName;
    }

    s_pathStrings.insert(m_groupSpace.start, pathString);
    return pathString;
}

QQStyleKitControlProperties *QQStyleKitPropertyGroup::controlProperties() const
{
    if (isControlProperties()) {
        Q_ASSERT(qobject_cast<const QQStyleKitControlProperties *>(this));
        auto *self = const_cast<QQStyleKitPropertyGroup *>(this);
        return static_cast<QQStyleKitControlProperties *>(self);
    }
    Q_ASSERT(qobject_cast<const QQStyleKitControlProperties *>(parent()));
    return static_cast<QQStyleKitControlProperties *>(parent());
}

template<typename T>
T *QQStyleKitPropertyGroup::lazyCreateGroup(T * const &ptr, QQSK::PropertyGroup group) const
{
    T *nestedGroup = QQSK::lazyCreate(ptr, controlProperties(), group);

    // Nested groups inherit path flags from their parents
    nestedGroup->m_pathFlags = m_pathFlags;

    if (group == QQSK::PropertyGroup::DelegateSubtype1) {
        /* Subtypes, like states, are not part of a property's path ID—they belong to the
         * storage ID instead. They are therefore prefixed later, during lookup, when
         * propagation determines which value to read.
         * For now, we simply record which subtype this group (and any nested groups) is
         * associated with. The subtype will then be taken into account later when reading
         * properties from the group. Setting aside space for the sub types was already
         * taken care of during the construction of the root QQStyleKitControlProperties. */
        nestedGroup->m_pathFlags.setFlag(QQSK::PropertyPathFlag::DelegateSubtype1);
        nestedGroup->m_groupSpace = m_groupSpace;
    } else if (group == QQSK::PropertyGroup::DelegateSubtype2) {
        nestedGroup->m_pathFlags.setFlag(QQSK::PropertyPathFlag::DelegateSubtype2);
        nestedGroup->m_groupSpace = m_groupSpace;
    } else {
        /* Calculate the available property ID space for the nested group. This is done by
         * dividing the available space inside _this_ group on the number of potential groups
         * that _this_ group can potentially contain. */
        const PropertyPathId_t nestedGroupIndex = PropertyPathId_t(group);
        const PropertyPathId_t nestedGroupSize = m_groupSpace.size / nestedGroupCount;
        nestedGroup->m_groupSpace.size = nestedGroupSize;
        nestedGroup->m_groupSpace.start = m_groupSpace.start + (nestedGroupIndex * nestedGroupSize);
        /* Ensure that we haven’t exhausted the available PropertyPathId space. There must be
         * enough room remaining to assign IDs for all properties defined in QQSK::Property.
         * If this assertion triggers, consider switching to a wider PropertyPathId_t type or
         * optimizing how the space is allocated. For example, certain nested paths (such as
         * control.handle.indicator) can never occur, yet we currently reserve INNER_GROUP_COUNT
         * for every nesting level, which is wasteful. */
        Q_ASSERT(nestedGroupSize >= PropertyPathId_t(QQSK::Property::COUNT));
    }
    return nestedGroup;
}

/* This macro will check if the caller has the same group path as \a GROUP_PATH.
 * This is needed since a QQSK::Property (e.g Color) can sometimes be a
 * property in several different subclasses of QQStyleKitPropertyGroup.
 * For example, both control.background.color and control.indicator.color has a
 * color property. But the group path differs, so they are in reality two completely
 * different properties. And in that case, when the former changes value, we want to
 * emit changes globally only to that property, and not the latter.
 * The caller of this macro will therefore need to go through all the usages of its
 * subclass in the API, to figure out which group itself is an instance of. For the
 * one that is a match, the macro will go through all readers and emit the same
 * signal for them. */
#define CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(CONTROL_PROPERTIES, GROUP_PATH) \
if (this == CONTROL_PROPERTIES -> GROUP_PATH ) { \
    for (QQStyleKitReader *reader : QQStyleKitReader::s_allReaders) { \
        const auto baseTypes = QQStyleKitPropertyResolver::baseTypesForType(reader->controlType()); \
        if (reader->controlType() != controlType && !baseTypes.contains(controlType)) \
            continue; \
        reader->clearLocalStorage(); \
        ((reader-> GROUP_PATH ->*changedSignals)(), ...); \
    } \
    return; \
}

template<typename SUBCLASS>
void QQStyleKitPropertyGroup::handleStylePropertyChanged(void (SUBCLASS::*changedSignal)()) {
    handleStylePropertiesChanged<SUBCLASS>(changedSignal);
}

template <typename SUBCLASS, typename... CHANGED_SIGNALS>
void QQStyleKitPropertyGroup::handleStylePropertiesChanged(CHANGED_SIGNALS... changedSignals)
{
    /* This function will check which subclass of QQStyleKitProperties this
     * group is (nested) inside. Based on that, it will decide if the signals
     * should be emitted locally or not, and if the changed properties affects
     * all existing QQStyleKitReaders, and therefore will need to be
     * emitted 'globally'. Note that it only makes sense to call this function
     * for changed properties that are available from a QQStyleKitReader.
     * Properities only available from e.g QQStyleKitControl (such as
     * variations), are anyway not readable from a QQStyleKitReader. */
    static_assert(std::is_base_of<QQStyleKitPropertyGroup, SUBCLASS>::value,
                  "SUBCLASS must inherit QQStyleKitPropertyGroup");

    auto *group = static_cast<SUBCLASS *>(this);
    const QQSK::Subclass objectWrittenTo = controlProperties()->subclass();

    if (objectWrittenTo == QQSK::Subclass::QQStyleKitState) {
        ((group->*changedSignals)(), ...);

        if (shouldEmitGlobally()) {
            const QQStyleKitControl *control = controlProperties()->asQQStyleKitState()->control();
            const QQStyleKitExtendableControlType type = control->controlType();
            group->emitGlobally(type, changedSignals...);
        }
        return;
    }

    if (objectWrittenTo == QQSK::Subclass::QQStyleKitReader) {
        /* Unless the StyleReader has told us not to emit any signals (because it's only
         * syncing it's own local storage with old values before starting a transition), we
         * emit the signal like normal. This will cause the control to repaint (perhaps
         * using a transition). */
        if (shouldEmitLocally()) {
            QQStyleKitReader *reader = controlProperties()->asQQStyleKitReader();
            /* Readers notify their consumer in two different ways depending on who is
             * consuming them. A QML control binds to the reader's change signals, so we
             * emit those directly. A widget consumer (m_target set) doesn't listen on
             * signals; it repaints in response to a QEvent::StyleAnimationUpdate posted
             * to the widget, and only while a transition is actually running. */
            if (!reader->m_target)
                ((group->*changedSignals)(), ...);
            else
                QCoreApplication::postEvent(reader->m_target, new QEvent(QEvent::StyleAnimationUpdate));
        }
        return;
    }

    Q_UNREACHABLE();
}

void QQStyleKitPropertyGroup::emitChangedForAllStylePropertiesRecursive(EmitFlags emitFlags)
{
    /* This function will emit changed signals for all style properties in the
     * StyleKit API (for a single QQStyleKitReader), which is needed after
     * doing a style-, or theme change. */
    const int startIndex = QQStyleKitPropertyGroup::staticMetaObject.propertyOffset();
    const QMetaObject* meta = metaObject();
    for (int i = startIndex; i < meta->propertyCount(); ++i) {
        const QMetaProperty prop = meta->property(i);
        const QMetaObject* propMetaObject = QMetaType::fromName(prop.typeName()).metaObject();
        if (propMetaObject) {
            if (propMetaObject->inherits(&QQStyleKitDelegateProperties::staticMetaObject)) {
                /* Skip recursing into QQStyleKitDelegateProperties, because those are lazy
                 * created when read, and reading them from here would accidentally
                 * create them. */
                continue;
            }
            if (propMetaObject->inherits(&QQStyleKitPropertyGroup::staticMetaObject)) {
                // The property is of type QQStyleKitPropertyGroup, so recurse into it
                QObject *childObj = qvariant_cast<QObject *>(property(prop.name()));
                if (auto *child = qobject_cast<QQStyleKitPropertyGroup *>(childObj))
                    child->emitChangedForAllStylePropertiesRecursive(emitFlags);
                continue;
            }
        }

        if (!emitFlags.testFlag(EmitFlag::AllProperties)) {
            // Only emit for color properties when the Colors flag is set
            if (emitFlags.testFlag(EmitFlag::Colors)) {
                if (prop.metaType() != QMetaType::fromType<QColor>())
                    continue;
            }
        }

        // Emit the changed signal for the property
        Q_ASSERT(prop.hasNotifySignal());
        QMetaMethod notify = prop.notifySignal();
        notify.invoke(this, Qt::DirectConnection);
    }
}

bool QQStyleKitPropertyGroup::shouldEmitLocally()
{
    return !controlProperties()->asQQStyleKitReader()->dontEmitChangedSignals();
}

bool QQStyleKitPropertyGroup::shouldEmitGlobally()
{
    QQStyleKitStyle *parentStyle = controlProperties()->style();
    if (!parentStyle)
        return false;

    if (parentStyle->loaded() && !parentStyle->m_isUpdatingPalette) {
        /* When a property has changed in the 'global' QQStyleKitStyle itself, it can
        * potentially affect all control instances. We therefore need to go through all
        * QQStyleKitReaders and inform that their own local property that matches the
        * 'global' property needs to be re-read. We emit the signals directly, omitting any
        * applied transitions in the QQStyleKitReaders, to optimize for speed. The exception
        * is if we're just updating the palette in the Style to match the palette in the current
        * control / QQStyleKitReader. Such a change will only affect a single control. */
        return parentStyle == QQStyleKitStyle::current();
    }
    return false;
}

// ************* QQStyleKitImageProperties ****************

QQStyleKitImageProperties::QQStyleKitImageProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitImageProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitImageProperties
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->image());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->image());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->image());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->image());
}

QUrl QQStyleKitImageProperties::source() const
{
    return styleProperty<QUrl>(QQSK::Property::Source);
}

void QQStyleKitImageProperties::setSource(const QUrl &source)
{
    if (setStyleProperty(QQSK::Property::Source, source))
        handleStylePropertyChanged(&QQStyleKitImageProperties::sourceChanged);
}

QColor QQStyleKitImageProperties::color() const
{
    return styleProperty<QColor>(QQSK::Property::Color);
}

void QQStyleKitImageProperties::setColor(const QColor &color)
{
    if (setStyleProperty(QQSK::Property::Color, color))
        handleStylePropertyChanged(&QQStyleKitImageProperties::colorChanged);
}

QQuickImage::FillMode QQStyleKitImageProperties::fillMode() const
{
    return styleProperty<QQuickImage::FillMode>(QQSK::Property::FillMode);
}

void QQStyleKitImageProperties::setFillMode(QQuickImage::FillMode fillMode)
{
    if (setStyleProperty(QQSK::Property::FillMode, fillMode))
        handleStylePropertyChanged(&QQStyleKitImageProperties::fillModeChanged);
}

// ************* QQStyleKitBorderProperties ****************

QQStyleKitBorderProperties::QQStyleKitBorderProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitBorderProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitBorderProperties
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->border());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->border());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->border());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->border());
}

qreal QQStyleKitBorderProperties::width() const
{
    return styleProperty<qreal>(QQSK::Property::Width);
}

void QQStyleKitBorderProperties::setWidth(qreal width)
{
    if (setStyleProperty(QQSK::Property::Width, width))
        handleStylePropertyChanged(&QQStyleKitBorderProperties::widthChanged);
}

QColor QQStyleKitBorderProperties::color() const
{
    return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
}

void QQStyleKitBorderProperties::setColor(const QColor &color)
{
    if (setStyleProperty(QQSK::Property::Color, color))
        handleStylePropertyChanged(&QQStyleKitBorderProperties::colorChanged);
}

// ************* QQStyleKitShadowProperties ****************

QQStyleKitShadowProperties::QQStyleKitShadowProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitShadowProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitShadowProperties
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->shadow());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->shadow());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->shadow());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->shadow());
}

QColor QQStyleKitShadowProperties::color() const
{
    return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
}

void QQStyleKitShadowProperties::setColor(QColor color)
{
    if (setStyleProperty(QQSK::Property::Color, color))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::colorChanged);
}

qreal QQStyleKitShadowProperties::opacity() const
{
    return styleProperty<qreal>(QQSK::Property::Opacity, 1.0);
}

void QQStyleKitShadowProperties::setOpacity(qreal opacity)
{
    if (setStyleProperty(QQSK::Property::Opacity, opacity))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::opacityChanged);
}

qreal QQStyleKitShadowProperties::scale() const
{
    return styleProperty<qreal>(QQSK::Property::Scale, 1.0);
}

void QQStyleKitShadowProperties::setScale(qreal scale)
{
    if (setStyleProperty(QQSK::Property::Scale, scale))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::scaleChanged);
}

qreal QQStyleKitShadowProperties::verticalOffset() const
{
    return styleProperty<qreal>(QQSK::Property::VOffset);
}

void QQStyleKitShadowProperties::setVerticalOffset(qreal verticalOffset)
{
    if (setStyleProperty(QQSK::Property::VOffset, verticalOffset))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::verticalOffsetChanged);
}

qreal QQStyleKitShadowProperties::horizontalOffset() const
{
    return styleProperty<qreal>(QQSK::Property::HOffset);
}

void QQStyleKitShadowProperties::setHorizontalOffset(qreal horizontalOffset)
{
    if (setStyleProperty(QQSK::Property::HOffset, horizontalOffset))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::horizontalOffsetChanged);
}

qreal QQStyleKitShadowProperties::blur() const
{
    return styleProperty<qreal>(QQSK::Property::Blur, 10.0);
}

void QQStyleKitShadowProperties::setBlur(qreal blur)
{
    if (setStyleProperty(QQSK::Property::Blur, blur))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::blurChanged);
}

bool QQStyleKitShadowProperties::visible() const
{
    return styleProperty<bool>(QQSK::Property::Visible, true);
}

void QQStyleKitShadowProperties::setVisible(bool visible)
{
    if (setStyleProperty(QQSK::Property::Visible, visible))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::visibleChanged);
}

QQmlComponent *QQStyleKitShadowProperties::delegate() const
{
    return styleProperty<QQmlComponent *>(QQSK::Property::Delegate);
}

void QQStyleKitShadowProperties::setDelegate(QQmlComponent *delegate)
{
    if (setStyleProperty(QQSK::Property::Delegate, delegate))
        handleStylePropertyChanged(&QQStyleKitShadowProperties::delegateChanged);
}

// ************* QQStyleKitDelegateProperties ****************

QQStyleKitDelegateProperties::QQStyleKitDelegateProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitDelegateProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitDelegateProperties
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground());
}

qreal QQStyleKitDelegateProperties::radius() const
{
    return styleProperty<qreal>(QQSK::Property::Radius);
}

void QQStyleKitDelegateProperties::setRadius(qreal radius)
{
    if (setStyleProperty(QQSK::Property::Radius, radius))
        handleStylePropertiesChanged<QQStyleKitDelegateProperties>(
            &QQStyleKitDelegateProperties::radiusChanged,
            &QQStyleKitDelegateProperties::topLeftRadiusChanged,
            &QQStyleKitDelegateProperties::topRightRadiusChanged,
            &QQStyleKitDelegateProperties::bottomLeftRadiusChanged,
            &QQStyleKitDelegateProperties::bottomRightRadiusChanged);
}

qreal QQStyleKitDelegateProperties::topLeftRadius() const
{
    return styleProperty<qreal>(QQSK::Property::TopLeftRadius, QQSK::Property::Radius);
}

void QQStyleKitDelegateProperties::setTopLeftRadius(qreal radius)
{
    if (setStyleProperty(QQSK::Property::TopLeftRadius, radius))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::topLeftRadiusChanged);
}

qreal QQStyleKitDelegateProperties::topRightRadius() const
{
    return styleProperty<qreal>(QQSK::Property::TopRightRadius, QQSK::Property::Radius);
}

void QQStyleKitDelegateProperties::setTopRightRadius(qreal radius)
{
    if (setStyleProperty(QQSK::Property::TopRightRadius, radius))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::topRightRadiusChanged);
}

qreal QQStyleKitDelegateProperties::bottomLeftRadius() const
{
    return styleProperty<qreal>(QQSK::Property::BottomLeftRadius, QQSK::Property::Radius);
}

void QQStyleKitDelegateProperties::setBottomLeftRadius(qreal radius)
{
    if (setStyleProperty(QQSK::Property::BottomLeftRadius, radius))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomLeftRadiusChanged);
}

qreal QQStyleKitDelegateProperties::bottomRightRadius() const
{
    return styleProperty<qreal>(QQSK::Property::BottomRightRadius, QQSK::Property::Radius);
}

void QQStyleKitDelegateProperties::setBottomRightRadius(qreal radius)
{
    if (setStyleProperty(QQSK::Property::BottomRightRadius, radius))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomRightRadiusChanged);
}

qreal QQStyleKitDelegateProperties::scale() const
{
    return styleProperty<qreal>(QQSK::Property::Scale, 1.0);
}

void QQStyleKitDelegateProperties::setScale(qreal scale)
{
    if (setStyleProperty(QQSK::Property::Scale, scale))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::scaleChanged);
}

qreal QQStyleKitDelegateProperties::rotation() const
{
    return styleProperty<qreal>(QQSK::Property::Rotation);
}

void QQStyleKitDelegateProperties::setRotation(qreal rotation)
{
    if (setStyleProperty(QQSK::Property::Rotation, rotation))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::rotationChanged);
}

qreal QQStyleKitDelegateProperties::implicitWidth() const
{
    return styleProperty<qreal>(QQSK::Property::ImplicitWidth);
}

void QQStyleKitDelegateProperties::setImplicitWidth(qreal width)
{
    if (setStyleProperty(QQSK::Property::ImplicitWidth, width))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::implicitWidthChanged);
}

qreal QQStyleKitDelegateProperties::implicitHeight() const
{
    return styleProperty<qreal>(QQSK::Property::ImplicitHeight);
}

void QQStyleKitDelegateProperties::setImplicitHeight(qreal height)
{
    if (setStyleProperty(QQSK::Property::ImplicitHeight, height))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::implicitHeightChanged);
}

qreal QQStyleKitDelegateProperties::minimumWidth() const
{
    return styleProperty<qreal>(QQSK::Property::MinimumWidth);
}

void QQStyleKitDelegateProperties::setMinimumWidth(qreal width)
{
    if (setStyleProperty(QQSK::Property::MinimumWidth, width))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::minimumWidthChanged);
}

bool QQStyleKitDelegateProperties::fillWidth() const
{
    return styleProperty<bool>(QQSK::Property::FillWidth);
}

void QQStyleKitDelegateProperties::setFillWidth(bool fill)
{
    if (setStyleProperty(QQSK::Property::FillWidth, fill))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::fillWidthChanged);
}

bool QQStyleKitDelegateProperties::fillHeight() const
{
    return styleProperty<bool>(QQSK::Property::FillHeight);
}

void QQStyleKitDelegateProperties::setFillHeight(bool fill)
{
    if (setStyleProperty(QQSK::Property::FillHeight, fill))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::fillHeightChanged);
}

qreal QQStyleKitDelegateProperties::margins() const
{
    return styleProperty<qreal>(QQSK::Property::Margins);
}

void QQStyleKitDelegateProperties::setMargins(qreal margins)
{
    if (setStyleProperty(QQSK::Property::Margins, margins))
        handleStylePropertiesChanged<QQStyleKitDelegateProperties>(
            &QQStyleKitDelegateProperties::marginsChanged,
            &QQStyleKitDelegateProperties::leftMarginChanged,
            &QQStyleKitDelegateProperties::rightMarginChanged,
            &QQStyleKitDelegateProperties::topMarginChanged,
            &QQStyleKitDelegateProperties::bottomMarginChanged);
}

qreal QQStyleKitDelegateProperties::leftMargin() const
{
    return styleProperty<qreal>(QQSK::Property::LeftMargin, QQSK::Property::Margins);
}

void QQStyleKitDelegateProperties::setLeftMargin(qreal margin)
{
    if (setStyleProperty(QQSK::Property::LeftMargin, margin))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::leftMarginChanged);
}

qreal QQStyleKitDelegateProperties::rightMargin() const
{
    return styleProperty<qreal>(QQSK::Property::RightMargin, QQSK::Property::Margins);
}

void QQStyleKitDelegateProperties::setRightMargin(qreal margin)
{
    if (setStyleProperty(QQSK::Property::RightMargin, margin))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::rightMarginChanged);
}

qreal QQStyleKitDelegateProperties::topMargin() const
{
    return styleProperty<qreal>(QQSK::Property::TopMargin, QQSK::Property::Margins);
}

void QQStyleKitDelegateProperties::setTopMargin(qreal margin)
{
    if (setStyleProperty(QQSK::Property::TopMargin, margin))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::topMarginChanged);
}

qreal QQStyleKitDelegateProperties::bottomMargin() const
{
    return styleProperty<qreal>(QQSK::Property::BottomMargin, QQSK::Property::Margins);
}

void QQStyleKitDelegateProperties::setBottomMargin(qreal margin)
{
    if (setStyleProperty(QQSK::Property::BottomMargin, margin))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomMarginChanged);
}

Qt::Alignment QQStyleKitDelegateProperties::alignment() const
{
    return styleProperty<Qt::Alignment>(QQSK::Property::Alignment, Qt::AlignLeft | Qt::AlignVCenter);
}

void QQStyleKitDelegateProperties::setAlignment(Qt::Alignment alignment)
{
    if (setStyleProperty(QQSK::Property::Alignment, alignment))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::alignmentChanged);
}

qreal QQStyleKitDelegateProperties::opacity() const
{
    return styleProperty<qreal>(QQSK::Property::Opacity, 1.0);
}

void QQStyleKitDelegateProperties::setOpacity(qreal opacity)
{
    if (setStyleProperty(QQSK::Property::Opacity, opacity))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::opacityChanged);
}

QColor QQStyleKitDelegateProperties::color() const
{
    return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
}

void QQStyleKitDelegateProperties::setColor(const QColor &color)
{
    if (setStyleProperty(QQSK::Property::Color, color))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::colorChanged);
}

bool QQStyleKitDelegateProperties::visible() const
{
    return styleProperty<bool>(QQSK::Property::Visible, true);
}

void QQStyleKitDelegateProperties::setVisible(bool visible)
{
    if (setStyleProperty(QQSK::Property::Visible, visible))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::visibleChanged);
}

bool QQStyleKitDelegateProperties::clip() const
{
    return styleProperty<bool>(QQSK::Property::Clip, false);
}

void QQStyleKitDelegateProperties::setClip(bool clip)
{
    if (setStyleProperty(QQSK::Property::Clip, clip))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::clipChanged);
}

QQuickGradient *QQStyleKitDelegateProperties::gradient() const
{
    return styleProperty<QQuickGradient *>(QQSK::Property::Gradient);
}

void QQStyleKitDelegateProperties::setGradient(QQuickGradient *gradient)
{
    if (setStyleProperty(QQSK::Property::Gradient, gradient))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::gradientChanged);
}

QObject *QQStyleKitDelegateProperties::data() const
{
    return styleProperty<QObject *>(QQSK::Property::Data);
}

void QQStyleKitDelegateProperties::setData(QObject *data)
{
    if (setStyleProperty(QQSK::Property::Data, data))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::dataChanged);
}

QQmlComponent *QQStyleKitDelegateProperties::delegate() const
{
    return styleProperty<QQmlComponent *>(QQSK::Property::Delegate);
}

void QQStyleKitDelegateProperties::setDelegate(QQmlComponent *delegate)
{
    if (setStyleProperty(QQSK::Property::Delegate, delegate))
        handleStylePropertyChanged(&QQStyleKitDelegateProperties::delegateChanged);
}

QQStyleKitBorderProperties *QQStyleKitDelegateProperties::border() const
{
    return lazyCreateGroup(m_border, QQSK::PropertyGroup::Border);
}

QQStyleKitShadowProperties *QQStyleKitDelegateProperties::shadow() const
{
    return lazyCreateGroup(m_shadow, QQSK::PropertyGroup::Shadow);
}

QQStyleKitImageProperties *QQStyleKitDelegateProperties::image() const
{
    return lazyCreateGroup(m_image, QQSK::PropertyGroup::Image);
}

// ************* QQStyleKitHandleProperties ****************

QQStyleKitHandleProperties::QQStyleKitHandleProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitDelegateProperties(group, parent)
{
}

QQStyleKitDelegateProperties *QQStyleKitHandleProperties::first() const
{
    return lazyCreateGroup(m_first, QQSK::PropertyGroup::DelegateSubtype1);
}

QQStyleKitDelegateProperties *QQStyleKitHandleProperties::second() const
{
    return lazyCreateGroup(m_second, QQSK::PropertyGroup::DelegateSubtype2);
}

// ************* QQStyleKitIndicatorProperties ****************

QQStyleKitIndicatorProperties::QQStyleKitIndicatorProperties(
    QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitDelegateProperties(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitIndicatorProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitIndicatorProperties
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->first());
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->second());
}

QQStyleKitDelegateProperties *QQStyleKitIndicatorProperties::foreground() const
{
    return lazyCreateGroup(m_foreground, QQSK::PropertyGroup::Foreground);
}

// ************* QQStyleKitIndicatorWithSubTypes ****************

QQStyleKitIndicatorWithSubTypes::QQStyleKitIndicatorWithSubTypes(
    QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitDelegateProperties(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitIndicatorWithSubTypes::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    // Go through all instances of QQStyleKitIndicatorWithSubTypes
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator());
}

QQStyleKitDelegateProperties *QQStyleKitIndicatorWithSubTypes::foreground() const
{
    return lazyCreateGroup(m_foreground, QQSK::PropertyGroup::Foreground);
}

QQStyleKitIndicatorProperties *QQStyleKitIndicatorWithSubTypes::up() const
{
    return first();
}

QQStyleKitIndicatorProperties *QQStyleKitIndicatorWithSubTypes::down() const
{
    return second();
}

QQStyleKitIndicatorProperties *QQStyleKitIndicatorWithSubTypes::first() const
{
    return lazyCreateGroup(m_first, QQSK::PropertyGroup::DelegateSubtype1);
}

QQStyleKitIndicatorProperties *QQStyleKitIndicatorWithSubTypes::second() const
{
    return lazyCreateGroup(m_second, QQSK::PropertyGroup::DelegateSubtype2);
}

// ************* QQStyleKitTextProperties ****************
QQStyleKitTextProperties::QQStyleKitTextProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitTextProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    const QQStyleKitControlProperties *cp = controlProperties();
    CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, text());
}

QColor QQStyleKitTextProperties::color() const
{
    return styleProperty<QColor>(QQSK::Property::Color);
}

void QQStyleKitTextProperties::setColor(const QColor &color)
{
    if (setStyleProperty(QQSK::Property::Color, color))
        handleStylePropertyChanged(&QQStyleKitTextProperties::colorChanged);
}

Qt::Alignment QQStyleKitTextProperties::alignment() const
{
    return styleProperty<Qt::Alignment>(QQSK::Property::Alignment);
}

void QQStyleKitTextProperties::setAlignment(Qt::Alignment alignment)
{
    if (setStyleProperty(QQSK::Property::Alignment, alignment))
        handleStylePropertyChanged(&QQStyleKitTextProperties::alignmentChanged);
}

bool QQStyleKitTextProperties::bold() const
{
    return styleProperty<bool>(QQSK::Property::Bold, false);
}

void QQStyleKitTextProperties::setBold(bool bold)
{
    if (setStyleProperty(QQSK::Property::Bold, bold))
        handleStylePropertyChanged(&QQStyleKitTextProperties::boldChanged);
}

bool QQStyleKitTextProperties::italic() const
{
    return styleProperty<bool>(QQSK::Property::Italic, false);
}

void QQStyleKitTextProperties::setItalic(bool italic)
{
    if (setStyleProperty(QQSK::Property::Italic, italic))
        handleStylePropertyChanged(&QQStyleKitTextProperties::italicChanged);
}

qreal QQStyleKitTextProperties::pointSize() const
{
    return styleProperty<qreal>(QQSK::Property::PointSize);
}

void QQStyleKitTextProperties::setPointSize(qreal pointSize)
{
    if (setStyleProperty(QQSK::Property::PointSize, pointSize))
        handleStylePropertyChanged(&QQStyleKitTextProperties::pointSizeChanged);
}

qreal QQStyleKitTextProperties::padding() const
{
    return styleProperty<qreal>(QQSK::Property::Padding);
}

void QQStyleKitTextProperties::setPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::Padding, padding))
        handleStylePropertiesChanged<QQStyleKitTextProperties>(
            &QQStyleKitTextProperties::paddingChanged,
            &QQStyleKitTextProperties::leftPaddingChanged,
            &QQStyleKitTextProperties::rightPaddingChanged,
            &QQStyleKitTextProperties::topPaddingChanged,
            &QQStyleKitTextProperties::bottomPaddingChanged);
}

qreal QQStyleKitTextProperties::leftPadding() const
{
    return styleProperty<qreal>(QQSK::Property::LeftPadding, QQSK::Property::Padding);
}

void QQStyleKitTextProperties::setLeftPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::LeftPadding, padding))
        handleStylePropertyChanged(&QQStyleKitTextProperties::leftPaddingChanged);
}

qreal QQStyleKitTextProperties::rightPadding() const
{
    return styleProperty<qreal>(QQSK::Property::RightPadding, QQSK::Property::Padding);
}

void QQStyleKitTextProperties::setRightPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::RightPadding, padding))
        handleStylePropertyChanged(&QQStyleKitTextProperties::rightPaddingChanged);
}

qreal QQStyleKitTextProperties::topPadding() const
{
    return styleProperty<qreal>(QQSK::Property::TopPadding, QQSK::Property::Padding);
}

void QQStyleKitTextProperties::setTopPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::TopPadding, padding))
        handleStylePropertyChanged(&QQStyleKitTextProperties::topPaddingChanged);
}

qreal QQStyleKitTextProperties::bottomPadding() const
{
    return styleProperty<qreal>(QQSK::Property::BottomPadding, QQSK::Property::Padding);
}

void QQStyleKitTextProperties::setBottomPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::BottomPadding, padding))
        handleStylePropertyChanged(&QQStyleKitTextProperties::bottomPaddingChanged);
}

// ************* QQStyleKitControlProperties ****************

QQStyleKitControlProperties::QQStyleKitControlProperties(QQSK::PropertyGroup group, QObject *parent)
    : QQStyleKitPropertyGroup(group, parent)
{
    /* Calculate the free space storage ID space that can accommodate all unique style
     * properties that may be applied to a control. Since we'll prepend different states
     * and subtypes during the property propagation lookup phase later, we need to reserve
     * ID space for them both already now. More docs about the property space is written in
     * the implementation of PropertyPathId. */
    m_groupSpace.size = nestedGroupsStartSize;
    m_groupSpace.start = 0;

    if (group == QQSK::PropertyGroup::GlobalFlag) {
        /* A property path may include pseudo-groups that offers a convenient API for
         * reading properties with specific options applied. The 'global' group is one such
         * pseudo-group. When it is prefixed to a property path, it indicates that the property
         * should be read directly from the style, bypassing any active transitions that might
         * otherwise affect its value.
         * Note: The global group should be ignored when computing a PropertyPathId_t, as it
         * only affect _where_ the property should be read from, not its ID. */
        m_pathFlags.setFlag(QQSK::PropertyPathFlag::Global);
    }
}

QQStyleKitStyle *QQStyleKitControlProperties::style() const
{
    if (subclass() == QQSK::Subclass::QQStyleKitState) {
        /* A QQStyleKitControlState (and its subclasses) should always be a (grand)child of a
         * QQStyleKitStyle. And it belongs to that style, even it that style is not the
         * currently active application style. This is opposed to a QQStyleKitReader,
         * that normally belongs / communicates with the currently active style.
         * NOTE: a style can also be a fallback style for another style (which can be recursive,
         * meaning that a fallback style can also have its own fallback style, and so on). But
         * this function will return the nearest style, and not the root style */
        QObject *obj = parent();
        while (obj && !obj->metaObject()->inherits(&QQStyleKitStyle::staticMetaObject))
            obj = obj->parent();
        return obj ? static_cast<QQStyleKitStyle *>(obj) : nullptr;
    }

    if (subclass() == QQSK::Subclass::QQStyleKitReader) {
        const auto *reader = static_cast<const QQStyleKitReader *>(this);
        if (reader->m_explicitStyle)
            return reader->m_explicitStyle;
    }

    // For a QQStyleKitReader without an explicit style, we return the currently active style for now
    return QQStyleKitStyle::current();
}

QQSK::Subclass QQStyleKitControlProperties::subclass() const
{
    /* QQStyleKitControlProperties is subclassed by several different classes in this
     * framework. As such, it's basically just an interface because it only declares the
     * different properties that can be read or written to in a QQStyleKitStyle, such as
     * hovered.background.color or pressed.indicator.foreground.color. It says nothing
     * about how those properties are stored, instead that is up to each individual
     * subclass to decide */
    if (metaObject()->inherits(&QQStyleKitReader::staticMetaObject))
        return QQSK::Subclass::QQStyleKitReader;
    if (metaObject()->inherits(&QQStyleKitControlState::staticMetaObject))
        return QQSK::Subclass::QQStyleKitState;
    Q_UNREACHABLE();
}

QQStyleKitReader *QQStyleKitControlProperties::asQQStyleKitReader() const
{
    Q_ASSERT(subclass() == QQSK::Subclass::QQStyleKitReader);
    return static_cast<QQStyleKitReader *>(const_cast<QQStyleKitControlProperties *>(this));
}

QQStyleKitControlState *QQStyleKitControlProperties::asQQStyleKitState() const
{
    Q_ASSERT(subclass() == QQSK::Subclass::QQStyleKitState);
    Q_ASSERT(metaObject()->inherits(&QQStyleKitControlState::staticMetaObject));
    return static_cast<QQStyleKitControlState *>(const_cast<QQStyleKitControlProperties *>(this));
}

void QQStyleKitControlProperties::forEachUsedDelegate(
    std::function<void (QQStyleKitDelegateProperties *, QQSK::Delegate, const QString &)> f)
{
    // If adding more delegates here, remember to keep StyleAnimation.qml in sync
    if (m_background)
        f(m_background, QQSK::Delegate::Background, "background"_L1);

    if (m_indicator) {
        f(m_indicator, QQSK::Delegate::Indicator, "indicator"_L1);
        if (m_indicator->m_foreground)
            f(m_indicator->m_foreground, QQSK::Delegate::IndicatorForeground, "indicator.foreground"_L1);
        if (m_indicator->m_first) {
            f(m_indicator->m_first, QQSK::Delegate::IndicatorFirst, "indicator.first"_L1);
            if (m_indicator->m_first->m_foreground)
                f(m_indicator->m_first->m_foreground, QQSK::Delegate::IndicatorFirstForeground, "indicator.first.foreground"_L1);
        }
        if (m_indicator->m_second) {
            f(m_indicator->m_second, QQSK::Delegate::IndicatorSecond, "indicator.second"_L1);
            if (m_indicator->m_second->m_foreground)
                f(m_indicator->m_second->m_foreground, QQSK::Delegate::IndicatorSecondForeground, "indicator.second.foreground"_L1);
        }
    }

    if (m_handle) {
        f(m_handle, QQSK::Delegate::Handle, "handle"_L1);
        if (m_handle->m_first)
            f(m_handle->m_first, QQSK::Delegate::HandleFirst, "handle.first"_L1);
        if (m_handle->m_second)
            f(m_handle->m_second, QQSK::Delegate::HandleSecond, "handle.second"_L1);
    }
}

void QQStyleKitControlProperties::emitChangedForAllStyleProperties(EmitFlags emitFlags)
{
    /* This brute-force function will emit update signals for _all_ style properties
     * in the QQStyleKitStyle API. Doing so is typically needed after a style-, or theme
     * change, as we don't know which properties are affected by such a big change. */
    if (emitFlags.testFlag(EmitFlag::AllProperties)) {
        emit leftPaddingChanged();
        emit rightPaddingChanged();
        emit topPaddingChanged();
        emit bottomPaddingChanged();
        emit spacingChanged();
        emit transitionChanged();
        emit textChanged();
    }

    forEachUsedDelegate([=](QQStyleKitDelegateProperties *delegate, QQSK::Delegate, const QString &){
        delegate->emitChangedForAllStylePropertiesRecursive(emitFlags);
    });
}

template <typename... CHANGED_SIGNALS>
void QQStyleKitControlProperties::emitGlobally(
    QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
{
    for (QQStyleKitReader *reader : QQStyleKitReader::s_allReaders) {
        if (reader->controlType() != controlType)
            continue;
        ((reader->*changedSignals)(), ...);
    }
}

qreal QQStyleKitControlProperties::spacing() const
{
    return styleProperty<qreal>(QQSK::Property::Spacing);
}

void QQStyleKitControlProperties::setSpacing(qreal spacing)
{
    if (setStyleProperty(QQSK::Property::Spacing, spacing))
        handleStylePropertyChanged(&QQStyleKitControlProperties::spacingChanged);
}

qreal QQStyleKitControlProperties::padding() const
{
    return styleProperty<qreal>(QQSK::Property::Padding);
}

void QQStyleKitControlProperties::setPadding(qreal padding)
{
    if (setStyleProperty(QQSK::Property::Padding, padding))
        handleStylePropertiesChanged<QQStyleKitControlProperties>(
            &QQStyleKitControlProperties::paddingChanged,
            &QQStyleKitControlProperties::leftPaddingChanged,
            &QQStyleKitControlProperties::rightPaddingChanged,
            &QQStyleKitControlProperties::topPaddingChanged,
            &QQStyleKitControlProperties::bottomPaddingChanged);
}

qreal QQStyleKitControlProperties::leftPadding() const
{
    return styleProperty<qreal>(QQSK::Property::LeftPadding, QQSK::Property::Padding);
}

void QQStyleKitControlProperties::setLeftPadding(qreal leftPadding)
{
    if (setStyleProperty(QQSK::Property::LeftPadding, leftPadding))
        handleStylePropertyChanged(&QQStyleKitControlProperties::leftPaddingChanged);
}

qreal QQStyleKitControlProperties::rightPadding() const
{
    return styleProperty<qreal>(QQSK::Property::RightPadding, QQSK::Property::Padding);
}

void QQStyleKitControlProperties::setRightPadding(qreal rightPadding)
{
    if (setStyleProperty(QQSK::Property::RightPadding, rightPadding))
        handleStylePropertyChanged(&QQStyleKitControlProperties::rightPaddingChanged);
}

qreal QQStyleKitControlProperties::topPadding() const
{
    return styleProperty<qreal>(QQSK::Property::TopPadding, QQSK::Property::Padding);
}

void QQStyleKitControlProperties::setTopPadding(qreal topPadding)
{
    if (setStyleProperty(QQSK::Property::TopPadding, topPadding))
        handleStylePropertyChanged(&QQStyleKitControlProperties::topPaddingChanged);
}

qreal QQStyleKitControlProperties::bottomPadding() const
{
    return styleProperty<qreal>(QQSK::Property::BottomPadding, QQSK::Property::Padding);
}

void QQStyleKitControlProperties::setBottomPadding(qreal bottomPadding)
{
    if (setStyleProperty(QQSK::Property::BottomPadding, bottomPadding))
        handleStylePropertyChanged(&QQStyleKitControlProperties::bottomPaddingChanged);
}

QQuickTransition *QQStyleKitControlProperties::transition() const
{
    return styleProperty<QQuickTransition *>(QQSK::Property::Transition);
}

void QQStyleKitControlProperties::setTransition(QQuickTransition *transition)
{
    if (setStyleProperty(QQSK::Property::Transition, transition))
        handleStylePropertyChanged(&QQStyleKitControlProperties::transitionChanged);
}

QQStyleKitTextProperties *QQStyleKitControlProperties::text() const
{
    return lazyCreateGroup(m_text, QQSK::PropertyGroup::Text);
}

QQStyleKitDelegateProperties *QQStyleKitControlProperties::background() const
{
    return lazyCreateGroup(m_background, QQSK::PropertyGroup::Background);
}

QQStyleKitHandleProperties *QQStyleKitControlProperties::handle() const
{
    return lazyCreateGroup(m_handle, QQSK::PropertyGroup::Handle);
}

QQStyleKitIndicatorWithSubTypes *QQStyleKitControlProperties::indicator() const
{
    return lazyCreateGroup(m_indicator, QQSK::PropertyGroup::Indicator);
}

QT_END_NAMESPACE

#include "moc_qqstylekitcontrolproperties_p.cpp"
