diff options
Diffstat (limited to 'src/controls')
71 files changed, 4323 insertions, 66 deletions
diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index 51e08316..542cfe4e 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -145,7 +145,7 @@ Window { /*! The style Component for the window. \sa {Qt Quick Controls Styles QML Types} */ - property Component style: Qt.createComponent(Settings.style + "/ApplicationWindowStyle.qml", root) + property Component style: Settings.styleComponent(Settings.style, "ApplicationWindowStyle.qml", root) /*! \internal */ property alias __style: styleLoader.item diff --git a/src/controls/BusyIndicator.qml b/src/controls/BusyIndicator.qml index 4922ecd0..fbd6d665 100644 --- a/src/controls/BusyIndicator.qml +++ b/src/controls/BusyIndicator.qml @@ -78,5 +78,5 @@ Control { Accessible.role: Accessible.Indicator Accessible.name: "busy" - style: Qt.createComponent(Settings.style + "/BusyIndicatorStyle.qml", indicator) + style: Settings.styleComponent(Settings.style, "BusyIndicatorStyle.qml", indicator) } diff --git a/src/controls/Button.qml b/src/controls/Button.qml index 00ccfa4e..6daf595c 100644 --- a/src/controls/Button.qml +++ b/src/controls/Button.qml @@ -88,7 +88,7 @@ BasicButton { Accessible.name: text - style: Qt.createComponent(Settings.style + "/ButtonStyle.qml", button) + style: Settings.styleComponent(Settings.style, "ButtonStyle.qml", button) Binding { target: menu diff --git a/src/controls/Calendar.qml b/src/controls/Calendar.qml index ba40893a..f6a779db 100644 --- a/src/controls/Calendar.qml +++ b/src/controls/Calendar.qml @@ -218,7 +218,7 @@ Control { visibleDate: new Date(visibleYear, visibleMonth, 1) } - style: Qt.createComponent(Settings.style + "/CalendarStyle.qml", calendar) + style: Settings.styleComponent(Settings.style, "CalendarStyle.qml", calendar) /*! \qmlsignal Calendar::hovered(date date) diff --git a/src/controls/CheckBox.qml b/src/controls/CheckBox.qml index 0d27c1ac..b5df8846 100644 --- a/src/controls/CheckBox.qml +++ b/src/controls/CheckBox.qml @@ -139,7 +139,7 @@ AbstractCheckable { */ property bool __ignoreCheckedState: false - style: Qt.createComponent(Settings.style + "/CheckBoxStyle.qml", checkBox) + style: Settings.styleComponent(Settings.style, "CheckBoxStyle.qml", checkBox) activeFocusOnTab: true diff --git a/src/controls/ComboBox.qml b/src/controls/ComboBox.qml index f0067822..81d56030 100644 --- a/src/controls/ComboBox.qml +++ b/src/controls/ComboBox.qml @@ -332,7 +332,7 @@ Control { /*! \internal */ property var __popup: popup - style: Qt.createComponent(Settings.style + "/ComboBoxStyle.qml", comboBox) + style: Settings.styleComponent(Settings.style, "ComboBoxStyle.qml", comboBox) activeFocusOnTab: true diff --git a/src/controls/GroupBox.qml b/src/controls/GroupBox.qml index f51ac757..99d520fe 100644 --- a/src/controls/GroupBox.qml +++ b/src/controls/GroupBox.qml @@ -154,7 +154,7 @@ FocusScope { readonly property alias contentItem: container /*! \internal */ - property Component style: Qt.createComponent(Settings.style + "/GroupBoxStyle.qml", groupbox) + property Component style: Settings.styleComponent(Settings.style, "GroupBoxStyle.qml", groupbox) /*! \internal */ property alias __checkbox: check diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml index 2ab229ab..17b27977 100644 --- a/src/controls/Menu.qml +++ b/src/controls/Menu.qml @@ -132,7 +132,7 @@ MenuPrivate { Component.onCompleted: { if (!style) { __usingDefaultStyle = true - style = Qt.binding(function() { return Qt.createComponent(Settings.style + "/MenuStyle.qml", root) }) + style = Qt.binding(function() { return Settings.styleComponent(Settings.style, "MenuStyle.qml", root) }) } } diff --git a/src/controls/MenuBar.qml b/src/controls/MenuBar.qml index 5f444dac..0c0a2004 100644 --- a/src/controls/MenuBar.qml +++ b/src/controls/MenuBar.qml @@ -77,7 +77,7 @@ import QtQuick.Controls.Private 1.0 MenuBarPrivate { id: root - property Component style: Qt.createComponent(Settings.style + "/MenuBarStyle.qml", root) + property Component style: Settings.styleComponent(Settings.style, "MenuBarStyle.qml", root) /*! \internal */ property QtObject __style: styleLoader.item diff --git a/src/controls/Private/FocusFrame.qml b/src/controls/Private/FocusFrame.qml index d1205708..66090038 100644 --- a/src/controls/Private/FocusFrame.qml +++ b/src/controls/Private/FocusFrame.qml @@ -59,6 +59,6 @@ Item { id: loader z: 2 anchors.fill: parent - sourceComponent: Qt.createComponent(Settings.style + "/FocusFrameStyle.qml", root) + sourceComponent: Settings.styleComponent(Settings.style, "FocusFrameStyle.qml", root) } } diff --git a/src/controls/Private/qquickcontrolsettings.cpp b/src/controls/Private/qquickcontrolsettings.cpp index 017a0714..c000a658 100644 --- a/src/controls/Private/qquickcontrolsettings.cpp +++ b/src/controls/Private/qquickcontrolsettings.cpp @@ -106,6 +106,57 @@ bool QQuickControlSettings::hoverEnabled() const return !isMobile() || !hasTouchScreen(); } +QString QQuickControlSettings::makeStyleComponentPath(const QString &controlStyleName, const QString &styleDirPath) +{ + return styleDirPath + QStringLiteral("/") + controlStyleName; +} + +QUrl QQuickControlSettings::makeStyleComponentUrl(const QString &controlStyleName, QString styleDirPath) +{ + QString styleFilePath = makeStyleComponentPath(controlStyleName, styleDirPath); + + if (styleDirPath.startsWith(QStringLiteral(":/"))) + return QUrl(QStringLiteral("qrc") + styleFilePath); + + return QUrl::fromLocalFile(styleFilePath); +} + +QQmlComponent *QQuickControlSettings::styleComponent(const QUrl &styleDirUrl, const QString &controlStyleName, QObject *control) +{ + Q_UNUSED(styleDirUrl); // required for hack that forces this function to be re-called from within QML when style changes + + // QUrl doesn't consider qrc-based URLs as local files, so bypass it here. + QString styleFilePath = makeStyleComponentPath(controlStyleName, m_styleMap.value(m_name).m_styleDirPath); + QUrl styleFileUrl; + if (QFile::exists(styleFilePath)) { + styleFileUrl = makeStyleComponentUrl(controlStyleName, m_styleMap.value(m_name).m_styleDirPath); + } else { + // It's OK for a style to pick and choose which controls it wants to provide style files for. + styleFileUrl = makeStyleComponentUrl(controlStyleName, m_styleMap.value(QStringLiteral("Base")).m_styleDirPath); + } + + return new QQmlComponent(qmlEngine(control), styleFileUrl); +} + +static QString relativeStyleImportPath(QQmlEngine *engine, const QString &styleName) +{ + QString path; + bool found = false; + foreach (const QString &import, engine->importPathList()) { + QDir dir(import + QStringLiteral("/QtQuick/Controls/Styles")); + if (dir.exists(styleName)) { + found = true; + path = dir.absolutePath(); + break; + } + if (found) + break; + } + if (!found) + path = ":/QtQuick/Controls/Styles"; + return path; +} + static QString styleImportPath(QQmlEngine *engine, const QString &styleName) { QString path = qgetenv("QT_QUICK_CONTROLS_STYLE"); @@ -113,19 +164,7 @@ static QString styleImportPath(QQmlEngine *engine, const QString &styleName) if (fromResource(path)) { path = info.path(); } else if (info.isRelative()) { - bool found = false; - foreach (const QString &import, engine->importPathList()) { - QDir dir(import + QLatin1String("/QtQuick/Controls/Styles")); - if (dir.exists(styleName)) { - found = true; - path = dir.absolutePath(); - break; - } - if (found) - break; - } - if (!found) - path = ":/QtQuick/Controls/Styles"; + path = relativeStyleImportPath(engine, styleName); } else { path = info.absolutePath(); } @@ -134,40 +173,109 @@ static QString styleImportPath(QQmlEngine *engine, const QString &styleName) QQuickControlSettings::QQuickControlSettings(QQmlEngine *engine) { + // First, register all style paths in the default style location. + QDir dir; + const QString defaultStyle = defaultStyleName(); + dir.setPath(relativeStyleImportPath(engine, defaultStyle)); + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &styleDirectory, dir.entryList()) { + findStyle(engine, styleDirectory); + } + m_name = styleImportName(); - m_path = styleImportPath(engine, m_name); - QString path = styleFilePath(); + // If the style name is a path.. + const QString styleNameFromEnvVar = qgetenv("QT_QUICK_CONTROLS_STYLE"); + if (QFile::exists(styleNameFromEnvVar)) { + StyleData styleData; + styleData.m_styleDirPath = styleNameFromEnvVar; + m_styleMap[m_name] = styleData; + } - QDir dir(path); - if (!dir.exists()) { + // Then check if the style the user wanted is known to us. Otherwise, use the fallback style. + if (m_styleMap.contains(m_name)) { + m_path = m_styleMap.value(m_name).m_styleDirPath; + } else { QString unknownStyle = m_name; - m_name = defaultStyleName(); - m_path = styleImportPath(engine, m_name); + m_name = defaultStyle; + m_path = m_styleMap.value(defaultStyle).m_styleDirPath; qWarning() << "WARNING: Cannot find style" << unknownStyle << "- fallback:" << styleFilePath(); - } else { - typedef bool (*StyleInitFunc)(); - typedef const char *(*StylePathFunc)(); - - foreach (const QString &fileName, dir.entryList()) { - if (QLibrary::isLibrary(fileName)) { - QLibrary lib(dir.absoluteFilePath(fileName)); - StyleInitFunc initFunc = (StyleInitFunc) lib.resolve("qt_quick_controls_style_init"); - if (initFunc) - initFunc(); - StylePathFunc pathFunc = (StylePathFunc) lib.resolve("qt_quick_controls_style_path"); - if (pathFunc) - m_path = QString::fromLocal8Bit(pathFunc()); - if (initFunc || pathFunc) - break; - } - } } + // Can't really do anything about this failing here, so don't bother checking... + resolveCurrentStylePath(); + connect(this, SIGNAL(styleNameChanged()), SIGNAL(styleChanged())); connect(this, SIGNAL(stylePathChanged()), SIGNAL(styleChanged())); } +bool QQuickControlSettings::resolveCurrentStylePath() +{ + if (!m_styleMap.contains(m_name)) { + qWarning() << "WARNING: Cannot find style" << m_name; + return false; + } + + StyleData styleData = m_styleMap.value(m_name); + + if (styleData.m_stylePluginPath.isEmpty()) + return true; // It's not a plugin; don't have to do anything. + + typedef bool (*StyleInitFunc)(); + typedef const char *(*StylePathFunc)(); + + QLibrary lib(styleData.m_stylePluginPath); + if (!lib.load()) { + qWarning().nospace() << "WARNING: Cannot load plugin " << styleData.m_stylePluginPath + << " for style " << m_name << ": " << lib.errorString(); + return false; + } + + // Check for the existence of this first, as we don't want to init if this doesn't exist. + StylePathFunc pathFunc = (StylePathFunc) lib.resolve("qt_quick_controls_style_path"); + if (!pathFunc) { + qWarning() << "WARNING: Style" << m_name << "is a plugin but does not expose qt_quick_controls_style_path"; + return false; + } + + StyleInitFunc initFunc = (StyleInitFunc) lib.resolve("qt_quick_controls_style_init"); + if (initFunc) + initFunc(); + + styleData.m_styleDirPath = QString::fromLocal8Bit(pathFunc()); + m_styleMap[m_name] = styleData; + return true; +} + +void QQuickControlSettings::findStyle(QQmlEngine *engine, const QString &styleName) +{ + QString path = styleImportPath(engine, styleName); + QDir dir; + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + dir.setPath(path); + dir.cd(styleName); + + StyleData styleData; + + foreach (const QString &fileName, dir.entryList()) { + // This assumes that there is only one library in the style directory, + // which should be a safe assumption. If at some point it's determined + // not to be safe, we'll have to resolve the init and path functions + // here, to be sure that it is the correct library. + if (QLibrary::isLibrary(fileName)) { + styleData.m_stylePluginPath = dir.absoluteFilePath(fileName); + break; + } + } + + // If there's no plugin for the style, then the style's files are + // contained in this directory (which contains a qmldir file instead). + if (styleData.m_stylePluginPath.isEmpty()) + styleData.m_styleDirPath = dir.absolutePath(); + + m_styleMap[styleName] = styleData; +} + QUrl QQuickControlSettings::style() const { QUrl result; @@ -189,8 +297,14 @@ QString QQuickControlSettings::styleName() const void QQuickControlSettings::setStyleName(const QString &name) { if (m_name != name) { + QString oldName = m_name; m_name = name; - emit styleNameChanged(); + + // Don't change the style if it can't be resolved. + if (!resolveCurrentStylePath()) + m_name = oldName; + else + emit styleNameChanged(); } } @@ -209,7 +323,7 @@ void QQuickControlSettings::setStylePath(const QString &path) QString QQuickControlSettings::styleFilePath() const { - return m_path + QLatin1Char('/') + m_name; + return m_path; } extern Q_GUI_EXPORT int qt_defaultDpiX(); diff --git a/src/controls/Private/qquickcontrolsettings_p.h b/src/controls/Private/qquickcontrolsettings_p.h index ed21101d..1b99fd3e 100644 --- a/src/controls/Private/qquickcontrolsettings_p.h +++ b/src/controls/Private/qquickcontrolsettings_p.h @@ -39,6 +39,9 @@ #include <QtCore/qurl.h> #include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtCore/qhash.h> +#include <QtQml/qqmlcomponent.h> QT_BEGIN_NAMESPACE @@ -74,6 +77,9 @@ public: bool isMobile() const; bool hoverEnabled() const; + Q_INVOKABLE QQmlComponent *styleComponent(const QUrl &styleDirUrl, + const QString &controlStyleName, QObject *control); + signals: void styleChanged(); void styleNameChanged(); @@ -81,9 +87,22 @@ signals: private: QString styleFilePath() const; + void findStyle(QQmlEngine *engine, const QString &styleName); + bool resolveCurrentStylePath(); + QString makeStyleComponentPath(const QString &controlStyleName, const QString &styleDirPath); + QUrl makeStyleComponentUrl(const QString &controlStyleName, QString styleDirPath); + + struct StyleData + { + // Path to the style's plugin or qmldir file. + QString m_stylePluginPath; + // Path to the directory containing the style's files. + QString m_styleDirPath; + }; QString m_name; QString m_path; + QHash<QString, StyleData> m_styleMap; }; QT_END_NAMESPACE diff --git a/src/controls/ProgressBar.qml b/src/controls/ProgressBar.qml index 7bc3bec5..681cb135 100644 --- a/src/controls/ProgressBar.qml +++ b/src/controls/ProgressBar.qml @@ -114,7 +114,7 @@ Control { readonly property alias hovered: hoverArea.containsMouse /*! \internal */ - style: Qt.createComponent(Settings.style + "/ProgressBarStyle.qml", progressbar) + style: Settings.styleComponent(Settings.style, "ProgressBarStyle.qml", progressbar) /*! \internal */ property bool __initialized: false diff --git a/src/controls/RadioButton.qml b/src/controls/RadioButton.qml index d24a5d44..b03028a1 100644 --- a/src/controls/RadioButton.qml +++ b/src/controls/RadioButton.qml @@ -90,7 +90,7 @@ AbstractCheckable { \codeline Qt.createComponent("path/to/style.qml", radioButtonId); */ - style: Qt.createComponent(Settings.style + "/RadioButtonStyle.qml", radioButton) + style: Settings.styleComponent(Settings.style, "RadioButtonStyle.qml", radioButton) __cycleStatesHandler: function() { checked = !checked; } } diff --git a/src/controls/ScrollView.qml b/src/controls/ScrollView.qml index 05f33b84..c82bb7e5 100644 --- a/src/controls/ScrollView.qml +++ b/src/controls/ScrollView.qml @@ -180,7 +180,7 @@ FocusScope { \sa {Qt Quick Controls Styles QML Types} */ - property Component style: Qt.createComponent(Settings.style + "/ScrollViewStyle.qml", root) + property Component style: Settings.styleComponent(Settings.style, "ScrollViewStyle.qml", root) /*! \internal */ property Style __style: styleLoader.item diff --git a/src/controls/Slider.qml b/src/controls/Slider.qml index 8bce7e8f..437749eb 100644 --- a/src/controls/Slider.qml +++ b/src/controls/Slider.qml @@ -189,7 +189,7 @@ Control { range.decreaseSingleStep() } - style: Qt.createComponent(Settings.style + "/SliderStyle.qml", slider) + style: Settings.styleComponent(Settings.style, "SliderStyle.qml", slider) Keys.onRightPressed: if (__horizontal) range.increaseSingleStep() Keys.onLeftPressed: if (__horizontal) range.decreaseSingleStep() diff --git a/src/controls/SpinBox.qml b/src/controls/SpinBox.qml index 95ce2b47..96d8d0a2 100644 --- a/src/controls/SpinBox.qml +++ b/src/controls/SpinBox.qml @@ -208,7 +208,7 @@ Control { */ property Component menu: input.editMenu.defaultMenu - style: Qt.createComponent(Settings.style + "/SpinBoxStyle.qml", spinbox) + style: Settings.styleComponent(Settings.style, "SpinBoxStyle.qml", spinbox) /*! \internal */ function __increment() { diff --git a/src/controls/StatusBar.qml b/src/controls/StatusBar.qml index 4f0dd374..74df9439 100644 --- a/src/controls/StatusBar.qml +++ b/src/controls/StatusBar.qml @@ -82,7 +82,7 @@ FocusScope { + Math.max(container.layoutHeight, __panel ? __panel.implicitHeight : 0) /*! \internal */ - property Component style: Qt.createComponent(Settings.style + "/StatusBarStyle.qml", statusbar) + property Component style: Settings.styleComponent(Settings.style, "StatusBarStyle.qml", statusbar) /*! \internal */ property alias __style: styleLoader.item diff --git a/src/controls/Styles/Base/CircularButtonStyle.qml b/src/controls/Styles/Base/CircularButtonStyle.qml new file mode 100644 index 00000000..e4576226 --- /dev/null +++ b/src/controls/Styles/Base/CircularButtonStyle.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras.Private 1.0 + +ButtonStyle { + id: buttonStyle + + label: Text { + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: control.text + font.pixelSize: TextSingleton.font.pixelSize * 1.25 + color: control.pressed || control.checked ? __buttonHelper.textColorDown : __buttonHelper.textColorUp + styleColor: control.pressed || control.checked ? __buttonHelper.textRaisedColorDown : __buttonHelper.textRaisedColorUp + style: Text.Raised + wrapMode: Text.Wrap + fontSizeMode: Text.Fit + } + + /*! \internal */ + property alias __buttonHelper: buttonHelper + + CircularButtonStyleHelper { + id: buttonHelper + control: buttonStyle.control + } + + background: Item { + implicitWidth: __buttonHelper.implicitWidth + implicitHeight: __buttonHelper.implicitHeight + + Canvas { + id: backgroundCanvas + anchors.fill: parent + + Connections { + target: control + onPressedChanged: backgroundCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + __buttonHelper.paintBackground(ctx); + } + } + } +} diff --git a/src/controls/Styles/Base/CircularGaugeStyle.qml b/src/controls/Styles/Base/CircularGaugeStyle.qml new file mode 100644 index 00000000..99b0091b --- /dev/null +++ b/src/controls/Styles/Base/CircularGaugeStyle.qml @@ -0,0 +1,494 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 + +/*! + \qmltype CircularGaugeStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for CircularGauge. + + You can create a custom circular gauge by replacing the following delegates: + \list + \li \l background + \li \l tickmark + \li \l minorTickmark + \li \l tickmarkLabel + \li \l needle + \li \l foreground + \endlist + + Below is an example that changes the needle to a basic orange \l Rectangle: + \code + CircularGauge { + style: CircularGaugeStyle { + needle: Rectangle { + y: outerRadius * 0.15 + implicitWidth: outerRadius * 0.03 + implicitHeight: outerRadius * 0.9 + antialiasing: true + color: Qt.rgba(0.66, 0.3, 0, 1) + } + } + } + \endcode + + The result: + \image circulargauge-needle-example-2.png CircularGaugeStyle example + + \section2 Direction + + \l minimumValueAngle and \l maximumValueAngle determine not only the + position of the tickmarks, labels and needle, but the direction in which + they are displayed around the gauge. For example, if \l minimumValueAngle + is greater than \l maximumValueAngle, the gauge will be anti-clockwise. + Below, there are two gauges: the top gauge has a \l minimumValueAngle of + \c -90 degrees and a \l maximumValueAngle of \c 90 degrees, and the bottom + gauge has the opposite. + + \image circulargauge-reversed.png Reversed CircularGauge + + \sa {Styling CircularGauge} +*/ + +Style { + id: circularGaugeStyle + + /*! + The \l CircularGauge that this style is attached to. + */ + readonly property CircularGauge control: __control + + /*! + The distance from the center of the gauge to the outer edge of the + gauge. + + This property is useful for determining the size of the various + components of the style, in order to ensure that they are scaled + proportionately when the gauge is resized. + */ + readonly property real outerRadius: Math.min(control.width, control.height) * 0.5 + + /*! + This property determines the angle at which the minimum value is + displayed on the gauge. + + The angle set affects the following components of the gauge: + \list + \li The angle of the needle + \li The position of the tickmarks and labels + \endlist + + The angle origin points north: + + \image circulargauge-angles.png + + There is no minimum or maximum angle for this property, but the default + style only supports angles whose absolute range is less than or equal + to \c 360 degrees. This is because ranges higher than \c 360 degrees + will cause the tickmarks and labels to overlap each other. + + The default value is \c -145. + */ + property real minimumValueAngle: -145 + + /*! + This property determines the angle at which the maximum value is + displayed on the gauge. + + The angle set affects the following components of the gauge: + \list + \li The angle of the needle + \li The position of the tickmarks and labels + \endlist + + The angle origin points north: + + \image circulargauge-angles.png + + There is no minimum or maximum angle for this property, but the default + style only supports angles whose absolute range is less than or equal + to \c 360 degrees. This is because ranges higher than \c 360 degrees + will cause the tickmarks and labels to overlap each other. + + The default value is \c 145. + */ + property real maximumValueAngle: 145 + + /*! + The range between \l minimumValueAngle and \l maximumValueAngle, in + degrees. This value will always be positive. + */ + readonly property real angleRange: control.__panel.circularTickmarkLabel.angleRange + + /*! + This property holds the rotation of the needle in degrees. + */ + property real needleRotation: { + var percentage = (control.value - control.minimumValue) / (control.maximumValue - control.minimumValue); + minimumValueAngle + percentage * angleRange; + } + + /*! + The interval at which tickmarks are displayed. + + For example, if this property is set to \c 10 (the default), + control.minimumValue to \c 0, and control.maximumValue to \c 100, + the tickmarks displayed will be 0, 10, 20, etc., to 100, + around the gauge. + */ + property real tickmarkStepSize: 10 + + /*! + The distance in pixels from the outside of the gauge (outerRadius) at + which the outermost point of the tickmark line is drawn. + */ + property real tickmarkInset: 0 + + + /*! + The amount of tickmarks displayed by the gauge, calculated from + \l tickmarkStepSize and the control's + \l {CircularGauge::minimumValue}{minimumValue} and + \l {CircularGauge::maximumValue}{maximumValue}. + + \sa minorTickmarkCount + */ + readonly property int tickmarkCount: control.__panel.circularTickmarkLabel.tickmarkCount + + /*! + The amount of minor tickmarks between each tickmark. + + The default value is \c 4. + + \sa tickmarkCount + */ + property int minorTickmarkCount: 4 + + /*! + The distance in pixels from the outside of the gauge (outerRadius) at + which the outermost point of the minor tickmark line is drawn. + */ + property real minorTickmarkInset: 0 + + /*! + The distance in pixels from the outside of the gauge (outerRadius) at + which the center of the value marker text is drawn. + */ + property real labelInset: __protectedScope.toPixels(0.19) + + /*! + The interval at which tickmark labels are displayed. + + For example, if this property is set to \c 10 (the default), + control.minimumValue to \c 0, and control.maximumValue to \c 100, the + tickmark labels displayed will be 0, 10, 20, etc., to 100, + around the gauge. + */ + property real labelStepSize: tickmarkStepSize + + /*! + The amount of tickmark labels displayed by the gauge, calculated from + \l labelStepSize and the control's + \l {CircularGauge::minimumValue}{minimumValue} and + \l {CircularGauge::maximumValue}{maximumValue}. + + \sa tickmarkCount, minorTickmarkCount + */ + readonly property int labelCount: control.__panel.circularTickmarkLabel.labelCount + + /*! + Returns \a value as an angle in degrees. + + This function is useful for custom drawing or positioning of items in + the style's components. For example, it can be used to calculate the + angles at which to draw an arc around the gauge indicating the safe + area for the needle to be within. + + For example, if minimumValueAngle is set to \c 270 and + maximumValueAngle is set to \c 90, this function will return \c 270 + when passed minimumValue and \c 90 when passed maximumValue. + + \sa {Styling CircularGauge#styling-circulargauge-background}{ + Styling CircularGauge's background} + */ + function valueToAngle(value) { + return control.__panel.circularTickmarkLabel.valueToAngle(value); + } + + property QtObject __protectedScope: QtObject { + /*! + Converts a value expressed as a percentage of \l outerRadius to + a pixel value. + */ + function toPixels(percentageOfOuterRadius) { + return percentageOfOuterRadius * outerRadius; + } + } + + /*! + The background of the gauge. + + If set, the background determines the implicit size of the gauge. + + By default, there is no background defined. + + \sa {Styling CircularGauge#styling-circulargauge-background}{ + Styling CircularGauge's background} + */ + property Component background + + /*! + This component defines each individual tickmark. The position of each + tickmark is already set; only the + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight} need to be specified. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this tickmark represents. + \endtable + + To illustrate what these properties refer to, we can use the following + example: + + \snippet circulargauge-tickmark-indices-values.qml tickmarks + + We've replaced the conventional \e line tickmarks with \l Text items + and have hidden the tickmarkLabel component in order to make the + association clearer: + + \image circulargauge-tickmark-indices-values.png Tickmarks + + The index property can be useful if you have another model that + contains images to display for each index, for example. + + The value property is useful for drawing lower and upper limits around + the gauge to indicate the recommended value ranges. For example, speeds + above 200 kilometers an hour in a car's speedometer could be indicated + as dangerous using this property. + + \sa {Styling CircularGauge#styling-circulargauge-tickmark}{ + Styling CircularGauge's tickmark} + */ + property Component tickmark: Rectangle { + implicitWidth: outerRadius * 0.02 + antialiasing: true + implicitHeight: outerRadius * 0.06 + color: "#c8c8c8" + } + + /*! + This component defines each individual minor tickmark. The position of + each minor tickmark is already set; only the + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight} need to be specified. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this tickmark represents. + \endtable + + \sa {Styling CircularGauge#styling-circulargauge-minorTickmark}{ + Styling CircularGauge's minorTickmark} + */ + property Component minorTickmark: Rectangle { + implicitWidth: outerRadius * 0.01 + antialiasing: true + implicitHeight: outerRadius * 0.03 + color: "#c8c8c8" + } + + /*! + This defines the text of each tickmark label on the gauge. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this label. + \row \li \c {readonly property real} \b styleData.value + \li The value that this label represents. + \endtable + + \sa {Styling CircularGauge#styling-circulargauge-tickmarkLabel}{ + Styling CircularGauge's tickmarkLabel} + */ + property Component tickmarkLabel: Text { + font.pixelSize: Math.max(6, __protectedScope.toPixels(0.12)) + text: styleData.value + color: "#c8c8c8" + antialiasing: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + /*! + The needle that points to the gauge's current value. + + This component is drawn below the \l foreground component. + + The style expects the needle to be pointing up at a rotation of \c 0, + in order for the rotation to be correct. For example: + + \image circulargauge-needle.png CircularGauge's needle + + When defining your own needle component, the only properties that the + style requires you to set are the + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight}. + + Optionally, you can set \l {Item::x}{x} and \l {Item::y}{y} to change + the needle's transform origin. Setting the \c x position can be useful + for needle images where the needle is not centered exactly + horizontally. Setting the \c y position allows you to make the base of + the needle hang over the center of the gauge. + + \sa {Styling CircularGauge#styling-circulargauge-needle}{ + Styling CircularGauge's needle} + */ + property Component needle: Item { + implicitWidth: __protectedScope.toPixels(0.08) + implicitHeight: 0.9 * outerRadius + + Image { + anchors.fill: parent + source: "images/needle.png" + } + } + + /*! + The foreground of the gauge. This component is drawn above all others. + + Like \l background, the foreground component fills the entire gauge. + + By default, the knob of the gauge is defined here. + + \sa {Styling CircularGauge#styling-circulargauge-foreground}{ + Styling CircularGauge's foreground} + */ + property Component foreground: Item { + Image { + source: "images/knob.png" + anchors.centerIn: parent + scale: { + var idealHeight = __protectedScope.toPixels(0.2); + var originalImageHeight = sourceSize.height; + idealHeight / originalImageHeight; + } + } + } + + /*! \internal */ + property Component panel: Item { + id: panelItem + implicitWidth: backgroundLoader.item ? backgroundLoader.implicitWidth : TextSingleton.implicitHeight * 16 + implicitHeight: backgroundLoader.item ? backgroundLoader.implicitHeight : TextSingleton.implicitHeight * 16 + + property alias background: backgroundLoader.item + property alias circularTickmarkLabel: circularTickmarkLabel_ + + Loader { + id: backgroundLoader + sourceComponent: circularGaugeStyle.background + width: outerRadius * 2 + height: outerRadius * 2 + anchors.centerIn: parent + } + + CircularTickmarkLabel { + id: circularTickmarkLabel_ + anchors.fill: backgroundLoader + + minimumValue: control.minimumValue + maximumValue: control.maximumValue + stepSize: control.stepSize + tickmarksVisible: control.tickmarksVisible + minimumValueAngle: circularGaugeStyle.minimumValueAngle + maximumValueAngle: circularGaugeStyle.maximumValueAngle + tickmarkStepSize: circularGaugeStyle.tickmarkStepSize + tickmarkInset: circularGaugeStyle.tickmarkInset + minorTickmarkCount: circularGaugeStyle.minorTickmarkCount + minorTickmarkInset: circularGaugeStyle.minorTickmarkInset + labelInset: circularGaugeStyle.labelInset + labelStepSize: circularGaugeStyle.labelStepSize + + style: CircularTickmarkLabelStyle { + tickmark: circularGaugeStyle.tickmark + minorTickmark: circularGaugeStyle.minorTickmark + tickmarkLabel: circularGaugeStyle.tickmarkLabel + } + } + + Loader { + id: needleLoader + sourceComponent: circularGaugeStyle.needle + transform: [ + Rotation { + angle: needleRotation + origin.x: needleLoader.width / 2 + origin.y: needleLoader.height + }, + Translate { + x: panelItem.width / 2 - needleLoader.width / 2 + y: panelItem.height / 2 - needleLoader.height + } + ] + } + + Loader { + id: foreground + sourceComponent: circularGaugeStyle.foreground + anchors.fill: backgroundLoader + } + } +} diff --git a/src/controls/Styles/Base/CircularTickmarkLabelStyle.qml b/src/controls/Styles/Base/CircularTickmarkLabelStyle.qml new file mode 100644 index 00000000..010e9245 --- /dev/null +++ b/src/controls/Styles/Base/CircularTickmarkLabelStyle.qml @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras.Private 1.0 +import QtQuick.Extras.Private.CppUtils 1.0 + +Style { + id: circularTickmarkLabelStyle + + /*! + The distance from the center of the control to the outer edge. + */ + readonly property real outerRadius: Math.min(control.width, control.height) * 0.5 + + property QtObject __protectedScope: QtObject { + /*! + Converts a value expressed as a percentage of \l outerRadius to + a pixel value. + */ + function toPixels(percentageOfOuterRadius) { + return percentageOfOuterRadius * outerRadius; + } + } + + /*! + This component defines each individual tickmark. The position of each + tickmark is already set; only the size needs to be specified. + */ + property Component tickmark: Rectangle { + width: outerRadius * 0.02 + antialiasing: true + height: outerRadius * 0.06 + color: "#c8c8c8" + } + + /*! + This component defines each individual minor tickmark. The position of + each minor tickmark is already set; only the size needs to be specified. + */ + property Component minorTickmark: Rectangle { + width: outerRadius * 0.01 + antialiasing: true + height: outerRadius * 0.03 + color: "#c8c8c8" + } + + /*! + This defines the text of each tickmark label on the gauge. + */ + property Component tickmarkLabel: Text { + font.pixelSize: Math.max(6, __protectedScope.toPixels(0.12)) + text: styleData.value + color: "#c8c8c8" + antialiasing: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + /*! \internal */ + property Component panel: Item { + id: panelItem + implicitWidth: 250 + implicitHeight: 250 + + function rangeUsed(count, stepSize) { + return (((count - 1) * stepSize) / (control.maximumValue - control.minimumValue)) * control.angleRange; + } + + readonly property real tickmarkSectionSize: rangeUsed(control.tickmarkCount, control.tickmarkStepSize) / (control.tickmarkCount - 1) + readonly property real tickmarkSectionValue: (control.maximumValue - control.minimumValue) / (control.tickmarkCount - 1) + readonly property real minorTickmarkSectionSize: tickmarkSectionSize / (control.minorTickmarkCount + 1) + readonly property real minorTickmarkSectionValue: tickmarkSectionValue / (control.minorTickmarkCount + 1) + readonly property int totalMinorTickmarkCount: { + // The size of each section within two major tickmarks, expressed as a percentage. + var minorSectionPercentage = 1 / (control.minorTickmarkCount + 1); + // The amount of major tickmarks not able to be displayed; will be 0 if they all fit. + var tickmarksNotDisplayed = control.__tickmarkCount - control.tickmarkCount; + var count = control.minorTickmarkCount * (control.tickmarkCount - 1); + // We'll try to display as many minor tickmarks as we can to fill up the space. + count + tickmarksNotDisplayed / minorSectionPercentage; + } + readonly property real labelSectionSize: rangeUsed(control.labelCount, control.labelStepSize) / (control.labelCount - 1) + + function toPixels(percentageOfOuterRadius) { + return percentageOfOuterRadius * outerRadius; + } + + /*! + Returns the angle of \a marker (in the range 0 ... n - 1, where n + is the amount of markers) on the gauge where sections are of size + tickmarkSectionSize. + */ + function tickmarkAngleFromIndex(tickmarkIndex) { + return tickmarkIndex * tickmarkSectionSize + control.minimumValueAngle; + } + + function labelAngleFromIndex(labelIndex) { + return labelIndex * labelSectionSize + control.minimumValueAngle; + } + + function labelPosFromIndex(index, labelWidth, labelHeight) { + return MathUtils.centerAlongCircle(outerRadius, outerRadius, labelWidth, labelHeight, + MathUtils.degToRadOffset(labelAngleFromIndex(index)), + outerRadius - control.labelInset) + } + + function minorTickmarkAngleFromIndex(minorTickmarkIndex) { + var baseAngle = tickmarkAngleFromIndex(Math.floor(minorTickmarkIndex / control.minorTickmarkCount)); + // + minorTickmarkSectionSize because we don't want the first minor tickmark to start on top of its "parent" tickmark. + var relativeMinorAngle = (minorTickmarkIndex % control.minorTickmarkCount * minorTickmarkSectionSize) + minorTickmarkSectionSize; + return baseAngle + relativeMinorAngle; + } + + function tickmarkValueFromIndex(majorIndex) { + return (majorIndex * tickmarkSectionValue) + control.minimumValue; + } + + function tickmarkValueFromMinorIndex(minorIndex) { + var majorIndex = Math.floor(minorIndex / control.minorTickmarkCount); + var relativeMinorIndex = minorIndex % control.minorTickmarkCount; + return tickmarkValueFromIndex(majorIndex) + ((relativeMinorIndex * minorTickmarkSectionValue) + minorTickmarkSectionValue); + } + + Loader { + active: control.tickmarksVisible && tickmark != null + width: outerRadius * 2 + height: outerRadius * 2 + anchors.centerIn: parent + + sourceComponent: Repeater { + id: tickmarkRepeater + model: control.tickmarkCount + delegate: Loader { + id: tickmarkLoader + objectName: "tickmark" + styleData.index + x: tickmarkRepeater.width / 2 + y: tickmarkRepeater.height / 2 + + transform: [ + Translate { + y: -outerRadius + control.tickmarkInset + }, + Rotation { + angle: panelItem.tickmarkAngleFromIndex(styleData.index) - __tickmarkWidthAsAngle / 2 + } + ] + + sourceComponent: tickmark + + property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: tickmarkLoader.__index + readonly property real value: tickmarkValueFromIndex(index) + } + + readonly property real __tickmarkWidthAsAngle: MathUtils.radToDeg((width / (MathUtils.pi2 * outerRadius)) * MathUtils.pi2) + } + } + } + Loader { + active: control.tickmarksVisible && minorTickmark != null + width: outerRadius * 2 + height: outerRadius * 2 + anchors.centerIn: parent + + sourceComponent: Repeater { + id: minorRepeater + anchors.fill: parent + model: totalMinorTickmarkCount + delegate: Loader { + id: minorTickmarkLoader + objectName: "minorTickmark" + styleData.index + x: minorRepeater.width / 2 + y: minorRepeater.height / 2 + transform: [ + Translate { + y: -outerRadius + control.minorTickmarkInset + }, + Rotation { + angle: panelItem.minorTickmarkAngleFromIndex(styleData.index) - __minorTickmarkWidthAsAngle / 2 + } + ] + + sourceComponent: minorTickmark + + property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: minorTickmarkLoader.__index + readonly property real value: tickmarkValueFromMinorIndex(index) + } + + readonly property real __minorTickmarkWidthAsAngle: MathUtils.radToDeg((width / (MathUtils.pi2 * outerRadius)) * MathUtils.pi2) + } + } + } + Loader { + id: labelLoader + active: control.tickmarksVisible && tickmarkLabel != null + width: outerRadius * 2 + height: outerRadius * 2 + anchors.centerIn: parent + + sourceComponent: Item { + id: labelItem + width: outerRadius * 2 + height: outerRadius * 2 + anchors.centerIn: parent + + Connections { + target: control + onMinimumValueChanged: valueTextModel.update() + onMaximumValueChanged: valueTextModel.update() + onTickmarkStepSizeChanged: valueTextModel.update() + onLabelStepSizeChanged: valueTextModel.update() + } + + Repeater { + id: labelItemRepeater + + Component.onCompleted: valueTextModel.update(); + + model: ListModel { + id: valueTextModel + + function update() { + if (control.labelStepSize === 0) { + return; + } + + // Make bigger if it's too small and vice versa. + // +1 because we want to show 11 values, with, for example: 0, 10, 20... 100. + var difference = control.labelCount - count; + if (difference > 0) { + for (; difference > 0; --difference) { + append({ value: 0 }); + } + } else if (difference < 0) { + for (; difference < 0; ++difference) { + remove(count - 1); + } + } + + var index = 0; + for (var value = control.minimumValue; + value <= control.maximumValue && index < count; + value += control.labelStepSize, ++index) { + setProperty(index, "value", value); + } + } + } + delegate: Loader { + id: tickmarkLabelDelegateLoader + sourceComponent: tickmarkLabel + x: pos.x + y: pos.y + + readonly property point pos: panelItem.labelPosFromIndex(index, width, height); + + readonly property int __index: index + property QtObject styleData: QtObject { + readonly property var value: index != -1 ? labelItemRepeater.model.get(index).value : 0 + readonly property alias index: tickmarkLabelDelegateLoader.__index + } + } + } + } + } + } +} diff --git a/src/controls/Styles/Base/CommonStyleHelper.qml b/src/controls/Styles/Base/CommonStyleHelper.qml new file mode 100644 index 00000000..4263a743 --- /dev/null +++ b/src/controls/Styles/Base/CommonStyleHelper.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +QtObject { + property Item control + + property color buttonColorUpTop: "#e3e3e3" + property color buttonColorUpBottom: "#b3b3b3" + property color buttonColorDownTop: "#d3d3d3" + property color buttonColorDownBottom: "#939393" + property color textColorUp: "#4e4e4e" + property color textColorDown: "#303030" + property color textRaisedColorUp: "#ffffff" + property color textRaisedColorDown: "#e3e3e3" + property color offColor: "#ff0000" + property color offColorShine: "#ff6666" + property color onColor: "#00cc00" + property color onColorShine: "#66ff66" + property color inactiveColor: "#1f1f1f" + property color inactiveColorShine: "#666666" +} diff --git a/src/controls/Styles/Base/DelayButtonStyle.qml b/src/controls/Styles/Base/DelayButtonStyle.qml new file mode 100644 index 00000000..447932b7 --- /dev/null +++ b/src/controls/Styles/Base/DelayButtonStyle.qml @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private.CppUtils 1.1 + +/*! + \qmltype DelayButtonStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for DelayButton. + + You can create a custom DelayButton by replacing the following delegates: + \list + \li \l foreground + \li \l {QtQuick.Controls.Styles::ButtonStyle::label}{label} + \endlist +*/ + +CircularButtonStyle { + id: delayButtonStyle + + /*! + The \l DelayButton that this style is attached to. + */ + readonly property DelayButton control: __control + + /*! + The gradient of the progress bar around the button. + */ + property Gradient progressBarGradient: Gradient { + GradientStop { + position: 0 + color: "#ff6666" + } + GradientStop { + position: 1 + color: "#ff0000" + } + } + + /*! + The color of the drop shadow under the progress bar. + */ + property color progressBarDropShadowColor: "#ff6666" + + background: Item { + implicitWidth: __buttonHelper.implicitWidth + implicitHeight: __buttonHelper.implicitHeight + + Canvas { + id: backgroundCanvas + anchors.fill: parent + + Connections { + target: control + onPressedChanged: backgroundCanvas.requestPaint() + onCheckedChanged: backgroundCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + __buttonHelper.paintBackground(ctx); + } + } + } + + /*! + The foreground of the button. + + The progress bar is drawn here. + */ + property Component foreground: Item { + id: foregroundItem + + state: "normal" + states: [ + State { + name: "normal" + + PropertyChanges { + target: foregroundItem + opacity: 1 + } + }, + State { + name: "activated" + } + ] + + transitions: [ + Transition { + from: "normal" + to: "activated" + SequentialAnimation { + loops: Animation.Infinite + + NumberAnimation { + target: foregroundItem + property: "opacity" + from: 0.8 + to: 0 + duration: 500 + easing.type: Easing.InOutSine + } + NumberAnimation { + target: foregroundItem + property: "opacity" + from: 0 + to: 0.8 + duration: 500 + easing.type: Easing.InOutSine + } + } + } + ] + + Connections { + target: control + onActivated: state = "activated" + onCheckedChanged: if (!control.checked) state = "normal" + } + + CircularProgressBar { + id: progressBar + visible: false + width: Math.min(parent.width, parent.height) + progressBarDropShadow.radius * 3 * 2 + height: width + anchors.centerIn: parent + antialiasing: true + barWidth: __buttonHelper.outerArcLineWidth + inset: progressBarDropShadow.radius * 3 + minimumValueAngle: -180 + maximumValueAngle: 180 + + progress: control.progress + + // TODO: Add gradient property if/when we drop support for building with 5.1. + function updateGradient() { + clearStops(); + for (var i = 0; i < progressBarGradient.stops.length; ++i) { + addStop(progressBarGradient.stops[i].position, progressBarGradient.stops[i].color); + } + } + + Component.onCompleted: updateGradient() + + Connections { + target: delayButtonStyle + onProgressBarGradientChanged: progressBar.updateGradient() + } + } + + DropShadow { + id: progressBarDropShadow + anchors.fill: progressBar + fast: true + // QTBUG-33747 +// cached: !control.pressed + radius: 4 + samples: radius * 2 + color: progressBarDropShadowColor + source: progressBar + } + } + + panel: Item { + implicitWidth: backgroundLoader.implicitWidth + implicitHeight: backgroundLoader.implicitHeight + + Loader { + id: backgroundLoader + anchors.fill: parent + sourceComponent: background + } + + Loader { + id: foregroundLoader + anchors.fill: parent + sourceComponent: foreground + } + + Loader { + id: labelLoader + sourceComponent: label + anchors.fill: parent + anchors.leftMargin: padding.left + anchors.topMargin: padding.top + anchors.rightMargin: padding.right + anchors.bottomMargin: padding.bottom + } + } +} diff --git a/src/controls/Styles/Base/DialStyle.qml b/src/controls/Styles/Base/DialStyle.qml new file mode 100644 index 00000000..dc4f370d --- /dev/null +++ b/src/controls/Styles/Base/DialStyle.qml @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 +import QtQuick.Extras.Private.CppUtils 1.0 + +/*! + \qmltype DialStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for Dial. + + You can create a custom dial by replacing the following delegates: + \list + \li \l background + \endlist +*/ + +Style { + id: dialStyle + + /*! + The \l Dial that this style is attached to. + */ + readonly property Dial control: __control + + /*! + The distance from the center of the dial to the outer edge of the dial. + + This property is useful for determining the size of the various + components of the style, in order to ensure that they are scaled + proportionately when the dial is resized. + */ + readonly property real outerRadius: Math.min(control.height, control.width) / 2 + + /*! + The distance in pixels from the outside of the dial (outerRadius) + to the center of the handle. + */ + property real handleInset: (__tickmarkRadius * 4) + ((__handleRadius * 2) * 0.55) + + /*! + The interval at which tickmarks are displayed. + + For example, if this property is set to \c 10, + control.minimumValue to \c 0, and control.maximumValue to \c 100, + the tickmarks displayed will be 0, 10, 20, etc., to 100, along + the circumference of the dial. + */ + property real tickmarkStepSize: 1 + + /*! + The distance in pixels from the outside of the dial (outerRadius) at + which the outermost point of the tickmark line is drawn. + */ + property real tickmarkInset: 0 + + + /*! + The amount of tickmarks displayed by the dial, calculated from + \l tickmarkStepSize and the control's + \l {Dial::minimumValue}{minimumValue} and + \l {Dial::maximumValue}{maximumValue}. + + \sa minorTickmarkCount + */ + readonly property int tickmarkCount: control.__panel.circularTickmarkLabel.tickmarkCount + + /*! + The amount of minor tickmarks between each tickmark. + + \sa tickmarkCount + */ + property int minorTickmarkCount: 0 + + /*! + The distance in pixels from the outside of the dial (outerRadius) at + which the outermost point of the minor tickmark line is drawn. + */ + property real minorTickmarkInset: 0 + + /*! + The distance in pixels from the outside of the dial (outerRadius) at + which the center of the value marker text is drawn. + */ + property real labelInset: 0 + + /*! + The interval at which tickmark labels are displayed. + + For example, if this property is set to \c 10 (the default), + control.minimumValue to \c 0, and control.maximumValue to \c 100, the + tickmark labels displayed will be 0, 10, 20, etc., to 100, + along the circumference of the dial. + */ + property real labelStepSize: tickmarkStepSize + + /*! + The amount of tickmark labels displayed by the dial, calculated from + \l labelStepSize and the control's + \l {Dial::minimumValue}{minimumValue} and + \l {Dial::maximumValue}{maximumValue}. + + \sa tickmarkCount, minorTickmarkCount + */ + readonly property int labelCount: control.__panel.circularTickmarkLabel.labelCount + + /*! + Returns \a value as an angle in degrees. + + This function is useful for custom drawing or positioning of items in + the style's components. For example, it can be used to calculate the + angles at which to draw an arc around the dial indicating the safe + range of values. + + For example, if minimumValueAngle is set to \c 270 and + maximumValueAngle is set to \c 90, this function will return \c 270 + when passed minimumValue and \c 90 when passed maximumValue. + */ + function valueToAngle(value) { + return control.__panel.circularTickmarkLabel.valueToAngle(value); + } + + /*! \internal */ + readonly property real __tickmarkRadius: outerRadius * 0.06 + + /*! \internal */ + readonly property real __handleRadius: outerRadius * 0.15 + + /*! + \internal + + This property determines whether it is possible to change the value of + the dial simply by pressing/tapping. + + If \c false, the user must drag to rotate the dial and hence change the + value. + + This property is useful for touch devices, where it is easy to + accidentally tap while flicking, for example. + */ + property bool __dragToSet: Settings.hasTouchScreen && Settings.isMobile + + /*! + The background of the dial. + + The implicit size of the dial is taken from this component. + */ + property Component background: Item { + id: backgroundItem + implicitWidth: backgroundHelper.implicitWidth + implicitHeight: backgroundHelper.implicitHeight + + CircularButtonStyleHelper { + id: backgroundHelper + control: dialStyle.control + property color zeroMarkerColor: "#a8a8a8" + property color zeroMarkerColorTransparent: "transparent" + property real zeroMarkerLength: outerArcLineWidth * 1.25 + property real zeroMarkerWidth: outerArcLineWidth * 0.3 + + smallestAxis: Math.min(backgroundItem.width, backgroundItem.height) - __tickmarkRadius * 4 + } + + Canvas { + id: backgroundCanvas + anchors.fill: parent + + readonly property real xCenter: width / 2 + readonly property real yCenter: height / 2 + + onPaint: { + var ctx = getContext("2d"); + backgroundHelper.paintBackground(ctx); + } + } + } + + /*! + The handle of the dial. + + The handle is automatically positioned within the dial, based on the + \l handleInset and the implicit width and height of the handle itself. + */ + property Component handle: Canvas { + implicitWidth: __handleRadius * 2 + implicitHeight: __handleRadius * 2 + + HandleStyleHelper { + id: handleHelper + } + + onPaint: { + var ctx = getContext("2d"); + handleHelper.paintHandle(ctx, 1, 1, width - 2, height - 2); + } + } + + /*! + This component defines each individual tickmark. The position of each + tickmark is already set; only the + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight} need to be specified. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this tickmark represents. + \endtable + */ + property Component tickmark: Rectangle { + implicitWidth: outerRadius * 0.015 + (styleData.index === 0 || styleData.index === tickmarkCount ? 1 : (styleData.index) / tickmarkCount) * __tickmarkRadius * 0.75 + implicitHeight: implicitWidth + radius: height / 2 + color: styleData.index === 0 ? "transparent" : Qt.rgba(0, 0, 0, 0.266) + antialiasing: true + border.width: styleData.index === 0 ? Math.max(1, outerRadius * 0.0075) : 0 + border.color: Qt.rgba(0, 0, 0, 0.266) + } + + /*! + This component defines each individual minor tickmark. The position of each + minor tickmark is already set; only the + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight} need to be specified. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this tickmark represents. + \endtable + + By default, no minor tickmark is defined. + */ + property Component minorTickmark + + /*! + This defines the text of each tickmark label on the dial. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this label. + \row \li \c {readonly property real} \b styleData.value + \li The value that this label represents. + \endtable + + By default, no label is defined. + */ + property Component tickmarkLabel + + /*! \internal */ + property Component panel: Item { + implicitWidth: backgroundLoader.implicitWidth + implicitHeight: backgroundLoader.implicitHeight + + property alias background: backgroundLoader.item + property alias circularTickmarkLabel: circularTickmarkLabel_ + + Loader { + id: backgroundLoader + sourceComponent: dialStyle.background + width: outerRadius * 2 + height: width + anchors.centerIn: parent + } + + Loader { + id: handleLoader + sourceComponent: dialStyle.handle + x: backgroundLoader.x + __pos.x - width / 2 + y: backgroundLoader.y + __pos.y - height / 2 + + readonly property point __pos: { + var radians = 0; + if (control.__wrap) { + radians = (control.value - control.minimumValue) / + (control.maximumValue - control.minimumValue) * + (MathUtils.pi2) + backgroundHelper.zeroAngle; + } else { + radians = -(Math.PI * 8 - (control.value - control.minimumValue) * 10 * + Math.PI / (control.maximumValue - control.minimumValue)) / 6; + } + + return MathUtils.centerAlongCircle(backgroundLoader.width / 2, backgroundLoader.height / 2, + 0, 0, radians, outerRadius - handleInset) + } + } + + CircularTickmarkLabel { + id: circularTickmarkLabel_ + anchors.fill: backgroundLoader + + minimumValue: control.minimumValue + maximumValue: control.maximumValue + stepSize: control.stepSize + tickmarksVisible: control.tickmarksVisible + minimumValueAngle: -150 + maximumValueAngle: 150 + tickmarkStepSize: dialStyle.tickmarkStepSize + tickmarkInset: dialStyle.tickmarkInset + minorTickmarkCount: dialStyle.minorTickmarkCount + minorTickmarkInset: dialStyle.minorTickmarkInset + labelInset: dialStyle.labelInset + labelStepSize: dialStyle.labelStepSize + + style: CircularTickmarkLabelStyle { + tickmark: dialStyle.tickmark + minorTickmark: dialStyle.minorTickmark + tickmarkLabel: dialStyle.tickmarkLabel + } + } + } +} diff --git a/src/controls/Styles/Base/GaugeStyle.qml b/src/controls/Styles/Base/GaugeStyle.qml new file mode 100644 index 00000000..35da65d5 --- /dev/null +++ b/src/controls/Styles/Base/GaugeStyle.qml @@ -0,0 +1,541 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 + +/*! + \qmltype GaugeStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for Gauge. + + You can create a custom gauge by replacing the following delegates: + \list + \li \l background + \li valueBar + \li tickmarkLabel + \endlist + + Below, you'll find an example of how to create a temperature gauge that + changes color as its value increases: + + \code + import QtQuick 2.2 + import QtQuick.Controls 1.4 + import QtQuick.Controls.Styles 1.4 + import QtQuick.Extras 1.4 + + Rectangle { + width: 80 + height: 200 + + Timer { + running: true + repeat: true + interval: 2000 + onTriggered: gauge.value = gauge.value == gauge.maximumValue ? 5 : gauge.maximumValue + } + + Gauge { + id: gauge + anchors.fill: parent + anchors.margins: 10 + + value: 5 + Behavior on value { + NumberAnimation { + duration: 1000 + } + } + + style: GaugeStyle { + valueBar: Rectangle { + implicitWidth: 16 + color: Qt.rgba(gauge.value / gauge.maximumValue, 0, 1 - gauge.value / gauge.maximumValue, 1) + } + } + } + } + \endcode + + \image gauge-temperature.png + The gauge displaying values at various points during the animation. + + \sa {Styling Gauge} +*/ + +Style { + id: gaugeStyle + + /*! + The \l Gauge that this style is attached to. + */ + readonly property Gauge control: __control + + /*! + This property holds the value displayed by the gauge as a position in + pixels. + + It is useful for custom styling. + */ + readonly property real valuePosition: control.__panel.valuePosition + + /*! + The background of the gauge, displayed behind the \l valueBar. + + By default, no background is defined. + */ + property Component background + + /*! + Each tickmark displayed by the gauge. + + To set the size of the tickmarks, specify an + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight}. + + The widest tickmark will determine the space set aside for all + tickmarks. For this reason, the \c implicitWidth of each tickmark + should be greater than or equal to that of each minor tickmark. If you + need minor tickmarks to have greater widths than the major tickmarks, + set the larger width in a child item of the \l minorTickmark component. + + For layouting reasons, each tickmark should have the same + \c implicitHeight. If different heights are needed for individual + tickmarks, specify those heights in a child item of the component. + + In the example below, we decrease the height of the tickmarks: + + \code + tickmark: Item { + implicitWidth: 18 + implicitHeight: 1 + + Rectangle { + color: "#c8c8c8" + anchors.fill: parent + anchors.leftMargin: 3 + anchors.rightMargin: 3 + } + } + \endcode + + \image gauge-tickmark-example.png Gauge tickmark example + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this tickmark represents. + \row \li \c {readonly property real} \b styleData.valuePosition + \li The value that this tickmark represents as a position in + pixels, with 0 being at the bottom of the gauge. + \endtable + + \sa minorTickmark + */ + property Component tickmark: Item { + implicitWidth: Math.round(TextSingleton.height * 1.1) + implicitHeight: Math.max(2, Math.round(TextSingleton.height * 0.1)) + + Rectangle { + color: "#c8c8c8" + anchors.fill: parent + anchors.leftMargin: Math.round(TextSingleton.implicitHeight * 0.2) + anchors.rightMargin: Math.round(TextSingleton.implicitHeight * 0.2) + } + } + + /*! + Each minor tickmark displayed by the gauge. + + To set the size of the minor tickmarks, specify an + \l {Item::implicitWidth}{implicitWidth} and + \l {Item::implicitHeight}{implicitHeight}. + + For layouting reasons, each minor tickmark should have the same + \c implicitHeight. If different heights are needed for individual + tickmarks, specify those heights in a child item of the component. + + In the example below, we decrease the width of the minor tickmarks: + + \code + minorTickmark: Item { + implicitWidth: 8 + implicitHeight: 1 + + Rectangle { + color: "#cccccc" + anchors.fill: parent + anchors.leftMargin: 2 + anchors.rightMargin: 4 + } + } + \endcode + + \image gauge-minorTickmark-example.png Gauge minorTickmark example + + Each instance of this component has access to the following property: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this minor tickmark. + \row \li \c {readonly property real} \b styleData.value + \li The value that this minor tickmark represents. + \row \li \c {readonly property real} \b styleData.valuePosition + \li The value that this minor tickmark represents as a + position in pixels, with 0 being at the bottom of the + gauge. + \endtable + + \sa tickmark + */ + property Component minorTickmark: Item { + implicitWidth: Math.round(TextSingleton.implicitHeight * 0.65) + implicitHeight: Math.max(1, Math.round(TextSingleton.implicitHeight * 0.05)) + + Rectangle { + color: "#c8c8c8" + anchors.fill: parent + anchors.leftMargin: control.__tickmarkAlignment === Qt.AlignBottom || control.__tickmarkAlignment === Qt.AlignRight + ? Math.max(3, Math.round(TextSingleton.implicitHeight * 0.2)) + : 0 + anchors.rightMargin: control.__tickmarkAlignment === Qt.AlignBottom || control.__tickmarkAlignment === Qt.AlignRight + ? 0 + : Math.max(3, Math.round(TextSingleton.implicitHeight * 0.2)) + } + } + + /*! + This defines the text of each tickmark label on the gauge. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this label. + \row \li \c {readonly property real} \b styleData.value + \li The value that this label represents. + \endtable + */ + property Component tickmarkLabel: Text { + text: control.formatValue(styleData.value) + font: control.font + color: "#c8c8c8" + antialiasing: true + } + + /*! + The bar that represents the value of the gauge. + + To height of the value bar is automatically resized according to + \l {Gauge::value}{value}, and does not need to be specified. + + When a custom valueBar is defined, its + \l {Item::implicitWidth}{implicitWidth} property must be set. + */ + property Component valueBar: Rectangle { + color: "#00bbff" + implicitWidth: TextSingleton.implicitHeight + } + + /*! + The bar that represents the foreground of the gauge. + + This component is drawn above every other component. + */ + property Component foreground: Canvas { + readonly property real xCenter: width / 2 + readonly property real yCenter: height / 2 + property real shineLength: height * 0.95 + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + ctx.beginPath(); + ctx.rect(0, 0, width, height); + + var gradient = ctx.createLinearGradient(0, yCenter, width, yCenter); + + gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0.08)); + gradient.addColorStop(1, Qt.rgba(1, 1, 1, 0.20)); + ctx.fillStyle = gradient; + ctx.fill(); + } + } + + /*! \internal */ + property Component panel: Item { + id: panelComponent + implicitWidth: control.orientation === Qt.Vertical ? tickmarkLabelBoundsWidth + rawBarWidth : TextSingleton.height * 14 + implicitHeight: control.orientation === Qt.Vertical ? TextSingleton.height * 14 : tickmarkLabelBoundsWidth + rawBarWidth + + readonly property int tickmarkCount: (control.maximumValue - control.minimumValue) / control.tickmarkStepSize + 1 + readonly property real tickmarkSpacing: (tickmarkLabelBounds.height - tickmarkWidth * tickmarkCount) / (tickmarkCount - 1) + + property real tickmarkLength: tickmarkColumn.width + // Can't deduce this from the column, so we set it from within the first tickmark delegate loader. + property real tickmarkWidth: 2 + + readonly property real tickmarkOffset: control.orientation === Qt.Vertical ? control.__hiddenText.height / 2 : control.__hiddenText.width / 2 + + readonly property real minorTickmarkStep: control.tickmarkStepSize / (control.minorTickmarkCount + 1); + + /*! + Returns the marker text that should be displayed based on + \a markerPos (\c 0 to \c 1.0). + */ + function markerTextFromPos(markerPos) { + return markerPos * (control.maximumValue - control.minimumValue) + control.minimumValue; + } + + readonly property real rawBarWidth: valueBarLoader.item.implicitWidth + readonly property real barLength: (control.orientation === Qt.Vertical ? control.height : control.width) - (tickmarkOffset * 2 - 2) + + readonly property real tickmarkLabelBoundsWidth: tickmarkLength + (control.orientation === Qt.Vertical ? control.__hiddenText.width : control.__hiddenText.height) + readonly property int valuePosition: valueBarLoader.height + + Item { + id: container + + width: control.orientation === Qt.Vertical ? parent.width : parent.height + height: control.orientation === Qt.Vertical ? parent.height : parent.width + rotation: control.orientation === Qt.Horizontal ? 90 : 0 + transformOrigin: Item.Center + anchors.centerIn: parent + + Item { + id: valueBarItem + + x: control.__tickmarkAlignment === Qt.AlignLeft || control.__tickmarkAlignment === Qt.AlignTop ? tickmarkLabelBounds.x + tickmarkLabelBounds.width : 0 + width: rawBarWidth + height: barLength + anchors.verticalCenter: parent.verticalCenter + + Loader { + id: backgroundLoader + sourceComponent: background + anchors.fill: parent + } + + Loader { + id: valueBarLoader + sourceComponent: valueBar + + readonly property real valueAsPercentage: (control.value - control.minimumValue) / (control.maximumValue - control.minimumValue) + + y: Math.round(parent.height - height) + height: Math.round(valueAsPercentage * parent.height) + } + } + Item { + id: tickmarkLabelBounds + + x: control.__tickmarkAlignment === Qt.AlignLeft || control.__tickmarkAlignment === Qt.AlignTop ? 0 : valueBarItem.width + width: tickmarkLabelBoundsWidth + height: barLength + anchors.verticalCenter: parent.verticalCenter + // We want our items to be laid out from bottom to top, but Column can't do that, so we flip + // the whole item containing the tickmarks and labels vertically. Then, we flip each tickmark + // and label back again. + transform: Rotation { + axis.x: 1 + axis.y: 0 + axis.z: 0 + origin.x: tickmarkLabelBounds.width / 2 + origin.y: tickmarkLabelBounds.height / 2 + angle: 180 + } + + Column { + id: tickmarkColumn + x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom ? 0 : tickmarkLabelBounds.width - width + spacing: tickmarkSpacing + anchors.verticalCenter: parent.verticalCenter + + Repeater { + id: tickmarkRepeater + model: tickmarkCount + delegate: Loader { + id: tickmarkDelegateLoader + + sourceComponent: gaugeStyle.tickmark + transform: Rotation { + axis.x: 1 + axis.y: 0 + axis.z: 0 + origin.x: tickmarkDelegateLoader.width / 2 + origin.y: tickmarkDelegateLoader.height / 2 + angle: 180 + } + + onHeightChanged: { + if (index == 0) + tickmarkWidth = height; + } + + readonly property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: tickmarkDelegateLoader.__index + readonly property real value: (index / (tickmarkCount - 1)) * (control.maximumValue - control.minimumValue) + control.minimumValue + readonly property int valuePosition: Math.round(tickmarkDelegateLoader.y) + } + } + } + } + + // Doesn't need to be in a column, since we assume that the major tickmarks will always be longer than us. + Repeater { + id: minorTickmarkRepeater + model: (tickmarkCount - 1) * control.minorTickmarkCount + delegate: Loader { + id: minorTickmarkDelegateLoader + + x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom ? 0 : tickmarkLabelBounds.width - width + y: { + var tickmarkWidthOffset = Math.floor(index / control.minorTickmarkCount) * tickmarkWidth + tickmarkWidth; + var relativePosition = (index % control.minorTickmarkCount + 1) * (tickmarkSpacing / (control.minorTickmarkCount + 1)); + var clusterOffset = Math.floor(index / control.minorTickmarkCount) * tickmarkSpacing; + // We assume that each minorTickmark's height is the same. + return clusterOffset + tickmarkWidthOffset + relativePosition - height / 2; + } + + transform: Rotation { + axis.x: 1 + axis.y: 0 + axis.z: 0 + origin.x: minorTickmarkDelegateLoader.width / 2 + origin.y: minorTickmarkDelegateLoader.height / 2 + angle: 180 + } + + sourceComponent: gaugeStyle.minorTickmark + + readonly property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: minorTickmarkDelegateLoader.__index + readonly property real value: { + var tickmarkIndex = Math.floor(index / control.minorTickmarkCount); + return index * minorTickmarkStep + minorTickmarkStep * tickmarkIndex + minorTickmarkStep + control.minimumValue; + } + readonly property int valuePosition: Math.round(minorTickmarkDelegateLoader.y) + } + } + } + + Item { + id: tickmarkLabelItem + x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom + ? tickmarkLength + : tickmarkLabelBounds.width - tickmarkLength - width + width: control.__hiddenText.width + // Use the bar height instead of the container's, as the labels seem to be translated by 1 when we + // flip the control vertically, and this fixes that. + height: parent.height + anchors.verticalCenter: parent.verticalCenter + + Repeater { + id: tickmarkTextRepeater + model: tickmarkCount + delegate: Item { + x: { + if (control.orientation === Qt.Vertical) + return 0; + + // Align the text to the edge of the tickmarks. + return ((width - height) / 2) * (control.__tickmarkAlignment === Qt.AlignBottom ? -1 : 1); + } + y: index * labelDistance - height / 2 + + width: control.__hiddenText.width + height: control.__hiddenText.height + + transformOrigin: Item.Center + rotation: control.orientation === Qt.Vertical ? 0 : 90 + + readonly property real labelDistance: tickmarkLabelBounds.height / (tickmarkCount - 1) + + Loader { + id: tickmarkTextRepeaterDelegate + + x: { + if (control.orientation === Qt.Horizontal) { + return parent.width / 2 - width / 2; + } + + return control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom + ? 0 + : parent.width - width; + } + + transform: Rotation { + axis.x: 1 + axis.y: 0 + axis.z: 0 + origin.x: tickmarkTextRepeaterDelegate.width / 2 + origin.y: tickmarkTextRepeaterDelegate.height / 2 + angle: 180 + } + + sourceComponent: tickmarkLabel + + readonly property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: tickmarkTextRepeaterDelegate.__index + readonly property real value: markerTextFromPos(index / (tickmarkTextRepeater.count - 1)) + } + } + } + } + } + } + Loader { + id: foregroundLoader + sourceComponent: foreground + anchors.fill: valueBarItem + } + } + } +} diff --git a/src/controls/Styles/Base/HandleStyle.qml b/src/controls/Styles/Base/HandleStyle.qml new file mode 100644 index 00000000..fd89ecac --- /dev/null +++ b/src/controls/Styles/Base/HandleStyle.qml @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 + +Style { + id: handleStyle + property alias handleColorTop: __helper.handleColorTop + property alias handleColorBottom: __helper.handleColorBottom + property alias handleColorBottomStop: __helper.handleColorBottomStop + + HandleStyleHelper { + id: __helper + } + + property Component handle: Item { + implicitWidth: 50 + implicitHeight: 50 + + Canvas { + id: handleCanvas + anchors.fill: parent + + onPaint: { + var ctx = getContext("2d"); + __helper.paintHandle(ctx); + } + } + } + + property Component panel: Item { + Loader { + id: handleLoader + sourceComponent: handle + anchors.fill: parent + } + } +} diff --git a/src/controls/Styles/Base/HandleStyleHelper.qml b/src/controls/Styles/Base/HandleStyleHelper.qml new file mode 100644 index 00000000..61ad3bb3 --- /dev/null +++ b/src/controls/Styles/Base/HandleStyleHelper.qml @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +QtObject { + id: handleStyleHelper + + property color handleColorTop: "#969696" + property color handleColorBottom: Qt.rgba(0.9, 0.9, 0.9, 0.298) + property real handleColorBottomStop: 0.7 + + property color handleRingColorTop: "#b0b0b0" + property color handleRingColorBottom: "transparent" + + /*! + If \a ctx is the only argument, this is equivalent to calling + paintHandle(\c ctx, \c 0, \c 0, \c ctx.canvas.width, \c ctx.canvas.height). + */ + function paintHandle(ctx, handleX, handleY, handleWidth, handleHeight) { + ctx.reset(); + + if (handleWidth < 0) + return; + + if (arguments.length == 1) { + handleX = 0; + handleY = 0; + handleWidth = ctx.canvas.width; + handleHeight = ctx.canvas.height; + } + + ctx.beginPath(); + var gradient = ctx.createRadialGradient(handleX, handleY, handleWidth / 2, + handleX, handleY, handleWidth); + gradient.addColorStop(0, handleColorTop); + gradient.addColorStop(handleColorBottomStop, handleColorBottom); + ctx.ellipse(handleX, handleY, handleWidth, handleHeight); + ctx.fillStyle = gradient; + ctx.fill(); + + /* Draw the ring gradient around the handle. */ + // Clip first, so we only draw inside the ring. + ctx.beginPath(); + ctx.ellipse(handleX, handleY, handleWidth, handleHeight); + ctx.ellipse(handleX + 2, handleY + 2, handleWidth - 4, handleHeight - 4); + ctx.clip(); + + ctx.beginPath(); + gradient = ctx.createLinearGradient(handleX + handleWidth / 2, handleY, + handleX + handleWidth / 2, handleY + handleHeight); + gradient.addColorStop(0, handleRingColorTop); + gradient.addColorStop(1, handleRingColorBottom); + ctx.ellipse(handleX, handleY, handleWidth, handleHeight); + ctx.fillStyle = gradient; + ctx.fill(); + } +} diff --git a/src/controls/Styles/Base/PieMenuStyle.qml b/src/controls/Styles/Base/PieMenuStyle.qml new file mode 100644 index 00000000..916a0c9e --- /dev/null +++ b/src/controls/Styles/Base/PieMenuStyle.qml @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 +import QtQuick.Extras.Private.CppUtils 1.0 + +/*! + \qmltype PieMenuStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for PieMenu. + + PieMenuStyle is a style for PieMenu that draws each section of the menu as a + filled "slice". + + You can create a custom pie menu by replacing the following delegates: + \list + \li \l background + \li \l cancel + \li \l menuItem + \li \l title + \endlist + + To customize the appearance of each menuItem without having to define your + own, you can use the \l backgroundColor and \l selectionColor properties. + To customize the drop shadow, use the \l shadowColor, \l shadowRadius and + \l shadowSpread properties. + + Icons that are too large for the section that they are in will be scaled + down appropriately. + + To style individual sections of the menu, use the menuItem component: + \code + PieMenuStyle { + shadowRadius: 0 + + menuItem: Item { + id: item + rotation: -90 + sectionCenterAngle(styleData.index) + + Rectangle { + width: parent.height * 0.2 + height: width + color: "darkorange" + radius: width / 2 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + id: textItem + text: control.menuItems[styleData.index].text + anchors.centerIn: parent + color: control.currentIndex === styleData.index ? "red" : "white" + rotation: -item.rotation + } + } + } + } + \endcode + + \image piemenu-menuitem-example.png A custom PieMenu +*/ + +Style { + id: pieMenuStyle + + /*! + The \l PieMenu that this style is attached to. + */ + readonly property PieMenu control: __control + + /*! The background color. */ + property color backgroundColor: Qt.rgba(0.6, 0.6, 0.6, 0.66) + + /*! The selection color. */ + property color selectionColor: "#eee" + + /*! The shadow color. */ + property color shadowColor: Qt.rgba(0, 0, 0, 0.26) + + /*! The shadow radius. */ + property real shadowRadius: 50 + + /*! The shadow spread. */ + property real shadowSpread: 0.3 + + /*! + The distance from the center of the menu to the outer edge of the menu. + + \sa cancelRadius + */ + readonly property real radius: Math.min(control.width, control.height) * 0.5 + + /*! + The radius of the area that is used to cancel the menu. + + \sa radius + */ + property real cancelRadius: radius * 0.4 + + /*! + The angle (in degrees) at which the first menu item will be drawn. + + The absolute range formed by \a startAngle and \l endAngle must be + less than or equal to \c 360 degrees. + + Menu items are displayed clockwise when \a startAngle is less than + \l endAngle, otherwise they are displayed anti-clockwise. + + \sa endAngle + */ + property real startAngle: -90 + + /*! + The angle (in degrees) at which the last menu item will be drawn. + + The absolute range formed by \l startAngle and \a endAngle must be + less than or equal to \c 360 degrees. + + Menu items are displayed clockwise when \l startAngle is less than + \a endAngle, otherwise they are displayed anti-clockwise. + + \sa startAngle + */ + property real endAngle: 90 + + /*! + Returns the start of the section at \a itemIndex as an angle in degrees. + */ + function sectionStartAngle(itemIndex) { + return MathUtils.radToDegOffset(control.__protectedScope.sectionStartAngle(itemIndex)); + } + + /*! + Returns the center of the section at \a itemIndex as an angle in + degrees. + */ + function sectionCenterAngle(itemIndex) { + return MathUtils.radToDegOffset(control.__protectedScope.sectionCenterAngle(itemIndex)); + } + + /*! + Returns the end of the section at \a itemIndex as an angle in degrees. + */ + function sectionEndAngle(itemIndex) { + return MathUtils.radToDegOffset(control.__protectedScope.sectionEndAngle(itemIndex)); + } + + /*! + \internal + + The distance in pixels from the center of each menu item's icon to the + center of the menu. A higher value means that the icons will be further + from the center of the menu. + */ + readonly property real __iconOffset: cancelRadius + ((radius - cancelRadius) / 2) + + /*! \internal */ + readonly property real __selectableRadius: radius - cancelRadius + + /*! \internal */ + property int __implicitWidth: Math.round(TextSingleton.implicitHeight * 12.5) + + /*! \internal */ + property int __implicitHeight: __implicitWidth + + /*! + The background of the menu. + + By default, there is no background defined. + */ + property Component background + + /*! + The cancel component of the menu. + + This is an area in the center of the menu that closes the menu when + clicked. + + By default, it is not visible. + */ + property Component cancel: null + + /*! + The component that displays the text of the currently selected menu + item, or the title if there is no current item. + + The current item's text is available via the \c styleData.text + property. + */ + property Component title: Text { + font.pointSize: 20 + text: styleData.text + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "#ccc" + antialiasing: true + } + + /*! + This component defines each section of the pie menu. + + This component covers the width and height of the control. + + No mouse events are propagated to this component, which means that + controls like Button will not function when used within it. You can + check if the mouse is over this section by comparing + \c control.currentIndex to \c styleData.index. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this menu item. + \row \li \c {readonly property bool} \b styleData.hovered + \li \c true if this menu item is under the mouse. + \row \li \c {readonly property bool} \b styleData.pressed + \li \c true if the mouse is pressed down on this menu item. + \endtable + */ + property Component menuItem: Item { + id: actionRootDelegateItem + + function drawRingSection(ctx, x, y, section, r, ringWidth, ringColor) { + ctx.fillStyle = ringColor; + + // Draw one section. + ctx.beginPath(); + ctx.moveTo(x,y); + + // Canvas draws 0 degrees at 3 o'clock, whereas we want it to draw it at 12. + var start = control.__protectedScope.sectionStartAngle(section); + var end = control.__protectedScope.sectionEndAngle(section); + ctx.arc(x, y, r, start, end, start > end); + ctx.fill(); + + // Either change this to the background color, or use the global composition. + ctx.fillStyle = "black"; + ctx.globalCompositeOperation = "destination-out"; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.arc(x, y, ringWidth, 0, Math.PI * 2); + ctx.closePath(); + ctx.fill(); + + // If using the global composition method, make sure to change it back to default. + ctx.globalCompositeOperation = "source-over"; + } + + Canvas { + id: actionCanvas + anchors.fill: parent + property color currentColor: control.currentIndex === styleData.index ? selectionColor : backgroundColor + + Connections { + target: pieMenuStyle + onStartAngleChanged: actionCanvas.requestPaint() + onEndAngleChanged: actionCanvas.requestPaint() + } + + Connections { + target: control + onCurrentIndexChanged: actionCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + drawRingSection(ctx, width / 2, height / 2, styleData.index, radius, cancelRadius, currentColor); + } + } + + readonly property var __styleData: styleData + + PieMenuIcon { + control: pieMenuStyle.control + styleData: __styleData + } + } + + /*! \internal */ + property Component panel: Item { + implicitWidth: __implicitWidth + implicitHeight: __implicitHeight + + property alias titleItem: titleLoader.item + + Item { + id: itemgroup + anchors.fill: parent + visible: false + + Loader { + id: backgroundLoader + sourceComponent: background + anchors.fill: parent + } + + Loader { + id: cancelLoader + sourceComponent: cancel + anchors.centerIn: parent + } + + Repeater { + id: menuItemRepeater + model: control.__protectedScope.visibleItems + + delegate: Loader { + id: menuItemLoader + anchors.fill: parent + sourceComponent: menuItem + + readonly property int __index: index + property QtObject styleData: QtObject { + readonly property alias index: menuItemLoader.__index + readonly property bool hovered: control.currentIndex === index + readonly property bool pressed: control.__protectedScope.pressedIndex === index + } + } + } + } + DropShadow { + id: dropShadow + anchors.fill: itemgroup + fast: true + radius: shadowRadius + spread: shadowSpread + transparentBorder: true + samples: 12 + color: shadowColor + source: itemgroup + } + + Loader { + id: titleLoader + sourceComponent: title + x: parent.x + parent.width / 2 - width / 2 + y: -height - 10 + + property QtObject styleData: QtObject { + property string text: control.currentIndex !== -1 + ? control.__protectedScope.visibleItems[control.currentIndex].text + : control.title + } + } + } +} diff --git a/src/controls/Styles/Base/StatusIndicatorStyle.qml b/src/controls/Styles/Base/StatusIndicatorStyle.qml new file mode 100644 index 00000000..62ed1038 --- /dev/null +++ b/src/controls/Styles/Base/StatusIndicatorStyle.qml @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 + +/*! + \qmltype StatusIndicatorStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for StatusIndicatorStyle. + + You can create a custom status indicator by defining the \l indicator + component. +*/ + +Style { + id: pieMenuStyle + + /*! + The \l StatusIndicator that this style is attached to. + */ + readonly property StatusIndicator control: __control + + /*! + The color that instances of + \l {QtQuick.Extras::}{StatusIndicator} will have. + The \l {QtQuick.Extras::StatusIndicator::}{color} + property in \l {QtQuick.Extras::}{StatusIndicator} + will override this property when set. + */ + property color color: "red" + + /*! + This defines the indicator in both its on and off status. + */ + property Component indicator: Item { + readonly property real shineStep: 0.05 + readonly property real smallestAxis: Math.min(control.width, control.height) + readonly property real shadowRadius: smallestAxis * 0.4 + readonly property real outerRecessPercentage: 0.11 + readonly property color offColor: Qt.rgba(0.13, 0.13, 0.13) + readonly property color baseColor: control.active ? control.color : offColor + + implicitWidth: TextSingleton.implicitHeight * 2 + implicitHeight: implicitWidth + + Canvas { + id: backgroundCanvas + width: Math.min(parent.width, parent.height) + // height: width --- QTBUG-42878 + height: Math.min(parent.width, parent.height) + anchors.centerIn: parent + + Connections { + target: control + onActiveChanged: backgroundCanvas.requestPaint() + onColorChanged: backgroundCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + // Draw the semi-transparent background. + ctx.beginPath(); + var gradient = ctx.createLinearGradient(width / 2, 0, width / 2, height * 0.75); + gradient.addColorStop(0.0, Qt.rgba(0, 0, 0, control.active ? 0.1 : 0.25)); + gradient.addColorStop(1.0, control.active ? Qt.rgba(0, 0, 0, 0.1) : Qt.rgba(0.74, 0.74, 0.74, 0.25)); + + ctx.fillStyle = gradient; + ctx.ellipse(0, 0, width, height); + ctx.fill(); + } + } + + Item { + id: shadowGuard + anchors.fill: backgroundCanvas + anchors.margins: -shadowRadius + + Canvas { + id: colorCanvas + anchors.fill: parent + anchors.margins: shadowRadius + + Connections { + target: control + onActiveChanged: colorCanvas.requestPaint() + onColorChanged: colorCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + // Draw the actual color within the circle. + ctx.beginPath(); + ctx.fillStyle = baseColor; + var recess = smallestAxis * outerRecessPercentage; + ctx.ellipse(recess, recess, width - recess * 2, height - recess * 2); + ctx.fill(); + } + } + } + + DropShadow { + id: shadow + source: shadowGuard + color: control.color + // Don't set fast here because Qt < 5.3 will run into QTBUG-36931 + radius: shadowRadius + samples: Math.min(32, radius) + cached: true + anchors.fill: shadowGuard + visible: control.active + } + + Canvas { + id: foregroundCanvas + anchors.fill: backgroundCanvas + + Connections { + target: control + onActiveChanged: foregroundCanvas.requestPaint() + onColorChanged: foregroundCanvas.requestPaint() + } + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + // Draw the first shine. + ctx.beginPath(); + ctx.fillStyle = Qt.rgba(1, 1, 1, 0.03); + var recessPercentage = outerRecessPercentage + shineStep * 0.65; + var recess = smallestAxis * recessPercentage; + ctx.ellipse(recess, recess, width - recess * 2, height - recess * 2); + ctx.fill(); + + // Draw the second, inner shine. + ctx.beginPath(); + ctx.fillStyle = Qt.rgba(1, 1, 1, 0.06); + recessPercentage += shineStep; + recess = smallestAxis * recessPercentage; + ctx.ellipse(recess, recess, width - recess * 2, height - recess * 2); + ctx.fill(); + + // Now draw the final arced shine that goes over the first and second shines. + // First, clip the entire shine area. + ctx.beginPath(); + recessPercentage -= shineStep; + recess = smallestAxis * recessPercentage; + ctx.ellipse(recess, recess, width - recess * 2, height - recess * 2); + ctx.clip(); + + if (!control.active) { + // Then, clip the bottom area out of the shine. + ctx.ellipse(recess, height * 0.425, width - recess * 2, height - recess * 2); + ctx.clip(); + } + + ctx.beginPath(); + var gradient; + if (!control.active) { + // Draw the shine arc. + gradient = ctx.createLinearGradient(width / 2, height * 0.2, width / 2, height * 0.65); + gradient.addColorStop(0.0, Qt.rgba(1, 1, 1, 0.05)); + gradient.addColorStop(1.0, "transparent"); + } else { + // Draw the radial shine. + gradient = ctx.createRadialGradient(width / 2, height / 2, width * 0.25, width / 2, height / 2, width * 0.25); + gradient.addColorStop(0.0, Qt.lighter(baseColor, 1.4)); + gradient.addColorStop(1.0, "transparent"); + } + + ctx.fillStyle = gradient; + ctx.ellipse(recess, recess, width - recess * 2, height - recess * 2); + ctx.fill(); + } + } + } + + /*! \internal */ + property Component panel: Item { + implicitWidth: indicatorLoader.implicitWidth + implicitHeight: indicatorLoader.implicitHeight + + Loader { + id: indicatorLoader + width: Math.max(1, parent.width) + height: Math.max(1, parent.height) + anchors.centerIn: parent + sourceComponent: indicator + } + } +} diff --git a/src/controls/Styles/Base/ToggleButtonStyle.qml b/src/controls/Styles/Base/ToggleButtonStyle.qml new file mode 100644 index 00000000..de06d72b --- /dev/null +++ b/src/controls/Styles/Base/ToggleButtonStyle.qml @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 +import QtQuick.Extras.Private.CppUtils 1.0 + +/*! + \qmltype ToggleButtonStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for ToggleButton. + + You can create a custom toggle button by replacing the same delegates that + \l {QtQuick.Controls.Styles::ButtonStyle}{ButtonStyle} provides. +*/ + +CircularButtonStyle { + id: circularButtonStyle + + /*! + The \l ToggleButton that this style is attached to. + */ + readonly property ToggleButton control: __control + + /*! + The gradient that is displayed on the inactive state indicator. The + inactive state indicator will be the checked gradient when the button + is unchecked, and the unchecked gradient when the button is checked. + + \sa checkedGradient, uncheckedGradient + */ + property Gradient inactiveGradient: Gradient { + GradientStop { + position: 0 + color: commonStyleHelper.inactiveColor + } + GradientStop { + position: 1 + color: commonStyleHelper.inactiveColorShine + } + } + + /*! + The gradient that is displayed on the checked state indicator. + + \sa uncheckedGradient, inactiveGradient + */ + property Gradient checkedGradient: Gradient { + GradientStop { + position: 0 + color: commonStyleHelper.onColor + } + GradientStop { + position: 1 + color: commonStyleHelper.onColorShine + } + } + + /*! + The gradient that is displayed on the unchecked state indicator. + + \sa checkedGradient, inactiveGradient + */ + property Gradient uncheckedGradient: Gradient { + GradientStop { + position: 0 + color: commonStyleHelper.offColor + } + GradientStop { + position: 1 + color: commonStyleHelper.offColorShine + } + } + + /*! + The color that is used for the drop shadow below the checked state + indicator. + + \sa uncheckedDropShadowColor + */ + property color checkedDropShadowColor: commonStyleHelper.onColor + + /*! + The color that is used for the drop shadow below the checked state + indicator. + + \sa checkedDropShadowColor + */ + property color uncheckedDropShadowColor: commonStyleHelper.offColor + + CommonStyleHelper { + id: commonStyleHelper + } + + background: Item { + implicitWidth: __buttonHelper.implicitWidth + implicitHeight: __buttonHelper.implicitHeight + + Connections { + target: control + onPressedChanged: { + backgroundCanvas.requestPaint(); + } + + onCheckedChanged: { + uncheckedCanvas.requestPaint(); + checkedCanvas.requestPaint(); + } + } + + Connections { + target: circularButtonStyle + + onCheckedGradientChanged: checkedCanvas.requestPaint() + onCheckedDropShadowColorChanged: checkedCanvas.requestPaint() + onUncheckedGradientChanged: uncheckedCanvas.requestPaint() + onUncheckedDropShadowColorChanged: uncheckedCanvas.requestPaint() + onInactiveGradientChanged: { + checkedCanvas.requestPaint(); + uncheckedCanvas.requestPaint(); + } + } + + Connections { + target: circularButtonStyle.checkedGradient + onUpdated: checkedCanvas.requestPaint() + } + + Connections { + target: circularButtonStyle.uncheckedGradient + onUpdated: uncheckedCanvas.requestPaint() + } + + Connections { + target: circularButtonStyle.inactiveGradient + onUpdated: { + uncheckedCanvas.requestPaint(); + checkedCanvas.requestPaint(); + } + } + + Canvas { + id: backgroundCanvas + anchors.fill: parent + + onPaint: { + var ctx = getContext("2d"); + __buttonHelper.paintBackground(ctx); + } + } + + Canvas { + id: uncheckedCanvas + anchors.fill: parent + anchors.margins: -(__buttonHelper.radius * 3) + visible: control.checked + + readonly property real xCenter: width / 2 + readonly property real yCenter: height / 2 + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + /* Draw unchecked indicator */ + ctx.beginPath(); + ctx.lineWidth = __buttonHelper.outerArcLineWidth - __buttonHelper.innerArcLineWidth; + ctx.arc(xCenter, yCenter, __buttonHelper.outerArcRadius + __buttonHelper.innerArcLineWidth / 2, + MathUtils.degToRad(180), MathUtils.degToRad(270), false); + var gradient = ctx.createLinearGradient(xCenter, yCenter + __buttonHelper.radius, + xCenter, yCenter - __buttonHelper.radius); + var relevantGradient = control.checked ? inactiveGradient : uncheckedGradient; + for (var i = 0; i < relevantGradient.stops.length; ++i) { + gradient.addColorStop(relevantGradient.stops[i].position, relevantGradient.stops[i].color); + } + ctx.strokeStyle = gradient; + ctx.stroke(); + } + } + + Canvas { + id: checkedCanvas + anchors.fill: parent + anchors.margins: -(__buttonHelper.radius * 3) + visible: !control.checked + + readonly property real xCenter: width / 2 + readonly property real yCenter: height / 2 + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + /* Draw checked indicator */ + ctx.beginPath(); + ctx.lineWidth = __buttonHelper.outerArcLineWidth - __buttonHelper.innerArcLineWidth; + ctx.arc(xCenter, yCenter, __buttonHelper.outerArcRadius + __buttonHelper.innerArcLineWidth / 2, + MathUtils.degToRad(270), MathUtils.degToRad(0), false); + var gradient = ctx.createLinearGradient(xCenter, yCenter + __buttonHelper.radius, + xCenter, yCenter - __buttonHelper.radius); + var relevantGradient = control.checked ? checkedGradient : inactiveGradient; + for (var i = 0; i < relevantGradient.stops.length; ++i) { + gradient.addColorStop(relevantGradient.stops[i].position, relevantGradient.stops[i].color); + } + ctx.strokeStyle = gradient; + ctx.stroke(); + } + } + + DropShadow { + id: uncheckedDropShadow + anchors.fill: uncheckedCanvas + cached: true + radius: 4 + samples: 8 + color: uncheckedDropShadowColor + source: uncheckedCanvas + visible: !control.checked + } + + DropShadow { + id: checkedDropShadow + anchors.fill: checkedCanvas + cached: true + radius: 4 + samples: 8 + color: checkedDropShadowColor + source: checkedCanvas + visible: control.checked + } + } + + panel: Item { + implicitWidth: backgroundLoader.implicitWidth + implicitHeight: backgroundLoader.implicitHeight + + Loader { + id: backgroundLoader + anchors.fill: parent + sourceComponent: background + } + + Loader { + id: labelLoader + sourceComponent: label + anchors.fill: parent + anchors.leftMargin: padding.left + anchors.topMargin: padding.top + anchors.rightMargin: padding.right + anchors.bottomMargin: padding.bottom + } + } +} diff --git a/src/controls/Styles/Base/TumblerStyle.qml b/src/controls/Styles/Base/TumblerStyle.qml new file mode 100644 index 00000000..2661837e --- /dev/null +++ b/src/controls/Styles/Base/TumblerStyle.qml @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Extras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtGraphicalEffects 1.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls.Private 1.0 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 + +/*! + \qmltype TumblerStyle + \inqmlmodule QtQuick.Controls.Styles + \since 5.5 + \ingroup controlsstyling + \brief Provides custom styling for Tumbler. + + \note TumblerStyle requires Qt 5.3.2 or later. + + You can create a custom tumbler by replacing the following delegates: + \list + \li \l background + \li \l foreground + \li \l separator + \li \l delegate + \li \l highlight + \li \l frame + \endlist +*/ + +Style { + id: tumblerStyle + + padding.left: __padding + padding.right: __padding + padding.top: __padding + padding.bottom: __padding + + /*! + The \l Tumbler that this style is attached to. + */ + readonly property Tumbler control: __control + + /*! + This property holds the spacing between each delegate. + */ + property real spacing: 0 + + /*! + This property holds the amount of items visible in each column. + + This value should be an odd number. + */ + property int visibleItemCount: 3 + + /*! + \internal + + TODO: how do we handle differing padding values? + */ + readonly property real __padding: Math.max(6, Math.round(TextSingleton.implicitHeight * 0.4)) + /*! \internal */ + property real __delegateHeight: 0 + /*! \internal */ + property real __separatorWidth: 0 + + /*! + The background of the tumbler. + */ + property Component background: Rectangle { + gradient: Gradient { + GradientStop { position: 0.00; color: "#acacac" } + GradientStop { position: 0.12; color: "#d5d5d5" } + GradientStop { position: 0.24; color: "#e8e8e8" } + GradientStop { position: 0.39; color: "#ffffff" } + GradientStop { position: 0.61; color: "#ffffff" } + GradientStop { position: 0.76; color: "#e8e8e8" } + GradientStop { position: 0.88; color: "#d5d5d5" } + GradientStop { position: 1.00; color: "#acacac" } + } + } + + /*! + The foreground of the tumbler. + */ + property Component foreground: Item { + clip: true + + Rectangle { + id: rect + anchors.fill: parent + // Go one pixel larger than our parent so that we can hide our one pixel frame + // that the shadow is created from. + anchors.margins: -1 + color: "transparent" + border.color: "black" + visible: false + } + + DropShadow { + anchors.fill: rect + source: rect + radius: __padding + samples: Math.min(32, radius * 2) + } + } + + /*! + The separator between each column. + + The \l {Item::}{implicitWidth} property must be set, and should be the + same value for each separator. + */ + property Component separator: Canvas { + implicitWidth: Math.max(10, Math.round(TextSingleton.implicitHeight * 0.4)) + + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + ctx.fillStyle = "#11000000"; + ctx.fillRect(0, 0, width, height); + ctx.fillStyle = "#11000000"; + ctx.fillRect(width * 0.2, 0, width * 0.6, height); + ctx.fillStyle = "#66000000"; + ctx.fillRect(width * 0.4, 0, width * 0.2, height); + } + } + + /*! + The foreground of each column. + + In terms of stacking order, this component is displayed above the + delegate and highlight components, but below the foreground component. + + \table + \row \li \c {readonly property int} \b styleData.column + \li The index of the column that contains this item. + \row \li \c {readonly property bool} \b styleData.activeFocus + \li \c true if the column that contains this item has active focus. + + \endtable + + Delegates for items in specific columns can be defined using + TumblerColumn's \l {TumblerColumn::columnForeground}{columnForeground} + property, which will be used instead of this component. + */ + property Component columnForeground + + /*! + The frame around the tumbler. + + The \l {Item::}{implicitWidth} property must be set, and should be the + same value for each separator. + */ + property Component frame: Canvas { + onPaint: { + // workaround for QTBUG-40792 + var ctx = getContext("2d"); + ctx.reset(); + + var cornerRadius = Math.max(2, Math.round(TextSingleton.implicitHeight * 0.2)); + var outerLineWidth = Math.max(1, Math.round(TextSingleton.implicitHeight * 0.05)); + var innerLineWidth = __padding - outerLineWidth; + + ctx.save(); + ctx.lineWidth = outerLineWidth; + ctx.beginPath(); + ctx.roundedRect(0, 0, width, height, cornerRadius, cornerRadius); + ctx.roundedRect(outerLineWidth, outerLineWidth, width - outerLineWidth * 2, height - outerLineWidth * 2, + cornerRadius - outerLineWidth, cornerRadius - outerLineWidth); + ctx.clip(); + + ctx.beginPath(); + ctx.rect(0, 0, width, height); + var gradient = ctx.createLinearGradient(width / 2, 0, width / 2, height); + gradient.addColorStop(0, "#33b3b3b3"); + gradient.addColorStop(1, "#4ce6e6e6"); + ctx.fillStyle = gradient; + ctx.fill(); + ctx.restore(); + + // The inner stroke must account for its corner radius. + cornerRadius -= outerLineWidth; + + ctx.save(); + ctx.lineWidth = innerLineWidth; + ctx.beginPath(); + ctx.roundedRect(outerLineWidth, outerLineWidth, width - outerLineWidth * 2, height - outerLineWidth * 2, + cornerRadius, cornerRadius); + ctx.roundedRect(outerLineWidth + innerLineWidth, outerLineWidth + innerLineWidth, + width - outerLineWidth * 2 - innerLineWidth * 2, height - outerLineWidth * 2 - innerLineWidth * 2, + cornerRadius - innerLineWidth, cornerRadius - innerLineWidth); + ctx.clip(); + + ctx.beginPath(); + ctx.rect(0, 0, width, height); + gradient = ctx.createLinearGradient(width / 2, 0, width / 2, height); + gradient.addColorStop(0, "#4c666666"); + gradient.addColorStop(1, "#40cccccc"); + ctx.fillStyle = gradient; + ctx.fill(); + ctx.restore(); + } + } + + /*! + The delegate provides a template defining each item instantiated in the + column. Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this delegate in the model. + \row \li \c {readonly property int} \b styleData.column + \li The index of the column that contains this item. + \row \li \c {readonly property real} \b styleData.value + \li The value for this delegate from the model. + \row \li \c {readonly property bool} \b styleData.current + \li \c true if this delegate is the current item. + \row \li \c {readonly property real} \b styleData.displacement + \li \c A value from \c {-visibleItemCount / 2} to + \c {visibleItemCount / 2} which represents how far away + this item is from being the current item, with \c 0 being + completely current. + + For example, the item below will be 40% opaque when + it is not the current item, and transition to 100% + opacity when it becomes the current item: + + \code + delegate: Text { + text: styleData.value + opacity: 0.4 + Math.max(0, 1 - Math.abs(styleData.displacement)) * 0.6 + } + \endcode + \row \li \c {readonly property bool} \b styleData.activeFocus + \li \c true if the column that contains this item has active focus. + + \endtable + + Properties of the model are also available depending upon the type of + \l {qml-data-models}{Data Model}. + + Delegates for items in specific columns can be defined using + TumblerColumn's \l {TumblerColumn::delegate}{delegate} property, which + will be used instead of this delegate. + + The \l {Item::}{implicitHeight} property must be set, and it must be + the same for each delegate. + */ + property Component delegate: Item { + implicitHeight: (control.height - padding.top - padding.bottom) / tumblerStyle.visibleItemCount + + Text { + id: label + text: styleData.value + color: "#666666" + opacity: 0.4 + Math.max(0, 1 - Math.abs(styleData.displacement)) * 0.6 + font.pixelSize: Math.round(TextSingleton.font.pixelSize * 1.25) + anchors.centerIn: parent + } + } + + /*! + The delegate for the highlight of each column. + + Delegates for the highlight of specific columns can be defined using + TumblerColumn's \l {TumblerColumn::highlight}{highlight} property, + which will be used instead of this delegate. + + Each instance of this component has access to the following properties: + + \table + \row \li \c {readonly property int} \b styleData.index + \li The index of this column in the tumbler. + \row \li \c {readonly property int} \b styleData.columnIndex + \li The index of the column that contains this highlight. + \row \li \c {readonly property bool} \b styleData.activeFocus + \li \c true if the column that contains this highlight has active focus. + \endtable + */ + property Component highlight + + /*! \internal */ + property Component panel: Item { + implicitWidth: { + var w = (__separatorWidth * (control.columnCount - 1)) + tumblerStyle.padding.left + tumblerStyle.padding.right; + for (var i = 0; i < control.columnCount; ++i) + w += control.getColumn(i).width; + return w; + } + implicitHeight: TextSingleton.implicitHeight * 10 + tumblerStyle.padding.top + tumblerStyle.padding.bottom + } +} diff --git a/src/controls/Styles/Base/images/knob.png b/src/controls/Styles/Base/images/knob.png Binary files differnew file mode 100644 index 00000000..ecac4d09 --- /dev/null +++ b/src/controls/Styles/Base/images/knob.png diff --git a/src/controls/Styles/Base/images/needle.png b/src/controls/Styles/Base/images/needle.png Binary files differnew file mode 100644 index 00000000..8237e516 --- /dev/null +++ b/src/controls/Styles/Base/images/needle.png diff --git a/src/controls/Styles/qmldir b/src/controls/Styles/qmldir index 582a7898..4b2f9844 100644 --- a/src/controls/Styles/qmldir +++ b/src/controls/Styles/qmldir @@ -20,4 +20,19 @@ TextAreaStyle 1.1 Base/TextAreaStyle.qml TextFieldStyle 1.0 Base/TextFieldStyle.qml ToolBarStyle 1.0 Base/ToolBarStyle.qml StatusBarStyle 1.0 Base/StatusBarStyle.qml + +CircularGaugeStyle 1.0 Base/CircularGaugeStyle.qml +CircularButtonStyle 1.0 Base/CircularButtonStyle.qml +CircularTickmarkLabelStyle 1.0 Base/CircularTickmarkLabelStyle.qml +CommonStyleHelper 1.0 Base/CommonStyleHelper.qml +DelayButtonStyle 1.0 Base/DelayButtonStyle.qml +DialStyle 1.1 Base/DialStyle.qml +GaugeStyle 1.0 Base/GaugeStyle.qml +HandleStyle 1.0 Base/HandleStyle.qml +HandleStyleHelper 1.0 Base/HandleStyleHelper.qml +PieMenuStyle 1.3 Base/PieMenuStyle.qml +StatusIndicatorStyle 1.1 Base/StatusIndicatorStyle.qml +ToggleButtonStyle 1.0 Base/ToggleButtonStyle.qml +TumblerStyle 1.2 Base/TumblerStyle.qml + designersupported diff --git a/src/controls/Styles/styles.pri b/src/controls/Styles/styles.pri index f91a9d1a..eaad43ea 100644 --- a/src/controls/Styles/styles.pri +++ b/src/controls/Styles/styles.pri @@ -27,6 +27,22 @@ STYLES_QML_FILES = \ $$PWD/Base/ToolBarStyle.qml \ $$PWD/Base/ToolButtonStyle.qml +# Extras +STYLES_QML_FILES += \ + $$PWD/Base/CircularGaugeStyle.qml \ + $$PWD/Base/CircularButtonStyle.qml \ + $$PWD/Base/CircularTickmarkLabelStyle.qml \ + $$PWD/Base/CommonStyleHelper.qml \ + $$PWD/Base/DelayButtonStyle.qml \ + $$PWD/Base/DialStyle.qml \ + $$PWD/Base/GaugeStyle.qml \ + $$PWD/Base/HandleStyle.qml \ + $$PWD/Base/HandleStyleHelper.qml \ + $$PWD/Base/PieMenuStyle.qml \ + $$PWD/Base/StatusIndicatorStyle.qml \ + $$PWD/Base/ToggleButtonStyle.qml \ + $$PWD/Base/TumblerStyle.qml + # Desktop !no_desktop { STYLES_QML_FILES += \ @@ -88,7 +104,9 @@ STYLES_QML_FILES += \ $$PWD/Base/images/spinner_medium.png \ $$PWD/Base/images/spinner_large.png \ $$PWD/Base/images/check.png \ - $$PWD/Base/images/check@2x.png + $$PWD/Base/images/check@2x.png \ + $$PWD/Base/images/knob.png \ + $$PWD/Base/images/needle.png STYLES_QML_FILES += $$PWD/qmldir diff --git a/src/controls/Switch.qml b/src/controls/Switch.qml index abfb79b0..d416b7f3 100644 --- a/src/controls/Switch.qml +++ b/src/controls/Switch.qml @@ -159,5 +159,5 @@ Control { \codeline Qt.createComponent("path/to/style.qml", switchId); */ - style: Qt.createComponent(Settings.style + "/SwitchStyle.qml", root) + style: Settings.styleComponent(Settings.style, "SwitchStyle.qml", root) } diff --git a/src/controls/TabView.qml b/src/controls/TabView.qml index ebab068a..ea8bf4bd 100644 --- a/src/controls/TabView.qml +++ b/src/controls/TabView.qml @@ -176,7 +176,7 @@ FocusScope { property ListModel __tabs: ListModel { } /*! \internal */ - property Component style: Qt.createComponent(Settings.style + "/TabViewStyle.qml", root) + property Component style: Settings.styleComponent(Settings.style, "TabViewStyle.qml", root) /*! \internal */ property var __styleItem: loader.item diff --git a/src/controls/TableView.qml b/src/controls/TableView.qml index 198ec3c7..e4357739 100644 --- a/src/controls/TableView.qml +++ b/src/controls/TableView.qml @@ -281,7 +281,7 @@ BasicTableView { onModelChanged: selection.clear() - style: Qt.createComponent(Settings.style + "/TableViewStyle.qml", root) + style: Settings.styleComponent(Settings.style, "TableViewStyle.qml", root) Accessible.role: Accessible.Table diff --git a/src/controls/TextArea.qml b/src/controls/TextArea.qml index 0199cc81..c141f9e2 100644 --- a/src/controls/TextArea.qml +++ b/src/controls/TextArea.qml @@ -695,7 +695,7 @@ ScrollView { Accessible.role: Accessible.EditableText - style: Qt.createComponent(Settings.style + "/TextAreaStyle.qml", area) + style: Settings.styleComponent(Settings.style, "TextAreaStyle.qml", area) /*! \qmlproperty TextDocument TextArea::textDocument diff --git a/src/controls/TextField.qml b/src/controls/TextField.qml index 078fee2d..db42cfb7 100644 --- a/src/controls/TextField.qml +++ b/src/controls/TextField.qml @@ -611,7 +611,7 @@ Control { /*! \internal */ property alias __baselineOffset: textInput.baselineOffset - style: Qt.createComponent(Settings.style + "/TextFieldStyle.qml", textInput) + style: Settings.styleComponent(Settings.style, "TextFieldStyle.qml", textInput) activeFocusOnTab: true diff --git a/src/controls/ToolBar.qml b/src/controls/ToolBar.qml index 796d25dd..48f62f16 100644 --- a/src/controls/ToolBar.qml +++ b/src/controls/ToolBar.qml @@ -100,7 +100,7 @@ FocusScope { + Math.max(container.layoutHeight, __panel ? __panel.implicitHeight : 0) /*! \internal */ - property Component style: Qt.createComponent(Settings.style + "/ToolBarStyle.qml", toolbar) + property Component style: Settings.styleComponent(Settings.style, "ToolBarStyle.qml", toolbar) /*! \internal */ property alias __style: styleLoader.item diff --git a/src/controls/ToolButton.qml b/src/controls/ToolButton.qml index e98f7ffd..a9904e11 100644 --- a/src/controls/ToolButton.qml +++ b/src/controls/ToolButton.qml @@ -80,5 +80,5 @@ import QtQuick.Controls.Private 1.0 Button { id: button - style: Qt.createComponent(Settings.style + "/ToolButtonStyle.qml", button) + style: Settings.styleComponent(Settings.style, "ToolButtonStyle.qml", button) } diff --git a/src/controls/TreeView.qml b/src/controls/TreeView.qml index 8aaf3bbf..f1175b72 100644 --- a/src/controls/TreeView.qml +++ b/src/controls/TreeView.qml @@ -252,7 +252,7 @@ BasicTableView { modelAdaptor.expand(index) } - style: Qt.createComponent(Settings.style + "/TreeViewStyle.qml", root) + style: Settings.styleComponent(Settings.style, "TreeViewStyle.qml", root) // Internal stuff. Do not look diff --git a/src/controls/controls.pro b/src/controls/controls.pro index 151ccb4b..7261b5c6 100644 --- a/src/controls/controls.pro +++ b/src/controls/controls.pro @@ -43,6 +43,8 @@ QML_FILES += $$CONTROLS_QML_FILES SOURCES += $$PWD/plugin.cpp HEADERS += $$PWD/plugin.h +OTHER_FILES += doc/src/* + include(plugin.pri) include(Private/private.pri) include(Styles/styles.pri) diff --git a/src/controls/doc/images/circulargauge-angles.png b/src/controls/doc/images/circulargauge-angles.png Binary files differnew file mode 100644 index 00000000..fb93bcaa --- /dev/null +++ b/src/controls/doc/images/circulargauge-angles.png diff --git a/src/controls/doc/images/circulargauge-needle-example-2.png b/src/controls/doc/images/circulargauge-needle-example-2.png Binary files differnew file mode 100644 index 00000000..a2b5f322 --- /dev/null +++ b/src/controls/doc/images/circulargauge-needle-example-2.png diff --git a/src/controls/doc/images/circulargauge-needle.png b/src/controls/doc/images/circulargauge-needle.png Binary files differnew file mode 100644 index 00000000..b2780b41 --- /dev/null +++ b/src/controls/doc/images/circulargauge-needle.png diff --git a/src/controls/doc/images/circulargauge-reversed.png b/src/controls/doc/images/circulargauge-reversed.png Binary files differnew file mode 100644 index 00000000..aeadfbcc --- /dev/null +++ b/src/controls/doc/images/circulargauge-reversed.png diff --git a/src/controls/doc/images/circulargauge-tickmark-indices-values.png b/src/controls/doc/images/circulargauge-tickmark-indices-values.png Binary files differnew file mode 100644 index 00000000..d117f483 --- /dev/null +++ b/src/controls/doc/images/circulargauge-tickmark-indices-values.png diff --git a/src/controls/doc/images/gauge-minorTickmark-example.png b/src/controls/doc/images/gauge-minorTickmark-example.png Binary files differnew file mode 100644 index 00000000..591120d6 --- /dev/null +++ b/src/controls/doc/images/gauge-minorTickmark-example.png diff --git a/src/controls/doc/images/gauge-temperature.png b/src/controls/doc/images/gauge-temperature.png Binary files differnew file mode 100644 index 00000000..95fc1679 --- /dev/null +++ b/src/controls/doc/images/gauge-temperature.png diff --git a/src/controls/doc/images/gauge-tickmark-example.png b/src/controls/doc/images/gauge-tickmark-example.png Binary files differnew file mode 100644 index 00000000..c46fb8a5 --- /dev/null +++ b/src/controls/doc/images/gauge-tickmark-example.png diff --git a/src/controls/doc/images/piemenu-menuitem-example.png b/src/controls/doc/images/piemenu-menuitem-example.png Binary files differnew file mode 100644 index 00000000..57a2f956 --- /dev/null +++ b/src/controls/doc/images/piemenu-menuitem-example.png diff --git a/src/controls/doc/images/styling-circulargauge-background-example.png b/src/controls/doc/images/styling-circulargauge-background-example.png Binary files differnew file mode 100644 index 00000000..e816c6f6 --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-background-example.png diff --git a/src/controls/doc/images/styling-circulargauge-knob-example.png b/src/controls/doc/images/styling-circulargauge-knob-example.png Binary files differnew file mode 100644 index 00000000..793a36c2 --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-knob-example.png diff --git a/src/controls/doc/images/styling-circulargauge-minorTickmark-example.png b/src/controls/doc/images/styling-circulargauge-minorTickmark-example.png Binary files differnew file mode 100644 index 00000000..f3f2d8d7 --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-minorTickmark-example.png diff --git a/src/controls/doc/images/styling-circulargauge-needle-example.png b/src/controls/doc/images/styling-circulargauge-needle-example.png Binary files differnew file mode 100644 index 00000000..08049fc9 --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-needle-example.png diff --git a/src/controls/doc/images/styling-circulargauge-tickmark-example.png b/src/controls/doc/images/styling-circulargauge-tickmark-example.png Binary files differnew file mode 100644 index 00000000..01c71cd1 --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-tickmark-example.png diff --git a/src/controls/doc/images/styling-circulargauge-tickmarkLabel-example.png b/src/controls/doc/images/styling-circulargauge-tickmarkLabel-example.png Binary files differnew file mode 100644 index 00000000..7520827e --- /dev/null +++ b/src/controls/doc/images/styling-circulargauge-tickmarkLabel-example.png diff --git a/src/controls/doc/images/styling-gauge-font-size.png b/src/controls/doc/images/styling-gauge-font-size.png Binary files differnew file mode 100644 index 00000000..97d7ebb2 --- /dev/null +++ b/src/controls/doc/images/styling-gauge-font-size.png diff --git a/src/controls/doc/images/styling-gauge-foreground.png b/src/controls/doc/images/styling-gauge-foreground.png Binary files differnew file mode 100644 index 00000000..5a4f65b9 --- /dev/null +++ b/src/controls/doc/images/styling-gauge-foreground.png diff --git a/src/controls/doc/images/styling-gauge-minorTickmark.png b/src/controls/doc/images/styling-gauge-minorTickmark.png Binary files differnew file mode 100644 index 00000000..34e36192 --- /dev/null +++ b/src/controls/doc/images/styling-gauge-minorTickmark.png diff --git a/src/controls/doc/images/styling-gauge-tickmark.png b/src/controls/doc/images/styling-gauge-tickmark.png Binary files differnew file mode 100644 index 00000000..1cd4fb8b --- /dev/null +++ b/src/controls/doc/images/styling-gauge-tickmark.png diff --git a/src/controls/doc/images/styling-gauge-valueBar.png b/src/controls/doc/images/styling-gauge-valueBar.png Binary files differnew file mode 100644 index 00000000..581b8240 --- /dev/null +++ b/src/controls/doc/images/styling-gauge-valueBar.png diff --git a/src/controls/doc/qtquickcontrols.qdocconf b/src/controls/doc/qtquickcontrols.qdocconf index 6b4b14c7..8d41247e 100644 --- a/src/controls/doc/qtquickcontrols.qdocconf +++ b/src/controls/doc/qtquickcontrols.qdocconf @@ -28,12 +28,13 @@ qhp.QtQuickControls.subprojects.qtquickcontrolsstyles.title = Qt Quick Cont qhp.QtQuickControls.subprojects.qtquickcontrolsstyles.indexTitle = Qt Quick Controls Styles Structure qhp.QtQuickControls.subprojects.qtquickcontrolsstyles.type = manual -depends = qtqml qtquick qtgui qtwidgets qtdoc qtquicklayouts qtquickdialogs qtcore +depends = qtcore qtdoc qtgui qtwidgets qtqml qtquick qtquicklayouts qtquickdialogs qtquickextras # Specify the install path under QT_INSTALL_EXAMPLES # Examples will be installed under quick/controls - 'controls' subdirectory # is given as part of \example commands -exampledirs += ../../../examples/quick/controls +exampledirs += ../../../examples/quick/controls \ + snippets examplesinstallpath = quick/controls headerdirs += ../ \ @@ -54,7 +55,8 @@ sources += ../Private/AbstractCheckable.qml \ ../Private/qquickabstractstyle.h \ ../Private/qquickabstractstyle.cpp -imagedirs += images +imagedirs += images \ + ../../extras/doc/images navigation.landingpage = "Qt Quick Controls" navigation.qmltypespage = "Qt Quick Controls QML Types" diff --git a/src/controls/doc/snippets/circulargauge-background-range.qml b/src/controls/doc/snippets/circulargauge-background-range.qml new file mode 100644 index 00000000..f218e916 --- /dev/null +++ b/src/controls/doc/snippets/circulargauge-background-range.qml @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [range] +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 +import QtQuick.Extras.Private 1.0 + +Rectangle { + width: 300 + height: 300 + color: "#494d53" + + CircularGauge { + id: gauge + anchors.centerIn: parent + style: CircularGaugeStyle { + id: style + + //! [background] + function degreesToRadians(degrees) { + return degrees * (Math.PI / 180); + } + + + background: Canvas { + onPaint: { + var ctx = getContext("2d"); + ctx.reset(); + + ctx.beginPath(); + ctx.strokeStyle = "#e34c22"; + ctx.lineWidth = outerRadius * 0.02; + + ctx.arc(outerRadius, outerRadius, outerRadius - ctx.lineWidth / 2, + degreesToRadians(valueToAngle(80) - 90), degreesToRadians(valueToAngle(100) - 90)); + ctx.stroke(); + } + } + //! [background] + + //! [tickmark] + tickmark: Rectangle { + visible: styleData.value < 80 || styleData.value % 10 == 0 + implicitWidth: outerRadius * 0.02 + antialiasing: true + implicitHeight: outerRadius * 0.06 + color: styleData.value >= 80 ? "#e34c22" : "#e5e5e5" + } + //! [tickmark] + + //! [minorTickmark] + minorTickmark: Rectangle { + visible: styleData.value < 80 + implicitWidth: outerRadius * 0.01 + antialiasing: true + implicitHeight: outerRadius * 0.03 + color: "#e5e5e5" + } + //! [minorTickmark] + + //! [tickmarkLabel] + tickmarkLabel: Text { + font.pixelSize: Math.max(6, outerRadius * 0.1) + text: styleData.value + color: styleData.value >= 80 ? "#e34c22" : "#e5e5e5" + antialiasing: true + } + //! [tickmarkLabel] + + //! [needle] + needle: Rectangle { + y: outerRadius * 0.15 + implicitWidth: outerRadius * 0.03 + implicitHeight: outerRadius * 0.9 + antialiasing: true + color: "#e5e5e5" + } + //! [needle] + + //! [foreground] + foreground: Item { + Rectangle { + width: outerRadius * 0.2 + height: width + radius: width / 2 + color: "#e5e5e5" + anchors.centerIn: parent + } + } + //! [foreground] + } + } +} +//! [range] diff --git a/src/controls/doc/snippets/circulargauge-tickmark-indices-values.qml b/src/controls/doc/snippets/circulargauge-tickmark-indices-values.qml new file mode 100644 index 00000000..d3afd6f4 --- /dev/null +++ b/src/controls/doc/snippets/circulargauge-tickmark-indices-values.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [tickmarks] +import QtQuick 2.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 + +Rectangle { + width: 400 + height: 400 + + CircularGauge { + id: gauge + anchors.fill: parent + style: CircularGaugeStyle { + labelInset: outerRadius * 0.2 + + tickmarkLabel: null + + tickmark: Text { + text: styleData.value + + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.bottom + text: styleData.index + color: "blue" + } + } + + minorTickmark: Text { + text: styleData.value + font.pixelSize: 8 + + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.bottom + text: styleData.index + font.pixelSize: 8 + color: "blue" + } + } + } + + Text { + id: indexText + text: "Major and minor indices" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: valueText.top + color: "blue" + } + Text { + id: valueText + text: "Major and minor values" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + } + } +} +//! [tickmarks] diff --git a/src/controls/doc/snippets/styling-gauge.qml b/src/controls/doc/snippets/styling-gauge.qml new file mode 100644 index 00000000..e999832b --- /dev/null +++ b/src/controls/doc/snippets/styling-gauge.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [all] +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 + +Rectangle { + width: 100 + height: 220 + color: "#494d53" + + Gauge { + value: 50 + tickmarkStepSize: 20 + minorTickmarkCount: 1 + //! [font-size] + font.pixelSize: 15 + //! [font-size] + anchors.centerIn: parent + anchors.horizontalCenterOffset: -4 + + style: GaugeStyle { + //! [valueBar] + valueBar: Rectangle { + color: "#e34c22" + implicitWidth: 28 + } + //! [valueBar] + + //! [foreground] + foreground: null + //! [foreground] + + //! [tickmark] + tickmark: Item { + implicitWidth: 8 + implicitHeight: 4 + + Rectangle { + x: control.tickmarkAlignment === Qt.AlignLeft + || control.tickmarkAlignment === Qt.AlignTop ? parent.implicitWidth : -28 + width: 28 + height: parent.height + color: "#ffffff" + } + } + //! [tickmark] + + //! [minorTickmark] + minorTickmark: Item { + implicitWidth: 8 + implicitHeight: 2 + + Rectangle { + x: control.tickmarkAlignment === Qt.AlignLeft + || control.tickmarkAlignment === Qt.AlignTop ? parent.implicitWidth : -28 + width: 28 + height: parent.height + color: "#ffffff" + } + } + //! [minorTickmark] + } + } +} +//! [all] diff --git a/src/controls/doc/src/qtquickcontrolsstyles-index.qdoc b/src/controls/doc/src/qtquickcontrolsstyles-index.qdoc index d9c813e4..59951190 100644 --- a/src/controls/doc/src/qtquickcontrolsstyles-index.qdoc +++ b/src/controls/doc/src/qtquickcontrolsstyles-index.qdoc @@ -48,6 +48,11 @@ \internal */ +/*! + \group stylingtutorials + \title Styling Tutorials +*/ + /*! \page qtquickcontrolsstyles-index.html @@ -66,12 +71,48 @@ import QtQuick.Controls.Styles 1.4 \endcode + \section1 Styles + + \section2 Base Style + + The Base Style is the default style used when none is specified. It is also + used as a fallback when the specified style cannot be found. + + \image tumbler.png + \caption The Base Style Tumbler. + + \section2 Flat Style + + The Flat Style is designed for touch devices. + + \image tumbler-flat-style.png + \caption The Flat Style Tumbler. + + \section2 Selecting Styles + + You can apply a different style to the controls by setting the + \e QT_QUICK_CONTROLS_STYLE environment variable to the name of the style. + For example, to use the Flat style, you can do the following: + + \code + QT_QUICK_CONTROLS_STYLE=Flat ./app + \endcode + + This can also be done in C++, using qputenv(): + + \code + qputenv("QT_QUICK_CONTROLS_STYLE", "Flat"); + \endcode + \section1 Styling Views \annotatedlist viewsstyling \section1 Styling Controls \annotatedlist controlsstyling + \section1 Styling Tutorials + \annotatedlist stylingtutorials + \section1 Related information \list diff --git a/src/controls/doc/src/styling-circulargauge.qdoc b/src/controls/doc/src/styling-circulargauge.qdoc new file mode 100644 index 00000000..e0260db4 --- /dev/null +++ b/src/controls/doc/src/styling-circulargauge.qdoc @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page styling-circulargauge.html + \title Styling CircularGauge + \brief Tutorial for styling CircularGauge. + \ingroup stylingtutorials + + \target styling-circulargauge-needle + \section2 The Needle Component + + The \l {QtQuick.Controls.Styles::CircularGaugeStyle::}{needle} + component is rotated around the gauge to represent the current value. + + Starting from the default style, we'll add a very basic white needle: + + \snippet circulargauge-background-range.qml needle + + \image styling-circulargauge-needle-example.png + + As mentioned in the documentation for \l {QtQuick.Controls.Styles::} + {CircularGaugeStyle}, \c implicitWidth + and \c implicitHeight properties need to be set. This is so that the needle + can be positioned properly. We always scale items by the + \l {QtQuick.Controls.Styles::CircularGaugeStyle::}{outerRadius} + property of the style, ensuring the control resizes gracefully. + + We offset the needle vertically so that its back sits beyond the knob. + + \target styling-circulargauge-foreground + \section2 The Foreground Component + + We've now changed the needle, but the default knob is still there; let's + replace it. The \l {QtQuick.Controls.Styles::CircularGaugeStyle::} + {foreground} component defines the default knob, so we can specify our own by + overriding it (note that we could also set it to \c null if we didn't want a + foreground): + + \snippet circulargauge-background-range.qml foreground + + \image styling-circulargauge-knob-example.png + + Firstly, we create a circle from the Rectangle item by setting the radius to + be half the width (either width or height will work here; they are always + equal in this case). We make it a child of the Item, because the foreground + fills the gauge. We then center it within the Item. + + We set the color of the knob to the same white that we used before. + + \target styling-circulargauge-tickmarkLabel + \section2 The Tickmark Label Component + + Suppose we want to caution the user if the value displayed by the gauge goes + above or below a certain range. We could present this range to the user in + several ways: + + \list A + \li Change the color of the tickmarks depending on \c styleData.value + \li Add an image to the background + \li Draw it with \l {QtQuick::}{Canvas} + \endlist + + We'll choose options 1 and 3, as they are more flexible than using an + image. + + Firstly, let's change the color of the three highest tickmark labels: + + \snippet circulargauge-background-range.qml tickmarkLabel + + \image styling-circulargauge-tickmarkLabel-example.png tickmarkLabel + + We also change the color of the rest of the labels to the same white that + we used for the needle and knob. + + \target styling-circulargauge-tickmark + \section2 The Tickmark Component + + Now let's do the same for the three highest tickmarks: + + \snippet circulargauge-background-range.qml tickmark + + \image styling-circulargauge-tickmark-example.png tickmark + + \target styling-circulargauge-minorTickmark + \section2 The Minor Tickmark Component + + For the minor tickmarks, we'll only show those which are not higher than + \c 80: + + \snippet circulargauge-background-range.qml minorTickmark + + \image styling-circulargauge-minorTickmark-example.png minorTickmark + + This is because we'll draw something between that range in the next section. + + \target styling-circulargauge-background + \section2 The Background Component + + We'll display the range that indicates caution with an orange arc: + + \snippet circulargauge-background-range.qml background + + We define a function to convert degrees to radians, which are the + units used by \l {QtQuick::}{Canvas}. + + Next, we do the drawing of the range using Canvas. We draw an arc between + \c 80 and \c 100, using the + \l {QtQuick.Controls.Styles::CircularGaugeStyle::}{valueToAngle()} + function provided by CircularGaugeStyle. Note that we subtract \c 90 degrees + before converting to radians, as our origin is north and Canvas' is east. + + The finished product: + + \image styling-circulargauge-background-example.png background + + The complete code for this example is as follows: + + \snippet circulargauge-background-range.qml range +*/ + diff --git a/src/controls/doc/src/styling-gauge.qdoc b/src/controls/doc/src/styling-gauge.qdoc new file mode 100644 index 00000000..195cc4c8 --- /dev/null +++ b/src/controls/doc/src/styling-gauge.qdoc @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page styling-gauge.html + \title Styling Gauge + \brief Tutorial for styling the Gauge control. + \ingroup stylingtutorials + + As GaugeStyle's documentation adequately covers common use cases, this + tutorial will cover a different scenario: one where the gauge's tickmarks + cover the value bar, instead of being aligned to the left or right of it. + + \target styling-gauge-valueBar + \section2 The Value Bar Component + + The \l {QtQuick.Controls.Styles::GaugeStyle::}{valueBar} + component is resized according to the gauge's value; if the value is low, + the bar will be small, and vice versa. + + Starting from the default style, we'll change the color of the value bar to + orange, and increase its width slightly: + + \snippet styling-gauge.qml valueBar + + \image styling-gauge-valueBar.png + + As mentioned in the documentation for GaugeStyle, \c implicitWidth needs to + be set when defining your own value bar. + + \target styling-gauge-foreground + \section2 The Foreground Component + + The \l {QtQuick.Controls.Styles::GaugeStyle::}{foreground} + component covers the full width and height of the value bar, even when the + value bar is not at its highest. By default, the foreground component + provides a "sheen". We'll choose to discard this, and leave it empty + instead: + + \snippet styling-gauge.qml foreground + + \image styling-gauge-foreground.png + + \target styling-gauge-tickmark + \section2 The Tickmark Component + + The \l {QtQuick.Controls.Styles::GaugeStyle::}{tickmark} + component sits to the left or right of the value bar, depending on the + control's \l {Gauge::tickmarkAlignment}{tickmarkAlignment}. In order to + have the tickmarks cover the width of the value bar instead, we need to do + two things: + \list 1 + \li Remove the space the tickmarks previously assumed so that there is + just enough space for margins between the tickmarks and value bar. + \li Position the tickmarks according to the control's orientation and + tickmark alignment. + \endlist + + \snippet styling-gauge.qml tickmark + + In this case we chose \c 8 pixel margins, so we set the \c implicitWidth of + the tickmarks to that. + + We account for every possible orientation and tickmark alignment, something + that is not necessary if the gauge will only ever have one orientation and + alignment. For example, if the gauge will always be of a vertical + orientation and the tickmarks left-aligned, then it is enough to set the + \c x property of the \c Rectangle to the following: + + \code + x: parent.implicitWidth + \endcode + + The value bar is \c 28 pixels wide, so we give the same width to our + tickmarks so that they cover the width of it. + + \image styling-gauge-tickmark.png + + \target styling-gauge-minorTickmark + \section2 The Minor Tickmark Component + + The \l {QtQuick.Controls.Styles::GaugeStyle::}{minorTickmark} + component is almost identical to its larger counterpart, except that its + width does not affect the layout of the gauge's components. We'll do + similar adjustments to the ones in the previous section - the only + difference being the height: + + \snippet styling-gauge.qml minorTickmark + + \image styling-gauge-minorTickmark.png + + \target styling-gauge-font-size + \section2 Adjusting Font Size + + Finally, we increase the \l {Gauge::font}{font} size to \c 15 pixels: + + \snippet styling-gauge.qml font-size + \image styling-gauge-font-size.png + + \target styling-gauge-complete + \section2 Complete Source Code + + \snippet styling-gauge.qml all +*/ + |