GaugeStyle.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of the Qt Quick Extras module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and The Qt Company. For licensing terms
  14. ** and conditions see https://www.qt.io/terms-conditions. For further
  15. ** information use the contact form at https://www.qt.io/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 3 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 3 requirements
  23. ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 2.0 or (at your option) the GNU General
  28. ** Public license version 3 or any later version approved by the KDE Free
  29. ** Qt Foundation. The licenses are as published by the Free Software
  30. ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  31. ** included in the packaging of this file. Please review the following
  32. ** information to ensure the GNU General Public License requirements will
  33. ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  34. ** https://www.gnu.org/licenses/gpl-3.0.html.
  35. **
  36. ** $QT_END_LICENSE$
  37. **
  38. ****************************************************************************/
  39. import QtQuick 2.2
  40. import QtQuick.Controls 1.4
  41. import QtQuick.Controls.Styles 1.4
  42. import QtQuick.Controls.Private 1.0
  43. import QtQuick.Extras 1.4
  44. import QtQuick.Extras.Private 1.0
  45. /*!
  46. \qmltype GaugeStyle
  47. \inqmlmodule QtQuick.Controls.Styles
  48. \since 5.5
  49. \ingroup controlsstyling
  50. \brief Provides custom styling for Gauge.
  51. You can create a custom gauge by replacing the following delegates:
  52. \list
  53. \li \l background
  54. \li valueBar
  55. \li tickmarkLabel
  56. \endlist
  57. Below, you'll find an example of how to create a temperature gauge that
  58. changes color as its value increases:
  59. \code
  60. import QtQuick 2.2
  61. import QtQuick.Controls 1.4
  62. import QtQuick.Controls.Styles 1.4
  63. import QtQuick.Extras 1.4
  64. Rectangle {
  65. width: 80
  66. height: 200
  67. Timer {
  68. running: true
  69. repeat: true
  70. interval: 2000
  71. onTriggered: gauge.value = gauge.value == gauge.maximumValue ? 5 : gauge.maximumValue
  72. }
  73. Gauge {
  74. id: gauge
  75. anchors.fill: parent
  76. anchors.margins: 10
  77. value: 5
  78. Behavior on value {
  79. NumberAnimation {
  80. duration: 1000
  81. }
  82. }
  83. style: GaugeStyle {
  84. valueBar: Rectangle {
  85. implicitWidth: 16
  86. color: Qt.rgba(gauge.value / gauge.maximumValue, 0, 1 - gauge.value / gauge.maximumValue, 1)
  87. }
  88. }
  89. }
  90. }
  91. \endcode
  92. \image gauge-temperature.png
  93. The gauge displaying values at various points during the animation.
  94. \sa {Styling Gauge}
  95. */
  96. Style {
  97. id: gaugeStyle
  98. /*!
  99. The \l Gauge that this style is attached to.
  100. */
  101. readonly property Gauge control: __control
  102. /*!
  103. This property holds the value displayed by the gauge as a position in
  104. pixels.
  105. It is useful for custom styling.
  106. */
  107. readonly property real valuePosition: control.__panel.valuePosition
  108. /*!
  109. The background of the gauge, displayed behind the \l valueBar.
  110. By default, no background is defined.
  111. */
  112. property Component background
  113. /*!
  114. Each tickmark displayed by the gauge.
  115. To set the size of the tickmarks, specify an
  116. \l {Item::implicitWidth}{implicitWidth} and
  117. \l {Item::implicitHeight}{implicitHeight}.
  118. The widest tickmark will determine the space set aside for all
  119. tickmarks. For this reason, the \c implicitWidth of each tickmark
  120. should be greater than or equal to that of each minor tickmark. If you
  121. need minor tickmarks to have greater widths than the major tickmarks,
  122. set the larger width in a child item of the \l minorTickmark component.
  123. For layouting reasons, each tickmark should have the same
  124. \c implicitHeight. If different heights are needed for individual
  125. tickmarks, specify those heights in a child item of the component.
  126. In the example below, we decrease the height of the tickmarks:
  127. \code
  128. tickmark: Item {
  129. implicitWidth: 18
  130. implicitHeight: 1
  131. Rectangle {
  132. color: "#c8c8c8"
  133. anchors.fill: parent
  134. anchors.leftMargin: 3
  135. anchors.rightMargin: 3
  136. }
  137. }
  138. \endcode
  139. \image gauge-tickmark-example.png Gauge tickmark example
  140. Each instance of this component has access to the following properties:
  141. \table
  142. \row \li \c {readonly property int} \b styleData.index
  143. \li The index of this tickmark.
  144. \row \li \c {readonly property real} \b styleData.value
  145. \li The value that this tickmark represents.
  146. \row \li \c {readonly property real} \b styleData.valuePosition
  147. \li The value that this tickmark represents as a position in
  148. pixels, with 0 being at the bottom of the gauge.
  149. \endtable
  150. \sa minorTickmark
  151. */
  152. property Component tickmark: Item {
  153. implicitWidth: Math.round(TextSingleton.height * 1.1)
  154. implicitHeight: Math.max(2, Math.round(TextSingleton.height * 0.1))
  155. Rectangle {
  156. color: "#c8c8c8"
  157. anchors.fill: parent
  158. anchors.leftMargin: Math.round(TextSingleton.implicitHeight * 0.2)
  159. anchors.rightMargin: Math.round(TextSingleton.implicitHeight * 0.2)
  160. }
  161. }
  162. /*!
  163. Each minor tickmark displayed by the gauge.
  164. To set the size of the minor tickmarks, specify an
  165. \l {Item::implicitWidth}{implicitWidth} and
  166. \l {Item::implicitHeight}{implicitHeight}.
  167. For layouting reasons, each minor tickmark should have the same
  168. \c implicitHeight. If different heights are needed for individual
  169. tickmarks, specify those heights in a child item of the component.
  170. In the example below, we decrease the width of the minor tickmarks:
  171. \code
  172. minorTickmark: Item {
  173. implicitWidth: 8
  174. implicitHeight: 1
  175. Rectangle {
  176. color: "#cccccc"
  177. anchors.fill: parent
  178. anchors.leftMargin: 2
  179. anchors.rightMargin: 4
  180. }
  181. }
  182. \endcode
  183. \image gauge-minorTickmark-example.png Gauge minorTickmark example
  184. Each instance of this component has access to the following property:
  185. \table
  186. \row \li \c {readonly property int} \b styleData.index
  187. \li The index of this minor tickmark.
  188. \row \li \c {readonly property real} \b styleData.value
  189. \li The value that this minor tickmark represents.
  190. \row \li \c {readonly property real} \b styleData.valuePosition
  191. \li The value that this minor tickmark represents as a
  192. position in pixels, with 0 being at the bottom of the
  193. gauge.
  194. \endtable
  195. \sa tickmark
  196. */
  197. property Component minorTickmark: Item {
  198. implicitWidth: Math.round(TextSingleton.implicitHeight * 0.65)
  199. implicitHeight: Math.max(1, Math.round(TextSingleton.implicitHeight * 0.05))
  200. Rectangle {
  201. color: "#c8c8c8"
  202. anchors.fill: parent
  203. anchors.leftMargin: control.__tickmarkAlignment === Qt.AlignBottom || control.__tickmarkAlignment === Qt.AlignRight
  204. ? Math.max(3, Math.round(TextSingleton.implicitHeight * 0.2))
  205. : 0
  206. anchors.rightMargin: control.__tickmarkAlignment === Qt.AlignBottom || control.__tickmarkAlignment === Qt.AlignRight
  207. ? 0
  208. : Math.max(3, Math.round(TextSingleton.implicitHeight * 0.2))
  209. }
  210. }
  211. /*!
  212. This defines the text of each tickmark label on the gauge.
  213. Each instance of this component has access to the following properties:
  214. \table
  215. \row \li \c {readonly property int} \b styleData.index
  216. \li The index of this label.
  217. \row \li \c {readonly property real} \b styleData.value
  218. \li The value that this label represents.
  219. \endtable
  220. */
  221. property Component tickmarkLabel: Text {
  222. text: control.formatValue(styleData.value)
  223. font: control.font
  224. color: "#c8c8c8"
  225. antialiasing: true
  226. }
  227. /*!
  228. The bar that represents the value of the gauge.
  229. To height of the value bar is automatically resized according to
  230. \l {Gauge::value}{value}, and does not need to be specified.
  231. When a custom valueBar is defined, its
  232. \l {Item::implicitWidth}{implicitWidth} property must be set.
  233. */
  234. property Component valueBar: Rectangle {
  235. color: "#00bbff"
  236. implicitWidth: TextSingleton.implicitHeight
  237. }
  238. /*!
  239. The bar that represents the foreground of the gauge.
  240. This component is drawn above every other component.
  241. */
  242. property Component foreground: Canvas {
  243. readonly property real xCenter: width / 2
  244. readonly property real yCenter: height / 2
  245. property real shineLength: height * 0.95
  246. onPaint: {
  247. var ctx = getContext("2d");
  248. ctx.reset();
  249. ctx.beginPath();
  250. ctx.rect(0, 0, width, height);
  251. var gradient = ctx.createLinearGradient(0, yCenter, width, yCenter);
  252. gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0.08));
  253. gradient.addColorStop(1, Qt.rgba(1, 1, 1, 0.20));
  254. ctx.fillStyle = gradient;
  255. ctx.fill();
  256. }
  257. }
  258. /*! \internal */
  259. property Component panel: Item {
  260. id: panelComponent
  261. implicitWidth: control.orientation === Qt.Vertical ? tickmarkLabelBoundsWidth + rawBarWidth : TextSingleton.height * 14
  262. implicitHeight: control.orientation === Qt.Vertical ? TextSingleton.height * 14 : tickmarkLabelBoundsWidth + rawBarWidth
  263. readonly property int tickmarkCount: (control.maximumValue - control.minimumValue) / control.tickmarkStepSize + 1
  264. readonly property real tickmarkSpacing: (tickmarkLabelBounds.height - tickmarkWidth * tickmarkCount) / (tickmarkCount - 1)
  265. property real tickmarkLength: tickmarkColumn.width
  266. // Can't deduce this from the column, so we set it from within the first tickmark delegate loader.
  267. property real tickmarkWidth: 2
  268. readonly property real tickmarkOffset: control.orientation === Qt.Vertical ? control.__hiddenText.height / 2 : control.__hiddenText.width / 2
  269. readonly property real minorTickmarkStep: control.tickmarkStepSize / (control.minorTickmarkCount + 1);
  270. /*!
  271. Returns the marker text that should be displayed based on
  272. \a markerPos (\c 0 to \c 1.0).
  273. */
  274. function markerTextFromPos(markerPos) {
  275. return markerPos * (control.maximumValue - control.minimumValue) + control.minimumValue;
  276. }
  277. readonly property real rawBarWidth: valueBarLoader.item.implicitWidth
  278. readonly property real barLength: (control.orientation === Qt.Vertical ? control.height : control.width) - (tickmarkOffset * 2 - 2)
  279. readonly property real tickmarkLabelBoundsWidth: tickmarkLength + (control.orientation === Qt.Vertical ? control.__hiddenText.width : control.__hiddenText.height)
  280. readonly property int valuePosition: valueBarLoader.height
  281. Item {
  282. id: container
  283. width: control.orientation === Qt.Vertical ? parent.width : parent.height
  284. height: control.orientation === Qt.Vertical ? parent.height : parent.width
  285. rotation: control.orientation === Qt.Horizontal ? 90 : 0
  286. transformOrigin: Item.Center
  287. anchors.centerIn: parent
  288. Item {
  289. id: valueBarItem
  290. x: control.__tickmarkAlignment === Qt.AlignLeft || control.__tickmarkAlignment === Qt.AlignTop ? tickmarkLabelBounds.x + tickmarkLabelBounds.width : 0
  291. width: rawBarWidth
  292. height: barLength
  293. anchors.verticalCenter: parent.verticalCenter
  294. Loader {
  295. id: backgroundLoader
  296. sourceComponent: background
  297. anchors.fill: parent
  298. }
  299. Loader {
  300. id: valueBarLoader
  301. sourceComponent: valueBar
  302. readonly property real valueAsPercentage: (control.value - control.minimumValue) / (control.maximumValue - control.minimumValue)
  303. y: Math.round(parent.height - height)
  304. height: Math.round(valueAsPercentage * parent.height)
  305. }
  306. }
  307. Item {
  308. id: tickmarkLabelBounds
  309. x: control.__tickmarkAlignment === Qt.AlignLeft || control.__tickmarkAlignment === Qt.AlignTop ? 0 : valueBarItem.width
  310. width: tickmarkLabelBoundsWidth
  311. height: barLength
  312. anchors.verticalCenter: parent.verticalCenter
  313. // We want our items to be laid out from bottom to top, but Column can't do that, so we flip
  314. // the whole item containing the tickmarks and labels vertically. Then, we flip each tickmark
  315. // and label back again.
  316. transform: Rotation {
  317. axis.x: 1
  318. axis.y: 0
  319. axis.z: 0
  320. origin.x: tickmarkLabelBounds.width / 2
  321. origin.y: tickmarkLabelBounds.height / 2
  322. angle: 180
  323. }
  324. Column {
  325. id: tickmarkColumn
  326. x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom ? 0 : tickmarkLabelBounds.width - width
  327. spacing: tickmarkSpacing
  328. anchors.verticalCenter: parent.verticalCenter
  329. Repeater {
  330. id: tickmarkRepeater
  331. model: tickmarkCount
  332. delegate: Loader {
  333. id: tickmarkDelegateLoader
  334. sourceComponent: gaugeStyle.tickmark
  335. transform: Rotation {
  336. axis.x: 1
  337. axis.y: 0
  338. axis.z: 0
  339. origin.x: tickmarkDelegateLoader.width / 2
  340. origin.y: tickmarkDelegateLoader.height / 2
  341. angle: 180
  342. }
  343. onHeightChanged: {
  344. if (index == 0)
  345. tickmarkWidth = height;
  346. }
  347. readonly property int __index: index
  348. property QtObject styleData: QtObject {
  349. readonly property alias index: tickmarkDelegateLoader.__index
  350. readonly property real value: (index / (tickmarkCount - 1)) * (control.maximumValue - control.minimumValue) + control.minimumValue
  351. readonly property int valuePosition: Math.round(tickmarkDelegateLoader.y)
  352. }
  353. }
  354. }
  355. }
  356. // Doesn't need to be in a column, since we assume that the major tickmarks will always be longer than us.
  357. Repeater {
  358. id: minorTickmarkRepeater
  359. model: (tickmarkCount - 1) * control.minorTickmarkCount
  360. delegate: Loader {
  361. id: minorTickmarkDelegateLoader
  362. x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom ? 0 : tickmarkLabelBounds.width - width
  363. y: {
  364. var tickmarkWidthOffset = Math.floor(index / control.minorTickmarkCount) * tickmarkWidth + tickmarkWidth;
  365. var relativePosition = (index % control.minorTickmarkCount + 1) * (tickmarkSpacing / (control.minorTickmarkCount + 1));
  366. var clusterOffset = Math.floor(index / control.minorTickmarkCount) * tickmarkSpacing;
  367. // We assume that each minorTickmark's height is the same.
  368. return clusterOffset + tickmarkWidthOffset + relativePosition - height / 2;
  369. }
  370. transform: Rotation {
  371. axis.x: 1
  372. axis.y: 0
  373. axis.z: 0
  374. origin.x: minorTickmarkDelegateLoader.width / 2
  375. origin.y: minorTickmarkDelegateLoader.height / 2
  376. angle: 180
  377. }
  378. sourceComponent: gaugeStyle.minorTickmark
  379. readonly property int __index: index
  380. property QtObject styleData: QtObject {
  381. readonly property alias index: minorTickmarkDelegateLoader.__index
  382. readonly property real value: {
  383. var tickmarkIndex = Math.floor(index / control.minorTickmarkCount);
  384. return index * minorTickmarkStep + minorTickmarkStep * tickmarkIndex + minorTickmarkStep + control.minimumValue;
  385. }
  386. readonly property int valuePosition: Math.round(minorTickmarkDelegateLoader.y)
  387. }
  388. }
  389. }
  390. Item {
  391. id: tickmarkLabelItem
  392. x: control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom
  393. ? tickmarkLength
  394. : tickmarkLabelBounds.width - tickmarkLength - width
  395. width: control.__hiddenText.width
  396. // Use the bar height instead of the container's, as the labels seem to be translated by 1 when we
  397. // flip the control vertically, and this fixes that.
  398. height: parent.height
  399. anchors.verticalCenter: parent.verticalCenter
  400. Repeater {
  401. id: tickmarkTextRepeater
  402. model: tickmarkCount
  403. delegate: Item {
  404. x: {
  405. if (control.orientation === Qt.Vertical)
  406. return 0;
  407. // Align the text to the edge of the tickmarks.
  408. return ((width - height) / 2) * (control.__tickmarkAlignment === Qt.AlignBottom ? -1 : 1);
  409. }
  410. y: index * labelDistance - height / 2
  411. width: control.__hiddenText.width
  412. height: control.__hiddenText.height
  413. transformOrigin: Item.Center
  414. rotation: control.orientation === Qt.Vertical ? 0 : 90
  415. readonly property real labelDistance: tickmarkLabelBounds.height / (tickmarkCount - 1)
  416. Loader {
  417. id: tickmarkTextRepeaterDelegate
  418. x: {
  419. if (control.orientation === Qt.Horizontal) {
  420. return parent.width / 2 - width / 2;
  421. }
  422. return control.__tickmarkAlignment === Qt.AlignRight || control.__tickmarkAlignment === Qt.AlignBottom
  423. ? 0
  424. : parent.width - width;
  425. }
  426. transform: Rotation {
  427. axis.x: 1
  428. axis.y: 0
  429. axis.z: 0
  430. origin.x: tickmarkTextRepeaterDelegate.width / 2
  431. origin.y: tickmarkTextRepeaterDelegate.height / 2
  432. angle: 180
  433. }
  434. sourceComponent: tickmarkLabel
  435. readonly property int __index: index
  436. property QtObject styleData: QtObject {
  437. readonly property alias index: tickmarkTextRepeaterDelegate.__index
  438. readonly property real value: markerTextFromPos(index / (tickmarkTextRepeater.count - 1))
  439. }
  440. }
  441. }
  442. }
  443. }
  444. }
  445. Loader {
  446. id: foregroundLoader
  447. sourceComponent: foreground
  448. anchors.fill: valueBarItem
  449. }
  450. }
  451. }
  452. }